From 544fef0d3e020a09843387fcd949cdec6e773755 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Sat, 20 Apr 2024 16:15:45 +0200
Subject: [PATCH] chore: move from devenv to flake.nix #53

---
 Taskfile.yml => .back/Taskfile.yml           |   0
 .back/scripts/hello.nix                      |   5 +
 .envrc                                       |   5 +-
 .gitignore                                   |   1 +
 .gitlab-ci.yml                               |   2 +-
 .golangci.yaml                               |   9 +
 .idea/.gitignore                             |  10 -
 .idea/aws.xml                                |  17 -
 .idea/go.imports.xml                         |   6 -
 .idea/inspectionProfiles/Project_Default.xml |   7 +
 .idea/job-queues.iml                         |   5 +-
 .idea/markdown.xml                           |   9 -
 .idea/misc.xml                               |  16 +-
 .idea/workspace.xml                          | 211 +++++
 README.md                                    |  54 +-
 database-6_test.go                           |   9 +-
 database.go                                  |   4 +-
 database_test.go                             |   1 +
 devenv.lock                                  | 190 -----
 devenv.nix                                   | 765 -------------------
 devenv.yaml                                  |   7 -
 errors.go                                    |   5 +
 event-bus_test.go                            |   8 +-
 flake.lock                                   | 126 +++
 flake.nix                                    |  93 +++
 go.mod                                       |   3 +-
 go.sum                                       |  17 +-
 job-generic.go                               |   2 +-
 job-syncer.go                                |   4 +-
 job.go                                       |   2 +-
 logger.go                                    |   2 +-
 manager.go                                   |  80 +-
 nix/scripts/build-cover-report.nix           |  45 ++
 nix/scripts/check-code.nix                   |  51 ++
 nix/scripts/common.nix                       | 144 ++++
 nix/scripts/go-task.nix                      |  43 ++
 nix/scripts/run-ci-tests.nix                 |  38 +
 nix/scripts/run-extended-tests.nix           |  40 +
 nix/scripts/run-tests.nix                    |  66 ++
 queue.go                                     |  10 +-
 runnable-fileoperation.go                    |   2 +-
 runnable-mail_test.go                        |   3 +-
 runnable-sftp_test.go                        |   5 +-
 schedule-time.go                             |  14 +-
 schedule-time_test.go                        |  39 +
 scheduler-inotify.go                         |   2 +-
 stat.go                                      |  10 +-
 worker.go                                    |   4 +-
 48 files changed, 1104 insertions(+), 1087 deletions(-)
 rename Taskfile.yml => .back/Taskfile.yml (100%)
 create mode 100644 .back/scripts/hello.nix
 create mode 100644 .golangci.yaml
 delete mode 100644 .idea/.gitignore
 delete mode 100644 .idea/aws.xml
 delete mode 100644 .idea/go.imports.xml
 create mode 100644 .idea/inspectionProfiles/Project_Default.xml
 delete mode 100644 .idea/markdown.xml
 create mode 100644 .idea/workspace.xml
 delete mode 100644 devenv.lock
 delete mode 100644 devenv.nix
 delete mode 100644 devenv.yaml
 create mode 100644 flake.lock
 create mode 100644 flake.nix
 create mode 100644 nix/scripts/build-cover-report.nix
 create mode 100644 nix/scripts/check-code.nix
 create mode 100644 nix/scripts/common.nix
 create mode 100644 nix/scripts/go-task.nix
 create mode 100644 nix/scripts/run-ci-tests.nix
 create mode 100644 nix/scripts/run-extended-tests.nix
 create mode 100644 nix/scripts/run-tests.nix
 create mode 100644 schedule-time_test.go

diff --git a/Taskfile.yml b/.back/Taskfile.yml
similarity index 100%
rename from Taskfile.yml
rename to .back/Taskfile.yml
diff --git a/.back/scripts/hello.nix b/.back/scripts/hello.nix
new file mode 100644
index 0000000..44bc156
--- /dev/null
+++ b/.back/scripts/hello.nix
@@ -0,0 +1,5 @@
+{ pkgs }:
+
+pkgs.writeShellScriptBin "hello-world" ''
+  echo "Hello, world!"
+''
diff --git a/.envrc b/.envrc
index 6de8a8a..80ce713 100644
--- a/.envrc
+++ b/.envrc
@@ -1,3 +1,2 @@
-source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0="
-
-use devenv
\ No newline at end of file
+watch_file $(find . -name "*.nix" -printf '"%p" ')
+use flake
diff --git a/.gitignore b/.gitignore
index 35183b1..b2e5998 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ smell.go
 /.attach_*
 .direnv/
 .direnv/
+.back
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 35965c4..7da94d7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -37,7 +37,7 @@ test:
   tags:
     - nixos
   script:
-    - devenv shell test-lib
+    - nix develop --command run-ci-tests
 
   cache:
     - key: nixos
diff --git a/.golangci.yaml b/.golangci.yaml
new file mode 100644
index 0000000..d876c9e
--- /dev/null
+++ b/.golangci.yaml
@@ -0,0 +1,9 @@
+run:
+  concurrency: 4
+  timeout: 5m
+  issues-exit-code: 2
+  tests: false
+  
+output:  
+  show-stats: true
+  
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index a9d7db9..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
-# GitHub Copilot persisted chat sessions
-/copilot/chatSessions
diff --git a/.idea/aws.xml b/.idea/aws.xml
deleted file mode 100644
index ec328d0..0000000
--- a/.idea/aws.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="accountSettings">
-    <option name="activeProfile" value="profile:default" />
-    <option name="activeRegion" value="eu-west-1" />
-    <option name="recentlyUsedProfiles">
-      <list>
-        <option value="profile:default" />
-      </list>
-    </option>
-    <option name="recentlyUsedRegions">
-      <list>
-        <option value="eu-west-1" />
-      </list>
-    </option>
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml
deleted file mode 100644
index be62eaf..0000000
--- a/.idea/go.imports.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="GoImports">
-    <option name="optimizeImportsOnTheFly" value="false" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9c69411
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/job-queues.iml b/.idea/job-queues.iml
index 9b4b749..25ed3f6 100644
--- a/.idea/job-queues.iml
+++ b/.idea/job-queues.iml
@@ -3,10 +3,7 @@
   <component name="Go" enabled="true" />
   <component name="NewModuleRootManager" inherit-compiler-output="true">
     <exclude-output />
-    <content url="file://$MODULE_DIR$">
-      <excludeFolder url="file://$MODULE_DIR$/.devenv" />
-      <excludeFolder url="file://$MODULE_DIR$/.direnv" />
-    </content>
+    <content url="file://$MODULE_DIR$" />
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
deleted file mode 100644
index ec0b30f..0000000
--- a/.idea/markdown.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="MarkdownSettings">
-    <enabledExtensions>
-      <entry key="MermaidLanguageExtension" value="false" />
-      <entry key="PlantUMLLanguageExtension" value="true" />
-    </enabledExtensions>
-  </component>
-</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 639900d..7aa94ca 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="ProjectRootManager">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/out" />
   </component>
+  <component name="accountSettings">
+    <option name="activeProfile" value="profile:default" />
+    <option name="activeRegion" value="eu-west-1" />
+    <option name="recentlyUsedProfiles">
+      <list>
+        <option value="profile:default" />
+      </list>
+    </option>
+    <option name="recentlyUsedRegions">
+      <list>
+        <option value="eu-west-1" />
+      </list>
+    </option>
+  </component>
 </project>
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..1250234
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="9979eb22-471e-4f2f-b624-fd3edb5e8c6e" name="Changes" comment="">
+      <change afterPath="$PROJECT_DIR$/.back/scripts/hello.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/.golangci.yaml" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/flake.lock" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/flake.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/build-cover-report.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/check-code.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/common.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/go-task.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/run-ci-tests.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/run-extended-tests.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/nix/scripts/run-tests.nix" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/schedule-time_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.envrc" beforeDir="false" afterPath="$PROJECT_DIR$/.envrc" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.gitlab-ci.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.gitlab-ci.yml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/aws.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/go.imports.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/job-queues.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/job-queues.iml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/markdown.xml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/Taskfile.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.back/Taskfile.yml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/database-6_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database-6_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/database.go" beforeDir="false" afterPath="$PROJECT_DIR$/database.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/database_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/database_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/devenv.lock" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/devenv.nix" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/devenv.yaml" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/errors.go" beforeDir="false" afterPath="$PROJECT_DIR$/errors.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/event-bus_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/event-bus_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/job-generic.go" beforeDir="false" afterPath="$PROJECT_DIR$/job-generic.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/job-syncer.go" beforeDir="false" afterPath="$PROJECT_DIR$/job-syncer.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/job.go" beforeDir="false" afterPath="$PROJECT_DIR$/job.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/logger.go" beforeDir="false" afterPath="$PROJECT_DIR$/logger.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/manager.go" beforeDir="false" afterPath="$PROJECT_DIR$/manager.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/queue.go" beforeDir="false" afterPath="$PROJECT_DIR$/queue.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/runnable-fileoperation.go" beforeDir="false" afterPath="$PROJECT_DIR$/runnable-fileoperation.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/runnable-mail_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/runnable-mail_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/runnable-sftp_test.go" beforeDir="false" afterPath="$PROJECT_DIR$/runnable-sftp_test.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/schedule-time.go" beforeDir="false" afterPath="$PROJECT_DIR$/schedule-time.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/scheduler-inotify.go" beforeDir="false" afterPath="$PROJECT_DIR$/scheduler-inotify.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/stat.go" beforeDir="false" afterPath="$PROJECT_DIR$/stat.go" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/worker.go" beforeDir="false" afterPath="$PROJECT_DIR$/worker.go" afterDir="false" />
+    </list>
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="ComposerSettings">
+    <execution />
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="Go File" />
+      </list>
+    </option>
+  </component>
+  <component name="GOROOT" url="file:///nix/store/fqzds43dcw7164rm1h51n54l2ipxi1qj-go-1.21.9/share/go" />
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="PhpWorkspaceProjectConfiguration" interpreter_name="PHP 8.2" />
+  <component name="ProjectColorInfo"><![CDATA[{
+  "customColor": "",
+  "associatedIndex": 8
+}]]></component>
+  <component name="ProjectId" id="2fGcSxoYpsPbNVbRh8sUxQtBiHv" />
+  <component name="ProjectViewState">
+    <option name="flattenModules" value="true" />
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+    <option name="showVisibilityIcons" value="true" />
+    <option name="sortByType" value="true" />
+    <option name="sortKey" value="BY_TYPE" />
+  </component>
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "DefaultGoTemplateProperty": "Go File",
+    "Go Test.TestScheduleJob in gitlab.schukai.com/oss/libraries/go/services/job-queues.executor": "Debug",
+    "Go Test.TestStructure in gitlab.schukai.com/oss/libraries/go/services/job-queues.executor": "Debug",
+    "Go Test.TestTimeFunctionSame in gitlab.schukai.com/oss/libraries/go/services/job-queues.executor": "Debug",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "RunOnceActivity.go.formatter.settings.were.checked": "true",
+    "RunOnceActivity.go.migrated.go.modules.settings": "true",
+    "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+    "git-widget-placeholder": "master",
+    "go.import.settings.migrated": "true",
+    "go.sdk.automatically.set": "true",
+    "last_opened_file_path": "/home/vs/workspaces/oss/go-libs/job-queues/nix/scripts",
+    "node.js.detected.package.eslint": "true",
+    "node.js.detected.package.tslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "node.js.selected.package.tslint": "(autodetect)",
+    "nodejs_package_manager_path": "npm",
+    "settings.editor.selected.configurable": "http.proxy",
+    "vue.rearranger.settings.migration": "true"
+  }
+}]]></component>
+  <component name="RecentsManager">
+    <key name="CopyFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/nix/scripts" />
+      <recent name="$PROJECT_DIR$/system/scripts" />
+    </key>
+    <key name="MoveFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/.back" />
+    </key>
+  </component>
+  <component name="RunManager" selected="Go Test.TestStructure in gitlab.schukai.com/oss/libraries/go/services/job-queues">
+    <configuration default="true" type="GoApplicationRunConfiguration" factoryName="Go Application">
+      <module name="job-queues" />
+      <working_directory value="$PROJECT_DIR$" />
+      <go_parameters value="-i" />
+      <kind />
+      <directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <method v="2" />
+    </configuration>
+    <configuration name="TestScheduleJob in gitlab.schukai.com/oss/libraries/go/services/job-queues" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
+      <module name="job-queues" />
+      <working_directory value="$PROJECT_DIR$" />
+      <kind value="PACKAGE" />
+      <package value="gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+      <directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <framework value="gotest" />
+      <pattern value="^\QTestScheduleJob\E$" />
+      <method v="2" />
+    </configuration>
+    <configuration name="TestStructure in gitlab.schukai.com/oss/libraries/go/services/job-queues" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
+      <module name="job-queues" />
+      <working_directory value="$PROJECT_DIR$" />
+      <kind value="PACKAGE" />
+      <package value="gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+      <directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <framework value="gotest" />
+      <pattern value="^\QTestStructure\E$" />
+      <method v="2" />
+    </configuration>
+    <configuration name="TestTimeFunctionSame in gitlab.schukai.com/oss/libraries/go/services/job-queues" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
+      <module name="job-queues" />
+      <working_directory value="$PROJECT_DIR$" />
+      <kind value="PACKAGE" />
+      <package value="gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+      <directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <framework value="gotest" />
+      <pattern value="^\QTestTimeFunctionSame\E$" />
+      <method v="2" />
+    </configuration>
+    <configuration default="true" type="GoTestRunConfiguration" factoryName="Go Test">
+      <module name="job-queues" />
+      <working_directory value="$PROJECT_DIR$" />
+      <go_parameters value="-i" />
+      <kind />
+      <directory value="$PROJECT_DIR$" />
+      <filePath value="$PROJECT_DIR$" />
+      <framework value="gotest" />
+      <method v="2" />
+    </configuration>
+    <recent_temporary>
+      <list>
+        <item itemvalue="Go Test.TestStructure in gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+        <item itemvalue="Go Test.TestScheduleJob in gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+        <item itemvalue="Go Test.TestTimeFunctionSame in gitlab.schukai.com/oss/libraries/go/services/job-queues" />
+      </list>
+    </recent_temporary>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+  <component name="VgoProject">
+    <settings-migrated>true</settings-migrated>
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager>
+      <breakpoints>
+        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
+          <url>file://$PROJECT_DIR$/schedule-time.go</url>
+          <line>61</line>
+          <option name="timeStamp" value="7" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
+          <url>file://$PROJECT_DIR$/schedule-time.go</url>
+          <line>53</line>
+          <option name="timeStamp" value="8" />
+        </line-breakpoint>
+        <line-breakpoint enabled="true" type="DlvLineBreakpoint">
+          <url>file://$PROJECT_DIR$/schedule-time.go</url>
+          <line>51</line>
+          <option name="timeStamp" value="9" />
+        </line-breakpoint>
+      </breakpoints>
+    </breakpoint-manager>
+  </component>
+</project>
\ No newline at end of file
diff --git a/README.md b/README.md
index 6751e01..0b5b9c8 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@
 
 ## Overview
 
-These `jobQueue` library written in Go provides a simple interface for managing jobs and workers. 
-It is designed to be used in a distributed environment where multiple workers can be assigned to a job 
-queue. The library provides a `Manager` that can be used to manage workers, schedule jobs, and handle 
+These `jobQueue` library written in Go provides a simple interface for managing jobs and workers.
+It is designed to be used in a distributed environment where multiple workers can be assigned to a job
+queue. The library provides a `Manager` that can be used to manage workers, schedule jobs, and handle
 the state of the job queue.
 
-Jobs can also be persisted to a database using Gorm. 
+Jobs can also be persisted to a database using Gorm.
 
 The library also provides a `Cron` instance for scheduling jobs.
 
@@ -15,7 +15,7 @@ The library also provides a `Cron` instance for scheduling jobs.
 
 ### Prerequisites
 
--   Go 1.22+
+- Go 1.22+
 
 ### Installation
 
@@ -32,43 +32,39 @@ import "gitlab.schukai.com/oss/libraries/go/services/job-queues"
 
 
 func main() {
-    var err error
-    
-    m := jobqueue.NewManager()
-    w := jobqueue.NewWorker("worker1")
-    _ = m.AddWorker(w)
-    
-    _ = m.Start()
-    
-    job := jobqueue.Job{
-        ID: "job1",
-    }
-    scheduler := jobqueue.InstantScheduler{}
-    
-    _ = m.ScheduleJob(job, &scheduler)
-    
-}
-
-```
+var err error
 
+m := jobqueue.NewManager()
+w := jobqueue.NewWorker("worker1")
+_ = m.AddWorker(w)
 
+_ = m.Start()
 
+job := jobqueue.Job{
+ID: "job1",
+}
+scheduler := jobqueue.InstantScheduler{}
 
-## Tests
+_ = m.ScheduleJob(job, &scheduler)
 
-Run tests using:
+}
 
-```bash
-task test
 ```
 
+## Development
+
+| Command            | Description                          |
+|--------------------|--------------------------------------|
+| ` task check-code` | Run linters and static code analysis |
+| ` task run-tests`  | Run tests                            |
+
 ## License
 
 This project is licensed under the AGPL-3.0 License - see the [LICENSE.md](LICENSE.md) file for details.
 
 ## Contact
 
--   schukai GmbH - [schukai.de](https://www.schukai.com)
--   [info@schukai.com](mailto:info@schukai.com)
+- schukai GmbH - [schukai.de](https://www.schukai.com)
+- [info@schukai.com](mailto:info@schukai.com)
 
 
diff --git a/database-6_test.go b/database-6_test.go
index 0e72761..731920f 100644
--- a/database-6_test.go
+++ b/database-6_test.go
@@ -7,7 +7,7 @@ package jobqueue
 import (
 	"fmt"
 	"github.com/stretchr/testify/assert"
-	"gorm.io/driver/mysql"
+	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
 	"testing"
 	"time"
@@ -20,8 +20,11 @@ func TestWriteToDB6(t *testing.T) {
 	// docker run --name mysql-test -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYSQL_DATABASE=testdb -p 3306:3306 -d mysql:latest && \
 	// docker logs -f  mysql-test
 
-	dsn := "root:my-secret-pw@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
-	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
+	//dsn := "root:my-secret-pw@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
+	//db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
+
+	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
+	assert.NoError(t, err)
 
 	manager := NewManager()
 	manager.SetDB(db)
diff --git a/database.go b/database.go
index 170cc06..8e09113 100644
--- a/database.go
+++ b/database.go
@@ -153,7 +153,7 @@ func update(job *JobPersistence, db *gorm.DB) error {
 			}
 		}
 
-		for i, _ := range job.Logs {
+		for i := range job.Logs {
 			job.Logs[i].LogID = 0
 			_ = tx.Create(&job.Logs[i])
 			// no error handling, if it fails, it fails
@@ -206,7 +206,7 @@ func save(job *JobPersistence, db *gorm.DB) error {
 			}
 		}
 
-		for i, _ := range job.Logs {
+		for i := range job.Logs {
 			job.Logs[i].LogID = 0
 			_ = tx.Create(&job.Logs[i])
 			// no error handling, if it fails, it fails
diff --git a/database_test.go b/database_test.go
index 1300fa3..7ee5f23 100644
--- a/database_test.go
+++ b/database_test.go
@@ -42,6 +42,7 @@ func TestDeleteJob(t *testing.T) {
 	db.Model(&JobPersistence{}).Where("id = ?", "testJobID").Count(&count)
 	assert.Equal(t, int64(0), count)
 }
+
 func TestResetLogs(t *testing.T) {
 	db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
 	assert.Nil(t, err)
diff --git a/devenv.lock b/devenv.lock
deleted file mode 100644
index 6414869..0000000
--- a/devenv.lock
+++ /dev/null
@@ -1,190 +0,0 @@
-{
-  "nodes": {
-    "devenv": {
-      "locked": {
-        "dir": "src/modules",
-        "lastModified": 1713353943,
-        "narHash": "sha256-1gDYT+Hhqpnt+CDYL1h2huE07c6BCod6qlsaFNTPcn8=",
-        "owner": "cachix",
-        "repo": "devenv",
-        "rev": "5b933eb8522b61873e859c9c68de16330d8f5d8b",
-        "type": "github"
-      },
-      "original": {
-        "dir": "src/modules",
-        "owner": "cachix",
-        "repo": "devenv",
-        "type": "github"
-      }
-    },
-    "flake-compat": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1696426674,
-        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
-        "owner": "edolstra",
-        "repo": "flake-compat",
-        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
-        "type": "github"
-      },
-      "original": {
-        "owner": "edolstra",
-        "repo": "flake-compat",
-        "type": "github"
-      }
-    },
-    "flake-utils": {
-      "inputs": {
-        "systems": "systems"
-      },
-      "locked": {
-        "lastModified": 1710146030,
-        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "gitignore": {
-      "inputs": {
-        "nixpkgs": [
-          "pre-commit-hooks",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1709087332,
-        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "type": "github"
-      }
-    },
-    "nixpkgs": {
-      "locked": {
-        "lastModified": 1713145326,
-        "narHash": "sha256-m7+IWM6mkWOg22EC5kRUFCycXsXLSU7hWmHdmBfmC3s=",
-        "owner": "nixos",
-        "repo": "nixpkgs",
-        "rev": "53a2c32bc66f5ae41a28d7a9a49d321172af621e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nixos",
-        "ref": "nixos-23.11",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs-stable": {
-      "locked": {
-        "lastModified": 1710695816,
-        "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixos-23.11",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_2": {
-      "locked": {
-        "lastModified": 1713145326,
-        "narHash": "sha256-m7+IWM6mkWOg22EC5kRUFCycXsXLSU7hWmHdmBfmC3s=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "53a2c32bc66f5ae41a28d7a9a49d321172af621e",
-        "type": "github"
-      },
-      "original": {
-        "id": "nixpkgs",
-        "ref": "nixos-23.11",
-        "type": "indirect"
-      }
-    },
-    "pre-commit-hooks": {
-      "inputs": {
-        "flake-compat": "flake-compat",
-        "flake-utils": "flake-utils",
-        "gitignore": "gitignore",
-        "nixpkgs": [
-          "nixpkgs"
-        ],
-        "nixpkgs-stable": "nixpkgs-stable"
-      },
-      "locked": {
-        "lastModified": 1712897695,
-        "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=",
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8",
-        "type": "github"
-      },
-      "original": {
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "type": "github"
-      }
-    },
-    "root": {
-      "inputs": {
-        "devenv": "devenv",
-        "nixpkgs": "nixpkgs",
-        "pre-commit-hooks": "pre-commit-hooks",
-        "version": "version"
-      }
-    },
-    "systems": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    },
-    "version": {
-      "inputs": {
-        "nixpkgs": "nixpkgs_2"
-      },
-      "locked": {
-        "lastModified": 1704542622,
-        "narHash": "sha256-HnFuaOXHoxv8tpBvMsEjfhcl/hFNxEY7GbBqoyJ1U8U=",
-        "ref": "refs/heads/master",
-        "rev": "6b4f85fe6d934429cf3055bbcd8cf15014730118",
-        "revCount": 114,
-        "type": "git",
-        "url": "https://gitlab.schukai.com/oss/utilities/version.git"
-      },
-      "original": {
-        "type": "git",
-        "url": "https://gitlab.schukai.com/oss/utilities/version.git"
-      }
-    }
-  },
-  "root": "root",
-  "version": 7
-}
diff --git a/devenv.nix b/devenv.nix
deleted file mode 100644
index 06f21d3..0000000
--- a/devenv.nix
+++ /dev/null
@@ -1,765 +0,0 @@
-{ pkgs ? import <nixpkgs> {}, inputs, phps, lib, config, modulesPath, ... }:
-
-{
-  # https://devenv.sh/packages/
-  packages = with pkgs; [
-    inputs.version.defaultPackage."${builtins.currentSystem}"
-    appimage-run
-    blackbox
-    blackbox-terminal
-    coreutils-full
-    dbeaver
-    glibc.static
-    dbeaver
-    delve
-    dialog
-    drill
-    eza
-    fd
-    fd
-    feh
-    gcc12
-    gdlv
-    git
-    glab
-    gnugrep
-    gnumake
-    gnused
-    go-licenses
-    gosec
-    go-task
-    gum
-    httpie
-    hurl
-    jq
-    libffi
-    logrotate
-    meld
-    memcached
-    netcat
-    nixfmt
-    nodePackages.mermaid-cli
-    openssh
-    procps
-    ranger
-    unixtools.xxd
-    unzip
-    util-linux
-    wget
-    zlib
-  ];
-
-  # https://devenv.sh/languages/
-  # languages.nix.enable = true;
-  languages = { go = { enable = true; }; };
-
-  difftastic.enable = true;
-
-  scripts.run-sshd.exec = ''
-  set -x
-  
-  cd ${config.devenv.root}/docker/sftp-server
-  ${pkgs.docker-client}/bin/docker docker build -t jobqueue-sftp-server .
-  cd -
-  
-  
-  
-  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.config/temp_rsa_key
-  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.config/temp_ed25518_key
-  
-  ${pkgs.coreutils}/bin/mkdir -p ${config.devenv.root}/.devenv/chroot/home/demo/.ssh 
-  ${pkgs.coreutils}/bin/cat ${config.devenv.root}/.config/demo_ssh_key.pub > ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
-  ${pkgs.coreutils}/bin/chmod 700 ${config.devenv.root}/.devenv/chroot/home/demo/.ssh
-  ${pkgs.coreutils}/bin/chmod 600 ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
-  
-  
-  ${pkgs.coreutils}/bin/cat <<EOF > ${config.devenv.root}/.config/sshd_config
-
-AuthorizedKeysCommand ${pkgs.coreutils}/bin/cat ${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys
-AuthorizedKeysCommandUser nobody
-
-Match User root
-    ChrootDirectory ${config.devenv.root}/.devenv/chroot
-    ForceCommand internal-sftp
-    PasswordAuthentication no
-    PermitTunnel no
-    AllowAgentForwarding no
-    AllowTcpForwarding no
-    X11Forwarding no
-    PermitRootLogin no
-    AllowUsers demo root
-EOF
-  
-  ${pkgs.openssh}/bin/sshd -D -e -o \
-     HostKey=${config.devenv.root}/.config/temp_rsa_key \
-     -f ${config.devenv.root}/.config/sshd_config \
-     -o HostKey=${config.devenv.root}/.config/temp_ed25518_key \
-     -o Port=''${1:-2222}  \
-     -o AuthorizedKeysFile=${config.devenv.root}/.devenv/chroot/home/demo/.ssh/authorized_keys \
-     -o PidFile=${config.devenv.root}/.devenv/sshd.pid   
-  '';
-  
-  scripts.draw-graph.exec = ''
-  echo -e "Enter Meirmaid graph definition. ''${RED}End with Ctrl+D''${RESET}\n"  
-  diagram=$(${pkgs.gum}/bin/gum write --placeholder "Enter Meirmaid graph definition. End with Ctrl+D")
-  
-  tmpOutput=$(mktemp).png
-  
-  echo "$diagram" | ${pkgs.nodePackages.mermaid-cli}/bin/mmdc -i - -o "$tmpOutput"
-  ${pkgs.feh}/bin/feh $tmpOutput
-  
-  # should delte the file, but does not work ask with gum
-  ${pkgs.gum}/bin/gum confirm "Delete temporary file?"
-    if [ $? -eq 0 ]; then
-       rm "$tmpOutput"
-       else
-         echo "not deleting; file is at $tmpOutput"
-    fi    
-  '';
-  
-  scripts.get-go-default-packages.exec = ''
-    #!${pkgs.bash}/bin/bash
-    echo $(awk -F ' ' '/^module / { print $2 }' go.mod)
-  '';
-
-  # This script is executed when the app is built
-  # You can use it to build the app  
-  scripts.test-lib.exec = ''
-    #!${pkgs.bash}/bin/bash
-    set -x
-
-${pkgs.gosec}/bin/gosec ${config.devenv.root}
-${pkgs.go}/bin/go test -mod=mod -tags 'runOnTask,osusergo,netgo' -a -ldflags "-extldflags '-static' -s -w" -cover -v ${config.devenv.root}
-${pkgs.go}/bin/go test -mod=mod -tags 'runOnTask,osusergo,netgo' -a -ldflags "-extldflags '-static' -s -w" -bench -v ${config.devenv.root}
-${pkgs.go}/bin/go test -mod=mod -tags 'runOnTask,osusergo,netgo' -a -ldflags "-extldflags '-static' -s -w" -race -v ${config.devenv.root}
-
-  '';
-
-  # This scritp is used to deploy the app to the gitlab registry
-  # It is used by the gitlab-ci.yml file
-  # The environment variables are set in the gitlab project settings
-  scripts.deploy-lib.exec = ''
-    #!${pkgs.bash}/bin/bash
-
-    PATH="''${PATH}":${pkgs.coreutils}/bin
-    PATH="''${PATH}":${pkgs.jq}/bin/
-    PATH="''${PATH}":${pkgs.curl}/bin/
-    PATH="''${PATH}":${pkgs.moreutils}/bin/
-    PATH="''${PATH}":${pkgs.gnutar}/bin
-    PATH="''${PATH}":${pkgs.gzip}/bin/
-    PATH="''${PATH}":${pkgs.eza}/bin/
-    PATH="''${PATH}":${pkgs.git}/bin/
-    PATH="''${PATH}":${
-      inputs.version.defaultPackage."${builtins.currentSystem}"
-    }/bin/
-
-    export PATH
-
-    if [[ -f .env-gitlab-ci ]]; then
-        source .env-gitlab-ci
-        rm .env-gitlab-ci
-    fi
-
-    set -x
-    ## if $HOME not set, set it to current directory
-    if [[ -z "''${HOME}" ]]; then
-        HOME=$(pwd)
-    fi
-
-    export HOME
-
-    git config user.email "''${GITLAB_USER_EMAIL}"
-    git config user.name "''${GITLAB_USER_NAME:-"Gitlab CI"}"
-    git config pull.rebase true
-    git config http.sslVerify "false"
-    git remote set-url origin https://pad:''${GITLAB_TOKEN}@''${CI_REPOSITORY_URL#*@}
-
-    git fetch --all --tags --unshallow
-    git reset --hard origin/master
-    git checkout $CI_COMMIT_REF_NAME
-    git pull origin $CI_COMMIT_REF_NAME
-
-    if [ ! -z "''${CI_PROJECT_DIR}" ]; then
-        echo "CI_PROJECT_DIR is set, using it as project root."
-        project_root=$(realpath "''${CI_PROJECT_DIR}")/
-    elif [ ! -z "''${DEVENV_ROOT}" ]; then
-        echo "DEVENV_ROOT is set, using it as project root."
-        project_root=$(realpath "''${DEVENV_ROOT}")/
-    else
-        echo "Error: DEVENV_ROOT or CI_PROJECT_DIR environment variables are not set."
-        exit 1  
-    fi
-
-    if [ ! -d "''${project_root}" ]; then
-        echo "Error: Project root directory does not seem to be valid."
-        echo "Check the DEVENV_ROOT or CI_PROJECT_DIR environment variables."
-        exit 1
-    fi
-
-    if [ -z "'CI_JOB_TOKEN" ]; then
-        echo "Error: CI_JOB_TOKEN variable is not set."
-        exit 1
-    fi
-
-    git --no-pager log --decorate=short --pretty=oneline
-    gitVersion=v$(version predict)
-    git tag -a $gitVersion -m"chore: bump version" 
-    git --no-pager log --decorate=short --pretty=oneline
-    git push -o ci.skip origin ''${CI_COMMIT_REF_NAME} --tags
-
-    echo "done"
-
-  '';
-
-  enterShell = ''
-       
-cat <<'EOF' > CONTRIBUTING.md
-# Contributing to schukai GmbH Projects
-
-## Code of Conduct
-
-Be a human, not an asshole. Common sense and basic human decency apply.
-
-## Getting Started
-
-### Setting up the Project
-
-1. Fork the project on GitLab.
-2. Clone your fork locally. Replace `[your-username]` with your GitLab username and `[project-name]` with the actual project name:
-    ```bash
-    git clone $(git config --get remote.origin.url)
-    ```
-3. Add the upstream repository. Replace `[original-username]` and `[project-name]` with the original repository's username and project name:
-    ```bash
-    git remote add upstream https://gitlab.schukai.com/[original-username]/[project-name].git
-    ```
-
-### Making Changes
-
-1. Create a new branch:
-    ```bash
-    git checkout -b new-feature-branch
-    ```
-2. Make your changes.
-3. Commit your changes:
-    ```bash
-    git commit -m "Description of change"
-    ```
-
-### Submitting a Merge Request
-
-1. Push your changes to your fork:
-    ```bash
-    git push origin new-feature-branch
-    ```
-2. Navigate to the original project repository on `gitlab.schukai.com`.
-3. Open a Merge Request and provide a clear description of the changes.
-
-## Coding Guidelines
-
-- Follow the coding style used in the project.
-- Write unit tests for new features.
-- Ensure that all tests pass before submitting a Merge Request.
-
-## Reporting Issues
-
-If you find an issue, please create a new issue on `gitlab.schukai.com`.
-
-## Additional Resources
-
-- [GitLab Flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html)
-- [GitLab Merge Request Guidelines](https://docs.gitlab.com/ee/user/project/merge_requests/)
-
-Thank you for your contribution! 
-    
-EOF
-    
-cat <<'EOF' > LICENSE
-Copyright (C) 2023 schukai GmbH 
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.  
-    
-EOF
- 
-cat <<'EOF' > Taskfile.yml
-
-# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL
-# DO NOT EDIT THIS FILE MANUALLY
-# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix 
-# AND OPEN A SHELL WITH THE COMMAND devenv shell
-#
-# Information about the task runner can be found here:
-# https://taskfile.dev
-
-version: '3'
-
-tasks:
-  default: 
-    cmds:
-      - task --list
-    silent: true
-    
-  test:
-    desc: Execute unit tests in Go.
-    env:
-      TEST_BY_TASK: true
-    cmds:
-      - docker pull atmoz/sftp:alpine
-      - docker pull axllent/mailpit 
-      - echo "Execute unit tests in Go."
-      - test-lib
-
-  test-fuzz:
-    desc: Conduct fuzzing tests.#
-    env:
-      TEST_BY_TASK: true
-    cmds:  
-      - echo "Conduct fuzzing tests."
-      - go test -tags=runOnTask -v -fuzztime=30s -fuzz=Fuzz ./...
-
-  add-licenses:
-    desc: Attach license headers to Go files.
-    cmds:
-      - echo "Attach license headers to Go files."
-      - go install github.com/google/addlicense@latest
-      - addlicense -c "schukai GmbH" -s -l "AGPL-3.0" ./*.go
-    silent: true
-    
-  check-licenses:
-    desc: Check license headers of Go files.
-    silent: true
-    cmds:
-      - go-licenses save "$(get-go-default-packages)"  --ignore "gitlab.schukai.com"  --force --save_path ''${DEVENV_ROOT}/licenses/
-
-  check:
-    desc: Confirm repository status.
-    cmds:
-      - git diff-index --quiet HEAD || (echo "There are uncommitted changes after running make. Please commit or stash them before running make."; exit 1)
-    silent: true
-
-  commit:
-    desc: Commit changes to the repository.
-    aliases:
-      - c
-      - ci
-      - git-commit
-    cmds:
-      - do-git-commit
-EOF
-
-cat <<'EOF' > .gitlab-ci.yml
-
-# THIS FILE IS AUTOGENERATED BY THE DEVENVSHELL
-# DO NOT EDIT THIS FILE MANUALLY
-# INSTEAD EDIT THE DEVENVSHELL CONFIGURATION FILE devenv.nix
-# AND OPEN A SHELL WITH THE COMMAND devenv shell
-#
-
-image: docker-registry.schukai.com:443/nixos-ci-devenv:latest
-
-#    services:
-#      - docker:dind
-
-variables:
-  # The repo name as used in
-  # https://github.com/nix-community/NUR/blob/master/repos.json
-  NIXOS_VERSION: "23.05"
-  NIXPKGS_ALLOW_UNFREE: "1"
-  NIXPKGS_ALLOW_INSECURE: "1"
-  GIT_DEPTH: 10
-
-stages:
-  - test
-  - deploy 
-
-before_script:
-  - echo > .env-gitlab-ci
-  - variables=("HOME=$HOME" "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME" "CI_REPOSITORY_URL=$CI_REPOSITORY_URL" "GITLAB_TOKEN=$GITLAB_TOKEN" "CI_JOB_TOKEN=$CI_JOB_TOKEN" "GITLAB_USER_EMAIL=$GITLAB_USER_EMAIL" "GITLAB_USER_NAME=\"$GITLAB_USER_NAME\"" "CI_REGISTRY_USER=$CI_REGISTRY_USER" "CI_PROJECT_ID=$CI_PROJECT_ID" "CI_PROJECT_DIR=$CI_PROJECT_DIR" "CI_API_V4_URL=$CI_API_V4_URL" "CI_PROJECT_NAME=$CI_PROJECT_NAME" "CI_COMMIT_SHORT_SHA=$CI_COMMIT_SHORT_SHA"); for var in "''${variables[@]}"; do echo "$var" >> .env-gitlab-ci; done
-  - cat .env-gitlab-ci
-  - env 
-  - nix shell nixpkgs#docker-client -c docker info
-
-after_script:
-  - if [ -f .env-gitlab-ci ]; then rm .env-gitlab-ci; fi
-
-test:
-  stage: test
-  tags:
-    - nixos
-  script:
-    - devenv shell test-lib
-
-  cache:
-    - key: nixos
-      paths:
-        - /nix/store
-  
-  artifacts:
-    paths:
-      - dist
-
-deploy:
-  stage: deploy
-  tags:
-    - nixos
-  script:
-    - devenv shell -c deploy-lib
-
-  when: on_success
-
-  cache:
-    - key: nixos
-      paths:
-        - /nix/store
-
-EOF
-        
-  '';
-
-  scripts.do-git-commit.exec = ''
-    #!/usr/bin/env bash
-
-    # Define colors if the terminal supports it
-    if [ -t 1 ]; then
-      RED='\033[0;31m'
-      GREEN='\033[0;32m'
-      RESET='\033[0m'
-      BOLD='\033[1m'
-    else
-      RED=""
-      GREEN=""
-      RESET=""
-    fi
-
-    step=1
-
-    reset
-    clear
-
-    # create random log file
-    LOGFILE="$(mktemp)"
-    if [ $? -ne 0 ]; then
-      echo -e "''${RED}✖ Could not create temporary log file. Exiting.''${RESET}"
-      exit 1
-    fi
-
-    log_and_display() {
-      echo -e "''${GREEN}==> $step. $1''${RESET}" | tee -a $LOGFILE
-      step=$((step + 1))
-    }
-
-    log_error_and_display() {
-      echo -e "''${RED}==> $step. $1''${RESET}" | tee -a $LOGFILE
-    }
-
-    printLogfileAndExit() {
-      exit_code=$1
-      echo -e "\n\n========================================\n\n\n"
-
-      echo -e "\n\n''${BOLD}Git and GitLab Automation Script''${RESET}\n\nI have performed the following actions:\n\n"
-      cat "$LOGFILE"
-
-      # Optional: Remove log file
-      rm -f "$LOGFILE"
-
-      if [ $exit_code -eq 0 ]; then
-        echo -e "\n''${GREEN}✔''${RESET} All actions were successful" | tee -a $LOGFILE
-      elif [ $exit_code -eq -1 ]; then
-        echo -e "\n''${RED}✖''${RESET} The script was manually cancelled" | tee -a $LOGFILE
-        exit_code=0
-      else
-        echo -e "\n''${RED}✖''${RESET} Some actions failed" | tee -a $LOGFILE
-      fi
-
-      exit $exit_code
-    }
-
-    print_headline() {
-      local title=$1
-      local underline=$(printf '─%.0s' $(seq 1 ''${#title}))
-      echo -e "\n\n''${BOLD}''${title}\n''${underline}''${RESET}\n"
-    }
-
-    do_cancel() {
-      echo -e "''${RED}==> ✖ Cancelled.''${RESET}" | tee -a $LOGFILE
-      printLogfileAndExit -1
-    }
-
-    # Function for unified logging and display
-    log_action() {
-      if [ $? -eq 0 ]; then
-        echo -e "    ''${GREEN}✔''${RESET} $1: Successful" | tee -a $LOGFILE
-      else
-        echo -e "    ''${RED}✖''${RESET} $1: Failed" | tee -a $LOGFILE
-        printLogfileAndExit 1
-      fi
-    }
-
-    print_what_to_do() {
-      echo -e "\n\nWhat do you want to do?\n"
-    }
-
-    git_status=$(git status --porcelain)
-    if [[ -z "$git_status" ]]; then
-      log_error_and_display "No changes to commit. Exiting."
-      printLogfileAndExit 0
-    fi
-
-    print_headline "Choose commit type"
-    selection=$(gum choose "feat: (new feature for the user, not a new feature for build script)" "fix: (bug fix for the user, not a fix to a build script)" "chore: (updating grunt tasks etc.; no production code change)" "docs: (changes to the documentation)" "style: (formatting, missing semi colons, etc; no production code change)" "refactor: (refactoring production code, eg. renaming a variable)" "test: (adding missing tests, refactoring tests; no production code change)" "Cancel")
-
-    commit_type=$(echo "$selection" | awk -F':' '{print $1}')
-
-    if [[ "$commit_type" == "Cancel" ]]; then
-      do_cancel
-    fi
-
-    log_and_display "You chose the commit type: $commit_type"
-
-    # NEXT STEP ISSUE HANDLING ############################################################################################################
-    #log_and_display "Issue handling"
-
-    gitlabIssues=()
-    while IFS= read -r line; do
-      if [[ $line =~ ^# ]]; then
-        id=$(echo "$line" | awk '{print substr($1, 2)}')
-        title=$(echo "$line" | awk -F'about' '{print $1}' | awk '{$1=$2=""; print substr($0, 3)}')
-        gitlabIssues+=("$id > $title")
-      fi
-    done < <(gum spin --spinner dot --show-output --title "Ask gitlab ..." -- glab issue list --output-format=details)
-
-    ## if issues are available, ask if user wants to use an existing issue or create a new one
-    createOption="Create new issue"
-    existingOption="Use existing issue"
-    cancelOption="Cancel"
-
-    print_headline "Choose issue handling"
-    if [ ''${#gitlabIssues[@]} -eq 0 ]; then
-      log_and_display "There are no issues available."
-
-      print_what_to_do
-      choice=$(gum choose "$createOption" "$cancelOption")
-
-    else
-      log_and_display "There are ''${#gitlabIssues[@]} issues available."
-      print_what_to_do
-      choice=$(gum choose "$createOption" "$existingOption" "$cancelOption")
-    fi
-
-    if [[ "$choice" == "$cancelOption" ]]; then
-      do_cancel
-    fi
-
-    ## array of issue ids
-    work_on_issue_ids=()
-
-    issue_text=""
-
-    if [[ "$choice" == "$createOption" ]]; then
-      print_headline "Create new issue"
-      issue_text=$(gum input --placeholder "Enter issue title")
-      echo -e "Enter issue description. ''${RED}End with Ctrl+D''${RESET}\n"
-      issue_description=$(gum write --placeholder "Enter issue description. End with Ctrl+D")
-      
-      if [[ -z "$issue_text" ]]; then
-        log_error_and_display "Issue title is empty. Exiting."
-        printLogfileAndExit 1
-      fi
-
-      log_and_display "You entered the issue title: $issue_text"
-      log_and_display "You entered the issue description: $issue_description"
-      echo -e "\n"
-
-      gum confirm "Do you want to create this issue?"
-      # gum confirm exits with status 0 if confirmed and status 1 if cancelled.
-      if [ $? -eq 1 ]; then
-        do_cancel
-      fi
-
-      issue_output=$(glab issue create -t"$issue_text" --no-editor --description "$issue_description")
-      issue_id=$(echo "$issue_output" | grep -oP '(?<=/issues/)\d+')
-
-      work_on_issue_ids+=("$issue_id")
-
-      log_action "glab issue with id $issue_id created"
-
-    else
-
-      print_headline "Use existing issue"
-      echo -e "Select issue with arrow keys and press tab or space to select. Press enter to confirm.\n"
-      issue_ids=$(gum choose --no-limit "''${gitlabIssues[@]}")
-
-      # assign issue_ids to work_on_issue_ids. iterate over lines and take integer from beginning of line
-      while IFS= read -r line; do
-        work_on_issue_ids+=($(echo "$line" | grep -oP '^\d+'))
-      done <<<"$issue_ids"
-
-    fi
-
-    if [ ''${#work_on_issue_ids[@]} -eq 0 ]; then
-      log_and_display "No issue selected. Exiting."
-      printLogfileAndExit 0
-    fi 
-
-    # NEXT STEP COMMIT MESSAGE ############################################################################################################
-    # print work_on_issue_ids
-    work_on_issue_ids_string=""
-    for i in "''${work_on_issue_ids[@]}"; do
-      work_on_issue_ids_string+="#$i "
-    done
-
-    log_and_display "You chose to work on the following issues: ''${work_on_issue_ids_string}"
-
-
-    print_headline "Check for changes to commit"
-
-    # ' ' = unmodified
-    # M = modified
-    # T = file type changed (regular file, symbolic link or submodule)
-    # A = added
-    # D = deleted
-    # R = renamed
-    # C = copied (if config option status.renames is set to "copies")
-    # U = updated but unmerged
-    # https://man.freebsd.org/cgi/man.cgi?query=git-status&sektion=1&manpath=freebsd-release-ports
-
-    count_all_changes=$(echo "$git_status" | wc -l)
-    count_staged_changes=$(echo "$git_status" | grep -c '^M')
-    count_new_staged_files=$(echo "$git_status" | grep -c '^A')
-    count_staged_changes=$((count_staged_changes + count_new_staged_files))
-
-
-
-    git_options_all="All $count_all_changes changes"
-    git_options_staged="Only the $count_staged_changes staged changes"
-    git_options_select_files="Select files"
-    git_options_cancel="Cancel"
-
-    git_options_array=()
-    if [[ $count_all_changes -gt 0 ]]; then
-      git_options_array+=("$git_options_all")
-    fi
-
-    if [[ $count_staged_changes -gt 0 ]]; then
-      git_options_array+=("$git_options_staged")
-    fi
-
-    git_options_array+=( "$git_options_select_files" )
-    git_options_array+=( "$git_options_cancel" )
-
-
-    selection=$(gum choose "''${git_options_array[@]}")
-    if [[ "$selection" == "$git_options_cancel" ]]; then
-      do_cancel
-    fi
-
-    if [[ "$selection" == "$git_options_all" ]]; then
-      git add -A
-      echo "1"
-    elif [[ "$selection" == "$git_options_select_files" ]]; then
-
-      files=()
-      while IFS= read -r line; do
-        files+=("$line")
-      done <<<"$git_status"
-        
-      selected_files=$(gum choose --no-limit "''${files[@]}")
-      
-      # no files selected
-      if [[ -z "$selected_files" ]]; then
-        log_and_display "No files selected. Exiting."
-        printLogfileAndExit 0
-      fi
-
-      # add selected files
-      while IFS= read -r line; do
-        ## git proclimne could have letter, ? or ! at the beginning of the line
-        file=$(echo "$line" | awk '{print $2}')
-        if [[ -z "$file" || ! -f "$file" ]]; then
-          log_and_display "No file found in line: $line"
-          continue
-        fi
-        
-        git add "$file"
-      done <<<"$selected_files"
-      
-    fi
-
-    ## count staged changes again and print
-    count_staged_changes=$(echo "$git_status" | grep -c '^M')
-    count_new_staged_files=$(echo "$git_status" | grep -c '^A')
-    count_staged_changes=$((count_staged_changes + count_new_staged_files))
-
-    log_and_display "You have $count_staged_changes staged changes to commit."
-
-    # NEXT STEP COMMIT MESSAGE ############################################################################################################
-
-    print_headline "Enter commit message"
-    commit_message=$(gum input --placeholder "Enter commit message" --value "$commit_type: $issue_text $work_on_issue_ids_string")
-
-    if [[ -z "$commit_message" ]]; then
-      log_error_and_display "Commit message is empty. Exiting."
-      printLogfileAndExit 1
-    fi
-
-    log_and_display "You entered the commit message: $commit_message"
-
-    gum confirm "Do you want to commit with this message?"
-    if [ $? -eq 1 ]; then
-      do_cancel
-    fi
-
-    # NEXT STEP COMMIT ####################################################################################################################
-    print_headline "Committing changes"
-
-    if ! git commit -m "$commit_message" ; then
-      log_error_and_display "Commit failed. Exiting."
-      printLogfileAndExit 1
-    fi
-
-    log_and_display "Commit successful."
-
-    # NEXT STEP PUSH ######################################################################################################################
-
-    print_headline "Pushing changes"
-
-    if ! git push ; then
-      log_error_and_display "Push failed. Exiting."
-      printLogfileAndExit 1
-    fi
-
-    log_and_display "Push successful."
-
-    # Close issue ######################################################################################################################
-
-    print_headline "Closing issues"
-
-    for issue_id in "''${work_on_issue_ids[@]}"; do
-      
-      gum confirm "Do you want to close issue #$issue_id?"
-      if [ $? -eq 1 ]; then
-        continue
-      fi
-      
-      if ! glab issue close "$issue_id" ; then
-        log_error_and_display "Closing issue $issue_id failed. Exiting."
-      else
-        log_and_display "Closing issue $issue_id successful."  
-      fi
-    done
-
-    printLogfileAndExit 0
-  '';
-
-
-}
diff --git a/devenv.yaml b/devenv.yaml
deleted file mode 100644
index 1fd1de9..0000000
--- a/devenv.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-inputs:
-  nixpkgs:
-    url: github:nixos/nixpkgs/nixos-23.11
-
-  version:
-    url: git+https://gitlab.schukai.com/oss/utilities/version.git
-    flake: true
diff --git a/errors.go b/errors.go
index b231eec..d04724a 100644
--- a/errors.go
+++ b/errors.go
@@ -57,4 +57,9 @@ var (
 	ErrFailedToCreate               = fmt.Errorf("failed to create")
 	ErrFailedToQueryExistingJob     = fmt.Errorf("failed to query an existing job")
 	ErrFailedToSaveJob              = fmt.Errorf("failed to save a job")
+	ErrScheduleTimeIsInThePast      = fmt.Errorf("scheduled time is in the past")
+	ErrParameterIsNil               = fmt.Errorf("parameter is nil")
+	ErrJobIDEmpty                   = fmt.Errorf("job ID is empty")
+	ErrManagerNotInitialized        = fmt.Errorf("manager is not initialized")
+	ErrJobSyncerNotInitialized      = fmt.Errorf("JobSyncer is not initialized")
 )
diff --git a/event-bus_test.go b/event-bus_test.go
index dfedcf6..7617b49 100644
--- a/event-bus_test.go
+++ b/event-bus_test.go
@@ -13,7 +13,7 @@ func TestSubscribeAndPublish(t *testing.T) {
 	eb := NewEventBus()
 
 	jobAddedCh := make(chan interface{}, 1)
-        defer close(jobAddedCh)
+	defer close(jobAddedCh)
 	eb.Subscribe(JobAdded, jobAddedCh)
 
 	jobData := "New Job Data"
@@ -37,7 +37,7 @@ func TestUnsubscribe(t *testing.T) {
 
 	jobAddedCh := make(chan interface{}, 1)
 	defer close(jobAddedCh)
-	
+
 	eb.Subscribe(JobAdded, jobAddedCh)
 	eb.Unsubscribe(JobAdded, jobAddedCh)
 
@@ -59,8 +59,8 @@ func TestMultipleSubscribers(t *testing.T) {
 	jobAddedCh2 := make(chan interface{}, 1)
 	eb.Subscribe(JobAdded, jobAddedCh1)
 	eb.Subscribe(JobAdded, jobAddedCh2)
-        defer close(jobAddedCh1)
-        defer close(jobAddedCh2)
+	defer close(jobAddedCh1)
+	defer close(jobAddedCh2)
 
 	jobData := "New Job Data"
 	eb.Publish(JobAdded, jobData)
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..77c6c65
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,126 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "git-commit": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      },
+      "locked": {
+        "lastModified": 1,
+        "narHash": "sha256-8hXrOA8SKy//ctoL1poIhJwuT7G4rvVu7FguROzEoDg=",
+        "path": "./git-commit",
+        "type": "path"
+      },
+      "original": {
+        "path": "./git-commit",
+        "type": "path"
+      }
+    },
+    "gitlab-commit": {
+      "inputs": {
+        "git-commit": "git-commit",
+        "nixpkgs": "nixpkgs_2"
+      },
+      "locked": {
+        "lastModified": 1704993617,
+        "narHash": "sha256-++u7I3KCDgP+JhW70k5tiLfBESWVSKdVK7aei0G+mR8=",
+        "ref": "refs/heads/master",
+        "rev": "e63b1d6ae5526158fa2a1f4cd53b0ea4f4c93573",
+        "revCount": 18,
+        "type": "git",
+        "url": "https://gitlab.schukai.com/schukai/entwicklung/nix-flakes"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://gitlab.schukai.com/schukai/entwicklung/nix-flakes"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1704874635,
+        "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "ref": "nixos-23.11",
+        "type": "indirect"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1713344939,
+        "narHash": "sha256-jpHkAt0sG2/J7ueKnG7VvLLkBYUMQbXQ2L8OBpVG53s=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "e402c3eb6d88384ca6c52ef1c53e61bdc9b84ddd",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "ref": "nixos-23.11",
+        "type": "indirect"
+      }
+    },
+    "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1713344939,
+        "narHash": "sha256-jpHkAt0sG2/J7ueKnG7VvLLkBYUMQbXQ2L8OBpVG53s=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "e402c3eb6d88384ca6c52ef1c53e61bdc9b84ddd",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-23.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "gitlab-commit": "gitlab-commit",
+        "nixpkgs": "nixpkgs_3"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..1dad0b8
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,93 @@
+{
+  description = "Development environment";
+
+  inputs = {
+    nixpkgs = {url = "github:nixos/nixpkgs/nixos-23.11";}; 
+    flake-utils = {url = "github:numtide/flake-utils";};
+    gitlab-commit = {url = "git+https://gitlab.schukai.com/schukai/entwicklung/nix-flakes";};
+  };
+
+  outputs = {
+    self,
+    nixpkgs,
+    flake-utils,
+    gitlab-commit,
+  }:
+    flake-utils.lib.eachDefaultSystem (system: let
+      inherit (nixpkgs.lib) optional;
+      pkgs' = import nixpkgs {inherit system;};
+
+      script-task = pkgs'.callPackage ./nix/scripts/go-task.nix {};
+      script-run-tests = pkgs'.callPackage ./nix/scripts/run-tests.nix {};
+      script-run-extended-tests = pkgs'.callPackage ./nix/scripts/run-extended-tests.nix {}; 
+      script-run-ci-tests = pkgs'.callPackage ./nix/scripts/run-ci-tests.nix {};
+      script-check-code = pkgs'.callPackage ./nix/scripts/check-code.nix {};
+      script-build-cover-report = pkgs'.callPackage ./nix/scripts/build-cover-report.nix {};
+      
+      import-gitlab-commit = gitlab-commit.packages.${system}.git-commit;
+      
+#      ciJobToken = builtins.getEnv "CI_JOB_TOKEN";
+#      gitCommit =
+#        if ciJobToken == ""
+#        then (builtins.getFlake "git+https://gitlab.schukai.com/schukai/entwicklung/nix-flakes").packages.${builtins.currentSystem}.git-commit
+#        else "";
+      
+      
+      
+    in {
+      devShells = {
+        default = let
+        in
+          pkgs'.mkShell {
+            nativeBuildInputs = with pkgs'; [
+              script-run-tests
+              script-run-extended-tests
+              script-run-ci-tests
+              script-check-code
+              script-build-cover-report 
+              script-task
+              import-gitlab-commit 
+
+              alejandra
+              blackbox
+              blackbox-terminal
+              coreutils-full
+              delve
+              dialog
+              drill
+              fd
+              feh
+              gcc12
+              gdlv
+              git
+              glab
+              gnugrep
+              gnumake
+              gnused
+              go
+              go-licenses
+              golangci-lint
+              gosec
+              gum
+              jq
+              niv
+              libffi
+              logrotate
+              meld
+              memcached
+              netcat
+              ranger
+              unixtools.xxd
+              unzip
+              util-linux
+              vhs
+              wget
+              zlib
+            ];
+            shellHook = ''
+              echo "Welcome to your development environment." 
+            '';
+          };
+      };
+    });
+}
diff --git a/go.mod b/go.mod
index 2164807..d586c3e 100644
--- a/go.mod
+++ b/go.mod
@@ -18,6 +18,7 @@ require (
 	gorm.io/driver/mysql v1.5.6
 	gorm.io/driver/sqlite v1.5.5
 	gorm.io/gorm v1.25.9
+	gotest.tools/v3 v3.5.1
 )
 
 require (
@@ -30,6 +31,7 @@ require (
 	github.com/go-ole/go-ole v1.3.0 // indirect
 	github.com/go-sql-driver/mysql v1.8.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/kr/fs v0.1.0 // indirect
@@ -52,5 +54,4 @@ require (
 	golang.org/x/sys v0.19.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/tools v0.6.0 // indirect
-	gotest.tools/v3 v3.5.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 8821dbc..c8d7124 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
 github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
@@ -29,6 +31,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -45,7 +48,9 @@ github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 h1:1KuuSOy4ZNgW0K
 github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
 github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
 github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
 github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@@ -66,6 +71,7 @@ github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhV
 github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
 github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -87,6 +93,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
 github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -116,6 +124,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -134,10 +144,13 @@ golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -150,6 +163,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -159,8 +173,7 @@ gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkD
 gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
 gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
 gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
-gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
-gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
 gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
 gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
 gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
diff --git a/job-generic.go b/job-generic.go
index 79c5f57..3a2efc3 100644
--- a/job-generic.go
+++ b/job-generic.go
@@ -10,7 +10,7 @@ import (
 
 type GenericJob interface {
 	GetID() JobID
-	
+
 	GetDependencies() []JobID
 
 	GetPriority() Priority
diff --git a/job-syncer.go b/job-syncer.go
index 250705b..c7349ca 100644
--- a/job-syncer.go
+++ b/job-syncer.go
@@ -56,7 +56,7 @@ func (js *JobSyncer) Wait(timeout time.Duration) error {
 	}
 }
 
-func (js *JobSyncer) Sync(job GenericJob) {
+func (js *JobSyncer) Sync(job GenericJob) error {
 	js.mu.Lock()
 	defer js.mu.Unlock()
 
@@ -70,6 +70,8 @@ func (js *JobSyncer) Sync(job GenericJob) {
 		}
 	}()
 
+	return nil
+
 }
 
 func (js *JobSyncer) LastError() error {
diff --git a/job.go b/job.go
index 262cdc5..b24f4c4 100644
--- a/job.go
+++ b/job.go
@@ -119,7 +119,7 @@ func (j *Job[T]) GetPersistence() JobPersistence {
 
 	job.Stats.JobID = job.ID
 
-	for i, _ := range job.Logs {
+	for i := range job.Logs {
 		job.Logs[i].JobID = job.ID
 	}
 
diff --git a/logger.go b/logger.go
index 9fe6a9a..b641e35 100644
--- a/logger.go
+++ b/logger.go
@@ -101,7 +101,7 @@ func Trace(msg string, keysAndValues ...interface{}) {
 
 type ZapAdapter struct {
 	logger *zap.Logger
-	mu     sync.Mutex
+	//	mu     sync.Mutex
 }
 
 func NewZapAdapter(logger *zap.Logger) *ZapAdapter {
diff --git a/manager.go b/manager.go
index cc5f5d1..405d327 100644
--- a/manager.go
+++ b/manager.go
@@ -137,6 +137,11 @@ func (m *Manager) GetActiveJobs() map[JobID]GenericJob {
 
 // DeleteJob removes a job from the active jobs and the database
 func (m *Manager) DeleteJob(id JobID) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -153,36 +158,54 @@ func (m *Manager) DeleteJob(id JobID) error {
 	}
 
 	if m.jobSyncer != nil {
-		err := m.jobSyncer.DeleteJob(job)
-		if err != nil {
-			return err
-		}
+		return m.jobSyncer.DeleteJob(job)
 	}
 
 	return nil
 
 }
 
-func (m *Manager) WaitSync() {
+func (m *Manager) WaitSync() error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	if m.jobSyncer != nil {
-		_ = m.jobSyncer.Wait(2 * time.Second)
+		return m.jobSyncer.Wait(2 * time.Second)
 	}
+
+	return nil
+
 }
 
-func (m *Manager) Sync(job GenericJob) {
+func (m *Manager) Sync(job GenericJob) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
 	if m.jobSyncer != nil {
-		m.jobSyncer.Sync(job)
+		return m.jobSyncer.Sync(job)
 	}
+
+	return nil
+
 }
 
 // RemoveJob removes a job from the active jobs
 // If you want to remove a job from the active jobs and the database, use DeleteJob instead
 func (m *Manager) RemoveJob(id JobID) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -195,6 +218,11 @@ func (m *Manager) RemoveJob(id JobID) error {
 
 // ResetJobLogs deletes the logs of a job
 func (m *Manager) ResetJobLogs(id JobID) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -215,6 +243,11 @@ func (m *Manager) ResetJobLogs(id JobID) error {
 
 // ResetJobStats deletes the stats of a job
 func (m *Manager) ResetJobStats(id JobID) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -250,6 +283,10 @@ func cancelSchedulerByManager(scheduler CancelScheduler, id JobID) error {
 
 func (m *Manager) removeJobInternal(id JobID) error {
 
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	scheduler := m.activeJobs[id].GetScheduler()
 	if scheduler == nil {
 		return ErrJobNotScheduled
@@ -270,6 +307,11 @@ func (m *Manager) removeJobInternal(id JobID) error {
 }
 
 func (m *Manager) UpdateJob(job GenericJob) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -318,6 +360,10 @@ func (m *Manager) GetDB() *gorm.DB {
 // GetQueue returns the queue
 func (m *Manager) checkAndSetRunningState() error {
 
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.state = ManagerStateStopped
 
 	if m.workerMap == nil {
@@ -340,6 +386,11 @@ func (m *Manager) checkAndSetRunningState() error {
 
 // AddWorker adds a worker to the manager
 func (m *Manager) AddWorker(worker Worker) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -369,6 +420,11 @@ func (m *Manager) AddWorker(worker Worker) error {
 
 // RemoveWorker removes a worker from the manager
 func (m *Manager) RemoveWorker(worker Worker) error {
+
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -400,6 +456,10 @@ func (m *Manager) RemoveWorker(worker Worker) error {
 // Start starts the manager
 func (m *Manager) Start() error {
 
+	if m == nil {
+		return ErrManagerNotInitialized
+	}
+
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -575,7 +635,7 @@ func (m *Manager) ScheduleJob(job GenericJob, scheduler Scheduler) error {
 	m.activeJobs[job.GetID()] = job
 
 	if m.jobSyncer != nil {
-		m.jobSyncer.Sync(job)
+		return m.jobSyncer.Sync(job)
 	}
 
 	return nil
@@ -656,7 +716,7 @@ func (m *Manager) handleJobEvents() {
 							}
 						}
 
-						if assigned == true {
+						if assigned {
 							break
 						}
 
diff --git a/nix/scripts/build-cover-report.nix b/nix/scripts/build-cover-report.nix
new file mode 100644
index 0000000..80d8863
--- /dev/null
+++ b/nix/scripts/build-cover-report.nix
@@ -0,0 +1,45 @@
+{ pkgs, lib, ... }:
+let
+  common = pkgs.callPackage ./common.nix {};
+
+  # Definieren eines schreibbaren Verzeichnisses
+  writableDir = pkgs.stdenv.mkDerivation {
+    name = "cover-report";
+    buildCommand = ''
+       mkdir -p $out
+    '';
+  };
+
+in
+  pkgs.writeShellScriptBin "build-cover-report" ''
+    ${common}
+
+    echo_header "Building cover report"
+
+    download_test_images
+    setup_go_env
+
+    echo_section "Building"
+    ${pkgs.go}/bin/go test -v -coverprofile=${writableDir}/coverage.out ./...
+    if [ $? -ne 0 ];
+    then
+      echo_fail "Failed to run tests"
+      exit 1
+    fi
+
+    if ! ${pkgs.go}/bin/go tool cover -html=${writableDir}/coverage.out -o ${writableDir}/coverage.html
+    then
+      echo_fail "Failed to generate coverage report"
+      exit 1
+    fi
+    echo_ok "Coverage report stored at ${writableDir}"
+
+    echo_step "Opening coverage report in browser"
+    if ! ${pkgs.xdg-utils}/bin/xdg-open ${writableDir}/coverage.html > /dev/null 2>&1
+    then
+      echo_fail "Failed to open coverage report in browser"
+      exit 1
+    fi
+
+    echo_ok "Report opened in browser"
+  ''
diff --git a/nix/scripts/check-code.nix b/nix/scripts/check-code.nix
new file mode 100644
index 0000000..2ee36b1
--- /dev/null
+++ b/nix/scripts/check-code.nix
@@ -0,0 +1,51 @@
+{
+  pkgs,
+  lib,
+  ...
+}: let
+  common = pkgs.callPackage ./common.nix {};
+in
+  pkgs.writeShellScriptBin "check-code" ''
+
+    ${common}
+    
+    echo_header "Checking code"
+    cd_working_dir
+
+    echo_step "Running go vet"
+    if ! ${pkgs.go}/bin/go vet .
+    then
+      echo_fail "go vet failed"
+      exit 1
+    fi
+
+    echo_step "Running go fmt"
+    if ! ${pkgs.go}/bin/go fmt .
+    then
+      echo_fail "go fmt failed"
+      exit 1
+    fi
+
+    echo_step "Running go mod tidy"
+    if ! ${pkgs.go}/bin/go mod tidy
+    then
+      echo_fail "go mod tidy failed"
+      exit 1
+    fi
+
+    echo_step "Running lint"
+    if ! ${pkgs.golangci-lint}/bin/golangci-lint run .
+    then
+      echo_fail "lint failed"
+      exit 1
+    fi
+
+    echo_step "Running gosec"
+    if ! ${pkgs.gosec}/bin/gosec .  
+    then
+      echo_fail "gosec failed"
+      exit 1
+    fi
+
+
+  ''
diff --git a/nix/scripts/common.nix b/nix/scripts/common.nix
new file mode 100644
index 0000000..93e91f4
--- /dev/null
+++ b/nix/scripts/common.nix
@@ -0,0 +1,144 @@
+{pkgs, ...}: ''
+    
+    if [ -t 1 ]; then
+      RED='\033[0;31m'
+      GREEN='\033[0;32m'
+      BLUE='\033[0;34m'
+      RESET='\033[0m'
+      BOLD='\033[1m'
+    else
+      RED=""
+      GREEN=""
+      BLUE=""
+      RESET=""
+    fi
+    
+    ## this function is used to change the working directory to the one specified in 
+    ## the USER_WORKING_DIR environment variable. this is necessary if you want to edit 
+    ## files in the repository.  
+    cd_working_dir() {
+            
+        working_dir=$(echo $USER_WORKING_DIR)
+        if [ -z "$working_dir" ]; then
+         echo_fail "USER_WORKING_DIR is not set"
+         echo_hint "Please set the USER_WORKING_DIR environment variable"
+         exit 1
+        fi
+        
+        if [ ! -d "$working_dir" ]; then
+         echo_fail "USER_WORKING_DIR does not exist"
+         echo_hint "Please set the USER_WORKING_DIR environment variable to a valid directory"
+         exit 1
+        fi
+        
+        cd $working_dir
+        git_root=$(git rev-parse --show-toplevel)
+        
+        if [ -z "$git_root" ]; then
+         echo_fail "Not a git repository"
+         echo_hint "Please run this script in a git repository"
+         exit 1
+        fi
+        
+        cd $git_root
+        
+        echo_ok "Changed directory to $working_dir"
+    }
+    
+    is_true() {
+      val="$1"
+      if [ "$val" = 'true' ] || [ "$val" = '1' ]; then
+          return 0
+      else
+          return 1
+      fi
+    }
+    
+    echo_header() {
+      ${pkgs.figlet}/bin/figlet -t -f doom -l "$1"
+    }
+    
+    echo_ok() {
+      echo -e "$GREEN✔$RESET $1"
+    }
+    
+    echo_delimiter() {
+      echo " "
+    }
+    
+    echo_section() {
+      echo -e "\n\n$BOLD$1$RESET"
+    }
+    
+    echo_text() {
+      echo -e "$1"
+    }
+    
+    echo_fail() {
+      echo -e "$RED✖ $1$RESET"
+    }
+    
+    echo_hint() {
+      echo -e "$BLUE→ $1$RESET"
+    }
+    
+    echo_step() {
+      echo -e "$BLUE•$RESET $1"
+    }
+    
+    convert_multiline_for_nix() {
+     printf "%s" "
+     }$1" | ${pkgs.gnused}/bin/sed 's/\\/\\\\/g; s/"/\\"/g; s/$/\\n/g' | tr -d '\n'
+    }
+    
+    if [ -z "$DEBUG_MODE" ]; then
+      DEBUG_MODE='false'
+    fi
+    
+    if [ -n "$CI_JOB_TOKEN" ]; then
+      CI_MODE='true'
+    fi
+    
+    if is_true "$DEBUG_MODE" || is_true "$CI_MODE"; then
+      set -x
+    fi    
+  
+    download_test_images() {
+
+        echo_step "Downloading docker images"
+        
+        if ! ${pkgs.docker}/bin/docker images | grep atmoz/sftp | grep alpine
+        then
+            echo_step "Downloading atmoz/sftp:alpine"
+            if ! ${pkgs.docker}/bin/docker pull atmoz/sftp:alpine
+            then
+                echo_fail "Failed to download atmoz/sftp:alpine"
+                exit 1
+            fi
+        fi
+        
+        if ! ${pkgs.docker}/bin/docker images | grep axllent/mailpit
+        then
+            echo_step "Downloading atmoz/sftp:alpine"
+            if ! ${pkgs.docker}/bin/docker pull axllent/mailpit
+            then
+                echo_fail "Failed to download axllent/mailpit"
+                exit 1
+            fi
+        fi
+    
+    }
+  
+    setup_go_env() {
+        echo_step "Changing directory to ${../..}"    
+        cd ${../..}
+        
+        echo_step "Setting GO111MODULE=auto"
+        if ! ${pkgs.go}/bin/go env -w GO111MODULE=auto
+        then
+            echo_fail "Failed to set GO111MODULE=auto"
+            exit 1
+        fi    
+    }
+  
+''
diff --git a/nix/scripts/go-task.nix b/nix/scripts/go-task.nix
new file mode 100644
index 0000000..b0f8cad
--- /dev/null
+++ b/nix/scripts/go-task.nix
@@ -0,0 +1,43 @@
+{
+  pkgs,
+}: let
+  taskfile = builtins.toFile "taskfile.yml" ''
+    version: '3'
+
+    tasks:
+      default:
+        cmds:
+          - task --list
+        silent: true
+
+      run-tests:
+        desc: Execute all tests.
+        env:
+          TEST_BY_TASK: true
+        cmds:
+          - run-tests
+      run-extended-tests:
+        desc: Execute all tests including cover, bench and race tests.
+        env:
+          TEST_BY_TASK: true
+        cmds:
+          - run-extended-tests
+      build-cover-report:
+        desc: Build coverage report.
+        aliases:
+          - cr
+        env:
+          TEST_BY_TASK: true
+        cmds:
+          - build-cover-report    
+      check-code:
+        desc: Check code style, linting, and formatting.
+        env:
+          USER_WORKING_DIR: "{{.USER_WORKING_DIR}}"
+        cmds:
+          - check-code
+  '';
+in
+  pkgs.writeShellScriptBin "task" ''
+    ${pkgs.go-task}/bin/task -t "${taskfile}" "$@"
+  ''
diff --git a/nix/scripts/run-ci-tests.nix b/nix/scripts/run-ci-tests.nix
new file mode 100644
index 0000000..2a73fba
--- /dev/null
+++ b/nix/scripts/run-ci-tests.nix
@@ -0,0 +1,38 @@
+{
+  pkgs,
+  lib,
+  ...
+}: let
+  common = pkgs.callPackage ./common.nix {};
+in
+  pkgs.writeShellScriptBin "run-ci-tests" ''
+
+    ${common}
+
+    echo_header "Running CI tests"
+
+    download_test_images
+    setup_go_env
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask" -cover ./...
+    then
+      echo_fail "Failed to run tests"
+      exit 1
+    fi   
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask,bench" -bench ./...
+    then
+      echo_fail "Failed to run benchmarks"
+      exit 1
+    fi   
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask,race" -race ./...  
+    then
+      echo_fail "Failed to run race tests"
+      exit 1
+    fi   
+
+  ''
diff --git a/nix/scripts/run-extended-tests.nix b/nix/scripts/run-extended-tests.nix
new file mode 100644
index 0000000..53b0ac9
--- /dev/null
+++ b/nix/scripts/run-extended-tests.nix
@@ -0,0 +1,40 @@
+{
+  pkgs,
+  lib,
+  ...
+}: let
+  common = pkgs.callPackage ./common.nix {};
+in
+  pkgs.writeShellScriptBin "run-extended-tests" ''
+
+    ${common}
+
+    echo_header "Running tests"
+    echo_step "Downloading docker images"
+
+    download_test_images
+    setup_go_env
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask" -cover ./...
+    then
+      echo_fail "Failed to run tests"
+      exit 1
+    fi   
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask,bench" -bench ./...
+    then
+      echo_fail "Failed to run benchmarks"
+      exit 1
+    fi   
+
+    echo_section "Running tests"
+    if ! ${pkgs.go}/bin/go test -tags "runOnTask,race" -race ./...  
+    then
+      echo_fail "Failed to run race tests"
+      exit 1
+    fi   
+
+
+  ''
diff --git a/nix/scripts/run-tests.nix b/nix/scripts/run-tests.nix
new file mode 100644
index 0000000..5c79288
--- /dev/null
+++ b/nix/scripts/run-tests.nix
@@ -0,0 +1,66 @@
+{
+  pkgs,
+  lib,
+  ...
+}: let
+  common = pkgs.callPackage ./common.nix {};
+in
+  pkgs.writeShellScriptBin "run-tests" ''
+
+    ${common}
+
+    echo_header "Running tests"
+    
+    
+    selection=$(${pkgs.gum}/bin/gum choose "run specific test" "run all tests" "Cancel")
+
+    if [[ "$selection" == "Cancel" ]]; then
+      echo_ok "Exiting."
+      exit 0
+    fi
+
+    download_test_images
+    setup_go_env
+
+    if [[ "$selection" == "run all tests" ]]; then
+      echo_ok "Running all tests"
+      if ! ${pkgs.go}/bin/go test -v -failfast ./...
+      then
+        echo_fail "ERROR: Tests failed, check your Go!"
+        exit 1
+      fi
+      echo_ok "All tests passed!"
+      exit 0
+    fi
+
+    test_files=$(${pkgs.findutils}/bin/find . -name "*_test.go")
+    
+    test_names=""
+    for file in $test_files; do
+        names=$(${pkgs.gnugrep}/bin/grep -oP 'func (Test\w+)' $file | ${pkgs.gawk}/bin/gawk '{print $2}')
+        test_names+="$names "
+    done
+    
+    if [[ -z "$test_names" ]]; then
+        echo_fail "No tests found!"
+        exit 1
+    fi
+    
+    selected_tests=$(echo "$test_names" | ${pkgs.coreutils}/bin/tr ' ' '\n' | ${pkgs.gum}/bin/gum filter --no-limit  )
+    
+    if [[ -z "$selected_tests" ]]; then
+        echo_ok "No tests selected, exiting."
+        exit 0
+    fi
+    
+    
+    if ! ${pkgs.go}/bin/go test -run "$(echo $selected_tests | tr '\n' "")"
+    then
+      echo_fail "ERROR: Tests failed, check your Go!"
+      exit 1
+    fi
+
+    echo_ok "All tests passed!"    
+    
+
+  ''
diff --git a/queue.go b/queue.go
index 4415df6..7bf6a35 100644
--- a/queue.go
+++ b/queue.go
@@ -86,9 +86,7 @@ func (q *Queue) Enqueue(job GenericJob) error {
 
 		// Run topological sort on jobs in the ready queue
 		readyJobList := []GenericJob{}
-		for _, readyJob := range q.readyQueue {
-			readyJobList = append(readyJobList, readyJob)
-		}
+		readyJobList = append(readyJobList, q.readyQueue...)
 
 		currentReadyJobIDs := make(map[JobID]struct{})
 		for _, job := range readyJobList {
@@ -96,9 +94,7 @@ func (q *Queue) Enqueue(job GenericJob) error {
 		}
 
 		var fullJobList []GenericJob
-		for _, job := range readyJobList {
-			fullJobList = append(fullJobList, job)
-		}
+		fullJobList = append(fullJobList, readyJobList...)
 
 		for i := range q.processedJobs {
 			id := q.processedJobs[i].ID
@@ -154,7 +150,7 @@ func (q *Queue) ClearProcessedJobs() {
 	}
 }
 
-func (q *Queue) isDependency(id JobID) bool {
+func (q *Queue) IsDependency(id JobID) bool {
 	for _, deps := range q.pendingDependencies {
 		for _, depID := range deps {
 			if depID == id {
diff --git a/runnable-fileoperation.go b/runnable-fileoperation.go
index 333b8b2..d7eb1bf 100644
--- a/runnable-fileoperation.go
+++ b/runnable-fileoperation.go
@@ -43,7 +43,7 @@ func (f *FileOperationResult) GetResult() string {
 }
 
 func (f *FileOperationResult) GetError() (string, int) {
-	if f.Success == false {
+	if !f.Success {
 		return "FileOperationResult failed", 1
 	}
 	return "", 0
diff --git a/runnable-mail_test.go b/runnable-mail_test.go
index c87f279..39208af 100644
--- a/runnable-mail_test.go
+++ b/runnable-mail_test.go
@@ -78,7 +78,8 @@ func startTestSMTPDockerImageAndContainer(t *testing.T, port string, ctx context
 			Timeout: &timeout,
 			Signal:  "SIGKILL",
 		}
-		newCtx, _ := context.WithTimeout(context.Background(), 60*time.Second)
+		newCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+		defer cancel()
 		if err := cli.ContainerStop(newCtx, resp.ID, stopOptions); err != nil {
 			t.Errorf("ContainerStop returned error: %v", err)
 		}
diff --git a/runnable-sftp_test.go b/runnable-sftp_test.go
index 2cb82dc..2e047c3 100644
--- a/runnable-sftp_test.go
+++ b/runnable-sftp_test.go
@@ -78,7 +78,10 @@ func startSFTPTestDockerImageAndContainer(t *testing.T, host string, port string
 			Timeout: &timeout,
 			Signal:  "SIGKILL",
 		}
-		newCtx, _ := context.WithTimeout(context.Background(), 60*time.Second)
+
+		newCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+		defer cancel()
+
 		if err := cli.ContainerStop(newCtx, resp.ID, stopOptions); err != nil {
 			t.Errorf("ContainerStop returned error: %v", err)
 		}
diff --git a/schedule-time.go b/schedule-time.go
index f04c32d..0cca797 100644
--- a/schedule-time.go
+++ b/schedule-time.go
@@ -4,6 +4,7 @@
 package jobqueue
 
 import (
+	"errors"
 	"fmt"
 	"time"
 )
@@ -16,12 +17,17 @@ type TimeScheduler struct {
 }
 
 func (s *TimeScheduler) Schedule(job GenericJob, eventBus *EventBus) error {
+
+	if job == nil {
+		return ErrParameterIsNil
+	}
+
 	if s.executed {
 		return fmt.Errorf("%w: job %s already scheduled", ErrJobAlreadyScheduled, job.GetID())
 	}
 
 	if s.Time.Before(time.Now()) {
-		return fmt.Errorf("%w: scheduled time is in the past", ErrInvalidTime)
+		return errors.Join(ErrScheduleTimeIsInThePast, ErrInvalidTime, fmt.Errorf("time: %s", s.Time))
 	}
 
 	if s.jobs == nil {
@@ -29,6 +35,10 @@ func (s *TimeScheduler) Schedule(job GenericJob, eventBus *EventBus) error {
 	}
 
 	id := job.GetID()
+	if id == "" {
+		return ErrJobIDEmpty
+	}
+
 	if _, ok := s.jobs[id]; ok {
 		return fmt.Errorf("%w: job %s already scheduled", ErrJobAlreadyScheduled, id)
 	}
@@ -36,7 +46,7 @@ func (s *TimeScheduler) Schedule(job GenericJob, eventBus *EventBus) error {
 	stopChan := make(StopChan)
 	s.jobs[id] = stopChan
 
-	timer := time.NewTimer(s.Time.Sub(time.Now()))
+	timer := time.NewTimer(time.Until(s.Time))
 
 	go func() {
 		select {
diff --git a/schedule-time_test.go b/schedule-time_test.go
new file mode 100644
index 0000000..635008d
--- /dev/null
+++ b/schedule-time_test.go
@@ -0,0 +1,39 @@
+package jobqueue
+
+import (
+	"gotest.tools/v3/assert"
+	"testing"
+	"time"
+)
+
+func TestStructure(t *testing.T) {
+
+	now := time.Now()
+	oneHourBefore := now.Add(-1 * time.Hour)
+	onHourAfter := now.Add(1 * time.Hour)
+
+	ts := TimeScheduler{
+		Time: oneHourBefore,
+	}
+
+	err := ts.Schedule(nil, nil)
+	assert.ErrorIs(t, err, ErrParameterIsNil)
+
+	job := &MockGenericJob{}
+	err = ts.Schedule(job, nil)
+	assert.ErrorIs(t, err, ErrScheduleTimeIsInThePast)
+
+	ts.Time = onHourAfter
+	err = ts.Schedule(job, nil)
+	assert.ErrorIs(t, err, ErrJobIDEmpty)
+
+	job.ID = "job-id"
+	err = ts.Schedule(job, nil)
+	assert.NilError(t, err)
+	time.Sleep(2 * time.Second)
+	err = ts.Cancel("job-id")
+	assert.NilError(t, err)
+
+	//time.Sleep(200 * time.Second)
+
+}
diff --git a/scheduler-inotify.go b/scheduler-inotify.go
index e605dbf..2585f83 100644
--- a/scheduler-inotify.go
+++ b/scheduler-inotify.go
@@ -62,7 +62,7 @@ func (s *InotifyScheduler) Schedule(job GenericJob, eventBus *EventBus) error {
 					}
 				}
 
-			case _, _ = <-watcher.Errors:
+			case <-watcher.Errors:
 
 			case <-stopChan:
 				_ = watcher.Close()
diff --git a/stat.go b/stat.go
index 2179936..eb303cd 100644
--- a/stat.go
+++ b/stat.go
@@ -26,11 +26,11 @@ func StopResourceMonitoring() {
 	}
 }
 
-func resetResourceStatsForTesting() {
-	if mainResourceStats != nil {
-		StopResourceMonitoring()
-	}
-}
+//func resetResourceStatsForTesting() {
+//	if mainResourceStats != nil {
+//		StopResourceMonitoring()
+//	}
+//}
 
 func GetCpuUsage() float64 {
 
diff --git a/worker.go b/worker.go
index c0a70de..604df3e 100644
--- a/worker.go
+++ b/worker.go
@@ -1,6 +1,8 @@
 // Copyright 2023 schukai GmbH
 // SPDX-License-Identifier: AGPL-3.0
 
+// #nosec
+
 package jobqueue
 
 import (
@@ -241,7 +243,7 @@ func (w *LocalWorker) run(jobChannel chan GenericJob, stopChan chan bool, cancel
 				w.mu.Lock()
 				defer w.mu.Unlock()
 				if w.manager != nil {
-					w.manager.Sync(job)
+					_ = w.manager.Sync(job)
 				}
 
 			}()
-- 
GitLab