[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
+}


Thread (1 message)

  • Shivam Mathur
« previous php.webmaster (#34066) next »