diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml new file mode 100644 index 00000000..ddef0b4f --- /dev/null +++ b/.github/workflows/build-plugin-zip.yml @@ -0,0 +1,64 @@ +name: Build Plugin Zip + +on: + pull_request: + types: [ 'opened', 'synchronize', 'reopened', 'edited' ] + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + build: + runs-on: 'ubuntu-24.04' + permissions: + contents: read + outputs: + job_status: ${{ job.status }} + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up PHP and Composer + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + coverage: none + tools: composer:v2 + + - name: Setup Node + uses: actions/setup-node@v5 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install NPM dependencies + run: npm ci + + - name: Install Composer dependencies + run: composer install --no-dev --no-interaction + + - name: Build plugin + run: npm run build + + - name: Create artifact + run: | + mv dist two-factor + zip -r two-factor.zip two-factor/ + + - name: Upload artifact + uses: actions/upload-artifact@v6 + with: + name: two-factor-plugin-zip-pr${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }} + path: two-factor.zip + if-no-files-found: error diff --git a/.github/workflows/pr-playground-preview.yml b/.github/workflows/pr-playground-preview.yml new file mode 100644 index 00000000..040863ad --- /dev/null +++ b/.github/workflows/pr-playground-preview.yml @@ -0,0 +1,117 @@ +name: PR Playground Preview + +# Use workflow_run for privileged operations +# Runs with write permissions and access to secrets +# Operates on artifacts from the unprivileged build workflow +on: + workflow_run: + workflows: ["Build Plugin Zip"] + types: + - completed + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + + # Leaves a comment on a pull request with a link to test the changes in a WordPress Playground instance. + playground-details: + name: Comment on a pull request with Playground details + runs-on: ubuntu-24.04 + # Only run if the build workflow succeeded and was triggered by a pull_request + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + outputs: + artifact-url: ${{ steps.expose.outputs.artifact-url }} + artifact-name: ${{ steps.expose.outputs.artifact-name }} + permissions: + contents: write + pull-requests: write + + steps: + - name: Extract PR metadata from artifact name + id: pr-metadata + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }}, + }); + + const artifact = artifacts.data.artifacts.find(a => + a.name.startsWith("two-factor-plugin-zip-pr") + ); + + if (!artifact) { + throw new Error('Could not find plugin artifact'); + } + + // Parse: two-factor-plugin-zip-pr123-abc123def... + const match = artifact.name.match(/^two-factor-plugin-zip-pr(\d+)-(.+)$/); + if (!match) { + throw new Error(`Could not parse artifact name: ${artifact.name}`); + } + + const [, prNumber, commitSha] = match; + + core.setOutput('pr-number', prNumber); + core.setOutput('commit-sha', commitSha); + core.setOutput('artifact-name', artifact.name); + + - name: Expose built artifact + id: expose + uses: WordPress/action-wp-playground-pr-preview/.github/actions/expose-artifact-on-public-url@c8607529dac8d2bf9a1e8493865fc97cd1c3c87b # v2 + with: + artifact-name: ${{ steps.pr-metadata.outputs.artifact-name }} + artifact-filename: two-factor.zip + pr-number: ${{ steps.pr-metadata.outputs.pr-number }} + commit-sha: ${{ steps.pr-metadata.outputs.commit-sha }} + artifact-source-run-id: ${{ github.event.workflow_run.id }} + artifacts-to-keep: '2' + + - name: Generate Playground blueprint JSON + id: blueprint + run: | + node - <<'NODE' >> "$GITHUB_OUTPUT" + const url = process.env.ARTIFACT_URL; + if (!url) { + throw new Error('ARTIFACT_URL is required'); + } + + const blueprint = { + landingPage: '/wp-admin/profile.php#two-factor-options', + preferredVersions: { + php: '8.2', + wp: 'latest', + }, + steps: [ + { + step: 'installPlugin', + pluginZipFile: { + resource: 'url', + url, + }, + }, + { + step: 'login', + username: 'admin', + }, + ], + }; + + console.log(`blueprint=${JSON.stringify(blueprint)}`); + NODE + env: + ARTIFACT_URL: ${{ steps.expose.outputs.artifact-url }} + + - name: Post Playground preview button + uses: WordPress/action-wp-playground-pr-preview@c8607529dac8d2bf9a1e8493865fc97cd1c3c87b # v2 + with: + mode: append-to-description + blueprint: ${{ steps.blueprint.outputs.blueprint }} + pr-number: ${{ steps.pr-metadata.outputs.pr-number }} + github-token: ${{ secrets.GITHUB_TOKEN }}