Browse Source

Merge pull request #27217 from nextcloud/techdebt/noid/make-debugging-a-cron-job-easier

John Molakvoæ 2 years ago
parent
commit
f7a4ff4d63
32 changed files with 260 additions and 54 deletions
  1. 2 2
      apps/accessibility/composer/composer/installed.php
  2. 2 2
      apps/admin_audit/composer/composer/installed.php
  3. 2 2
      apps/cloud_federation_api/composer/composer/installed.php
  4. 2 2
      apps/comments/composer/composer/installed.php
  5. 2 2
      apps/contactsinteraction/composer/composer/installed.php
  6. 2 2
      apps/dav/composer/composer/installed.php
  7. 2 2
      apps/encryption/composer/composer/installed.php
  8. 2 2
      apps/federatedfilesharing/composer/composer/installed.php
  9. 2 2
      apps/federation/composer/composer/installed.php
  10. 2 2
      apps/files/composer/composer/installed.php
  11. 2 2
      apps/files_sharing/composer/composer/installed.php
  12. 2 2
      apps/files_trashbin/composer/composer/installed.php
  13. 2 2
      apps/files_versions/composer/composer/installed.php
  14. 2 2
      apps/lookup_server_connector/composer/composer/installed.php
  15. 2 2
      apps/oauth2/composer/composer/installed.php
  16. 2 2
      apps/provisioning_api/composer/composer/installed.php
  17. 2 2
      apps/settings/composer/composer/installed.php
  18. 2 2
      apps/sharebymail/composer/composer/installed.php
  19. 2 2
      apps/systemtags/composer/composer/installed.php
  20. 2 2
      apps/testing/composer/composer/installed.php
  21. 2 2
      apps/twofactor_backupcodes/composer/composer/installed.php
  22. 2 2
      apps/updatenotification/composer/composer/installed.php
  23. 2 2
      apps/user_ldap/composer/composer/installed.php
  24. 2 2
      apps/user_status/composer/composer/installed.php
  25. 2 2
      apps/workflowengine/composer/composer/installed.php
  26. 156 0
      core/Command/Background/Job.php
  27. 1 0
      core/register_command.php
  28. 1 0
      lib/composer/composer/autoload_classmap.php
  29. 1 0
      lib/composer/composer/autoload_static.php
  30. 29 4
      lib/private/BackgroundJob/JobList.php
  31. 15 0
      lib/public/BackgroundJob/IJobList.php
  32. 7 0
      tests/lib/BackgroundJob/DummyJobList.php

+ 2 - 2
apps/accessibility/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/admin_audit/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/cloud_federation_api/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/comments/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/contactsinteraction/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/dav/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/encryption/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/federatedfilesharing/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/federation/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/files/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/files_sharing/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/files_trashbin/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/files_versions/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/lookup_server_connector/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/oauth2/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/provisioning_api/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/settings/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/sharebymail/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/systemtags/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/testing/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/twofactor_backupcodes/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/updatenotification/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/user_ldap/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => '62a814f4fbdec485e97e6b55a8320a02ced488bb',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => '62a814f4fbdec485e97e6b55a8320a02ced488bb',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/user_status/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 2 - 2
apps/workflowengine/composer/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../',
         'aliases' => array(),
-        'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+        'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
         'name' => '__root__',
         'dev' => false,
     ),
@@ -16,7 +16,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../',
             'aliases' => array(),
-            'reference' => 'dbf7905149222115a2cd0334efcf8c93afa8683e',
+            'reference' => '7239b3d560b52ed604484a0a549dd5805e3f21e4',
             'dev_requirement' => false,
         ),
     ),

+ 156 - 0
core/Command/Background/Job.php

@@ -0,0 +1,156 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021, Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * 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, or (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Core\Command\Background;
+
+use OCP\BackgroundJob\IJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\ILogger;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Job extends Command {
+	/** @var IJobList */
+	protected $jobList;
+	/** @var ILogger */
+	protected $logger;
+
+	public function __construct(IJobList $jobList,
+								ILogger $logger) {
+		parent::__construct();
+		$this->jobList = $jobList;
+		$this->logger = $logger;
+	}
+
+	protected function configure(): void {
+		$this
+			->setName('background-job:execute')
+			->setDescription('Execute a single background job manually')
+			->addArgument(
+				'job-id',
+				InputArgument::REQUIRED,
+				'The ID of the job in the database'
+			)
+			->addOption(
+				'force-execute',
+				null,
+				InputOption::VALUE_NONE,
+				'Force execute the background job, independent from last run and being reserved'
+			)
+		;
+	}
+
+	protected function execute(InputInterface $input, OutputInterface $output): int {
+		$jobId = (int) $input->getArgument('job-id');
+
+		$job = $this->jobList->getById($jobId);
+		if ($job === null) {
+			$output->writeln('<error>Job with ID ' . $jobId . ' could not be found in the database</error>');
+			return 1;
+		}
+
+		$this->printJobInfo($jobId, $job, $output);
+		$output->writeln('');
+
+		$lastRun = $job->getLastRun();
+		if ($input->getOption('force-execute')) {
+			$lastRun = 0;
+			$output->writeln('<comment>Forcing execution of the job</comment>');
+			$output->writeln('');
+
+			$this->jobList->resetBackgroundJob($job);
+		}
+
+		$job = $this->jobList->getById($jobId);
+		$job->execute($this->jobList, $this->logger);
+		$job = $this->jobList->getById($jobId);
+
+		if ($lastRun !== $job->getLastRun()) {
+			$output->writeln('<info>Job executed!</info>');
+			$output->writeln('');
+
+			if ($job instanceof \OC\BackgroundJob\TimedJob || $job instanceof \OCP\BackgroundJob\TimedJob) {
+				$this->printJobInfo($jobId, $job, $output);
+			}
+		} else {
+			$output->writeln('<comment>Job was not executed because it is not due</comment>');
+			$output->writeln('Specify the <question>--force-execute</question> option to run it anyway');
+		}
+
+		return 0;
+	}
+
+	protected function printJobInfo(int $jobId, IJob $job, OutputInterface$output): void {
+		$row = $this->jobList->getDetailsById($jobId);
+
+		$lastRun = new \DateTime();
+		$lastRun->setTimestamp((int) $row['last_run']);
+		$lastChecked = new \DateTime();
+		$lastChecked->setTimestamp((int) $row['last_checked']);
+		$reservedAt = new \DateTime();
+		$reservedAt->setTimestamp((int) $row['reserved_at']);
+
+		$output->writeln('Job class:            ' . get_class($job));
+		$output->writeln('Arguments:            ' . json_encode($job->getArgument()));
+
+		$isTimedJob = $job instanceof \OC\BackgroundJob\TimedJob || $job instanceof \OCP\BackgroundJob\TimedJob;
+		if ($isTimedJob) {
+			$output->writeln('Type:                 timed');
+		} elseif ($job instanceof \OC\BackgroundJob\QueuedJob || $job instanceof \OCP\BackgroundJob\QueuedJob) {
+			$output->writeln('Type:                 queued');
+		} else {
+			$output->writeln('Type:                 job');
+		}
+
+		$output->writeln('');
+		$output->writeln('Last checked:         ' . $lastChecked->format(\DateTimeInterface::ATOM));
+		if ((int) $row['reserved_at'] === 0) {
+			$output->writeln('Reserved at:          -');
+		} else {
+			$output->writeln('Reserved at:          <comment>' . $reservedAt->format(\DateTimeInterface::ATOM) . '</comment>');
+		}
+		$output->writeln('Last executed:        ' . $lastRun->format(\DateTimeInterface::ATOM));
+		$output->writeln('Last duration:        ' . $row['execution_duration']);
+
+		if ($isTimedJob) {
+			$reflection = new \ReflectionClass($job);
+			$intervalProperty = $reflection->getProperty('interval');
+			$intervalProperty->setAccessible(true);
+			$interval = $intervalProperty->getValue($job);
+
+			$nextRun = new \DateTime();
+			$nextRun->setTimestamp($row['last_run'] + $interval);
+
+			if ($nextRun > new \DateTime()) {
+				$output->writeln('Next execution:       <comment>' . $nextRun->format(\DateTimeInterface::ATOM) . '</comment>');
+			} else {
+				$output->writeln('Next execution:       <info>' . $nextRun->format(\DateTimeInterface::ATOM) . '</info>');
+			}
+		}
+	}
+}

+ 1 - 0
core/register_command.php

@@ -90,6 +90,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
 	$application->add(new OC\Core\Command\Background\Cron(\OC::$server->getConfig()));
 	$application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig()));
 	$application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig()));
+	$application->add(new OC\Core\Command\Background\Job(\OC::$server->getJobList(), \OC::$server->getLogger()));
 
 	$application->add(\OC::$server->query(\OC\Core\Command\Broadcast\Test::class));
 

+ 1 - 0
lib/composer/composer/autoload_classmap.php

@@ -818,6 +818,7 @@ return array(
     'OC\\Core\\Command\\Background\\Ajax' => $baseDir . '/core/Command/Background/Ajax.php',
     'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php',
     'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php',
+    'OC\\Core\\Command\\Background\\Job' => $baseDir . '/core/Command/Background/Job.php',
     'OC\\Core\\Command\\Background\\WebCron' => $baseDir . '/core/Command/Background/WebCron.php',
     'OC\\Core\\Command\\Base' => $baseDir . '/core/Command/Base.php',
     'OC\\Core\\Command\\Broadcast\\Test' => $baseDir . '/core/Command/Broadcast/Test.php',

+ 1 - 0
lib/composer/composer/autoload_static.php

@@ -847,6 +847,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Core\\Command\\Background\\Ajax' => __DIR__ . '/../../..' . '/core/Command/Background/Ajax.php',
         'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php',
         'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php',
+        'OC\\Core\\Command\\Background\\Job' => __DIR__ . '/../../..' . '/core/Command/Background/Job.php',
         'OC\\Core\\Command\\Background\\WebCron' => __DIR__ . '/../../..' . '/core/Command/Background/WebCron.php',
         'OC\\Core\\Command\\Base' => __DIR__ . '/../../..' . '/core/Command/Base.php',
         'OC\\Core\\Command\\Broadcast\\Test' => __DIR__ . '/../../..' . '/core/Command/Broadcast/Test.php',

+ 29 - 4
lib/private/BackgroundJob/JobList.php

@@ -236,19 +236,29 @@ class JobList implements IJobList {
 	 * @return IJob|null
 	 */
 	public function getById($id) {
+		$row = $this->getDetailsById($id);
+
+		if ($row) {
+			return $this->buildJob($row);
+		}
+
+		return null;
+	}
+
+	public function getDetailsById(int $id): ?array {
 		$query = $this->connection->getQueryBuilder();
 		$query->select('*')
 			->from('jobs')
 			->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
-		$result = $query->execute();
+		$result = $query->executeQuery();
 		$row = $result->fetch();
 		$result->closeCursor();
 
 		if ($row) {
-			return $this->buildJob($row);
-		} else {
-			return null;
+			return $row;
 		}
+
+		return null;
 	}
 
 	/**
@@ -330,4 +340,19 @@ class JobList implements IJobList {
 			->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT)));
 		$query->execute();
 	}
+
+	/**
+	 * Reset the $job so it executes on the next trigger
+	 *
+	 * @param IJob $job
+	 * @since 23.0.0
+	 */
+	public function resetBackgroundJob(IJob $job): void {
+		$query = $this->connection->getQueryBuilder();
+		$query->update('jobs')
+			->set('last_run',  $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))
+			->set('reserved_at',  $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))
+			->where($query->expr()->eq('id', $query->createNamedParameter($job->getId()), IQueryBuilder::PARAM_INT));
+		$query->executeStatement();
+	}
 }

+ 15 - 0
lib/public/BackgroundJob/IJobList.php

@@ -97,6 +97,13 @@ interface IJobList {
 	 */
 	public function getById($id);
 
+	/**
+	 * @param int $id
+	 * @return array|null
+	 * @since 23.0.0
+	 */
+	public function getDetailsById(int $id): ?array;
+
 	/**
 	 * set the job that was last ran to the current time
 	 *
@@ -129,4 +136,12 @@ interface IJobList {
 	 * @since 12.0.0
 	 */
 	public function setExecutionTime(IJob $job, $timeTaken);
+
+	/**
+	 * Reset the $job so it executes on the next trigger
+	 *
+	 * @param IJob $job
+	 * @since 23.0.0
+	 */
+	public function resetBackgroundJob(IJob $job): void;
 }

+ 7 - 0
tests/lib/BackgroundJob/DummyJobList.php

@@ -117,6 +117,10 @@ class DummyJobList extends \OC\BackgroundJob\JobList {
 		return null;
 	}
 
+	public function getDetailsById(int $id): ?array {
+		return null;
+	}
+
 	/**
 	 * set the lastRun of $job to now
 	 *
@@ -128,4 +132,7 @@ class DummyJobList extends \OC\BackgroundJob\JobList {
 
 	public function setExecutionTime(IJob $job, $timeTaken) {
 	}
+
+	public function resetBackgroundJob(IJob $job): void {
+	}
 }