mirror of
https://github.com/cline/cline.git
synced 2026-09-19 02:05:44 +08:00
vsce reads README.md from the extension root at vsce package / vsce
publish time and has no flag to point it elsewhere, so to use a
different README on the VS Code Marketplace than on the GitHub repo
landing page we swap README.marketplace.md into README.md just before
packaging and put the original back afterwards.
scripts/marketplace-readme.mjs is the shared swap helper. The swap is
idempotent so the CI step in publish.yml can wrap the whole
package-and-publish block while the inner npm scripts still swap
themselves when run locally.
Wired into all three marketplace-bound paths:
- npm run publish:marketplace and :prerelease via the new
scripts/publish-marketplace.mjs wrapper
- npm run publish:marketplace:nightly via publish-nightly.mjs
- The vsce package call in publish.yml that builds the .vsix
attached to the GitHub release, so manual installs match the
marketplace listing
README.marketplace.md starts as a verbatim copy of README.md; future
PRs can repurpose README.md as a multi-product landing page covering
the SDK, JetBrains plugin, CLI, and VS Code extension while the
marketplace listing stays focused on the VS Code UX.
231 lines
9.9 KiB
YAML
231 lines
9.9 KiB
YAML
name: "Publish Release"
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
release-type:
|
|
description: "Choose release type (release or pre-release)"
|
|
required: true
|
|
default: "release"
|
|
type: choice
|
|
options:
|
|
- pre-release
|
|
- release
|
|
auto_create_tag_from_main:
|
|
description: "Auto-create and push the provided tag from the tested main commit (recommended)"
|
|
required: true
|
|
default: true
|
|
type: boolean
|
|
tag:
|
|
description: "Tag to publish (required in both modes, e.g., v3.1.2)"
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: write
|
|
packages: write
|
|
checks: write
|
|
pull-requests: write
|
|
|
|
jobs:
|
|
test:
|
|
uses: ./.github/workflows/test.yml
|
|
|
|
publish:
|
|
needs: test
|
|
name: Publish Extension
|
|
runs-on: ubuntu-latest
|
|
environment: publish
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: main
|
|
fetch-depth: 0
|
|
fetch-tags: true
|
|
lfs: true
|
|
|
|
- name: Resolve Release Tag
|
|
id: resolve_tag
|
|
env:
|
|
TAG: ${{ github.event.inputs.tag }}
|
|
AUTO_CREATE: ${{ github.event.inputs.auto_create_tag_from_main }}
|
|
run: |
|
|
TESTED_SHA="${{ github.sha }}"
|
|
WORKFLOW_REF="${{ github.ref }}"
|
|
|
|
if [[ -z "$TAG" ]]; then
|
|
echo "Error: tag input is required"
|
|
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)"
|
|
exit 1
|
|
fi
|
|
TAG_REF="refs/tags/$TAG"
|
|
|
|
git fetch origin main --tags
|
|
|
|
if [[ "$AUTO_CREATE" == "true" ]]; then
|
|
if [[ "$WORKFLOW_REF" != "refs/heads/main" ]]; then
|
|
echo "Error: auto-create mode requires dispatching from main (current ref: $WORKFLOW_REF)"
|
|
exit 1
|
|
fi
|
|
echo "Auto-create enabled. Using tested workflow SHA: $TESTED_SHA"
|
|
|
|
if ! git merge-base --is-ancestor "$TESTED_SHA" origin/main; then
|
|
echo "Error: tested SHA $TESTED_SHA is not on origin/main"
|
|
exit 1
|
|
fi
|
|
|
|
if git show-ref --verify --quiet "$TAG_REF"; then
|
|
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
|
|
if [[ "$TAG_SHA" != "$TESTED_SHA" ]]; then
|
|
echo "Error: tag '$TAG' already exists at $TAG_SHA, not at tested SHA ($TESTED_SHA)"
|
|
exit 1
|
|
fi
|
|
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 push origin "$TAG_REF"
|
|
echo "Created and pushed tag '$TAG' from tested SHA $TESTED_SHA."
|
|
fi
|
|
else
|
|
if ! git show-ref --verify --quiet "$TAG_REF"; then
|
|
echo "Error: tag '$TAG' does not exist in the repository"
|
|
exit 1
|
|
fi
|
|
echo "Using existing tag '$TAG'."
|
|
fi
|
|
|
|
git checkout --detach "$TAG_REF^{commit}"
|
|
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
echo "resolved_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Install root dependencies
|
|
run: npm install --include=optional
|
|
|
|
- name: Install webview-ui dependencies
|
|
run: cd webview-ui && npm install --include=optional
|
|
|
|
- name: Install Publishing Tools
|
|
run: npm install -g @vscode/vsce ovsx
|
|
|
|
- name: Get Version
|
|
id: get_version
|
|
run: |
|
|
VERSION=$(node -p "require('./package.json').version")
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
- name: Verify Tag Matches Package Version
|
|
run: |
|
|
TAG="${{ steps.resolve_tag.outputs.tag }}"
|
|
VERSION="v${{ steps.get_version.outputs.version }}"
|
|
if [[ "$TAG" != "$VERSION" ]]; then
|
|
echo "Error: tag '$TAG' does not match package version '$VERSION'"
|
|
exit 1
|
|
fi
|
|
echo "Tag and package version match: $TAG"
|
|
|
|
- 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
|
|
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
|
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
- name: Package and Publish Extension
|
|
env:
|
|
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
|
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
|
CLINE_ENVIRONMENT: production
|
|
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
|
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
|
# OpenTelemetry production defaults (can be overridden at runtime)
|
|
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
|
OTEL_LOGS_EXPORTER: otlp
|
|
OTEL_METRICS_EXPORTER: otlp
|
|
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
|
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
|
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
|
RELEASE_TYPE: ${{ github.event.inputs.release-type }}
|
|
run: |
|
|
# Swap README.marketplace.md into README.md so both the GitHub
|
|
# release artifact (vsce package below) and the marketplace
|
|
# publish (npm run publish:marketplace below, which swaps
|
|
# internally as an idempotent no-op) ship the same README.
|
|
node scripts/marketplace-readme.mjs swap-in
|
|
trap 'node scripts/marketplace-readme.mjs restore' EXIT
|
|
|
|
# Required to generate the .vsix
|
|
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
|
|
|
|
if [ "$RELEASE_TYPE" = "pre-release" ]; then
|
|
npm run publish:marketplace:prerelease
|
|
echo "Successfully published pre-release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
|
|
else
|
|
npm run publish:marketplace
|
|
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
|
|
fi
|
|
|
|
- name: Get Previous Tag
|
|
id: prev_tag
|
|
run: |
|
|
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
|
|
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
|
|
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
|
|
|
|
- name: Get Changelog Entry
|
|
id: changelog
|
|
run: |
|
|
# Get content between first ## [ and second ## [
|
|
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
|
|
echo "content<<EOF" >> $GITHUB_OUTPUT
|
|
echo "$CONTENT" >> $GITHUB_OUTPUT
|
|
echo "EOF" >> $GITHUB_OUTPUT
|
|
|
|
- name: Create GitHub Release
|
|
uses: softprops/action-gh-release@v1
|
|
with:
|
|
tag_name: ${{ steps.resolve_tag.outputs.tag }}
|
|
files: "*.vsix"
|
|
body: |
|
|
${{ steps.changelog.outputs.content }}
|
|
|
|
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}
|
|
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Post release to Slack
|
|
uses: slackapi/slack-github-action@v3.0.1
|
|
with:
|
|
method: chat.postMessage
|
|
token: ${{ secrets.SLACK_RELEASE_BOT_TOKEN }}
|
|
payload: |
|
|
channel: "C0APVKGGZFC"
|
|
text: "Cline ${{ steps.resolve_tag.outputs.tag }}"
|
|
blocks:
|
|
- type: "section"
|
|
text:
|
|
type: "mrkdwn"
|
|
text: "*Cline ${{ steps.resolve_tag.outputs.tag }}*"
|
|
- type: "section"
|
|
text:
|
|
type: "mrkdwn"
|
|
text: ${{ toJSON(steps.changelog.outputs.content) }}
|
|
- type: "context"
|
|
elements:
|
|
- type: "mrkdwn"
|
|
text: "Full Changelog: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}"
|