Skip to content

Merge pull request #327 from ranxianglei/2026-08-20_fixed-nudge-growth #170

Merge pull request #327 from ranxianglei/2026-08-20_fixed-nudge-growth

Merge pull request #327 from ranxianglei/2026-08-20_fixed-nudge-growth #170

Workflow file for this run

name: Release
on:
push:
branches: [master]
workflow_dispatch:
inputs:
force:
description: "Force publish even if not a release branch merge"
required: false
default: "false"
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check commit type
id: check
run: |
MERGE_MSG=$(git log -1 --pretty=%B)
MERGE_TITLE=$(echo "$MERGE_MSG" | head -1)
echo "Commit title: $MERGE_TITLE"
FORCE="${{ github.event.inputs.force }}"
IS_RELEASE="false"
IS_PROMOTE_STABLE="false"
# Pattern 1: Standard merge of release branch
# "Merge pull request #171 from ranxianglei/2026-07-21_release-v1.13.2"
if echo "$MERGE_TITLE" | grep -qE 'Merge pull request #[0-9]+ from .*[0-9]{4}-[0-9]{2}-[0-9]{2}_release-v'; then
IS_RELEASE="true"
echo "Release branch detected (standard merge)"
# Pattern 2: Squash merge of a release PR
# "release: v1.13.4 — ... (#186)"
elif echo "$MERGE_TITLE" | grep -qE '^release: v[0-9]+\.[0-9]+\.[0-9]+'; then
IS_RELEASE="true"
echo "Release branch detected (squash merge)"
# Pattern 3: Squash merge of a promote-stable PR
# "promote: stable v1.14.7 — ... (#231)"
elif echo "$MERGE_TITLE" | grep -qE '^promote: stable v[0-9]+\.[0-9]+\.[0-9]+'; then
IS_PROMOTE_STABLE="true"
STABLE_VERSION=$(echo "$MERGE_TITLE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 | sed 's/^v//')
echo "Promote-stable detected: version $STABLE_VERSION"
echo "stable_version=$STABLE_VERSION" >> "$GITHUB_OUTPUT"
# Pattern 4: Standard merge of promote-stable branch
# "Merge pull request #231 from ranxianglei/2026-07-29_promote-stable-v1.14.7"
elif echo "$MERGE_TITLE" | grep -qE 'Merge pull request #[0-9]+ from .*promote-stable-v[0-9]+\.[0-9]+\.[0-9]+'; then
IS_PROMOTE_STABLE="true"
STABLE_VERSION=$(echo "$MERGE_TITLE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 | sed 's/^v//')
echo "Promote-stable detected (standard merge): version $STABLE_VERSION"
echo "stable_version=$STABLE_VERSION" >> "$GITHUB_OUTPUT"
elif [ "$FORCE" = "true" ]; then
IS_RELEASE="true"
echo "Force publish requested"
else
echo "Not a release or promote-stable branch merge — skipping"
fi
echo "is_release=$IS_RELEASE" >> "$GITHUB_OUTPUT"
echo "is_promote_stable=$IS_PROMOTE_STABLE" >> "$GITHUB_OUTPUT"
- name: Read version
if: steps.check.outputs.is_release == 'true'
id: version
run: |
VERSION=$(node -p "require('./package.json').version")
TAG="v${VERSION}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
# Detect prerelease versions (e.g. 1.13.0-dev.1, 1.13.0-beta.2)
if echo "$VERSION" | grep -q -- '-'; then
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
echo "npm_tag=dev" >> "$GITHUB_OUTPUT"
echo "Prerelease detected: $VERSION → npm tag: dev"
else
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
echo "npm_tag=latest" >> "$GITHUB_OUTPUT"
echo "Stable release: $VERSION → npm tag: latest"
fi
- uses: actions/setup-node@v4
if: steps.check.outputs.is_release == 'true' || steps.check.outputs.is_promote_stable == 'true'
with:
node-version: 22
cache: npm
registry-url: https://registry.npmjs.org
# ── Promote to npm stable tag ──────────────────────────
# Branch naming: YYYY-MM-DD_promote-stable-v{VERSION}
# Commit message: "promote: stable v{VERSION} — reason"
# CI runs `npm dist-tag add opencode-acp@VERSION stable`
- name: Promote to npm stable tag
if: steps.check.outputs.is_promote_stable == 'true'
run: |
VERSION="${{ steps.check.outputs.stable_version }}"
echo "Promoting opencode-acp@$VERSION to 'stable' tag"
npm dist-tag add "opencode-acp@$VERSION" stable
echo "✓ Done. Users can now install via opencode-acp@stable"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# ── Normal release flow (unchanged) ────────────────────
- if: steps.check.outputs.is_release == 'true'
run: npm ci
- if: steps.check.outputs.is_release == 'true'
run: npm run check:package
- if: steps.check.outputs.is_release == 'true'
run: npm test
- name: Create tag
if: steps.check.outputs.is_release == 'true'
run: |
TAG="${{ steps.version.outputs.tag }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists — skipping"
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "release $TAG (auto-tagged on merge)"
git push origin "$TAG"
echo "Created tag $TAG"
fi
- name: Publish to npm
if: steps.check.outputs.is_release == 'true'
run: |
NPM_TAG="${{ steps.version.outputs.npm_tag }}"
echo "Publishing ${{ steps.version.outputs.version }} with tag: $NPM_TAG"
npm publish --tag "$NPM_TAG"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
if: steps.check.outputs.is_release == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
generate_release_notes: true
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`);