diff --git a/src/infrastructure/lint/linter/__tests__/javelin/missing-dependency.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/missing-dependency.lint-test index 57320ba5cb..c1cad4b29f 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/missing-dependency.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/missing-dependency.lint-test @@ -1,6 +1,6 @@ /** * @requires javelin-install - * @provides javelin-test + * @provides javelin-test-missing-dependency * * @javelin-installs JX.Test * diff --git a/src/infrastructure/lint/linter/__tests__/javelin/private-access.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/private-access.lint-test index 49ad64a068..5070c51a0f 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/private-access.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/private-access.lint-test @@ -1,7 +1,7 @@ /** * @requires javelin-install * javelin-history - * @provides javelin-test + * @provides javelin-test-private-access * * @javelin-installs JX.Test * diff --git a/src/infrastructure/lint/linter/__tests__/javelin/undocumented-install.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/undocumented-install.lint-test index 7c28ff0e66..0d302e586c 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/undocumented-install.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/undocumented-install.lint-test @@ -1,6 +1,6 @@ /** * @requires javelin-install - * @provides javelin-test + * @provides javelin-test-undocumented-install * * @javelin-installs JX.Test * diff --git a/src/infrastructure/lint/linter/__tests__/javelin/uninstalled-documentation.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/uninstalled-documentation.lint-test index 693502f92d..4adcf444e0 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/uninstalled-documentation.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/uninstalled-documentation.lint-test @@ -1,6 +1,6 @@ /** * @requires javelin-install - * @provides javelin-test + * @provides javelin-test-uninstalled-documentation * * @javelin-installs JX.Test * @javelin-installs JX.Test2 diff --git a/src/infrastructure/lint/linter/__tests__/javelin/unknown-dependency.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/unknown-dependency.lint-test index 5acdcd117b..5281440be2 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/unknown-dependency.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/unknown-dependency.lint-test @@ -1,7 +1,7 @@ /** * @requires javelin-install * javelin-does-not-exist - * @provides javelin-test + * @provides javelin-test-unknown-dependency * * @javelin-installs JX.Test * diff --git a/src/infrastructure/lint/linter/__tests__/javelin/unnecessary-dependency.lint-test b/src/infrastructure/lint/linter/__tests__/javelin/unnecessary-dependency.lint-test index c7bf4a198f..9afc5eaae6 100644 --- a/src/infrastructure/lint/linter/__tests__/javelin/unnecessary-dependency.lint-test +++ b/src/infrastructure/lint/linter/__tests__/javelin/unnecessary-dependency.lint-test @@ -1,7 +1,7 @@ /** * @requires javelin-install * javelin-workflow - * @provides javelin-test + * @provides javelin-test-unnecessary-dependency * * @javelin-installs JX.Test * diff --git a/src/infrastructure/lint/linter/__tests__/map.php b/src/infrastructure/lint/linter/__tests__/map.php index fae3e4d3c1..501a7c4d79 100644 --- a/src/infrastructure/lint/linter/__tests__/map.php +++ b/src/infrastructure/lint/linter/__tests__/map.php @@ -7,37 +7,42 @@ */ return array( 'names' => array( - 'missing-dependency.lint-test' => '3f966f42', - 'private-access.lint-test' => 'fa831308', - 'undocumented-install.lint-test' => 'e575f333', - 'uninstalled-documentation.lint-test' => '9b61fd26', - 'unknown-dependency.lint-test' => 'abc533df', - 'unnecessary-dependency.lint-test' => '1608e0c1', + 'missing-dependency.lint-test' => 'd9d7accf', + 'private-access.lint-test' => 'c5a55aba', + 'undocumented-install.lint-test' => 'b40b9df9', + 'uninstalled-documentation.lint-test' => 'a7b7fab0', + 'unknown-dependency.lint-test' => '70c6209f', + 'unnecessary-dependency.lint-test' => 'f7db3a25', ), 'symbols' => array( - 'javelin-test' => '3f966f42', + 'javelin-test-missing-dependency' => 'd9d7accf', + 'javelin-test-private-access' => 'c5a55aba', + 'javelin-test-undocumented-install' => 'b40b9df9', + 'javelin-test-uninstalled-documentation' => 'a7b7fab0', + 'javelin-test-unknown-dependency' => '70c6209f', + 'javelin-test-unnecessary-dependency' => 'f7db3a25', ), 'requires' => array( - '1608e0c1' => array( + '70c6209f' => array( 'javelin-install', - 'javelin-workflow', + 'javelin-does-not-exist', ), - '3f966f42' => array( + 'a7b7fab0' => array( 'javelin-install', ), - '9b61fd26' => array( + 'b40b9df9' => array( 'javelin-install', ), - 'abc533df' => array( + 'c5a55aba' => array( 'javelin-install', - 'javelin-does-not-exist', + 'javelin-history', ), - 'e575f333' => array( + 'd9d7accf' => array( 'javelin-install', ), - 'fa831308' => array( + 'f7db3a25' => array( 'javelin-install', - 'javelin-history', + 'javelin-workflow', ), ), 'packages' => array(), -- 2.51.2 From cc52588832e192c5b5ef8caa01c789b00ecae404 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 1 Mar 2026 16:41:28 +0100 Subject: [PATCH 2/4] Fix crash 'Duplicate entry' in account creation via external GitHub auth Summary: Do not recreate the thumbnail profile picture via a file transform when creating a Phorge account via external authentication via GitLab but use the already existing thumbnail file. This saves database space and fixes this crash: #1062: Duplicate entry 'PHID-FILE-ffps4pzs36kkikrpvgdh-profile' for key 'originalPHID' Closes T16532 Ref T16527 Ref T16528 Co-authored-by: Andre Klapper Test Plan: Prepare GitHub OAuth: * Log into Github, create a new OAuth App via https://github.com/settings/applications/new * As an admin, set up GitHub Auth provider via http://phorge.localhost/auth/ * Copy Client ID into "OAuth App ID" and Client Secret into "OAuth App Secret" * Log out as admin Then: * Go to http://phorge.localhost/auth/start/ and click GitHub button * Enter Username and Password on GitHub website, click "Sign in" * Enter data on resulting http://phorge.localhost/auth/register/ and click "Register Account" * Without this patch, get an error `#1062: Duplicate entry 'PHID-FILE-foobar' for key 'originalPHID'` Also, to be sure: * Visit your {nav profile > Manage > Edit Settings > External Accounts} and Unlink your GitHub profile (with the "x" icon) * From the same page {nav Link External Account} to link GitHub again and confirm: it still works Reviewers: O1 Blessed Committers, avivey, aklapper Reviewed By: O1 Blessed Committers, avivey, aklapper Subscribers: avivey, ajhalili2006, #affects_filed_by_subscribed_by_recap_time_squad, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Tags: #auth, #affects_filed_by_subscribed_by_recap_time_squad Maniphest Tasks: T16527, T16528, T16532 Differential Revision: https://we.phorge.it/D26803 --- .../auth/controller/PhabricatorAuthRegisterController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index 7c330c75f1..2e5fbb9e81 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -699,7 +699,6 @@ final class PhabricatorAuthRegisterController // do meaningful policy checks anyway since they have not registered yet. // Reaching this means the user holds the account secret key and the // registration secret key, and thus has permission to view the image. - $file = id(new PhabricatorFileQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(array($phid)) @@ -710,7 +709,7 @@ final class PhabricatorAuthRegisterController $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); - return $xform->executeTransformExplicit($file); + return $xform->getOrExecuteTransformExplicit($file); } protected function renderError($message) { -- 2.51.2 From 48ac6306dfeb6739dda063e24a028e03f387205f Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sun, 1 Mar 2026 17:28:56 +0100 Subject: [PATCH 3/4] Remove "disable-utf8mb4" option from /bin/storage Summary: The utf8mb4 character set was added to MySQL in version 5.5.3 in 03/2010 per https://web.archive.org/web/20191114070848/https://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-3.html It's unclear since when MariaDB exactly supports it, but https://mariadb.com/docs/release-notes/community-server/changelogs/changelogs-mariadb-55-series/mariadb-5560-changelog for MariaDB 5.5.60 from 04/2018 mentions it. For completeness: Some web sources state that MariaDB 10.2.5 was the first version in which utf8mb4 tables can have indexes with type VARCHAR(255). In any case, Phorge requires MySQL 8.0 and MariaDB 10.5.1 since rP555fb3a8 / rP24666e18, see https://we.phorge.it/book/phorge/article/installation_guide/ Thus there is no sense in still supporting this storage option nowadays. (For some overall historical context, see https://adamhooper.medium.com/in-mysql-never-use-utf8-use-utf8mb4-11761243e434 ) Refs T16400 Test Plan: * Run `./bin/storage help`, see no more "--disable-utf8mb4" under "OPTION REFERENCE" * Grep the source code Reviewers: O1 Blessed Committers, valerio.bozzolan Reviewed By: O1 Blessed Committers, valerio.bozzolan Subscribers: tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T16400 Differential Revision: https://we.phorge.it/D26811 --- scripts/sql/manage_storage.php | 11 +---------- .../PhabricatorStorageManagementAPI.php | 16 ---------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/scripts/sql/manage_storage.php b/scripts/sql/manage_storage.php index c4fea2efa1..f2e22ec36c 100755 --- a/scripts/sql/manage_storage.php +++ b/scripts/sql/manage_storage.php @@ -70,14 +70,6 @@ try { 'help' => pht( 'Do not actually change anything, just show what would be changed.'), ), - array( - 'name' => 'disable-utf8mb4', - 'help' => pht( - 'Disable %s, even if the database supports it. This is an '. - 'advanced feature used for testing internal changes; you '. - 'should not normally use this flag.', - 'utf8mb4'), - ), )); } catch (PhutilArgumentUsageException $ex) { $args->printUsageException($ex); @@ -202,8 +194,7 @@ foreach ($refs as $ref) { ->setHost($default_host) ->setPort($default_port) ->setPassword($password) - ->setNamespace($args->getArg('namespace')) - ->setDisableUTF8MB4($args->getArg('disable-utf8mb4')); + ->setNamespace($args->getArg('namespace')); PhabricatorEnv::overrideConfig('mysql.user', $api->getUser()); $ref->setUser($selected_user); diff --git a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php index 31de0aaced..2a5a95dde6 100644 --- a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php +++ b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php @@ -9,7 +9,6 @@ final class PhabricatorStorageManagementAPI extends Phobject { private $password; private $namespace; private $conns = array(); - private $disableUTF8MB4; const CHARSET_DEFAULT = 'CHARSET'; const CHARSET_SORT = 'CHARSET_SORT'; @@ -21,15 +20,6 @@ final class PhabricatorStorageManagementAPI extends Phobject { const TABLE_STATUS = 'patch_status'; const TABLE_HOSTSTATE = 'hoststate'; - public function setDisableUTF8MB4($disable_utf8_mb4) { - $this->disableUTF8MB4 = $disable_utf8_mb4; - return $this; - } - - public function getDisableUTF8MB4() { - return $this->disableUTF8MB4; - } - public function setNamespace($namespace) { $this->namespace = $namespace; PhabricatorLiskDAO::pushStorageNamespace($namespace); @@ -312,12 +302,6 @@ final class PhabricatorStorageManagementAPI extends Phobject { } public function isCharacterSetAvailable($character_set) { - if ($character_set == 'utf8mb4') { - if ($this->getDisableUTF8MB4()) { - return false; - } - } - $conn = $this->getConn(null); return self::isCharacterSetAvailableOnConnection($character_set, $conn); } -- 2.51.2 From 7b91b1e77ff528a58e18fa9cef687b4c43aba553 Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Sat, 21 Feb 2026 11:14:19 +0100 Subject: [PATCH 4/4] Allow filtering Bulk Job Query results by status Summary: As a sysadmin, I'm much more interested in tasks which have not successfully completed, so allow me to filter Bulk Jobs on status to exclude completed ones. Closes T16514 Test Plan: * Go to http://phorge.localhost/applications/view/PhabricatorManiphestApplication/ and allow yourself to "Can Bulk Edit Tasks" * Create some Bulk Edit Jobs with different statuses (e.g. Completed; or created but not run yet which is called Confirmed) * Go to http://phorge.localhost/daemon/bulk/query/advanced/, see a new "Status" dropdown * Set some "Status" values, get filtered results * Check that this also works on http://phorge.localhost/daemon/bulk/query/authored/ and http://phorge.localhost/daemon/bulk/query/all/ * Click "Save Query", add a name, see new query in the sidebar, click new sidebar entry, get filtered results as expected Reviewers: O1 Blessed Committers, mainframe98, valerio.bozzolan Reviewed By: O1 Blessed Committers, mainframe98, valerio.bozzolan Subscribers: avivey, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno Maniphest Tasks: T16514 Differential Revision: https://we.phorge.it/D26781 --- .../query/PhabricatorWorkerBulkJobSearchEngine.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php index 3b7d05897f..5e477ec155 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php @@ -26,6 +26,10 @@ final class PhabricatorWorkerBulkJobSearchEngine $query->withAuthorPHIDs($map['authorPHIDs']); } + if ($map['status']) { + $query->withStatuses(array($map['status'])); + } + return $query; } @@ -35,6 +39,16 @@ final class PhabricatorWorkerBulkJobSearchEngine ->setLabel(pht('Authors')) ->setKey('authorPHIDs') ->setAliases(array('author', 'authors')), + id(new PhabricatorSearchSelectField()) + ->setKey('status') + ->setLabel(pht('Status')) + ->setOptions(array( + '' => pht('All'), + PhabricatorWorkerBulkJob::STATUS_CONFIRM => pht('Confirming'), + PhabricatorWorkerBulkJob::STATUS_WAITING => pht('Waiting'), + PhabricatorWorkerBulkJob::STATUS_RUNNING => pht('Running'), + PhabricatorWorkerBulkJob::STATUS_COMPLETE => pht('Complete'), + )), ); }