diff --git a/.github/workflows/reusable-phpunit-tests-v3.yml b/.github/workflows/reusable-phpunit-tests-v3.yml index 4f7addcd4e60e..02dd3e46a1dc8 100644 --- a/.github/workflows/reusable-phpunit-tests-v3.yml +++ b/.github/workflows/reusable-phpunit-tests-v3.yml @@ -13,6 +13,18 @@ on: required: false type: 'string' default: 'ubuntu-24.04' + repository: + description: 'The repository to check out and test. Defaults to the repository calling this workflow.' + required: false + type: 'string' + default: '' + # Also composes the Gutenberg build name, under the producer's constraint: a valid + # artifact name, so no slashes and none of : < > | * ? " + ref: + description: 'The branch, tag, or SHA to check out. Defaults to the ref that triggered the calling workflow.' + required: false + type: 'string' + default: '' php: description: 'The version of PHP to use, in the format of X.Y' required: true @@ -82,6 +94,11 @@ on: required: false type: string default: '' + overlay-artifact: + description: 'The name of a same-workflow artifact whose contents are unpacked over the checkout. Optional: for callers whose test files are not part of the repository being tested.' + required: false + type: string + default: '' secrets: CODECOV_TOKEN: description: 'The Codecov token required for uploading reports.' @@ -109,8 +126,10 @@ jobs: # Runs the PHPUnit tests for WordPress. # # Performs the following steps: + # - Requires a ref when the caller points at another repository. # - Sets environment variables. # - Checks out the repository. + # - Unpacks the caller-provided overlay artifact over the checkout, if any. # - Downloads the prepared Gutenberg build provided by the calling workflow. # - Sets up Node.js. # - Sets up PHP. @@ -135,6 +154,14 @@ jobs: contents: read steps: + # `ref` otherwise falls back to checkout's own default, which for another repository is + # its default branch: a "WordPress 6.2" job would test trunk and report green. + - name: Require a ref when testing another repository + if: ${{ inputs.repository != '' && inputs.ref == '' }} + run: | + echo 'Testing another repository needs an explicit ref.' >&2 + exit 1 + - name: Configure environment variables run: | echo "PHP_FPM_UID=$(id -u)" >> "$GITHUB_ENV" @@ -143,15 +170,30 @@ jobs: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + repository: ${{ inputs.repository || github.repository }} + ref: ${{ inputs.ref }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} persist-credentials: false + # If the caller specifies an overlay artifact then unpack it over the checkout. + - name: Download overlay artifact + if: inputs.overlay-artifact != '' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + # Without a run ID, this action reads only from the caller's current workflow run. + name: ${{ inputs.overlay-artifact }} + path: . + digest-mismatch: error + # Only WordPress 7.0+ include Gutenberg-maintained assets from a built zip file. - name: Download prepared Gutenberg build if: ${{ inputs.gutenberg-artifact }} uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: gutenberg-build + # Every branch in a run shares one artifact namespace, so the producer names each + # build after the ref it came from. Compose the same name here rather than taking + # it as an input: the two sides already agree on `ref`. + name: gutenberg-build${{ inputs.ref && format( '-{0}', inputs.ref ) || '' }} path: gutenberg digest-mismatch: error diff --git a/.github/workflows/reusable-prepare-gutenberg.yml b/.github/workflows/reusable-prepare-gutenberg.yml index c5e1f904a9680..1e95f09fd2a05 100644 --- a/.github/workflows/reusable-prepare-gutenberg.yml +++ b/.github/workflows/reusable-prepare-gutenberg.yml @@ -6,6 +6,19 @@ name: Prepare Gutenberg build on: workflow_call: + inputs: + repository: + description: 'The repository to check out. Defaults to the repository calling this workflow.' + required: false + type: 'string' + default: '' + # Names the uploaded build too, so it must also be a valid artifact name: no slashes, + # and none of : < > | * ? " — a `refs/heads/trunk` or `feature/x` value fails the upload. + ref: + description: 'The branch, tag, or SHA to check out. Defaults to the commit that started the calling workflow run.' + required: false + type: 'string' + default: '' outputs: gutenberg-sha: description: 'The immutable Gutenberg source SHA verified by this workflow.' @@ -19,18 +32,28 @@ jobs: prepare-gutenberg: name: Gutenberg runs-on: ubuntu-24.04 - timeout-minutes: 10 + # Two download attempts of up to six minutes each, where download.js retries internally. + timeout-minutes: 15 outputs: gutenberg-sha: ${{ steps.download.outputs.gutenberg-sha }} permissions: contents: read steps: + # `ref` otherwise falls back to this run's commit, which another repository does not have. + - name: Require a ref when preparing another repository + if: ${{ inputs.repository != '' && inputs.ref == '' }} + run: | + echo 'Preparing another repository needs an explicit ref.' >&2 + exit 1 + - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: - # Resolve the Gutenberg ref from the exact commit that started this workflow run. - ref: ${{ github.sha }} + repository: ${{ inputs.repository || github.repository }} + # Resolve the Gutenberg ref from the exact commit that started this workflow run, + # unless the caller is preparing a build for a repository or branch of its own. + ref: ${{ inputs.ref || github.sha }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} persist-credentials: false @@ -42,7 +65,17 @@ jobs: - name: Download and verify Gutenberg build id: download run: | - node tools/gutenberg/download.js + # Branches whose download.js predates the in-script retry stream the blob straight + # into tar in a single attempt, so an interrupted stream fails the run outright. + # One retry, not two: where download.js does retry, it already allows three tries of + # two minutes each, and a third attempt here would outrun `timeout-minutes` and have + # the job killed rather than reported. + if ! node tools/gutenberg/download.js; then + echo 'Gutenberg download failed; retrying in 5 seconds...' + sleep 5 + node tools/gutenberg/download.js + fi + gutenberg_sha="$(tr -d '\n' < gutenberg/.gutenberg-hash)" if [[ ! "$gutenberg_sha" =~ ^[a-fA-F0-9]{40}$ ]]; then echo "Expected a 40-character Gutenberg SHA, received: $gutenberg_sha" >&2 @@ -53,7 +86,10 @@ jobs: - name: Upload Gutenberg build uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: gutenberg-build + # Every branch in a run shares one artifact namespace, so name the build after the + # ref it came from. The PHPUnit consumer composes the same name from its own `ref`, + # so the name is never passed between them. + name: gutenberg-build${{ inputs.ref && format( '-{0}', inputs.ref ) || '' }} path: gutenberg/ if-no-files-found: error include-hidden-files: true