diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8facc27..57b2181 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: with: python-version: "3.13" - name: Bootstrap patched pip trust - uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@ff93e15dd3d3aa0b5ba8991fe5101a9e1be49c10 # patched-pip + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - name: Install policy dependencies run: >- python -m pip install --only-binary :all: PyYAML==6.0.2 @@ -57,12 +57,22 @@ jobs: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.13' + - name: Bootstrap verified dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + with: + clients: pip,python,composer,curl,git - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 with: php-version: ${{ matrix.php }} extensions: pcntl, posix - tools: composer:v2 + tools: composer:2.10.3 coverage: none + - name: Verify actual Composer dependency trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - run: composer install --prefer-dist --no-interaction - name: Validate installer scripts run: | @@ -85,6 +95,8 @@ jobs: node --test scripts/ci/test-cli-release-channel.js node --test scripts/ci/test-cli-upgrade-result.js node --test scripts/ci/test-release-metadata.js + - name: Reverify Composer before project scripts + uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - run: composer test build-phar: @@ -94,13 +106,24 @@ jobs: timeout-minutes: 15 steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.13' + - name: Bootstrap verified dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + with: + clients: pip,python,composer,curl,git - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 with: php-version: '8.4' - tools: composer:v2, box:4.6.6 + tools: composer:2.10.3, box:4.6.6 coverage: none ini-values: phar.readonly=0 - run: php scripts/generate-build-info.php + - name: Verify actual Composer dependency trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - run: composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader - run: box compile --no-parallel - run: box info build/dw.phar @@ -115,12 +138,22 @@ jobs: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 with: fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.13' + - name: Bootstrap verified dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + with: + clients: pip,python,composer,curl,git - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 with: php-version: '8.4' - tools: composer:v2, box:4.6.6 + tools: composer:2.10.3, box:4.6.6 coverage: none ini-values: phar.readonly=0 + - name: Verify actual Composer dependency trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust - name: Verify dw.phar reproduces bit-for-bit run: scripts/verify-reproducible-build.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 455f650..7d66fc0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -342,6 +342,7 @@ jobs: needs.release-preflight.outputs.public_assets_present != 'true' outputs: phar-name: dw.phar + launcher-vendor-id: ${{ steps.launcher_vendor.outputs.artifact-id }} env: BOX_VERSION: '4.6.6' GITHUB_TOKEN: ${{ github.token }} @@ -352,11 +353,21 @@ jobs: fetch-depth: 0 ref: ${{ needs.resolve-release.outputs.commit }} + - name: Set up Python for verified dependency trust + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.13' + + - name: Bootstrap verified dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + with: + clients: pip,python,composer,curl,git + - name: Setup PHP uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 with: php-version: ${{ env.PHP_VERSION }} - tools: composer:v2, box:${{ env.BOX_VERSION }} + tools: composer:2.10.3, box:${{ env.BOX_VERSION }} coverage: none ini-values: phar.readonly=0 @@ -376,6 +387,9 @@ jobs: export DW_CLI_COMMIT="$(git rev-parse HEAD)" php scripts/generate-build-info.php + - name: Verify actual Composer dependency trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + - name: Install dependencies run: composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader @@ -395,6 +409,15 @@ jobs: php build/dw.phar --version || true box info build/dw.phar + - name: Retain locked launcher qualification dependencies + id: launcher_vendor + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: launcher-vendor + path: vendor/ + if-no-files-found: error + retention-days: 1 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: dw-phar @@ -896,7 +919,7 @@ jobs: with: php-version: '8.4' extensions: pcntl, posix - tools: composer:v2 + tools: none coverage: none - name: Install macOS qualification tools @@ -906,7 +929,7 @@ jobs: HOMEBREW_NO_AUTO_UPDATE: '1' HOMEBREW_NO_INSTALL_CLEANUP: '1' run: | - brew install php@8.4 composer + brew install php@8.4 qualification_php_prefix="$(brew --prefix php@8.4)" printf '%s/bin\n' "$qualification_php_prefix" >> "$GITHUB_PATH" @@ -915,9 +938,7 @@ jobs: timeout-minutes: 2 run: | command -v php - command -v composer php --version - composer --version php -r ' if (PHP_VERSION_ID < 80200 || !extension_loaded("pcntl") || !extension_loaded("posix")) { fwrite(STDERR, "Native launcher qualification requires external PHP 8.2+ with pcntl and posix.\n"); @@ -933,11 +954,13 @@ jobs: ./build/dw-${{ matrix.name }} runtime:check ./build/dw-${{ matrix.name }} list - - name: Install native launcher qualification dependencies + - name: Restore locked launcher qualification dependencies if: needs.resolve-release.outputs.cache_warm != 'true' - timeout-minutes: 5 - run: | - composer install --no-dev --prefer-dist --no-interaction + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + artifact-ids: ${{ needs.build-phar.outputs.launcher-vendor-id }} + path: vendor + digest-mismatch: error - name: Qualify direct signals in the packaged POSIX launcher if: needs.resolve-release.outputs.cache_warm != 'true' diff --git a/scripts/ci/test-release-workflow-metadata.js b/scripts/ci/test-release-workflow-metadata.js index 28cfae3..66d6720 100644 --- a/scripts/ci/test-release-workflow-metadata.js +++ b/scripts/ci/test-release-workflow-metadata.js @@ -10,6 +10,18 @@ const repository = path.resolve(__dirname, '../..'); const resolver = path.join(__dirname, 'release-version.js'); const workflowPath = path.join(repository, '.github/workflows/release.yml'); +test('native launcher qualification reuses the guarded PHAR dependency artifact', () => { + const workflow = fs.readFileSync(workflowPath, 'utf8'); + const phar = workflow.slice(workflow.indexOf('\n build-phar:'), workflow.indexOf('\n build-binary:')); + const native = workflow.split('\n build-binary:')[1].split(/\n {2}(?=\S)/)[0]; + + assert.match(phar, /launcher-vendor-id: \$\{\{ steps\.launcher_vendor\.outputs\.artifact-id \}\}/); + assert.match(phar, /id: launcher_vendor[\s\S]*?name: launcher-vendor\n\s+path: vendor\//); + assert.match(workflow, /artifact-ids: \$\{\{ needs\.build-phar\.outputs\.launcher-vendor-id \}\}\n\s+path: vendor\n\s+digest-mismatch: error/); + assert.match(native, /needs: \[resolve-release, release-preflight, build-phar\]/); + assert.doesNotMatch(native, /composer install/); +}); + function resolveMetadata(tag) { const normalized = execFileSync(process.execPath, [resolver, 'normalize', tag], { encoding: 'utf8',