Author: Shivam Mathur (shivammathur) Committer: GitHub (web-flow) Pusher: shivammathur Date: 2026-10-13T19:40:46+05:30
Commit: https://github.com/php/web-downloads/commit/cb5b90301b4cd942e0e2aed1f7770f002b0518eb Raw diff: https://github.com/php/web-downloads/commit/cb5b90301b4cd942e0e2aed1f7770f002b0518eb.diff Merge pull request #8 from shivammathur/feature/winlibs-architecture-filter Allow publishing Winlibs artifacts for one architecture Changed paths: M .github/workflows/winlibs.yml M API.md M src/Console/Command/WinlibsCommand.php M src/Http/Controllers/WinlibsController.php M tests/Console/Command/WinlibsCommandTest.php M tests/Http/Controllers/WinlibsControllerTest.php Diff: diff --git a/.github/workflows/winlibs.yml b/.github/workflows/winlibs.yml index c10ccd3..39b0d48 100644 --- a/.github/workflows/winlibs.yml +++ b/.github/workflows/winlibs.yml @@ -35,6 +35,11 @@ on: options: - 'true' - 'false' + arch: + description: 'Artifact architecture to publish' + type: choice + default: all + options: [all, x64, x86, arm64] jobs: print-inputs: runs-on: ubuntu-latest @@ -53,14 +58,26 @@ jobs: echo "| vs_version_targets | ${{ inputs.vs_version_targets }} |" >> $GITHUB_STEP_SUMMARY echo "| stability | ${{ inputs.stability }} |" >> $GITHUB_STEP_SUMMARY echo "| update_series | ${{ inputs.update_series }} |" >> $GITHUB_STEP_SUMMARY + echo "| arch | ${{ inputs.arch }} |" >> $GITHUB_STEP_SUMMARY test: runs-on: ubuntu-latest environment: downloads.php.net steps: + - name: Check architecture filtering support + if: inputs.arch != 'all' + run: | + response=$(curl --silent --show-error --location --request POST \ + --header 'Authorization: Bearer ${{ secrets.AUTH_TOKEN }}' \ + --data '{ "arch": "unsupported" }' --write-out '\n%{http_code}' \ + https://downloads.php.net/api/winlibs) + if [[ "$response" != *'The arch field must match'* || "$response" != *$'\n400' ]]; then + echo 'The downloads server does not yet support filtering Winlibs artifacts by architecture.' + exit 1 + fi - name: Upload run: | - curl \ + curl --fail-with-body \ --request POST \ --location https://downloads.php.net/api/winlibs \ --header 'Authorization: Bearer ${{ secrets.AUTH_TOKEN }}' \ - --data '{ "library": "${{ inputs.library }}", "ref": "${{ inputs.ref }}", "type": "${{ inputs.type }}", "workflow_run_id": "${{ inputs.workflow_run_id }}", "php_versions": "${{ inputs.php_versions }}", "vs_version_targets": "${{ inputs.vs_version_targets }}", "stability": "${{ inputs.stability }}", "token": "${{ secrets.TOKEN }}", "update_series": "${{ inputs.update_series }}" }' + --data '{ "library": "${{ inputs.library }}", "ref": "${{ inputs.ref }}", "type": "${{ inputs.type }}", "workflow_run_id": "${{ inputs.workflow_run_id }}", "php_versions": "${{ inputs.php_versions }}", "vs_version_targets": "${{ inputs.vs_version_targets }}", "stability": "${{ inputs.stability }}", "token": "${{ secrets.TOKEN }}", "update_series": "${{ inputs.update_series }}", "arch": "${{ inputs.arch }}" }' diff --git a/API.md b/API.md index 7c28d40..9376c69 100644 --- a/API.md +++ b/API.md @@ -159,6 +159,7 @@ curl -i -X POST \ - `vs_version_targets` (string, required): Comma-separated list matching `^(v[c|s]\d{2})(,v[c|s]\d{2})*$`. - `stability` (string, required): `stable`, `staging`, or both (comma-separated) matching `^(stable|staging)(,(stable|staging))?$`. - `update_series` (string, required): `true` or `false`. + - `arch` (string, optional): `all` (default), `x64`, `x86`, or `arm64`. Only matching artifacts are published and their series entries updated; other architectures remain unchanged. - `token` (string, required): GitHub token used to download artifacts. - Success: `200 OK`, empty body. - Errors: diff --git a/src/Console/Command/WinlibsCommand.php b/src/Console/Command/WinlibsCommand.php index 3be1677..e0360e0 100644 --- a/src/Console/Command/WinlibsCommand.php +++ b/src/Console/Command/WinlibsCommand.php @@ -58,6 +58,10 @@ public function handle(): int $data = json_decode((string) file_get_contents($directoryPath . '/data.json'), true, 512, JSON_THROW_ON_ERROR); $files = glob($directoryPath . '/*.zip'); $files = $this->parseFiles($files); + $arch = $data['arch'] ?? 'all'; + if ($arch !== 'all') { + $files = array_filter($files, static fn (array $file): bool => $file['arch'] === $arch); + } if (empty($files)) { throw new Exception('No valid files found in ' . basename($directoryPath)); } diff --git a/src/Http/Controllers/WinlibsController.php b/src/Http/Controllers/WinlibsController.php index 56334a5..e7b359e 100644 --- a/src/Http/Controllers/WinlibsController.php +++ b/src/Http/Controllers/WinlibsController.php @@ -20,10 +20,11 @@ protected function validate(array $data): bool 'vs_version_targets' => 'required|string|regex:/^(v[c|s]\d{2})(,v[c|s]\d{2})*$/', 'stability' => 'required|string|regex:/^(stable|staging)(,(stable|staging))?$/', 'update_series' => 'required|string|regex:/^(true|false)$/', + 'arch' => 'required|string|regex:/\A(all|x64|x86|arm64)\z/', 'token' => 'required|string', ]); - $validator->validate($data); + $validator->validate($data + ['arch' => 'all']); $valid = $validator->isValid; diff --git a/tests/Console/Command/WinlibsCommandTest.php b/tests/Console/Command/WinlibsCommandTest.php index ebcdba7..a7308af 100644 --- a/tests/Console/Command/WinlibsCommandTest.php +++ b/tests/Console/Command/WinlibsCommandTest.php @@ -481,6 +481,74 @@ public function testSortsPhpSeriesFileAfterAddingLibrary(): void ); } + #[DataProvider('architectureFilterProvider')] + public function testPublishesOnlySelectedArchitecture(string $type, string $arch): void + { + $job = $this->winlibsDirectory . '/lib'; + $deps = $this->baseDirectory . ($type === 'php' ? '/php-sdk/deps' : '/pecl/deps'); + mkdir($job, 0755, true); + mkdir($deps . '/series', 0755, true); + file_put_contents($job . '/data.json', json_encode([ + 'type' => $type, 'library' => 'lib', 'ref' => '1.0', + 'vs_version_targets' => 'vs17', 'php_versions' => '8.4', + 'stability' => 'staging', 'update_series' => 'true', 'arch' => $arch, + ])); + foreach (['x64', 'x86', 'arm64'] as $artifactArch) { + $filename = "lib-1.0-vs17-$artifactArch.zip"; + $destination = $type === 'php' ? "$deps/vs17/$artifactArch" : $deps; + if (!is_dir($destination)) { + mkdir($destination, 0755, true); + } + file_put_contents("$destination/$filename", 'published'); + file_put_contents("$job/$filename", 'rebuilt'); + file_put_contents("$deps/series/packages-8.4-vs17-$artifactArch-staging.txt", "lib-0.9-vs17-$artifactArch.zip"); + } + + $command = new WinlibsCommand(); + $command->options = ['base-directory' => $this->baseDirectory, 'builds-directory' => $this->buildsDirectory]; + $this->assertSame(0, $command->handle()); + + foreach (['x64', 'x86', 'arm64'] as $artifactArch) { + $selected = $arch === 'all' || $artifactArch === $arch; + $destination = $type === 'php' ? "$deps/vs17/$artifactArch" : $deps; + $this->assertStringEqualsFile("$destination/lib-1.0-vs17-$artifactArch.zip", $selected ? 'rebuilt' : 'published'); + if ($type === 'php') { + $version = $selected ? '1.0' : '0.9'; + $this->assertStringEqualsFile("$deps/series/packages-8.4-vs17-$artifactArch-staging.txt", "lib-$version-vs17-$artifactArch.zip"); + } + } + $this->assertDirectoryDoesNotExist($job); + } + + public static function architectureFilterProvider(): array + { + return [ + ['php', 'arm64'], ['php', 'x64'], ['php', 'x86'], ['php', 'all'], + ['pecl', 'arm64'], + ]; + } + + public function testMissingSelectedArchitectureDoesNotPublishOtherArtifacts(): void + { + $job = $this->winlibsDirectory . '/lib'; + mkdir($job, 0755, true); + file_put_contents($job . '/data.json', json_encode([ + 'type' => 'php', 'library' => 'lib', 'ref' => '1.0', + 'vs_version_targets' => 'vs17', 'php_versions' => '8.4', + 'stability' => 'staging', 'update_series' => 'true', 'arch' => 'arm64', + ])); + file_put_contents($job . '/lib-1.0-vs17-x64.zip', 'rebuilt'); + $command = new WinlibsCommand(); + $command->options = ['base-directory' => $this->baseDirectory, 'builds-directory' => $this->buildsDirectory]; + ob_start(); + $result = $command->handle(); + $output = ob_get_clean(); + $this->assertSame(1, $result); + $this->assertStringContainsString('No valid files found', $output); + $this->assertDirectoryDoesNotExist($this->baseDirectory . '/php-sdk'); + $this->assertFileExists($job . '/lib-1.0-vs17-x64.zip'); + } + public function testCommandHandlesMissingBaseDirectory(): void { $command = new WinlibsCommand(); diff --git a/tests/Http/Controllers/WinlibsControllerTest.php b/tests/Http/Controllers/WinlibsControllerTest.php index f1ef347..5f70833 100644 --- a/tests/Http/Controllers/WinlibsControllerTest.php +++ b/tests/Http/Controllers/WinlibsControllerTest.php @@ -6,6 +6,7 @@ use App\Http\Controllers\WinlibsController; use JsonException; use Override; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class MockWinlibsController extends WinlibsController { @@ -31,6 +32,37 @@ public function handle(): void } class WinlibsControllerTest extends TestCase { + #[DataProvider('architectureProvider')] + public function testArchitectureValidation(array $override, bool $expected): void + { + $controller = new class extends WinlibsController { + public function validateRequest(array $data): bool { return parent::validate($data); } + }; + $data = $override + [ + 'library' => 'zlib', 'ref' => '1.3.2', 'type' => 'php', + 'workflow_run_id' => '123', 'php_versions' => '8.4', + 'vs_version_targets' => 'vs17', 'stability' => 'staging', + 'update_series' => 'true', 'token' => 'test', + ]; + ob_start(); + try { + $this->assertSame($expected, $controller->validateRequest($data)); + } finally { + ob_end_clean(); + http_response_code(200); + } + } + + public static function architectureProvider(): array + { + return [ + [[], true], [['arch' => 'all'], true], [['arch' => 'arm64'], true], + [['arch' => 'x64'], true], [['arch' => 'x86'], true], + [['arch' => ''], false], [['arch' => 'x64,arm64'], false], + [['arch' => "arm64\n"], false], [['arch' => null], false], + ]; + } + /** * @throws JsonException */
