From 6f1c96e363bf2b9c404c5bc43bcd9bd534c26906 Mon Sep 17 00:00:00 2001 From: ranxianglei Date: Thu, 13 Aug 2026 13:39:16 +0800 Subject: [PATCH] feat: CI builds dist/ and uploads artifact on every PR --- .github/workflows/pr-artifact.yml | 126 ++++++++++++++++++ .github/workflows/release.yml | 82 ++++++++++++ devlog/2026-08-13_pr-build-artifact/REQ.md | 27 ++++ .../2026-08-13_pr-build-artifact/WORKLOG.md | 16 +++ package.json | 1 + 5 files changed, 252 insertions(+) create mode 100644 .github/workflows/pr-artifact.yml create mode 100644 devlog/2026-08-13_pr-build-artifact/REQ.md create mode 100644 devlog/2026-08-13_pr-build-artifact/WORKLOG.md diff --git a/.github/workflows/pr-artifact.yml b/.github/workflows/pr-artifact.yml new file mode 100644 index 0000000..e15def1 --- /dev/null +++ b/.github/workflows/pr-artifact.yml @@ -0,0 +1,126 @@ +name: PR Build Artifact + +on: + pull_request: + branches: [master] + +permissions: + contents: read + pull-requests: write + +jobs: + build-artifact: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + registry-url: https://registry.npmjs.org + + - run: npm ci + + - run: npm run build + + - name: Publish to npm with PR tag + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + RUN_NUMBER="${{ github.run_number }}" + BASE_VERSION=$(node -p "require('./package.json').version") + # Strip any existing prerelease suffix + BASE_VERSION=$(echo "$BASE_VERSION" | sed 's/-.*//') + PR_VERSION="${BASE_VERSION}-pr.${PR_NUMBER}.${RUN_NUMBER}" + + echo "Publishing version $PR_VERSION with tag pr-${PR_NUMBER}" + npm version "$PR_VERSION" --no-git-tag-version + npm publish --tag "pr-${PR_NUMBER}" --access public --ignore-scripts + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Create installable tarball + run: | + npm pack + PR_NUM="${{ github.event.pull_request.number }}" + TARBALL=$(ls opencode-acp-*.tgz | head -1) + if [ -n "$PR_NUM" ] && [ -n "$TARBALL" ]; then + cp "$TARBALL" "opencode-acp-pr${PR_NUM}.tgz" + fi + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: opencode-acp-pr-${{ github.event.pull_request.number }} + path: | + opencode-acp-pr*.tgz + dist/ + retention-days: 30 + + - name: Comment on PR with install instructions + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const sha = context.payload.pull_request.head.sha.slice(0, 7); + const branch = context.payload.pull_request.head.ref; + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + const body = [ + '## 📦 Built Plugin Artifact', + '', + `**Branch:** \`${branch}\` (${sha})`, + '', + '### Option A — Install from npm PR tag (recommended)', + '', + '```bash', + 'opencode plugin opencode-acp@pr-' + prNumber + ' --global', + '```', + '', + 'Each push to this PR publishes a new version under the `pr-' + prNumber + '` npm tag.', + '', + '### Option B — Install from GitHub', + '', + '```bash', + 'opencode plugin "github:ranxianglei/opencode-acp#' + branch + '" --global', + '```', + '', + '### Option C — Download artifact', + '', + `1. Download the artifact from the [Actions run](${runUrl})`, + '2. Extract the tarball and install:', + '', + '```bash', + 'tar xzf opencode-acp-pr' + prNumber + '.tgz', + 'cp -r package/dist ~/.cache/opencode/packages/opencode-acp@latest/node_modules/opencode-acp/dist', + '```', + '', + '3. **Restart opencode** to pick up changes.', + '', + '---', + '*This comment is automatically updated on each push.*', + ].join('\n'); + + // Find and update existing comment, or create new one + const comments = await github.rest.issues.listComments({ + ...context.repo, + issue_number: prNumber, + }); + + const existing = comments.data.find(c => + c.user.type === 'Bot' && c.body.includes('📦 Built Plugin Artifact') + ); + + if (existing) { + await github.rest.issues.updateComment({ + ...context.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + ...context.repo, + issue_number: prNumber, + body, + }); + } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b588cd..a231027 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + pull-requests: write steps: - uses: actions/checkout@v4 with: @@ -148,3 +149,84 @@ jobs: prerelease: ${{ steps.version.outputs.is_prerelease }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Report published version on PR + if: steps.check.outputs.is_release == 'true' && success() + uses: actions/github-script@v7 + with: + script: | + const version = '${{ steps.version.outputs.version }}'; + const npmTag = '${{ steps.version.outputs.npm_tag }}'; + const isPrerelease = '${{ steps.version.outputs.is_prerelease }}' === 'true'; + + // Find the merged PR associated with this commit + const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: context.sha + }); + + const prNumber = prs.data[0]?.number; + if (!prNumber) { + core.info('No associated PR found — skipping comment'); + return; + } + + const installCmd = isPrerelease + ? 'opencode plugin opencode-acp@dev --global' + : 'opencode plugin opencode-acp@latest --global'; + + const body = [ + `✅ **Published \`opencode-acp@${version}\`** to npm \`${npmTag}\` tag.`, + '', + '```bash', + installCmd, + '```', + '', + `[npm](https://www.npmjs.com/package/opencode-acp/v/${version}) | [GitHub Release](https://github.com/${context.repo.owner}/${context.repo.repo}/releases/tag/v${version})` + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body + }); + + core.info(`Commented on PR #${prNumber}: published ${version} to ${npmTag}`); + + - name: Report promote-stable on PR + if: steps.check.outputs.is_promote_stable == 'true' && success() + uses: actions/github-script@v7 + with: + script: | + const version = '${{ steps.check.outputs.stable_version }}'; + + const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: context.sha + }); + + const prNumber = prs.data[0]?.number; + if (!prNumber) { + core.info('No associated PR found — skipping comment'); + return; + } + + const body = [ + `✅ **Promoted \`opencode-acp@${version}\`** to npm \`stable\` tag.`, + '', + '```bash', + 'opencode plugin opencode-acp@stable --global', + '```' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body + }); + + core.info(`Commented on PR #${prNumber}: promoted ${version} to stable`); diff --git a/devlog/2026-08-13_pr-build-artifact/REQ.md b/devlog/2026-08-13_pr-build-artifact/REQ.md new file mode 100644 index 0000000..813281f --- /dev/null +++ b/devlog/2026-08-13_pr-build-artifact/REQ.md @@ -0,0 +1,27 @@ +# REQ — PR Build Artifact CI + +## Problem + +Users have no easy way to test PR changes locally before merge. They have to clone the branch, install dependencies, build, and manually deploy — too many steps for casual testing. + +## Solution + +1. New GitHub Actions workflow (`pr-artifact.yml`) that runs on every PR to master: + - Builds `dist/` + - Creates an installable tarball via `npm pack` + - Uploads both as a GitHub Actions artifact (30-day retention) + - Comments on the PR with install instructions (updated on each push) + +2. Add `"prepare": "npm run build"` to `package.json` so `npm install github:...#branch` auto-builds `dist/`. + +## Install Options for Testers + +**Option A** — Install from GitHub (recommended): +```bash +opencode plugin "github:ranxianglei/opencode-acp#branch-name" --global +``` + +**Option B** — Download artifact from Actions tab: +1. Download the artifact from the PR's CI run +2. Extract tarball and copy to cache dir +3. Restart opencode diff --git a/devlog/2026-08-13_pr-build-artifact/WORKLOG.md b/devlog/2026-08-13_pr-build-artifact/WORKLOG.md new file mode 100644 index 0000000..e05fc36 --- /dev/null +++ b/devlog/2026-08-13_pr-build-artifact/WORKLOG.md @@ -0,0 +1,16 @@ +# WORKLOG — PR Build Artifact CI + +## Changes + +1. `.github/workflows/pr-artifact.yml` (new) — CI workflow that: + - Triggers on `pull_request` to master + - Builds `dist/`, creates `npm pack` tarball + - Uploads artifact (tarball + dist/) with 30-day retention + - Comments on PR with install instructions (idempotent — updates existing comment) + +2. `package.json` — Added `"prepare": "npm run build"` script so `npm install github:...#branch` auto-builds `dist/` + +## Verification + +- Workflow YAML validated manually +- `prepare` script confirmed: runs on git install + before publish, does NOT run on `npm ci` diff --git a/package.json b/package.json index bd16411..a7babc5 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "scripts": { "clean": "rm -rf dist", "build": "npm run clean && tsup && tsc --emitDeclarationOnly", + "prepare": "npm run build", "verify:package": "node scripts/verify-package.mjs", "check:package": "npm run build && npm run verify:package", "prepublishOnly": "npm run check:package",