[web-downloads] main: Allow deleting legacy Winlibs deletion jobs

From: Date: Fri, 02 Oct 2026 16:35:07 +0000
Subject: [web-downloads] main: Allow deleting legacy Winlibs deletion jobs
Groups: php.webmaster 
Request: Send a blank email to php-webmaster+get-34066@lists.php.net to get a copy of this message
Author: Shivam Mathur (shivammathur) Date: 2026-10-02T22:04:55+05:30 Commit: https://github.com/php/web-downloads/commit/165a1ad444b0d3125e1df6bbbf61a616754c79ef Raw diff: https://github.com/php/web-downloads/commit/165a1ad444b0d3125e1df6bbbf61a616754c79ef.diff Allow deleting legacy Winlibs deletion jobs Changed paths: M API.md M src/Http/Controllers/DeletePendingJobController.php M tests/Http/Controllers/DeletePendingJobControllerTest.php Diff: diff --git a/API.md b/API.md index 779e438..d3577cb 100644 --- a/API.md +++ b/API.md @@ -64,8 +64,8 @@ curl -i -X GET \ - Auth: Required - Purpose: Remove a queued build job before it is processed. - Request body (JSON): - - type (string, required): One of php, pecl, or winlibs. - - job (string, required): The job filename (for php/pecl) or directory name (for winlibs). + - type (string, required): One of php, pecl, winlibs, or winlibs-delete. + - job (string, required): The job filename (for php/pecl), directory name (for winlibs), or winlibs-delete-*.json filename (for legacy winlibs-delete jobs). - Success: 200 OK with { "status": "deleted" }. - Errors: - 400 if validation fails (missing/invalid fields). @@ -85,6 +85,12 @@ curl -i -X POST \ https://downloads.php.net/api/delete-pending-job ``` +To remove a legacy Winlibs deletion request, pass the filename from GET /api/list-builds without the winlibs-delete/ prefix: + +```json +{"type":"winlibs-delete","job":"winlibs-delete-7coij6somubocMYxf6G.json"} +``` + --- ### POST /api/php diff --git a/src/Http/Controllers/DeletePendingJobController.php b/src/Http/Controllers/DeletePendingJobController.php index 0162aa9..b275fef 100644 --- a/src/Http/Controllers/DeletePendingJobController.php +++ b/src/Http/Controllers/DeletePendingJobController.php @@ -23,8 +23,8 @@ public function __construct(string $inputPath = 'php://input', ?string $buildsDi protected function validate(array $data): bool { $validator = new Validator([ - 'type' => 'required|string|regex:/^(php|pecl|winlibs)$/i', - 'job' => 'required|string|regex:/^[A-Za-z0-9._-]+$/', + 'type' => 'required|string|regex:/\A(?:php|pecl|winlibs|winlibs-delete)\z/i', + 'job' => 'required|string|regex:/\A(?!\.{1,2}\z)[A-Za-z0-9._-]+\z/', ]); $validator->validate($data); @@ -34,6 +34,11 @@ protected function validate(array $data): bool if (!$valid) { http_response_code(400); echo 'Invalid request: ' . $validator; + } elseif (strtolower($data['type']) === 'winlibs-delete' + && preg_match('/\Awinlibs-delete-[A-Za-z0-9]+\.json\z/', $data['job']) !== 1) { + http_response_code(400); + echo 'Invalid request: job must be a winlibs-delete-*.json filename.'; + return false; } return $valid; @@ -78,7 +83,7 @@ private function deleteJob(string $type, string $jobName): void private function resolvePath(string $type, string $jobName): string { return match ($type) { - 'php', 'pecl' => $this->buildsDirectory . '/' . $type . '/' . $jobName, + 'php', 'pecl', 'winlibs-delete' => $this->buildsDirectory . '/' . $type . '/' . $jobName, 'winlibs' => $this->buildsDirectory . '/winlibs/' . $jobName, default => $this->buildsDirectory, }; diff --git a/tests/Http/Controllers/DeletePendingJobControllerTest.php b/tests/Http/Controllers/DeletePendingJobControllerTest.php index a90a2eb..81064b4 100644 --- a/tests/Http/Controllers/DeletePendingJobControllerTest.php +++ b/tests/Http/Controllers/DeletePendingJobControllerTest.php @@ -82,6 +82,73 @@ public function testDeletesWinlibsJobDirectory(): void unlink($inputFile); } + /** + * @throws JsonException + */ + public function testDeletesLegacyWinlibsDeleteFileAndLockWithoutTouchingActiveQueue(): void + { + $legacyDir = $this->tempDir . '/winlibs-delete'; + $activeDir = $this->tempDir . '/winlibs/delete-active'; + mkdir($legacyDir, 0755, true); + mkdir($activeDir, 0755, true); + + $jobName = 'winlibs-delete-7coij6somubocMYxf6G.json'; + $jobFile = $legacyDir . '/' . $jobName; + file_put_contents($jobFile, '{}'); + file_put_contents($jobFile . '.lock', ''); + file_put_contents($activeDir . '/data.json', '{}'); + + $inputFile = $this->createInputFile(json_encode([ + 'type' => 'WINLIBS-DELETE', + 'job' => $jobName, + ], JSON_THROW_ON_ERROR)); + + http_response_code(200); + $controller = new DeletePendingJobController($inputFile, $this->tempDir); + ob_start(); + $controller->handle(); + $output = ob_get_clean(); + + static::assertSame(200, http_response_code()); + static::assertJsonStringEqualsJsonString('{"status":"deleted"}', $output); + static::assertFileDoesNotExist($jobFile); + static::assertFileDoesNotExist($jobFile . '.lock'); + static::assertFileExists($activeDir . '/data.json'); + + unlink($inputFile); + } + + /** + * @throws JsonException + */ + public function testRejectsOtherFilesAndParentDirectoryNames(): void + { + $legacyDir = $this->tempDir . '/winlibs-delete'; + mkdir($legacyDir, 0755, true); + file_put_contents($legacyDir . '/other.json', '{}'); + + foreach ([ + ['type' => 'winlibs-delete', 'job' => 'other.json'], + ['type' => 'winlibs-delete', 'job' => 'winlibs-delete-..json'], + ['type' => 'winlibs', 'job' => '..'], + ] as $request) { + $inputFile = $this->createInputFile(json_encode($request, JSON_THROW_ON_ERROR)); + + http_response_code(200); + $controller = new DeletePendingJobController($inputFile, $this->tempDir); + ob_start(); + $controller->handle(); + $output = ob_get_clean(); + + static::assertSame(400, http_response_code()); + static::assertStringContainsString('Invalid request', $output); + static::assertDirectoryExists($this->tempDir); + static::assertFileExists($legacyDir . '/other.json'); + + unlink($inputFile); + } + } + /** * @throws JsonException */ @@ -109,4 +176,4 @@ private function createInputFile(string $json): string return $tempFile; } -} \ No newline at end of file +}

« previous php.webmaster (#34066) next »