Add GitHub Actions workflow for PR builds with WordPress Playground testing#289
Add GitHub Actions workflow for PR builds with WordPress Playground testing#289superdav42 merged 15 commits intomainfrom
Conversation
…esting Implements automated PR build and testing workflow that: ## Automated Build Process - Triggers on all PR changes to the repository - Sets up Node.js 20 and PHP 8.2 environment - Installs all dependencies (npm + composer) - Runs full production build (npm run build) - Creates distributable plugin zip ## Artifact Distribution - Uploads build to GitHub Actions (30-day retention) - Uploads to transfer.sh for public URL (14-day retention) - Provides download links in PR comments ## WordPress Playground Integration - Generates custom blueprint.json for each PR build - Uploads blueprint to transfer.sh - Creates one-click Playground testing URL - Sets up fresh WordPress Multisite with PR build - Auto-activates plugin and opens setup wizard - Login: admin / password ## PR Comments Automatically comments on each PR with: - 🚀 One-click Playground testing link - 📦 Build artifact download links - 📋 Build details (commit, branch, triggerer) Also updates blueprint.json to use WordPress.org plugin installation method for better compatibility with WordPress Playground. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. WalkthroughAdds a GitHub Actions PR workflow that builds the plugin, uploads a ZIP artifact, publishes a nightly.link URL, generates and URL-encoded Playground blueprint and URL, comments the PR with links and build metadata, updates Changes
Sequence Diagram(s)sequenceDiagram
participant PR as Pull Request
participant GH as GitHub Actions
participant Runner as CI Runner
participant Repo as Repository
participant Artifacts as GH Artifacts
participant Nightly as nightly.link
participant Playground as Playground Service
participant Comment as PR Comment
PR->>GH: trigger workflow (opened/synchronize/reopened)
GH->>Repo: checkout repository
GH->>Runner: setup Node.js v20 & PHP 8.2 (+extensions), install Composer
Runner->>Runner: npm ci && composer install
Runner->>Runner: npm run build → produce ultimate-multisite-*.zip
Runner->>Artifacts: upload ZIP artifact
Artifacts->>Nightly: expose artifact → return public nightly.link URL
Runner->>Runner: create Playground blueprint JSON (embed nightly URL & PR metadata) and URL-encode it
Runner->>Playground: construct Playground launch URL from blueprint
Runner->>Comment: post PR comment with Playground URL, artifact links, nightly URL, commit/branch/actor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/pr-build-test.yml(1 hunks)blueprint.json(2 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/pr-build-test.yml
121-121: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cypress (8.2, chrome)
- GitHub Check: cypress (8.1, chrome)
🔇 Additional comments (3)
blueprint.json (1)
1-33: Blueprint structure looks good; verify landing page exists.The metadata updates and plugins array are well-structured and align with the workflow. However, ensure that the landing page at
/wp-admin/network/admin.php?page=wp-ultimo-setupis accessible and exists in the Ultimate Multisite plugin after activation..github/workflows/pr-build-test.yml (2)
45-50: Verify artifact path extraction matches npm build output.The step assumes
npm run buildproduces a file matchingultimate-multisite-*.zip. If the build doesn't generate this file or uses a different naming pattern, the step will fail silently or use an incorrect artifact.Confirm that the npm build script produces an artifact matching
ultimate-multisite-*.zipin the repository root. You can verify by checking the build script and any existing build artifacts in the repo.
1-44: Workflow structure and setup are solid.The trigger conditions, permission scoping, Node/PHP setup, dependency installation, and build steps are all well-configured. The artifact extraction, GitHub Actions upload, and transfer.sh integration are strategically sound for enabling PR reviewers to test builds quickly.
.github/workflows/pr-build-test.yml
Outdated
| - name: Upload to temporary public hosting | ||
| id: upload | ||
| run: | | ||
| # Upload to transfer.sh for temporary public URL (14 days retention) | ||
| UPLOAD_URL=$(curl --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }}) | ||
| echo "url=$UPLOAD_URL" >> $GITHUB_OUTPUT | ||
| echo "Uploaded to: $UPLOAD_URL" |
There was a problem hiding this comment.
Add error handling for transfer.sh upload.
The curl upload to transfer.sh doesn't check for success. If the upload fails, invalid or empty URLs are stored in outputs, causing downstream failures (Playground URL, PR comment).
Apply this diff to add error handling:
- name: Upload to temporary public hosting
id: upload
run: |
# Upload to transfer.sh for temporary public URL (14 days retention)
UPLOAD_URL=$(curl --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }})
+ if [ -z "$UPLOAD_URL" ] || [[ "$UPLOAD_URL" == *"error"* ]]; then
+ echo "::error::Failed to upload to transfer.sh"
+ exit 1
+ fi
echo "url=$UPLOAD_URL" >> $GITHUB_OUTPUT
echo "Uploaded to: $UPLOAD_URL"🤖 Prompt for AI Agents
.github/workflows/pr-build-test.yml lines 59-65: the transfer.sh upload step
doesn't verify curl success so a failed upload can write an empty/invalid URL to
outputs; update the step to run curl with --fail --silent --show-error (so it
exits non-zero on HTTP errors), capture the response into a variable, validate
that it is non-empty and starts with "http" (or a known transfer.sh pattern),
and if validation fails echo an error and exit 1 so the job fails instead of
propagating an invalid URL; if valid, write it to $GITHUB_OUTPUT and print the
uploaded URL.
.github/workflows/pr-build-test.yml
Outdated
| # Upload blueprint to transfer.sh | ||
| BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) | ||
| echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT | ||
|
|
||
| # Create Playground URL with blueprint parameter | ||
| PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${BLUEPRINT_URL}" | ||
| echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
Add error handling for blueprint upload and validate Playground URL.
The blueprint upload to transfer.sh also lacks error handling. Additionally, URL-encode the blueprint URL before embedding in the Playground query parameter.
Apply this diff:
# Upload blueprint to transfer.sh
BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json)
+ if [ -z "$BLUEPRINT_URL" ] || [[ "$BLUEPRINT_URL" == *"error"* ]]; then
+ echo "::error::Failed to upload blueprint to transfer.sh"
+ exit 1
+ fi
echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT
# Create Playground URL with blueprint parameter
- PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${BLUEPRINT_URL}"
+ ENCODED_BLUEPRINT_URL=$(printf '%s\n' "$BLUEPRINT_URL" | jq -sRr @uri)
+ PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${ENCODED_BLUEPRINT_URL}"
echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Upload blueprint to transfer.sh | |
| BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) | |
| echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT | |
| # Create Playground URL with blueprint parameter | |
| PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${BLUEPRINT_URL}" | |
| echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT | |
| # Upload blueprint to transfer.sh | |
| BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) | |
| if [ -z "$BLUEPRINT_URL" ] || [[ "$BLUEPRINT_URL" == *"error"* ]]; then | |
| echo "::error::Failed to upload blueprint to transfer.sh" | |
| exit 1 | |
| fi | |
| echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT | |
| # Create Playground URL with blueprint parameter | |
| ENCODED_BLUEPRINT_URL=$(printf '%s\n' "$BLUEPRINT_URL" | jq -sRr @uri) | |
| PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${ENCODED_BLUEPRINT_URL}" | |
| echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT |
🤖 Prompt for AI Agents
.github/workflows/pr-build-test.yml around lines 110-116: the script currently
uploads the blueprint with curl without checking for errors and embeds the raw
URL into the Playground query string; change the upload step to run curl with
fail/quiet flags and capture its exit status and output (or fail the job with a
clear error message if upload fails), and URL-encode the returned BLUEPRINT_URL
before constructing PLAYGROUND_URL (use a small CLI encoder like jq -sRr @uri or
python -c "urllib.parse.quote(...)") so the query parameter is valid; finally,
ensure you only write valid values to $GITHUB_OUTPUT and log an error and exit
non-zero when upload fails.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
.github/workflows/pr-build-test.yml (3)
58-64: Add error handling for transfer.sh upload (unresolved from previous review).The curl upload to transfer.sh doesn't check for success. If the upload fails, invalid or empty URLs are stored in outputs, causing downstream failures.
Apply this diff to add error handling:
- name: Upload to temporary public hosting id: upload run: | # Upload to transfer.sh for temporary public URL (14 days retention) + UPLOAD_URL=$(curl --fail --silent --show-error --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }}) + if [ -z "$UPLOAD_URL" ]; then + echo "::error::Failed to upload to transfer.sh" + exit 1 + fi - UPLOAD_URL=$(curl --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }}) echo "url=$UPLOAD_URL" >> $GITHUB_OUTPUT echo "Uploaded to: $UPLOAD_URL"
109-115: Add error handling for blueprint upload and URL-encode the Playground URL (unresolved from previous review).The blueprint upload to transfer.sh lacks error handling, and the BLUEPRINT_URL is not URL-encoded before embedding in the query parameter.
Apply this diff:
# Upload blueprint to transfer.sh - BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) + BLUEPRINT_URL=$(curl --fail --silent --show-error --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) + if [ -z "$BLUEPRINT_URL" ]; then + echo "::error::Failed to upload blueprint to transfer.sh" + exit 1 + fi echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT # Create Playground URL with blueprint parameter - PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${BLUEPRINT_URL}" + ENCODED_BLUEPRINT_URL=$(printf '%s\n' "$BLUEPRINT_URL" | jq -sRr @uri) + PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${ENCODED_BLUEPRINT_URL}" echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT
117-159: Mitigate script injection risk by passing untrusted variables through environment variables (unresolved from previous review).GitHub Actions security guidelines recommend avoiding direct interpolation of untrusted context variables (like
github.event.pull_request.head.refandgithub.event.pull_request.user.login) into inline scripts. A malicious branch name or username could lead to code injection.Apply this diff to pass untrusted variables via env:
- name: Comment on PR uses: actions/github-script@v7 + env: + BRANCH_REF: ${{ github.event.pull_request.head.ref }} + PR_USER: ${{ github.event.pull_request.user.login }} + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} with: script: | const comment = `## 🔨 Build Complete - Ready for Testing! Your PR build is ready! You can test it in two ways: ### 🌐 Test in WordPress Playground (Recommended) Click the link below to instantly test this PR in your browser - no installation needed! **[🚀 Launch in Playground](${{ steps.blueprint.outputs.url }})** This will: - Set up a fresh WordPress Multisite installation - Install and activate this PR build - Take you directly to the Ultimate Multisite setup wizard Login credentials: \`admin\` / \`password\` ### 📦 Download Build Artifact Alternatively, download the build and test locally: - **Build:** \`${{ steps.artifact.outputs.name }}\` - **Download:** Available in the [Actions artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) - **Temporary URL:** ${{ steps.upload.outputs.url }} _(expires in 14 days)_ --- <details> <summary>📋 Build Details</summary> - - **Commit:** ${{ github.event.pull_request.head.sha }} - - **Branch:** \`${{ github.event.pull_request.head.ref }}\` - - **Triggered by:** @${{ github.event.pull_request.user.login }} + - **Commit:** ${process.env.COMMIT_SHA} + - **Branch:** \`${process.env.BRANCH_REF}\` + - **Triggered by:** @${process.env.PR_USER} </details>`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });Reference: GitHub Actions security hardening guide
🧹 Nitpick comments (1)
.github/workflows/pr-build-test.yml (1)
44-49: Consider hardening artifact path detection.Using
ls -tto find the latest ZIP could match unintended artifacts if multiple ZIPs exist. If the build process creates a single expected output (e.g.,dist/ultimate-multisite.zip), prefer an explicit path instead of globbing the root directory.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/pr-build-test.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/pr-build-test.yml
120-120: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cypress (8.2, chrome)
- GitHub Check: cypress (8.1, chrome)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
.github/workflows/pr-build-test.yml (3)
59-65: Unresolved: Add error handling for transfer.sh upload.This step lacks validation on the curl response. If the upload fails, an empty or error response is stored in
$GITHUB_OUTPUT, causing invalid URLs in downstream steps and silent failures.This issue was flagged in a previous review. Please apply the suggested fix:
- name: Upload to temporary public hosting id: upload run: | # Upload to transfer.sh for temporary public URL (14 days retention) - UPLOAD_URL=$(curl --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }}) + UPLOAD_URL=$(curl --fail --silent --show-error --upload-file ${{ steps.artifact.outputs.path }} https://transfer.sh/${{ steps.artifact.outputs.name }}) + if [ -z "$UPLOAD_URL" ] || [[ "$UPLOAD_URL" != http* ]]; then + echo "::error::Failed to upload artifact to transfer.sh" + exit 1 + fi echo "url=$UPLOAD_URL" >> $GITHUB_OUTPUT echo "Uploaded to: $UPLOAD_URL"
110-116: Unresolved: Add error handling for blueprint upload and URL-encode Playground URL.The blueprint upload to transfer.sh also has no error validation. Additionally, the blueprint URL embedded in the Playground query parameter is not URL-encoded, which could break if the URL contains special characters.
This issue was flagged in a previous review. Please apply the suggested fix:
# Upload blueprint to transfer.sh - BLUEPRINT_URL=$(curl --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) + BLUEPRINT_URL=$(curl --fail --silent --show-error --upload-file pr-blueprint.json https://transfer.sh/pr-${{ github.event.pull_request.number }}-blueprint.json) + if [ -z "$BLUEPRINT_URL" ] || [[ "$BLUEPRINT_URL" != http* ]]; then + echo "::error::Failed to upload blueprint to transfer.sh" + exit 1 + fi echo "blueprint_url=$BLUEPRINT_URL" >> $GITHUB_OUTPUT # Create Playground URL with blueprint parameter - PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${BLUEPRINT_URL}" + ENCODED_BLUEPRINT_URL=$(printf '%s\n' "$BLUEPRINT_URL" | jq -sRr @uri) + PLAYGROUND_URL="https://playground.wordpress.net/?blueprint-url=${ENCODED_BLUEPRINT_URL}" echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT
118-160: Unresolved: Mitigate script injection risk by passing untrusted variables through environment variables.Lines 151–152 directly interpolate untrusted context variables (
github.event.pull_request.head.refandgithub.event.pull_request.user.login) into the inline script. Per GitHub security hardening guidelines, these should be passed via environment variables to prevent potential script injection attacks.This issue was flagged in a previous review. Please apply the suggested fix:
- name: Comment on PR uses: actions/github-script@v7 + env: + BRANCH_REF: ${{ github.event.pull_request.head.ref }} + PR_USER: ${{ github.event.pull_request.user.login }} with: script: | const comment = `## 🔨 Build Complete - Ready for Testing! ... <details> <summary>📋 Build Details</summary> - **Commit:** ${{ github.event.pull_request.head.sha }} - - **Branch:** \`${{ github.event.pull_request.head.ref }}\` - - **Triggered by:** @${{ github.event.pull_request.user.login }} + - **Branch:** \`${process.env.BRANCH_REF}\` + - **Triggered by:** @${process.env.PR_USER} </details>`;Reference: GitHub Actions Security Hardening Guide
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/pr-build-test.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/pr-build-test.yml
121-121: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cypress (8.1, chrome)
.github/workflows/pr-build-test.yml
Outdated
| - name: Get artifact path | ||
| id: artifact | ||
| run: | | ||
| ARCHIVE_PATH=$(ls -t ultimate-multisite-*.zip | head -1) | ||
| echo "path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT | ||
| echo "name=$(basename $ARCHIVE_PATH)" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
Add error handling for artifact path detection.
Line 48 uses a glob pattern to find the built ZIP file, but has no guard against zero matches. If the build doesn't produce a ZIP, the subsequent steps will fail with cryptic errors.
Apply this diff to fail early with a clear error:
- name: Get artifact path
id: artifact
run: |
ARCHIVE_PATH=$(ls -t ultimate-multisite-*.zip | head -1)
+ if [ -z "$ARCHIVE_PATH" ]; then
+ echo "::error::No built ZIP artifact found"
+ exit 1
+ fi
echo "path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
echo "name=$(basename $ARCHIVE_PATH)" >> $GITHUB_OUTPUT🤖 Prompt for AI Agents
In .github/workflows/pr-build-test.yml around lines 45 to 50, the step that
finds the built ZIP uses a glob and assigns ARCHIVE_PATH without checking for
zero matches; update the step to check whether ARCHIVE_PATH is non-empty after
the ls head command and, if empty, print a clear error to stderr and exit with a
non-zero status so the workflow fails fast with a helpful message; if non-empty,
continue to write the path and basename to GITHUB_OUTPUT as before.
The build process requires MU_CLIENT_ID and MU_CLIENT_SECRET environment variables for the encrypt-sectrets.php script that runs during the prearchive step. These need to be set as GitHub repository secrets. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Removed the transfer.sh upload step since: - transfer.sh is currently down - GitHub Actions artifacts already provide download functionality - WordPress Playground requires public URLs which GitHub artifacts don't provide The workflow now: - Builds the plugin on every PR - Uploads artifact to GitHub Actions (30-day retention) - Comments on PR with download link and testing instructions - Provides manual instructions for testing in Playground This is simpler and more reliable since it only depends on GitHub's infrastructure. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
🔨 Build Complete - Ready for Testing!📦 Download Build ArtifactYour PR build is ready for testing! Build: Click the download link above, then scroll down to the "Artifacts" section to download the build. 🧪 How to Test
Or test in WordPress Playground:
📋 Build Details
|
Implements public artifact URLs using nightly.link service which acts as a proxy to GitHub Actions artifacts, making them accessible without authentication. ## Changes - Added nightly.link URL generation step - Creates blueprint.json with public artifact URL - Generates one-click WordPress Playground testing link - Updated PR comment with Playground launch button - Provides both authenticated and public download links ## How it Works 1. Builds plugin and uploads to GitHub Actions artifacts 2. Generates public URL via nightly.link (no extra upload needed) 3. Creates Playground blueprint with the public artifact URL 4. URL-encodes blueprint and creates Playground link 5. Comments on PR with one-click testing button ## Testing URLs Provided - 🚀 One-click Playground link (multisite pre-configured) - ⬇️ GitHub Actions download (requires auth) - ⬇️ nightly.link direct download (public) Uses nightly.link format: https://nightly.link/{owner}/{repo}/actions/runs/{run-id}/{artifact}.zip 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
🔨 Build Complete - Ready for Testing!Your PR build is ready! Test it instantly in your browser or download for local testing. 🌐 Test in WordPress Playground (Recommended)Click the link below to instantly test this PR in your browser - no installation needed! This will:
Login credentials: 📦 Download Build ArtifactAlternatively, download the build and test locally:
📋 Build Details
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/pr-build-test.yml (2)
48-53: Add error handling for artifact path detection.If the build step doesn't produce a ZIP, this step silently outputs empty values, causing cryptic failures downstream.
Apply this diff to validate the artifact path early:
- name: Get artifact path id: artifact run: | ARCHIVE_PATH=$(ls -t ultimate-multisite*.zip | head -1) + if [ -z "$ARCHIVE_PATH" ]; then + echo "::error::No built ZIP artifact found" + exit 1 + fi echo "path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT echo "name=$(basename $ARCHIVE_PATH)" >> $GITHUB_OUTPUT
124-167: Mitigate script injection risk by passing untrusted variables through environment variables.Directly interpolating
github.event.pull_request.head.refandgithub.event.pull_request.user.logininto the inline script creates a script injection vulnerability. GitHub security guidelines recommend passing untrusted context variables via environment variables instead.Apply this diff to fix the vulnerability:
- name: Comment on PR uses: actions/github-script@v7 + env: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} + BRANCH_REF: ${{ github.event.pull_request.head.ref }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} with: script: | const comment = `## 🔨 Build Complete - Ready for Testing! Your PR build is ready! Test it instantly in your browser or download for local testing. ### 🌐 Test in WordPress Playground (Recommended) Click the link below to instantly test this PR in your browser - no installation needed! **[🚀 Launch in Playground](${{ steps.blueprint.outputs.url }})** This will: - Set up a fresh WordPress Multisite installation - Install and activate this PR build automatically - Take you directly to the Ultimate Multisite setup wizard Login credentials: \`admin\` / \`password\` ### 📦 Download Build Artifact Alternatively, download the build and test locally: - **Build:** \`${{ steps.artifact.outputs.name }}\` - **[⬇️ Download from GitHub Actions](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** - **[⬇️ Direct Download (nightly.link)](${{ steps.nightlylink.outputs.url }})** --- <details> <summary>📋 Build Details</summary> - - **Commit:** ${{ github.event.pull_request.head.sha }} - - **Branch:** \`${{ github.event.pull_request.head.ref }}\` - - **Triggered by:** @${{ github.event.pull_request.user.login }} + - **Commit:** ${process.env.COMMIT_SHA} + - **Branch:** \`${process.env.BRANCH_REF}\` + - **Triggered by:** @${process.env.PR_AUTHOR} - **Artifact retention:** 30 days </details>`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/pr-build-test.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/pr-build-test.yml
127-127: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: cypress (8.2, chrome)
- GitHub Check: cypress (8.1, chrome)
- GitHub Check: Build Plugin for Testing
🔇 Additional comments (1)
.github/workflows/pr-build-test.yml (1)
62-69: LGTM!The nightly.link URL generation is straightforward and reliable—no external service calls needed, and the URL resolves to the GitHub Actions artifact at runtime.
| } | ||
| EOF | ||
|
|
||
| # Replace placeholder with actual nightly.link URL | ||
| sed -i "s|PLUGIN_URL_PLACEHOLDER|${{ steps.nightlylink.outputs.url }}|g" pr-blueprint.json | ||
|
|
||
| # URL encode the blueprint for Playground | ||
| BLUEPRINT_JSON=$(cat pr-blueprint.json | jq -c .) | ||
| BLUEPRINT_ENCODED=$(echo "$BLUEPRINT_JSON" | jq -sRr @uri) | ||
| PLAYGROUND_URL="https://playground.wordpress.net/#${BLUEPRINT_ENCODED}" |
There was a problem hiding this comment.
Add error handling for sed replacement and validate blueprint encoding.
The sed command doesn't check for success. If the placeholder replacement fails, an invalid blueprint URL is generated silently. Additionally, very long blueprints embedded in the URL fragment could exceed browser limits; consider this when blueprint size grows.
Apply this diff to add error handling:
# Replace placeholder with actual nightly.link URL
sed -i "s|PLUGIN_URL_PLACEHOLDER|${{ steps.nightlylink.outputs.url }}|g" pr-blueprint.json
+ if [ $? -ne 0 ]; then
+ echo "::error::Failed to update blueprint with plugin URL"
+ exit 1
+ fi
# URL encode the blueprint for Playground
BLUEPRINT_JSON=$(cat pr-blueprint.json | jq -c .)
BLUEPRINT_ENCODED=$(echo "$BLUEPRINT_JSON" | jq -sRr @uri)
PLAYGROUND_URL="https://playground.wordpress.net/#${BLUEPRINT_ENCODED}"🤖 Prompt for AI Agents
.github/workflows/pr-build-test.yml around lines 110 to 119: the sed replacement
and blueprint encoding are not validated, so add checks that sed successfully
replaced the PLUGIN_URL_PLACEHOLDER, that the resulting pr-blueprint.json
contains valid JSON and non-empty BLUEPRINT_JSON, and that the final
PLAYGROUND_URL fragment length is within a safe limit (fail the job or print a
clear error and exit if any check fails); implement this by checking sed's exit
status and/or searching the file for any remaining placeholder, running jq to
validate and fail on invalid JSON or empty output, computing the encoded URL
length and failing or truncating with a clear error if it exceeds a threshold
(e.g., 2000 chars), and ensure the job exits non-zero on any failure so invalid
blueprints don't produce a broken Playground URL.
The previous implementation uploaded the plugin zip as an artifact, which GitHub Actions then wrapped in another zip, resulting in ultimate-multisite.zip.zip containing ultimate-multisite.zip. Solution: - Extract the built plugin zip before uploading - Upload the extracted plugin directory as the artifact - GitHub Actions zips it once (not twice) - nightly.link serves a single zip file ready for use Now the nightly.link URL will provide ultimate-multisite.zip that can be used directly with WordPress Playground without double extraction. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
🔨 Build Complete - Ready for Testing!Your PR build is ready! Test it instantly in your browser or download for local testing. 🌐 Test in WordPress Playground (Recommended)Click the link below to instantly test this PR in your browser - no installation needed! This will:
Login credentials: 📦 Download Build ArtifactAlternatively, download the build and test locally:
📋 Build Details
|
🔨 Build Complete - Ready for Testing!Your PR build is ready! Test it instantly in your browser or download for local testing. 🌐 Test in WordPress Playground (Recommended)Click the link below to instantly test this PR in your browser - no installation needed! This will:
Login credentials: 📦 Download Build ArtifactAlternatively, download the build and test locally:
📋 Build Details
|
🔨 Build Complete - Ready for Testing!Your PR build is ready! Test it instantly in your browser or download for local testing. 🌐 Test in WordPress Playground (Recommended)Click the link below to instantly test this PR in your browser - no installation needed! [🚀 Launch in Playground](https://playground.wordpress.net/#{"$schema":"https://playground.wordpress.net/blueprint-schema.json","meta":{"author":"Ultimate Multisite","title":"Ultimate Multisite PR #289 Test","description":"Test build from PR #289"},"landingPage":"/wp-admin/network/admin.php?page=wp-ultimo-setup","preferredVersions":{"php":"8.4","wp":"latest"},"phpExtensionBundles":["kitchen-sink"],"features":{"networking":true},"plugins":[{"resource":"url","url":"https://nightly.link/Multisite-Ultimate/ultimate-multisite/actions/runs/20045379956/ultimate-multisite.zip"}],"steps":[{"step":"enableMultisite"},{"step":"wp-cli","command":"wp site create --slug=site01"},{"step":"wp-cli","command":"wp plugin activate ultimate-multisite --network"},{"step":"login","username":"admin","password":"password"}]}) This will:
Login credentials: 📦 Download Build ArtifactAlternatively, download the build and test locally:
📋 Build Details
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/pr-build-test.yml (2)
140-184: Mitigate script injection risk by passing untrusted variables through environment variables.GitHub security guidelines recommend avoiding direct interpolation of untrusted context variables (like
github.event.pull_request.head.refandgithub.event.pull_request.user.login) into inline scripts. These should be passed via environment variables instead.Apply this diff to fix the script injection vulnerability:
- name: Comment on PR uses: actions/github-script@v7 + env: + BRANCH_REF: ${{ github.event.pull_request.head.ref }} + PR_USER: ${{ github.event.pull_request.user.login }} with: script: | const comment = `## 🔨 Build Complete - Ready for Testing! Your PR build is ready! Test it instantly in your browser or download for local testing. ### 🌐 Test in WordPress Playground (Recommended) Click the link below to instantly test this PR in your browser - no installation needed! **[🚀 Launch in Playground](${{ steps.blueprint.outputs.url }})** This will: - Set up a fresh WordPress Multisite installation - Install and activate this PR build automatically - Take you directly to the Ultimate Multisite setup wizard Login credentials: \`admin\` / \`password\` ### 📦 Download Build Artifact Alternatively, download the build and test locally: - **Build:** \`${{ steps.artifact.outputs.name }}\` - **[⬇️ Download from GitHub Actions](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})** - **[⬇️ Direct Download (nightly.link)](${{ steps.nightlylink.outputs.url }})** --- <details> <summary>📋 Build Details</summary> - **Commit:** ${{ github.event.pull_request.head.sha }} - - **Branch:** \`${{ github.event.pull_request.head.ref }}\` - - **Triggered by:** @${{ github.event.pull_request.user.login }} + - **Branch:** \`${process.env.BRANCH_REF}\` + - **Triggered by:** @${process.env.PR_USER} - **Artifact retention:** 30 days </details>`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });
82-139: Add error handling for sed replacement and validate blueprint JSON.The sed command doesn't check for success, and the blueprint JSON is not validated before embedding in the URL. For complex Blueprints, WordPress Playground officially recommends Base64 encoding to prevent issues with spaces, quotes, or special characters.
Apply this diff to add error handling and use Base64 encoding for robustness:
# Replace placeholder with actual nightly.link URL sed -i "s|PLUGIN_URL_PLACEHOLDER|${{ steps.nightlylink.outputs.url }}|g" pr-blueprint.json + if [ $? -ne 0 ]; then + echo "::error::Failed to update blueprint with plugin URL" + exit 1 + fi # URL encode the blueprint for Playground BLUEPRINT_JSON=$(cat pr-blueprint.json | jq -c .) + if [ $? -ne 0 ] || [ -z "$BLUEPRINT_JSON" ]; then + echo "::error::Failed to generate valid blueprint JSON" + exit 1 + fi - PLAYGROUND_URL="https://playground.wordpress.net/#${BLUEPRINT_JSON}" + # Use Base64 encoding for Playground URL to handle special characters safely + BLUEPRINT_ENCODED=$(echo -n "$BLUEPRINT_JSON" | base64 -w 0) + PLAYGROUND_URL="https://playground.wordpress.net/#${BLUEPRINT_ENCODED}" echo "url=$PLAYGROUND_URL" >> $GITHUB_OUTPUT echo "Playground URL generated"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/pr-build-test.yml(1 hunks)blueprint.json(2 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/pr-build-test.yml
143-143: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: cypress (8.2, chrome)
- GitHub Check: cypress (8.1, chrome)
- GitHub Check: Build Plugin for Testing
🔇 Additional comments (1)
blueprint.json (1)
20-25: Clarify the purpose and lifecycle of the hardcoded plugin URL.The blueprint.json in the repository now includes a hardcoded plugin URL pointing to a specific external ZIP file. However, the PR build workflow (.github/workflows/pr-build-test.yml) dynamically generates its own pr-blueprint.json with a placeholder that gets replaced by the nightly.link URL. This creates ambiguity about which blueprint is used in which context and whether the static hardcoded URL should be here.
Please clarify:
- Is this static blueprint.json intended to be used for demonstration/public Playground links, while the PR workflow generates its own dynamic pr-blueprint.json?
- If so, should the hardcoded URL be updated to point to a stable, maintained artifact rather than a dated one (consider replacing with a dynamic download URL or documentation)?
- Should there be a comment in the file explaining that the PR workflow generates and uses a separate blueprint?
| - name: Prepare artifact for upload | ||
| id: artifact | ||
| run: | | ||
| # Find the built zip file | ||
| ARCHIVE_PATH=$(ls -t ultimate-multisite*.zip | head -1) | ||
| ARCHIVE_NAME=$(basename "$ARCHIVE_PATH" .zip) | ||
|
|
||
| # Extract the zip so GitHub Actions will zip it once (not double-zip) | ||
| mkdir -p artifact-temp | ||
| unzip -q "$ARCHIVE_PATH" -d artifact-temp | ||
|
|
||
| # The extracted folder should contain the plugin | ||
| PLUGIN_DIR=$(ls -d artifact-temp/ultimate-multisite* | head -1) | ||
|
|
||
| echo "path=$PLUGIN_DIR" >> $GITHUB_OUTPUT | ||
| echo "name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT | ||
| echo "Prepared artifact: $PLUGIN_DIR as $ARCHIVE_NAME" | ||
|
|
There was a problem hiding this comment.
Add error handling for artifact path detection.
The workflow doesn't guard against zero matches when finding the built ZIP. If the build doesn't produce a ZIP, the artifact extraction will fail silently with a cryptic error.
Apply this diff to fail early with a clear error:
- name: Prepare artifact for upload
id: artifact
run: |
# Find the built zip file
ARCHIVE_PATH=$(ls -t ultimate-multisite*.zip | head -1)
+ if [ -z "$ARCHIVE_PATH" ]; then
+ echo "::error::No built ZIP artifact found"
+ exit 1
+ fi
ARCHIVE_NAME=$(basename "$ARCHIVE_PATH" .zip)
# Extract the zip so GitHub Actions will zip it once (not double-zip)
mkdir -p artifact-temp
unzip -q "$ARCHIVE_PATH" -d artifact-temp
# The extracted folder should contain the plugin
PLUGIN_DIR=$(ls -d artifact-temp/ultimate-multisite* | head -1)
+ if [ -z "$PLUGIN_DIR" ]; then
+ echo "::error::Plugin directory not found in extracted artifact"
+ exit 1
+ fi
echo "path=$PLUGIN_DIR" >> $GITHUB_OUTPUT
echo "name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
echo "Prepared artifact: $PLUGIN_DIR as $ARCHIVE_NAME"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Prepare artifact for upload | |
| id: artifact | |
| run: | | |
| # Find the built zip file | |
| ARCHIVE_PATH=$(ls -t ultimate-multisite*.zip | head -1) | |
| ARCHIVE_NAME=$(basename "$ARCHIVE_PATH" .zip) | |
| # Extract the zip so GitHub Actions will zip it once (not double-zip) | |
| mkdir -p artifact-temp | |
| unzip -q "$ARCHIVE_PATH" -d artifact-temp | |
| # The extracted folder should contain the plugin | |
| PLUGIN_DIR=$(ls -d artifact-temp/ultimate-multisite* | head -1) | |
| echo "path=$PLUGIN_DIR" >> $GITHUB_OUTPUT | |
| echo "name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT | |
| echo "Prepared artifact: $PLUGIN_DIR as $ARCHIVE_NAME" | |
| - name: Prepare artifact for upload | |
| id: artifact | |
| run: | | |
| # Find the built zip file | |
| ARCHIVE_PATH=$(ls -t ultimate-multisite*.zip | head -1) | |
| if [ -z "$ARCHIVE_PATH" ]; then | |
| echo "::error::No built ZIP artifact found" | |
| exit 1 | |
| fi | |
| ARCHIVE_NAME=$(basename "$ARCHIVE_PATH" .zip) | |
| # Extract the zip so GitHub Actions will zip it once (not double-zip) | |
| mkdir -p artifact-temp | |
| unzip -q "$ARCHIVE_PATH" -d artifact-temp | |
| # The extracted folder should contain the plugin | |
| PLUGIN_DIR=$(ls -d artifact-temp/ultimate-multisite* | head -1) | |
| if [ -z "$PLUGIN_DIR" ]; then | |
| echo "::error::Plugin directory not found in extracted artifact" | |
| exit 1 | |
| fi | |
| echo "path=$PLUGIN_DIR" >> $GITHUB_OUTPUT | |
| echo "name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT | |
| echo "Prepared artifact: $PLUGIN_DIR as $ARCHIVE_NAME" |
🤖 Prompt for AI Agents
.github/workflows/pr-build-test.yml around lines 48 to 65: the workflow assumes
ls finds a matching ultimate-multisite*.zip and proceeds to unzip and set
outputs; if no ZIP is produced the script will fail later with a cryptic error.
Add explicit checks after assigning ARCHIVE_PATH and PLUGIN_DIR: if ARCHIVE_PATH
is empty or the file does not exist, echo a clear error message and exit 1 so
the job fails fast; after extracting, verify PLUGIN_DIR exists and is a
directory, otherwise echo a clear error and exit 1; only write to GITHUB_OUTPUT
when both values are valid.
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! [🚀 Launch in Playground](https://playground.wordpress.net/#{"$schema":"https://playground.wordpress.net/blueprint-schema.json","meta":{"author":"Ultimate Multisite","title":"Ultimate Multisite PR #289 Test","description":"Test build from PR #289"},"landingPage":"/wp-admin/network/admin.php?page=wp-ultimo-setup","preferredVersions":{"php":"8.4","wp":"latest"},"phpExtensionBundles":["kitchen-sink"],"features":{"networking":true},"plugins":[{"resource":"url","url":"https://nightly.link/Multisite-Ultimate/ultimate-multisite/actions/runs/20045484861/ultimate-multisite.zip"}],"steps":[{"step":"enableMultisite"},{"step":"wp-cli","command":"wp site create --slug=site01"},{"step":"wp-cli","command":"wp plugin activate ultimate-multisite --network"},{"step":"login","username":"admin","password":"password"}]}) Login credentials: |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
🔨 Build Complete - Ready for Testing!📦 Download Build Artifact (Recommended)Download the zip build, upload to WordPress and test:
🌐 Test in WordPress Playground (Very Experimental)Click the link below to instantly test this PR in your browser - no installation needed! Login credentials: |
This PR adds an automated build and testing workflow for pull requests.
Features
🔨 Automated Build Process
📦 Artifact Distribution
🌐 WordPress Playground Integration
The workflow generates a custom blueprint.json for each PR build and creates a one-click testing URL that:
💬 Automated PR Comments
Each PR receives a comment with:
Testing
This PR itself will test the workflow! Once merged, every future PR will automatically get:
Changes
.github/workflows/pr-build-test.yml- The GitHub Actions workflowblueprint.json- Now uses WordPress.org installation method🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.