Skip to content
Merged
87 changes: 59 additions & 28 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ on:
description: New version
default: patch
required: true
prerelease:
type: boolean
description: Release as rc (prerelease)
default: false

env:
NEW_VERSION_TYPE: ${{ inputs.newversion }}
PRERELEASE: ${{ inputs.prerelease }}
DRY_RUN: ${{ inputs.environment == 'dry-run' }}

permissions:
contents: read
Expand All @@ -41,6 +50,7 @@ jobs:
outputs:
new_tag: ${{ steps.bump_version.outputs.new_tag }}
previous_tag: ${{ steps.get_latest_tag.outputs.latest_tag }}
previous_stable_tag: ${{ steps.get_latest_tag.outputs.latest_stable_tag }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -56,15 +66,28 @@ jobs:
latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "latest_tag=${latest_tag}" >> "$GITHUB_OUTPUT"

latest_stable_tag=$(git tag --list 'v*' --sort=-v:refname | grep -v '-' | head -n 1)
latest_stable_tag="${latest_stable_tag:-v0.0.0}"
echo "latest_stable_tag=${latest_stable_tag}" >> "$GITHUB_OUTPUT"

- name: Bump version
id: bump_version
working-directory: apps/cli
env:
LATEST_TAG: ${{ steps.get_latest_tag.outputs.latest_tag }}
NEW_VERSION_TYPE: ${{ github.event.inputs.newversion }}
run: |
npm version "$LATEST_TAG" --no-git-tag-version --allow-same-version
npm version "$NEW_VERSION_TYPE" --no-git-tag-version

if [ "$PRERELEASE" = "true" ]; then
current_version=$(node -p "require('./package.json').version")
if echo "$current_version" | grep -q -- "-rc\."; then
npm version prerelease --preid rc --no-git-tag-version
else
npm version "pre${NEW_VERSION_TYPE}" --preid rc --no-git-tag-version
fi
else
npm version "$NEW_VERSION_TYPE" --no-git-tag-version
fi

new_version=$(node -p "require('./package.json').version")
new_tag="v${new_version}"
Expand All @@ -90,6 +113,10 @@ jobs:
permissions:
contents: write
id-token: write
env:
NEW_TAG: ${{ needs.build.outputs.new_tag }}
PREVIOUS_TAG: ${{ needs.build.outputs.previous_tag }}
PREVIOUS_STABLE_TAG: ${{ needs.build.outputs.previous_stable_tag }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

Expand All @@ -104,14 +131,16 @@ jobs:
path: apps/cli

- name: Publish to npm
if: inputs.environment != 'dry-run'
working-directory: apps/cli
run: npm publish --provenance --access public

- name: Publish dry run
if: inputs.environment == 'dry-run'
working-directory: apps/cli
run: npm publish --provenance --access public --dry-run
run: |
flags="--provenance --access public"
if [ "$PRERELEASE" = "true" ]; then
flags="$flags --tag rc"
fi
if [ "$DRY_RUN" = "true" ]; then
flags="$flags --dry-run"
fi
npm publish $flags

- name: Configure git
if: inputs.environment != 'dry-run'
Expand All @@ -122,7 +151,6 @@ jobs:
- name: Push version commit and tag
if: inputs.environment != 'dry-run'
env:
NEW_TAG: ${{ needs.build.outputs.new_tag }}
REF_NAME: ${{ github.ref_name }}
run: |
git add apps/cli/package.json
Expand All @@ -132,26 +160,29 @@ jobs:
git push origin "${NEW_TAG}"

- name: Create GitHub release
if: inputs.environment != 'dry-run'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_TAG: ${{ needs.build.outputs.new_tag }}
PREVIOUS_TAG: ${{ needs.build.outputs.previous_tag }}
run: gh release create "$NEW_TAG" --generate-notes --notes-start-tag "$PREVIOUS_TAG"

- name: Preview release notes (dry-run)
if: inputs.environment == 'dry-run'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NEW_TAG: ${{ needs.build.outputs.new_tag }}
PREVIOUS_TAG: ${{ needs.build.outputs.previous_tag }}
REPOSITORY: ${{ github.repository }}
TARGET_COMMITISH: ${{ github.sha }}
run: |
echo "## Release notes for ${NEW_TAG}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
gh api "repos/${REPOSITORY}/releases/generate-notes" \
-f tag_name="$NEW_TAG" \
-f target_commitish="$TARGET_COMMITISH" \
-f previous_tag_name="$PREVIOUS_TAG" \
--jq '.body' >> "$GITHUB_STEP_SUMMARY"
if [ "$PRERELEASE" = "true" ]; then
notes_start_tag="$PREVIOUS_TAG"
else
notes_start_tag="$PREVIOUS_STABLE_TAG"
fi

if [ "$DRY_RUN" = "true" ]; then
echo "## Release notes for ${NEW_TAG}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
gh api "repos/${REPOSITORY}/releases/generate-notes" \
-f tag_name="$NEW_TAG" \
-f target_commitish="$TARGET_COMMITISH" \
-f previous_tag_name="$notes_start_tag" \
--jq '.body' >> "$GITHUB_STEP_SUMMARY"
else
flags="--generate-notes --notes-start-tag $notes_start_tag"
if [ "$PRERELEASE" = "true" ]; then
flags="$flags --prerelease"
fi
gh release create "$NEW_TAG" $flags
fi
20 changes: 17 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,30 @@ pnpm --filter @nulab/bee build
Releases are triggered manually via the [Release workflow](https://github.com/nulab/bee/actions/workflows/release.yml) (`workflow_dispatch`).

1. Go to **Actions > Release > Run workflow**.
2. Select the **environment** (`dry-run` or production).
3. Select the **version bump** (`patch`, `minor`, or `major`).
4. The workflow will:
2. Select the inputs:
- **environment** — `dry-run` (default) or `production`
- **newversion** — `patch`, `minor`, or `major`
- **prerelease** — check to release as rc (prerelease)
3. The workflow will:
- Bump the version in `apps/cli/package.json`
- Build the CLI
- Publish to npm with provenance
- Create a git tag and GitHub release with auto-generated notes

Dry-run mode publishes with `--dry-run` and skips git tag/push, so it's safe to test.

### Input combinations

| newversion | prerelease | Example result | npm tag |
| ---------- | ---------- | --------------------------------------------------------- | -------- |
| `minor` | unchecked | `1.0.0` → `1.1.0` | `latest` |
| `major` | unchecked | `1.0.0` → `2.0.0` | `latest` |
| `patch` | unchecked | `1.0.1` → `1.0.2` | `latest` |
| `minor` | checked | `1.0.0` → `1.1.0-rc.0` | `rc` |
| `major` | checked | `1.0.0` → `2.0.0-rc.0` | `rc` |
| `minor` | checked | `1.1.0-rc.0` → `1.1.0-rc.1` (already rc: bumps rc number) | `rc` |
| `minor` | unchecked | `1.1.0-rc.1` → `1.1.0` (promote to stable) | `latest` |

## Documentation Site

The documentation site (`apps/docs`) uses Astro Starlight. Command reference pages are auto-generated from CLI source code — do not create markdown files under `apps/docs/src/content/docs/commands/`. See [CLAUDE.md](CLAUDE.md#documentation-site-appsdocs) for details.
Expand Down