Build Playground links with wordpress-playground-action #179
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Accessibility Checker Plugin with Ref Param | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| ref_param: | |
| description: "Ref param string to set in EDAC_REF_PARAM (optional)" | |
| required: false | |
| type: string | |
| pull_request: | |
| types: [labeled] | |
| release: | |
| types: [created, published] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| actions: read # wordpress-playground-action verifies the artifact exists before linking | |
| jobs: | |
| build-plugin: | |
| name: Build plugin zip | |
| runs-on: ubuntu-latest | |
| # Run on release, manual, or PR when a specific label is added | |
| if: | | |
| github.event_name == 'release' || | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'pull_request' && (contains(github.event.pull_request.labels.*.name, 'gha-build') || contains(github.event.pull_request.labels.*.name, 'gha-build-all'))) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine mode and ref param for this run | |
| id: setref | |
| run: | | |
| MODE="${{ github.event_name }}" | |
| echo "mode=$MODE" >> $GITHUB_OUTPUT | |
| # Manual run: take input if provided; else empty | |
| if [ "$MODE" = "workflow_dispatch" ]; then | |
| REF_INPUT="${{ github.event.inputs.ref_param }}" | |
| echo "ref_param=${REF_INPUT}" >> $GITHUB_OUTPUT | |
| # For manual mode: if ref is provided, skip primary build | |
| if [ -n "$REF_INPUT" ]; then | |
| echo "skip_primary=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "skip_primary=false" >> $GITHUB_OUTPUT | |
| fi | |
| # Release: use fixed ref 'woocommerce' | |
| elif [ "$MODE" = "release" ]; then | |
| echo "ref_param=woocommerce" >> $GITHUB_OUTPUT | |
| echo "skip_primary=false" >> $GITHUB_OUTPUT | |
| # PR labeled: check which label | |
| else | |
| # Check if gha-build-all label is present (build both) | |
| if echo "${{ github.event.pull_request.labels.*.name }}" | grep -q "gha-build-all"; then | |
| echo "ref_param=woocommerce" >> $GITHUB_OUTPUT | |
| echo "skip_primary=false" >> $GITHUB_OUTPUT | |
| else | |
| # gha-build label (build primary only) | |
| echo "ref_param=" >> $GITHUB_OUTPUT | |
| echo "skip_primary=false" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| - name: Show selected mode/ref | |
| run: | | |
| echo "Triggered on: $GITHUB_EVENT_NAME" | |
| echo "Mode: ${{ steps.setref.outputs.mode }}" | |
| echo "Head ref: ${{ github.head_ref }}" | |
| echo "PR number: ${{ github.event.pull_request.number }}" | |
| echo "Release tag: ${{ github.event.release.tag_name }}" | |
| echo "Ref param: ${{ steps.setref.outputs.ref_param }}" | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Cache node_modules | |
| id: cache-node-modules | |
| uses: actions/cache@v4 | |
| with: | |
| path: node_modules | |
| key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node_modules- | |
| - name: Set up PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.2' | |
| tools: composer | |
| coverage: none | |
| - name: Cache Composer dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: vendor | |
| key: ${{ runner.os }}-composer-vendor-${{ hashFiles('**/composer.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-composer-vendor- | |
| - name: Install Composer dependencies | |
| run: | | |
| if [ -f composer.json ]; then composer install --no-dev --prefer-dist --no-progress --no-interaction; else echo "No composer.json"; fi | |
| - name: Install npm dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' && hashFiles('package.json') != '' | |
| run: npm ci --prefer-offline --no-audit | |
| - name: Extract plugin version and commit hash | |
| id: version | |
| run: | | |
| VERSION=$(grep "Version:" accessibility-checker.php | head -1 | sed 's/.*Version:[[:space:]]*\([^ ]*\).*/\1/') | |
| SHORT_SHA=$(git rev-parse --short HEAD) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT | |
| echo "Plugin version: $VERSION" | |
| echo "Short commit hash: $SHORT_SHA" | |
| - name: Verify EDAC_REF_PARAM is empty before first build | |
| if: steps.setref.outputs.skip_primary != 'true' | |
| run: | | |
| grep -n "define( 'EDAC_REF_PARAM', '' )" accessibility-checker.php || { echo "EDAC_REF_PARAM should be empty for first build"; exit 1; } | |
| - name: Build primary dist zip (empty ref) | |
| if: steps.setref.outputs.skip_primary != 'true' | |
| run: | | |
| echo "Building primary zip with empty ref..." | |
| npm run dist | |
| ZIP_PATH=$(ls -1 dist/*.zip build/*.zip 2>/dev/null | head -n 1 || true) | |
| if [ -z "$ZIP_PATH" ]; then | |
| ZIP_PATH=$(ls -1 *.zip 2>/dev/null | head -n 1 || true) | |
| fi | |
| if [ -z "$ZIP_PATH" ]; then | |
| echo "Error: Could not locate produced zip after npm run dist" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p builds | |
| # Construct the new filename based on trigger mode | |
| VERSION="${{ steps.version.outputs.version }}" | |
| SHORT_SHA="${{ steps.version.outputs.short_sha }}" | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| # PR: accessibility-checker-{version}-{prnumber}-{hash}.zip | |
| PRIMARY_ZIP_NAME="accessibility-checker-${VERSION}-${{ github.event.pull_request.number }}-${SHORT_SHA}.zip" | |
| elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| # Manual: accessibility-checker-{version}-{hash}.zip | |
| PRIMARY_ZIP_NAME="accessibility-checker-${VERSION}-${SHORT_SHA}.zip" | |
| elif [ "${{ github.event_name }}" = "release" ]; then | |
| # Release: accessibility-checker-{version}.zip | |
| PRIMARY_ZIP_NAME="accessibility-checker-${VERSION}.zip" | |
| else | |
| # Fallback | |
| PRIMARY_ZIP_NAME="accessibility-checker-${VERSION}-${SHORT_SHA}.zip" | |
| fi | |
| mv "$ZIP_PATH" "builds/$PRIMARY_ZIP_NAME" | |
| PRIMARY_ZIP_BASENAME="${PRIMARY_ZIP_NAME%.zip}" | |
| unzip -q "builds/$PRIMARY_ZIP_NAME" -d "builds/$PRIMARY_ZIP_BASENAME" | |
| PRIMARY_ZIP_FOLDER="builds/$PRIMARY_ZIP_BASENAME" | |
| PRIMARY_ZIP_NAME_NO_EXT="${PRIMARY_ZIP_NAME%.zip}" | |
| echo "PRIMARY_ZIP_PATH=builds/$PRIMARY_ZIP_NAME" >> $GITHUB_ENV | |
| echo "PRIMARY_ZIP_NAME=$PRIMARY_ZIP_NAME" >> $GITHUB_ENV | |
| echo "PRIMARY_ZIP_NAME_NO_EXT=$PRIMARY_ZIP_NAME_NO_EXT" >> $GITHUB_ENV | |
| echo "PRIMARY_ZIP_FOLDER=$PRIMARY_ZIP_FOLDER" >> $GITHUB_ENV | |
| echo "Produced primary zip: builds/$PRIMARY_ZIP_NAME" | |
| echo "Produced primary folder: $PRIMARY_ZIP_FOLDER" | |
| - name: Upload primary build artifact | |
| if: steps.setref.outputs.skip_primary != 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.PRIMARY_ZIP_NAME_NO_EXT }} | |
| path: ${{ env.PRIMARY_ZIP_FOLDER }} | |
| if-no-files-found: error | |
| - name: Check if ref build is needed | |
| id: check_ref | |
| run: | | |
| MODE="${{ steps.setref.outputs.mode }}" | |
| REF_VALUE="${{ steps.setref.outputs.ref_param }}" | |
| # For manual: build ref if ref is provided | |
| # For release: always build ref (woocommerce) | |
| # For PR: never build ref | |
| if [ "$MODE" = "workflow_dispatch" ] && [ -n "$REF_VALUE" ]; then | |
| echo "need_ref_build=true" >> $GITHUB_OUTPUT | |
| echo "Ref build needed with value: $REF_VALUE" | |
| elif [ "$MODE" = "release" ] && [ -n "$REF_VALUE" ]; then | |
| echo "need_ref_build=true" >> $GITHUB_OUTPUT | |
| echo "Ref build needed with value: $REF_VALUE" | |
| else | |
| echo "need_ref_build=false" >> $GITHUB_OUTPUT | |
| echo "No ref build needed (mode=$MODE, ref='$REF_VALUE')" | |
| fi | |
| - name: Update EDAC_REF_PARAM for second build | |
| if: steps.check_ref.outputs.need_ref_build == 'true' | |
| run: | | |
| chmod +x ./scripts/update-ref-param.sh | |
| ./scripts/update-ref-param.sh "${{ steps.setref.outputs.ref_param }}" | |
| - name: Verify EDAC_REF_PARAM change | |
| if: steps.check_ref.outputs.need_ref_build == 'true' | |
| run: | | |
| grep -n "EDAC_REF_PARAM" accessibility-checker.php || { echo "EDAC_REF_PARAM not found"; exit 1; } | |
| echo "Updated EDAC_REF_PARAM contents:" | |
| grep "EDAC_REF_PARAM" accessibility-checker.php | |
| - name: Build ref dist zip (custom ref) | |
| if: steps.check_ref.outputs.need_ref_build == 'true' | |
| run: | | |
| echo "Building ref zip with custom ref value..." | |
| npm run dist | |
| ZIP_PATH=$(ls -1 dist/*.zip build/*.zip 2>/dev/null | head -n 1 || true) | |
| if [ -z "$ZIP_PATH" ]; then | |
| ZIP_PATH=$(ls -1 *.zip 2>/dev/null | head -n 1 || true) | |
| fi | |
| if [ -z "$ZIP_PATH" ]; then | |
| echo "Error: Could not locate produced zip after npm run dist" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p builds | |
| VERSION="${{ steps.version.outputs.version }}" | |
| SHORT_SHA="${{ steps.version.outputs.short_sha }}" | |
| REF_VALUE="${{ steps.setref.outputs.ref_param }}" | |
| # Ref build naming: accessibility-checker-{version}-ref-{refvalue}-{hash}.zip | |
| REF_ZIP_NAME="accessibility-checker-${VERSION}-ref-${REF_VALUE}-${SHORT_SHA}.zip" | |
| mv "$ZIP_PATH" "builds/$REF_ZIP_NAME" | |
| REF_ZIP_BASENAME="${REF_ZIP_NAME%.zip}" | |
| unzip -q "builds/$REF_ZIP_NAME" -d "builds/$REF_ZIP_BASENAME" | |
| REF_ZIP_FOLDER="builds/$REF_ZIP_BASENAME" | |
| REF_ZIP_NAME_NO_EXT="${REF_ZIP_NAME%.zip}" | |
| echo "REF_ZIP_PATH=builds/$REF_ZIP_NAME" >> $GITHUB_ENV | |
| echo "REF_ZIP_NAME=$REF_ZIP_NAME" >> $GITHUB_ENV | |
| echo "REF_ZIP_NAME_NO_EXT=$REF_ZIP_NAME_NO_EXT" >> $GITHUB_ENV | |
| echo "REF_ZIP_FOLDER=$REF_ZIP_FOLDER" >> $GITHUB_ENV | |
| echo "Produced ref zip: builds/$REF_ZIP_NAME" | |
| echo "Produced ref folder: $REF_ZIP_FOLDER" | |
| - name: Upload ref build artifact | |
| if: steps.check_ref.outputs.need_ref_build == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.REF_ZIP_NAME_NO_EXT }} | |
| path: ${{ env.REF_ZIP_FOLDER }} | |
| if-no-files-found: error | |
| # Playground links are built by pattonwebz/wordpress-playground-action, which | |
| # resolves the uploaded artifact to a public nightly.link URL and assembles the | |
| # blueprint. | |
| # | |
| # Only the primary build gets a link. Not run on `release`: releases carry the | |
| # real zip as a release asset, and nightly.link only serves Actions artifacts. | |
| # The ref (woocommerce) build gets no link either - `need_ref_build` is never | |
| # true for a pull_request, so a ref link could only ever have surfaced in a | |
| # manual-dispatch job log, never in the PR comment. | |
| # | |
| # plugin-slug is set explicitly rather than inferred: the action strips a | |
| # version/hash suffix (accessibility-checker-1.47.0-123-abc1234 -> accessibility-checker) | |
| # but would leave the whole "…-1.47.0-ref-woocommerce-abc1234" shape intact, | |
| # so pinning it keeps the activation path correct regardless of artifact naming. | |
| - name: Playground link (primary build) | |
| id: playground_primary | |
| if: steps.setref.outputs.skip_primary != 'true' && github.event_name != 'release' | |
| uses: pattonwebz/wordpress-playground-action@v0 | |
| with: | |
| artifact-name: ${{ env.PRIMARY_ZIP_NAME_NO_EXT }} | |
| plugin-slug: accessibility-checker | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload to release (both zips) | |
| if: github.event_name == 'release' | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| ${{ env.PRIMARY_ZIP_PATH }} | |
| ${{ steps.check_ref.outputs.need_ref_build == 'true' && env.REF_ZIP_PATH || '' }} | |
| fail_on_unmatched_files: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Comment on PR with primary build details | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| env: | |
| # Routed through env: rather than interpolated into the script body - a | |
| # ${{ }} substitution lands in the JS source text before Node parses it. | |
| PRIMARY_PLAYGROUND_URL: ${{ steps.playground_primary.outputs.playground-url }} | |
| with: | |
| script: | | |
| const prNumber = context.payload.pull_request.number; | |
| const runId = context.runId; | |
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; | |
| const primaryName = process.env.PRIMARY_ZIP_NAME_NO_EXT; | |
| // Get the artifact ID for the download URL | |
| const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: runId, | |
| }); | |
| const artifact = artifacts.data.artifacts.find(a => a.name === primaryName); | |
| const downloadUrl = artifact | |
| ? `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/artifacts/${artifact.id}` | |
| : runUrl; | |
| const primaryPlaygroundUrl = process.env.PRIMARY_PLAYGROUND_URL; | |
| const primaryPlaygroundLine = primaryPlaygroundUrl | |
| ? `\n- **Playground (primary)**: [Open with plugin preinstalled](${primaryPlaygroundUrl})` | |
| : `\n- **Playground (primary)**: Not available for this run.`; | |
| const body = `✅ Accessibility Checker build (primary only)\n\n- **Artifact**: [Download ${primaryName}.zip](${downloadUrl})\n- **Workflow run**: [View logs](${runUrl})${primaryPlaygroundLine}`; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body, | |
| }); | |
| - name: Remove triggering label from PR | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = context.payload.pull_request.number; | |
| const labels = context.payload.pull_request.labels.map(l => l.name); | |
| const labelToRemove = labels.includes('gha-build-all') ? 'gha-build-all' : 'gha-build'; | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| name: labelToRemove, | |
| }); | |
| console.log(`Removed label '${labelToRemove}' from PR #${prNumber}`); | |
| } catch (e) { | |
| console.log(`Could not remove label '${labelToRemove}' from PR #${prNumber}: ${e.message}`); | |
| } | |
| - name: Summary | |
| env: | |
| PRIMARY_PLAYGROUND_URL: ${{ steps.playground_primary.outputs.playground-url }} | |
| MODE: ${{ steps.setref.outputs.mode }} | |
| SKIP_PRIMARY: ${{ steps.setref.outputs.skip_primary }} | |
| run: | | |
| echo "=== Build Summary ===" | |
| echo "Mode: ${{ steps.setref.outputs.mode }}" | |
| echo "Primary zip (empty ref): ${{ env.PRIMARY_ZIP_PATH }}" | |
| if [ -n "${PRIMARY_PLAYGROUND_URL:-}" ]; then | |
| echo "Playground (primary): ${PRIMARY_PLAYGROUND_URL}" | |
| elif [ "${MODE}" = "release" ]; then | |
| echo "Playground (primary): not built (releases ship the zip as a release asset)" | |
| elif [ "${SKIP_PRIMARY}" = "true" ]; then | |
| echo "Playground (primary): not built (this dispatch passed a ref param, so only the ref zip was built)" | |
| else | |
| echo "Playground (primary): not built for this run" | |
| fi | |
| if [ "${{ steps.check_ref.outputs.need_ref_build }}" = "true" ]; then | |
| echo "Ref zip (ref=${{ steps.setref.outputs.ref_param }}): ${{ env.REF_ZIP_PATH }}" | |
| else | |
| echo "Ref zip: Not built" | |
| fi | |
| if [ "${{ github.event_name }}" = "release" ]; then | |
| echo "Uploaded to release: ${{ github.event.release.html_url }}" | |
| fi |