Skip to content

[codex] stabilize openrouter defaults and app identity (#197) #83

[codex] stabilize openrouter defaults and app identity (#197)

[codex] stabilize openrouter defaults and app identity (#197) #83

Workflow file for this run

# CD Release - Version bump + GitHub release creation
# Trigger: main push (skips version bump commits)
# Parallel: runs alongside ci-tests on main
name: cd-release
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
concurrency:
group: cd-release-${{ github.ref }}
cancel-in-progress: true
jobs:
release:
runs-on: blacksmith-2vcpu-ubuntu-2404
# Skip if commit message contains [skip-release] or is a version bump commit
if: |
!contains(github.event.head_commit.message, '[skip-release]') &&
!contains(github.event.head_commit.message, 'chore: bump version')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Skip bump merges
id: skip_release
run: |
MSG=$(git log -1 --pretty=%B)
if echo "$MSG" | grep -q "chore: bump version"; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping release for bump commit."
exit 0
fi
if echo "$MSG" | grep -q "chore/bump-version"; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping release for bump merge."
exit 0
fi
PARENTS=$(git show -s --pretty=%P HEAD)
PARENT_COUNT=$(echo "$PARENTS" | wc -w | tr -d ' ')
if [ "$PARENT_COUNT" -gt 1 ]; then
MERGED_HEAD=$(echo "$PARENTS" | awk '{print $2}')
MERGED_MSG=$(git show -s --pretty=%B "$MERGED_HEAD")
if echo "$MERGED_MSG" | grep -q "chore: bump version"; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping release for bump merge."
exit 0
fi
fi
# Skip releases when changes are limited to non-runtime paths (docs, OpenCode, CI, or the npm plugin package).
# This prevents bumping the app version when only publishing/iterating on the OpenCode plugin.
BEFORE_SHA="${{ github.event.before }}"
AFTER_SHA="${{ github.sha }}"
CHANGED_FILES="$(git diff --name-only "${BEFORE_SHA}..${AFTER_SHA}" || true)"
if [ -n "$CHANGED_FILES" ]; then
ONLY_IGNORED="true"
while IFS= read -r file; do
[ -z "$file" ] && continue
case "$file" in
README.md|AGENTS.md|bun.lock) ;;
docs/*|.codex/*) ;;
packages/opencode-excalidraw/*) ;;
.github/workflows/*) ;;
*)
ONLY_IGNORED="false"
break
;;
esac
done <<< "$CHANGED_FILES"
if [ "$ONLY_IGNORED" = "true" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping release: only non-runtime paths changed."
exit 0
fi
fi
- name: Setup Bun
uses: oven-sh/setup-bun@v2
if: steps.skip_release.outputs.skip != 'true'
with:
bun-version: latest
- name: Install dependencies
if: steps.skip_release.outputs.skip != 'true'
run: bun install
- name: Configure Git
if: steps.skip_release.outputs.skip != 'true'
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Get current version
id: current_version
if: steps.skip_release.outputs.skip != 'true'
run: echo "version=$(bun -e "console.log(require('./package.json').version)")" >> $GITHUB_OUTPUT
- name: Create bump branch
id: bump_branch
if: steps.skip_release.outputs.skip != 'true'
run: |
BRANCH="chore/bump-version-${{ github.run_id }}-${{ github.run_attempt }}"
git checkout -b "$BRANCH"
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
- name: Bump version
id: bump_version
if: steps.skip_release.outputs.skip != 'true'
run: |
CUR_VERSION="${{ steps.current_version.outputs.version }}"
CUR_VERSION="${CUR_VERSION%%-*}"
IFS='.' read -r CUR_MAJOR CUR_MINOR _ <<< "$CUR_VERSION"
for _ in {1..10}; do
npm version patch --no-git-tag-version
NEW_VERSION=$(bun -e "console.log(require('./package.json').version)")
NEW_VERSION_BASE="${NEW_VERSION%%-*}"
IFS='.' read -r NEW_MAJOR NEW_MINOR _ <<< "$NEW_VERSION_BASE"
if [ "$CUR_MAJOR" != "$NEW_MAJOR" ] || [ "$CUR_MINOR" != "$NEW_MINOR" ]; then
echo "Refusing to bump major/minor; patch-only enforced."
exit 1
fi
TAG="v$NEW_VERSION"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag $TAG already exists; bumping again."
continue
fi
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "Bumped version from ${{ steps.current_version.outputs.version }} to $NEW_VERSION"
exit 0
done
echo "Failed to find unused patch version after 10 bumps."
exit 1
- name: Commit version bump
if: steps.skip_release.outputs.skip != 'true'
run: |
git add package.json
git commit -m "chore: bump version to ${{ steps.bump_version.outputs.new_version }} [skip-release] [skip ci]"
git push --set-upstream origin "${{ steps.bump_branch.outputs.branch }}"
- name: Create PR
id: bump_pr
if: steps.skip_release.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_URL=$(gh pr create \
--repo "$GITHUB_REPOSITORY" \
--base main \
--head "${{ steps.bump_branch.outputs.branch }}" \
--title "chore: bump version to ${{ steps.bump_version.outputs.new_version }}" \
--body "Automated patch version bump.")
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
- name: Enable auto-merge
if: steps.skip_release.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
PR_URL="${{ steps.bump_pr.outputs.pr_url }}"
set +e
MERGE_OUTPUT=$(
gh pr merge "$PR_URL" \
--repo "$GITHUB_REPOSITORY" \
--squash \
--auto 2>&1
)
MERGE_STATUS=$?
set -e
if [ "$MERGE_STATUS" -eq 0 ]; then
echo "$MERGE_OUTPUT"
exit 0
fi
if printf '%s' "$MERGE_OUTPUT" | grep -q "in clean status"; then
echo "Auto-merge not applicable, merging immediately."
gh pr merge "$PR_URL" \
--repo "$GITHUB_REPOSITORY" \
--squash \
--delete-branch
exit 0
fi
echo "$MERGE_OUTPUT"
exit "$MERGE_STATUS"
- name: Wait for PR merge
if: steps.skip_release.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_URL="${{ steps.bump_pr.outputs.pr_url }}"
for _ in {1..30}; do
MERGED_AT=$(gh pr view "$PR_URL" --repo "$GITHUB_REPOSITORY" --json mergedAt -q .mergedAt)
if [ -n "$MERGED_AT" ] && [ "$MERGED_AT" != "null" ]; then
echo "PR merged at $MERGED_AT"
git fetch origin main
git checkout main
git reset --hard origin/main
exit 0
fi
sleep 5
done
echo "PR did not merge in time."
exit 1
- name: Generate release notes
id: release_notes
if: steps.skip_release.outputs.skip != 'true'
run: |
# Get the latest tag (if any)
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
# Generate release notes
if [ -z "$LATEST_TAG" ]; then
NOTES="## Initial Release"
echo "Initial release - no previous tags found"
else
echo "Generating notes from $LATEST_TAG to HEAD"
# Get commit messages since last tag, excluding version bump commits
COMMITS=$(git log $LATEST_TAG..HEAD --pretty=format:"- %s" --no-merges | grep -v "chore: bump version" | grep -v "\[skip-release\]")
if [ -z "$COMMITS" ]; then
NOTES="## Release v${{ steps.bump_version.outputs.new_version }}"
else
printf -v NOTES "## Release v${{ steps.bump_version.outputs.new_version }}\n\n### Changes\n%s" "$COMMITS"
fi
fi
# Save to file for multiline support
echo "$NOTES" > release_notes.md
echo "Release notes generated"
- name: Create GitHub Release
if: steps.skip_release.outputs.skip != 'true'
run: |
gh release create v${{ steps.bump_version.outputs.new_version }} \
--title "Release v${{ steps.bump_version.outputs.new_version }}" \
--notes-file release_notes.md \
--target main
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Clean up
if: always() && steps.skip_release.outputs.skip != 'true'
run: |
rm -f release_notes.md