Compare commits

...
11 changed files with 204 additions and 90 deletions
+27 -10
View File
@@ -21,7 +21,11 @@ git checkout main && git pull origin main
Get the latest release tag:
```bash
git tag --sort=-v:refname | head -1
LAST_TAG=$(git tag --list 'v*-vscode' --sort=-v:refname | head -1)
if [ -z "$LAST_TAG" ]; then
LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
fi
echo "$LAST_TAG"
```
## Step 2: Present Commits Since Last Release
@@ -29,15 +33,22 @@ git tag --sort=-v:refname | head -1
Show all commits on main since the last release tag:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
LAST_TAG=$(git tag --list 'v*-vscode' --sort=-v:refname | head -1)
if [ -z "$LAST_TAG" ]; then
LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
fi
git log ${LAST_TAG}..HEAD --oneline --format="%h %s (%an)"
```
Also get the commit messages already on the tag (to identify previously cherry-picked commits). Note: Run these as separate commands to avoid shell parsing issues with parentheses in author names:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
PREV_TAG=$(git tag --sort=-v:refname | head -2 | tail -1)
get_vscode_release_tags() {
git tag --list 'v*-vscode' --sort=-v:refname
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$'
}
LAST_TAG=$(get_vscode_release_tags | head -1)
PREV_TAG=$(get_vscode_release_tags | head -2 | tail -1)
```
```bash
@@ -64,7 +75,10 @@ Build a mental model of what these changes do for the changelog.
Parse the current version from package.json and the last tag:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
LAST_TAG=$(git tag --list 'v*-vscode' --sort=-v:refname | head -1)
if [ -z "$LAST_TAG" ]; then
LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
fi
echo "Last release: $LAST_TAG"
cat package.json | grep '"version"'
```
@@ -120,7 +134,10 @@ git push origin main
Checkout the last release tag (detached HEAD):
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
LAST_TAG=$(git tag --list 'v*-vscode' --sort=-v:refname | head -1)
if [ -z "$LAST_TAG" ]; then
LAST_TAG=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
fi
git checkout $LAST_TAG
```
@@ -146,10 +163,10 @@ After all cherry-picks are applied successfully:
```bash
# Tag the new release
git tag v{VERSION}
git tag -a v{VERSION}-vscode -m "VS Code v{VERSION}"
# Push the tag to remote
git push origin v{VERSION}
git push origin refs/tags/v{VERSION}-vscode
```
## Step 8: Return to Main and Summary
@@ -171,12 +188,12 @@ VS Code Hotfix v{VERSION} Published
Present a final summary:
- New version: v{VERSION}
- Tag pushed: yes
- Tag pushed: v{VERSION}-vscode
- Commits included: (list them)
- Slack message copied to clipboard: yes
Remind the user to:
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml (paste `v{VERSION}` as the tag)
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml (paste `v{VERSION}-vscode` as the tag)
2. Post the Slack message to announce the hotfix
## Important Notes
+5 -5
View File
@@ -36,8 +36,8 @@ Confirm the release version with the maintainer (patch/minor/major).
git add CHANGELOG.md package.json package-lock.json
git commit -m "v<version> Release Notes"
git push origin main
git tag v<version>
git push origin v<version>
git tag -a v<version>-vscode -m "VS Code v<version>"
git push origin refs/tags/v<version>-vscode
```
### 4) Trigger publish workflow
@@ -45,15 +45,15 @@ git push origin v<version>
Tell the maintainer to run:
https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml
Use `v<version>` as the release tag.
Use `v<version>-vscode` as the release tag.
### 5) Update GitHub release notes
After publish completes:
```bash
gh release view v<version> --json body --jq '.body'
gh release edit v<version> --notes "<final curated release notes>"
gh release view v<version>-vscode --json body --jq '.body'
gh release edit v<version>-vscode --notes "<final curated release notes>"
```
### 6) Final summary
+33 -6
View File
@@ -14,7 +14,7 @@ on:
- main
- nightly
git_tag:
description: "Existing release tag to publish when publish_target=main, for example cli-v0.1.0"
description: "Existing release tag to publish when publish_target=main, for example v0.1.0-cli"
required: false
type: string
confirm_publish:
@@ -96,12 +96,13 @@ jobs:
exit 1
fi
if ! printf "%s\n" "$TAG" | grep -Eq '^cli-v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "git_tag must look like cli-vX.Y.Z, got: ${TAG}"
if ! printf "%s\n" "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+-cli$'; then
echo "git_tag must look like vX.Y.Z-cli, got: ${TAG}"
exit 1
fi
VERSION="${TAG#cli-v}"
VERSION="${TAG#v}"
VERSION="${VERSION%-cli}"
PACKAGE_VERSION=$(node -p "require('./apps/cli/package.json').version")
if [ "$PACKAGE_VERSION" != "$VERSION" ]; then
@@ -181,7 +182,18 @@ jobs:
id: prev_tag
run: |
CURRENT_TAG="${{ steps.version.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 --match 'cli-v*' "$CURRENT_TAG^" 2>/dev/null || echo "")
NEW_STYLE_PREV=$(git describe --tags --abbrev=0 --match 'v*-cli' "$CURRENT_TAG^" 2>/dev/null || true)
LEGACY_PREV=$(git describe --tags --abbrev=0 --match 'cli-v*' "$CURRENT_TAG^" 2>/dev/null || true)
PREV_TAG="$NEW_STYLE_PREV"
if [ -n "$NEW_STYLE_PREV" ] && [ -n "$LEGACY_PREV" ]; then
if git merge-base --is-ancestor "${NEW_STYLE_PREV}^{commit}" "${LEGACY_PREV}^{commit}"; then
PREV_TAG="$LEGACY_PREV"
fi
elif [ -z "$PREV_TAG" ]; then
PREV_TAG="$LEGACY_PREV"
fi
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
@@ -235,7 +247,7 @@ jobs:
publish-nightly:
name: Publish cline nightly
permissions:
contents: read
contents: write
id-token: write
if: |
github.repository == 'cline/cline' &&
@@ -386,6 +398,21 @@ jobs:
run: bun script/publish-npm.ts --tag nightly
working-directory: sdk/apps/cli
- name: Create nightly git tag
if: steps.check_commits.outputs.skip != 'true'
run: |
BASE_VERSION="${{ steps.version.outputs.base_version }}"
NIGHTLY_VERSION="${{ steps.version.outputs.version }}"
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
TAG="v${BASE_VERSION}-cli-nightly.${TIMESTAMP}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "CLI nightly ${NIGHTLY_VERSION} from ${GITHUB_REF_NAME} at ${GITHUB_SHA}"
git push origin "refs/tags/${TAG}"
echo "Tagged published commit: $TAG"
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
run: |
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
permissions:
contents: read
contents: write
packages: write
checks: write
pull-requests: write
@@ -49,6 +49,12 @@ jobs:
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Capture base version
id: base_version
run: |
BASE_VERSION=$(node -p "require('./package.json').version")
echo "base_version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
- name: Verify LFS media assets are resolved
run: |
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
@@ -73,3 +79,19 @@ jobs:
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
run: npm run publish:marketplace:nightly
- name: Tag published commit
env:
GH_TOKEN: ${{ github.token }}
run: |
BASE_VERSION="${{ steps.base_version.outputs.base_version }}"
SOURCE_SHA=$(git rev-parse HEAD)
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
TAG="v${BASE_VERSION}-vscode-nightly.${TIMESTAMP}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "VS Code nightly from ${SDK_NIGHTLY_REF} at ${SOURCE_SHA}"
git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${TAG}"
echo "Tagged published commit: $TAG"
@@ -61,6 +61,12 @@ jobs:
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Capture base version
id: base_version
run: |
BASE_VERSION=$(node -p "require('./package.json').version")
echo "base_version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
- name: Verify LFS media assets are resolved
run: |
for FILE in webview-ui/src/assets/cline_kanban_demo.mp4 webview-ui/src/assets/cline_kanban_demo.webm; do
@@ -90,14 +96,13 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
SAFE_REF=$(echo "$GITHUB_REF_NAME" | tr '/[:upper:]' '-[:lower:]' | tr -cd 'a-z0-9._-')
SHORT_SHA=$(git rev-parse --short=12 HEAD)
BASE_VERSION="${{ steps.base_version.outputs.base_version }}"
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
TAG="nightly-${SAFE_REF}-${TIMESTAMP}-${SHORT_SHA}"
TAG="v${BASE_VERSION}-vscode-nightly.${TIMESTAMP}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Cline Nightly published from ${GITHUB_REF_NAME} at ${GITHUB_SHA}"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "VS Code nightly from ${GITHUB_REF_NAME} at ${GITHUB_SHA}"
# Use an explicit HTTPS remote with GH_TOKEN because checkout was run with
# persist-credentials: false, so actions/checkout did not persist a git credential helper.
git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${TAG}"
@@ -17,7 +17,7 @@ on:
default: true
type: boolean
tag:
description: "Tag to publish (required in both modes, e.g., v3.1.2)"
description: "Tag to publish (required in both modes, e.g., v3.1.2-vscode)"
required: true
type: string
@@ -59,8 +59,8 @@ jobs:
exit 1
fi
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
echo "Error: tag must match vX.Y.Z (optionally with -suffix or .suffix)"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-vscode$ ]]; then
echo "Error: tag must match vX.Y.Z-vscode"
exit 1
fi
TAG_REF="refs/tags/$TAG"
@@ -88,8 +88,8 @@ jobs:
echo "Tag '$TAG' already exists at tested SHA. Continuing."
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG" "$TESTED_SHA"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "VS Code ${TAG}" "$TESTED_SHA"
git push origin "$TAG_REF"
echo "Created and pushed tag '$TAG' from tested SHA $TESTED_SHA."
fi
@@ -128,8 +128,10 @@ jobs:
- name: Verify Tag Matches Package Version
run: |
TAG="${{ steps.resolve_tag.outputs.tag }}"
VERSION="v${{ steps.get_version.outputs.version }}"
if [[ "$TAG" != "$VERSION" ]]; then
TAG_VERSION="${TAG#v}"
TAG_VERSION="${TAG_VERSION%-vscode}"
VERSION="${{ steps.get_version.outputs.version }}"
if [[ "$TAG_VERSION" != "$VERSION" ]]; then
echo "Error: tag '$TAG' does not match package version '$VERSION'"
exit 1
fi
@@ -182,7 +184,13 @@ jobs:
id: prev_tag
run: |
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
PREV_TAG=$(git describe --tags --abbrev=0 --match 'v*-vscode' "$CURRENT_TAG^" 2>/dev/null || true)
if [[ -z "$PREV_TAG" ]]; then
# Migration fallback for old VS Code releases, which used plain vX.Y.Z tags.
# Exclude all hyphenated tags so CLI, SDK package, and nightly tags cannot
# become the changelog baseline.
PREV_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*.[0-9]*.[0-9]*' --exclude 'v*-*' "$CURRENT_TAG^" 2>/dev/null || echo "")
fi
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
+25 -7
View File
@@ -56,6 +56,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Determine publish channel
id: channel
@@ -155,6 +158,7 @@ jobs:
echo "Base version: $BASE_VERSION"
echo "Channel: $CHANNEL"
echo "Publish version: $VERSION"
echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Update all package versions and lockfile
@@ -236,7 +240,7 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
for PKG in shared llms agents core sdk; do
TAG="sdk/${PKG}/v${VERSION}"
TAG="v${VERSION}-sdk-${PKG}"
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
echo "Tag already exists locally: ${TAG}"
else
@@ -244,10 +248,24 @@ jobs:
echo "Created tag: ${TAG}"
fi
# Ensure remote has the tag; this is idempotent if tag already exists remotely.
git push origin "refs/tags/${TAG}"
done
- name: Create nightly git tag
if: steps.check_commits.outputs.skip != 'true' && steps.channel.outputs.channel == 'nightly'
run: |
BASE_VERSION="${{ steps.version.outputs.base_version }}"
NIGHTLY_VERSION="${{ steps.version.outputs.version }}"
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
TAG="v${BASE_VERSION}-sdk-nightly.${TIMESTAMP}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "SDK nightly ${NIGHTLY_VERSION} from ${GITHUB_REF_NAME} at ${GITHUB_SHA}"
git push origin "refs/tags/${TAG}"
echo "Tagged published commit: $TAG"
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
run: |
@@ -261,9 +279,9 @@ jobs:
echo " - @cline/sdk@${VERSION}"
if [ "$CHANNEL" = "latest" ]; then
echo "Created git tags:"
echo " - sdk/shared/v${VERSION}"
echo " - sdk/llms/v${VERSION}"
echo " - sdk/agents/v${VERSION}"
echo " - sdk/core/v${VERSION}"
echo " - sdk/sdk/v${VERSION}"
echo " - v${VERSION}-sdk-shared"
echo " - v${VERSION}-sdk-llms"
echo " - v${VERSION}-sdk-agents"
echo " - v${VERSION}-sdk-core"
echo " - v${VERSION}-sdk-sdk"
fi
+12 -12
View File
@@ -91,7 +91,7 @@ Additional SDK flags: `--skip-tests`, `--skip-git-tags`.
The script checks out `main` (and pulls latest) before starting. If the working tree is dirty it aborts.
The SDK flow runs: tests → version bump → lockfile regeneration → tarball verification → publish (shared → llms → agents → core) → optional `sdk-v{VERSION}` tag creation.
The SDK flow runs: tests → version bump → lockfile regeneration → tarball verification → publish (shared → llms → agents → core) → optional `v{VERSION}-sdk-{PACKAGE}` tag creation.
### CLI Release
@@ -101,7 +101,7 @@ Under the hood, every release starts the same way: prepare one release commit, t
Prepare the release commit from the code you want to release:
1. Draft user-facing release notes from the commits since the last `cli-vX.Y.Z` tag.
1. Draft user-facing release notes from the commits since the last `vX.Y.Z-cli` tag.
2. Choose the release version.
3. Update `apps/cli/package.json`.
4. Add the approved notes to `apps/cli/CHANGELOG.md`.
@@ -115,12 +115,12 @@ Path A: publish from GitHub Actions.
Use this for normal releases. Merge the release commit to `main`, create and push the matching release tag, then run:
```sh
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=cli-vX.Y.Z -f confirm_publish=publish
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=vX.Y.Z-cli -f confirm_publish=publish
```
The workflow checks out the provided `cli-vX.Y.Z` tag, verifies it matches `apps/cli/package.json`, builds the platform packages, publishes to npm with the `latest` dist-tag, creates the GitHub release, and posts to Slack.
The workflow checks out the provided `vX.Y.Z-cli` tag, verifies it matches `apps/cli/package.json`, builds the platform packages, publishes to npm with the `latest` dist-tag, creates the GitHub release, and posts to Slack.
Path B: publish locally.
@@ -129,13 +129,13 @@ Use this when publishing from an authenticated local machine. Start from a clean
```sh
gh auth status
npm whoami
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
bun release cli
gh release create cli-vX.Y.Z --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
gh release create vX.Y.Z-cli --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
```
The local helper verifies the working tree is clean, verifies `cli-vX.Y.Z` points at `HEAD` locally and on `origin`, runs tests, builds platform packages, and publishes to npm.
The local helper verifies the working tree is clean, verifies `vX.Y.Z-cli` points at `HEAD` locally and on `origin`, runs tests, builds platform packages, and publishes to npm.
Nightly release:
@@ -143,7 +143,7 @@ Nightly release:
gh workflow run cli-publish.yml -f publish_target=nightly
```
Nightly also runs on a schedule. It publishes `X.Y.Z-nightly.TIMESTAMP` to npm with the `nightly` dist-tag and skips if there were no commits in the last 24 hours unless forced.
Nightly also runs on a schedule. It publishes `X.Y.Z-nightly.TIMESTAMP` to npm with the `nightly` dist-tag, creates a `vX.Y.Z-cli-nightly.TIMESTAMP` git tag, and skips if there were no commits in the last 24 hours unless forced.
### Manual SDK Publish
@@ -158,7 +158,7 @@ If you need fine-grained control over individual steps:
```sh
cd packages/shared && bun publish && cd ../llms && bun publish && cd ../agents && bun publish && cd ../core && bun publish && cd ../../
```
7. For tagged production releases, create and push a git tag: `git tag -a sdk-v{VERSION} -m "SDK v{VERSION}" && git push origin sdk-v{VERSION}`.
7. For tagged production releases, create and push package tags such as `v{VERSION}-sdk-shared`, `v{VERSION}-sdk-llms`, `v{VERSION}-sdk-agents`, `v{VERSION}-sdk-core`, and `v{VERSION}-sdk-sdk`.
### Workspace Dependency Rules
+17 -16
View File
@@ -1,11 +1,11 @@
---
name: publish-cli
description: Use when preparing, tagging, and publishing an apps/cli npm release. Guides changelog drafting, apps/cli/package.json version bumps, cli-vX.Y.Z tags, local npm publishing, and the publish-cli GitHub workflow.
description: Use when preparing, tagging, and publishing an apps/cli npm release. Guides changelog drafting, apps/cli/package.json version bumps, vX.Y.Z-cli tags, local npm publishing, and the publish-cli GitHub workflow.
---
# CLI Release
Use this skill when the user asks to release the CLI, publish `cline`, bump the CLI version, draft release notes, create a `cli-vX.Y.Z` tag, or trigger the CLI publish workflow.
Use this skill when the user asks to release the CLI, publish `cline`, bump the CLI version, draft release notes, create a `vX.Y.Z-cli` tag, or trigger the CLI publish workflow.
The CLI is npm-only. Do not add alternate distribution or signing steps.
@@ -16,16 +16,16 @@ The skill should guide the user through one release preparation flow, then offer
## Release contract
- Version source: `apps/cli/package.json`.
- Main release tag: `cli-vX.Y.Z`, where `X.Y.Z` matches `apps/cli/package.json`.
- Main release tag: `vX.Y.Z-cli`, where `X.Y.Z` matches `apps/cli/package.json`.
- Nightly release version: `X.Y.Z-nightly.TIMESTAMP`.
- Release prep includes approved release notes, a version bump, and an `apps/cli/CHANGELOG.md` update.
- Publish paths:
- GitHub workflow: `.github/workflows/cli-publish.yml`.
- Local publish helper: `bun release cli`.
- npm dist-tags and git tags are separate. `--tag latest` and `--tag nightly` are npm registry channels. `cli-vX.Y.Z` is a git tag for source history and GitHub releases.
- The GitHub main release workflow runs from `main`, requires an existing `cli-vX.Y.Z` tag, checks out that tag, and publishes from it.
- The GitHub nightly workflow publishes to npm with the `nightly` dist-tag and does not create a tag.
- The local release helper requires a clean checkout and `cli-vX.Y.Z` to point at `HEAD` locally and on `origin` before publishing.
- npm dist-tags and git tags are separate. `--tag latest` and `--tag nightly` are npm registry channels. `vX.Y.Z-cli` is a git tag for source history and GitHub releases.
- The GitHub main release workflow runs from `main`, requires an existing `vX.Y.Z-cli` tag, checks out that tag, and publishes from it.
- The GitHub nightly workflow publishes to npm with the `nightly` dist-tag and creates a `vX.Y.Z-cli-nightly.TIMESTAMP` git tag.
- The local release helper requires a clean checkout and `vX.Y.Z-cli` to point at `HEAD` locally and on `origin` before publishing.
- Local GitHub release creation requires `gh` to be authenticated with release permissions for the repo.
- Always ask before pushing commits or tags.
- Do not amend commits unless explicitly requested.
@@ -37,11 +37,12 @@ The skill should guide the user through one release preparation flow, then offer
```sh
git status --short --branch
git fetch origin --tags
git tag --list 'cli-v*' --sort=-v:refname | head -10
LAST_CLI_TAG=$(git describe --tags --abbrev=0 --match 'v*-cli' HEAD)
printf '%s\n' "$LAST_CLI_TAG"
node -p "require('./apps/cli/package.json').version"
```
Find the latest CLI tag. If there is no `cli-v*` tag, use the first relevant CLI release commit as the baseline and say that the baseline is inferred.
Find the latest CLI tag by ancestry, not by version sort. The release contract uses `vX.Y.Z-cli` tags.
2. Collect release commits.
@@ -110,15 +111,15 @@ git push origin HEAD
For the GitHub main release path, ask before creating and pushing the release tag:
```sh
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
```
8. Publish.
Ask the user which path to use:
- GitHub main release. Use this after the release commit is on `main` and the matching `cli-vX.Y.Z` tag has been pushed. The workflow publishes to npm from that tag, creates the GitHub release, and posts to Slack.
- GitHub main release. Use this after the release commit is on `main` and the matching `vX.Y.Z-cli` tag has been pushed. The workflow publishes to npm from that tag, creates the GitHub release, and posts to Slack.
- Local release. Use this when the user wants to publish from this machine. The local machine must be authenticated to npm and GitHub.
- GitHub nightly release.
- Stop after the version commit.
@@ -126,7 +127,7 @@ Ask the user which path to use:
For GitHub main release:
```sh
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=cli-vX.Y.Z -f confirm_publish=publish
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=vX.Y.Z-cli -f confirm_publish=publish
gh run list --workflow=cli-publish.yml --limit=1 --json url,status,conclusion,createdAt --jq '.[0]'
```
@@ -147,15 +148,15 @@ For local publish:
```sh
gh auth status
npm whoami
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
bun release cli
```
After a successful local publish, ask before running:
```sh
gh release create cli-vX.Y.Z --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
gh release create vX.Y.Z-cli --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
```
If publishing with another npm dist-tag:
+10 -10
View File
@@ -79,7 +79,7 @@ cline auth # authenticate a provider
Every release starts by preparing one release commit from the code you want to publish:
1. Draft user-facing release notes from the commits since the last `cli-vX.Y.Z` tag.
1. Draft user-facing release notes from the commits since the last `vX.Y.Z-cli` tag.
2. Choose the release version. Because this publishes over the existing `cline` package, the version must be greater than the current published `cline` version. The handoff release is `3.0.0`.
3. Update `apps/cli/package.json`.
4. Add the approved notes to `apps/cli/CHANGELOG.md`.
@@ -93,12 +93,12 @@ Then publish that release commit with one of these paths.
Use this path for normal releases.
```bash
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=cli-vX.Y.Z -f confirm_publish=publish
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=vX.Y.Z-cli -f confirm_publish=publish
```
This path requires the release commit to be on `main` and the matching `cli-vX.Y.Z` tag to exist before the workflow runs. The workflow checks out the tag, publishes to npm with the `latest` dist-tag, creates the GitHub release, and posts to Slack.
This path requires the release commit to be on `main` and the matching `vX.Y.Z-cli` tag to exist before the workflow runs. The workflow checks out the tag, publishes to npm with the `latest` dist-tag, creates the GitHub release, and posts to Slack.
### Publish Locally
@@ -109,10 +109,10 @@ Start from a clean checkout at the release commit:
```bash
gh auth status
npm whoami
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
git tag -a vX.Y.Z-cli -m "CLI vX.Y.Z"
git push origin refs/tags/vX.Y.Z-cli
bun release cli
gh release create cli-vX.Y.Z --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
gh release create vX.Y.Z-cli --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
```
The release helper checks the working tree, verifies the tag points at `HEAD` locally and on `origin`, runs tests, builds all platform packages, and publishes the platform packages plus the generated `cline` wrapper package to npm. The package version and tag must match.
@@ -128,9 +128,9 @@ bun release cli --tag next
The GitHub workflow at `.github/workflows/cli-publish.yml` automates publishing:
- Main releases are manual. Select `publish_target=main` and set `confirm_publish=publish`.
- Main releases require `git_tag=cli-vX.Y.Z`, check out that tag, verify it matches `apps/cli/package.json`, run tests, build all platform packages, publish to npm with the `latest` dist-tag using trusted publishing, create a GitHub release, and post to Slack.
- Main releases require `git_tag=vX.Y.Z-cli`, check out that tag, verify it matches `apps/cli/package.json`, run tests, build all platform packages, publish to npm with the `latest` dist-tag using trusted publishing, create a GitHub release, and post to Slack.
- Nightly releases run on a schedule or manually with `publish_target=nightly`.
- Nightly releases publish `X.Y.Z-nightly.TIMESTAMP` to npm with the `nightly` dist-tag and skip if there were no commits in the last 24 hours unless forced.
- Nightly releases publish `X.Y.Z-nightly.TIMESTAMP` to npm with the `nightly` dist-tag, create a `vX.Y.Z-cli-nightly.TIMESTAMP` git tag, and skip if there were no commits in the last 24 hours unless forced.
CI publishing uses npm trusted publishing. Configure npm trusted publishers for the `cline` wrapper package and every platform package before relying on the workflow.
+26 -10
View File
@@ -47,7 +47,7 @@ if (!target || !["sdk", "cli"].includes(target)) {
console.error("");
console.error("Targets:");
console.error(" sdk Publish @cline/{shared,llms,agents,core,sdk} to npm");
console.error(" cli Publish cline from an existing cli-vX.Y.Z git tag");
console.error(" cli Publish cline from an existing vX.Y.Z-cli git tag");
console.error("");
console.error("Options:");
console.error(
@@ -235,7 +235,7 @@ async function ensureCleanWorkingTree(): Promise<void> {
}
async function ensureCliReleaseTag(version: string): Promise<void> {
const expectedTag = `cli-v${version}`;
const expectedTag = `v${version}-cli`;
if (dryRun) {
console.log(` [dry-run] Required pushed git tag: ${expectedTag}`);
return;
@@ -400,19 +400,35 @@ async function releaseSDK(version: string): Promise<number> {
// Step 5: Git tag
if (npmTag === "latest" && !skipGitTags) {
header("Step 5/5: Creating git tag");
const gitTag = `sdk-v${version}`;
console.log(` Creating tag: ${gitTag}`);
await run(["git", "tag", "-a", gitTag, "-m", `SDK v${version}`]);
const gitTags = SDK_PUBLISH_ORDER.map(
(workspace) => `v${version}-sdk-${workspace}`,
);
console.log(` Creating tags: ${gitTags.join(", ")}`);
for (const workspace of SDK_PUBLISH_ORDER) {
const gitTag = `v${version}-sdk-${workspace}`;
await run([
"git",
"tag",
"-a",
gitTag,
"-m",
`@cline/${workspace}@${version}`,
]);
}
if (!dryRun) {
const pushOk = await confirm(
"\nPush tag to remote? This makes the release public.",
"\nPush tags to remote? This makes the release public.",
);
if (pushOk) {
await run(["git", "push", "origin", `refs/tags/${gitTag}`]);
for (const gitTag of gitTags) {
await run(["git", "push", "origin", `refs/tags/${gitTag}`]);
}
} else {
console.log(` Skipped pushing tag. Push manually with:`);
console.log(` git push origin refs/tags/${gitTag}`);
console.log(" Skipped pushing tags. Push manually with:");
for (const gitTag of gitTags) {
console.log(` git push origin refs/tags/${gitTag}`);
}
}
}
} else if (skipGitTags) {
@@ -443,7 +459,7 @@ async function releaseSDK(version: string): Promise<number> {
async function releaseCLI(version: string): Promise<number> {
console.log(`\nRelease CLI`);
console.log(` Version: ${version}`);
console.log(` Git tag: cli-v${version}`);
console.log(` Git tag: v${version}-cli`);
console.log(` Tag: ${npmTag}`);
console.log(` Dry run: ${dryRun}`);
console.log(` Skip tests: ${skipTests}`);