Skip to content

Commit 0cab12d

Browse files
feat(vscode): add automated release workflow and bootstrap config
- Add .github/workflows/vscode-release.yml: weekly + manual dispatch workflow that finds new commits since the last release, computes a proposed version from lsp-server diff (even-minor convention), builds a CHANGELOG draft + VSIX, and opens a release-draft PR - Add packages/vscode/vsix-versions/ as the VSIX artifact directory - Add repository field to packages/vscode/package.json Closes W-22382827, W-22382831 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 342ea6b commit 0cab12d

3 files changed

Lines changed: 287 additions & 0 deletions

File tree

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# Copyright (c) 2026, Salesforce, Inc.
2+
# All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
# For full license text, see the LICENSE file in the repo root or https://www.apache.org/licenses/LICENSE-2.0
5+
6+
name: VSCode Release PR
7+
8+
on:
9+
schedule:
10+
# Every Wednesday at 09:00 UTC
11+
- cron: '0 9 * * 3'
12+
workflow_dispatch:
13+
inputs:
14+
override_last_version:
15+
description: 'Override last released version (e.g. 2.2.4) — leave empty to auto-detect from git log'
16+
required: false
17+
default: ''
18+
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
23+
concurrency:
24+
group: vscode-release
25+
cancel-in-progress: true
26+
27+
env:
28+
TREE_SITTER_VERSION: '0.25.3'
29+
30+
jobs:
31+
generate-release-pr:
32+
runs-on: ubuntu-latest
33+
34+
steps:
35+
- name: Checkout repository
36+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
37+
with:
38+
fetch-depth: 0
39+
40+
- name: Setup pnpm
41+
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
42+
43+
- name: Setup Node.js
44+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
45+
with:
46+
node-version: '22'
47+
cache: 'pnpm'
48+
49+
- name: Cache tree-sitter CLI
50+
id: cache-tree-sitter
51+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
52+
with:
53+
path: ~/.local/bin/tree-sitter
54+
key: tree-sitter-${{ env.TREE_SITTER_VERSION }}-linux-x64
55+
56+
- name: Install tree-sitter CLI
57+
if: steps.cache-tree-sitter.outputs.cache-hit != 'true'
58+
run: |
59+
mkdir -p ~/.local/bin
60+
curl -sSL "https://github.com/tree-sitter/tree-sitter/releases/download/v${TREE_SITTER_VERSION}/tree-sitter-linux-x64.gz" \
61+
| gunzip > ~/.local/bin/tree-sitter
62+
chmod +x ~/.local/bin/tree-sitter
63+
64+
- name: Add tree-sitter to PATH
65+
run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
66+
67+
- name: Install dependencies
68+
run: pnpm install --frozen-lockfile
69+
70+
- name: Build vscode package and its dependencies
71+
run: pnpm build --filter @agentscript/vscode...
72+
73+
- name: Generate release PR
74+
env:
75+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76+
OVERRIDE_LAST_VERSION: ${{ inputs.override_last_version }}
77+
run: |
78+
set -euo pipefail
79+
80+
# Validate override input format before doing anything else
81+
if [ -n "${OVERRIDE_LAST_VERSION:-}" ] && ! echo "$OVERRIDE_LAST_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
82+
echo "ERROR: override_last_version must be a semver string (e.g. 2.2.4), got: $OVERRIDE_LAST_VERSION"
83+
exit 1
84+
fi
85+
86+
# Determine last released version
87+
if [ -n "${OVERRIDE_LAST_VERSION:-}" ]; then
88+
LAST_VERSION="$OVERRIDE_LAST_VERSION"
89+
echo "Using override version: $LAST_VERSION"
90+
else
91+
LAST_VERSION=$(node -p "require('./packages/vscode/package.json').version")
92+
echo "Detected version from package.json: $LAST_VERSION"
93+
fi
94+
95+
echo "Last release version: $LAST_VERSION"
96+
97+
# Find the commit where packages/vscode/package.json was set to LAST_VERSION
98+
LAST_RELEASE_COMMIT=$(git log \
99+
--oneline \
100+
-- packages/vscode/package.json \
101+
| while read -r hash _; do
102+
VERSION=$(git show "${hash}:packages/vscode/package.json" \
103+
| node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version" 2>/dev/null)
104+
if [ "$VERSION" = "$LAST_VERSION" ]; then
105+
echo "$hash"
106+
break
107+
fi
108+
done)
109+
110+
echo "Commit where vscode was last set to $LAST_VERSION: $LAST_RELEASE_COMMIT"
111+
112+
if [ -z "$LAST_RELEASE_COMMIT" ]; then
113+
echo "ERROR: could not find release commit for version $LAST_VERSION"
114+
exit 1
115+
fi
116+
117+
# Gather commits from the bundle closure since the release commit
118+
COMMITS=$(git log \
119+
"${LAST_RELEASE_COMMIT}..HEAD" \
120+
--oneline \
121+
--no-merges \
122+
-- \
123+
packages/vscode \
124+
packages/lsp-server \
125+
packages/lsp \
126+
packages/language \
127+
packages/compiler \
128+
packages/parser \
129+
packages/parser-javascript \
130+
packages/agentforce \
131+
dialect/ \
132+
| grep -v 'bump version' || true)
133+
134+
if [ -z "$COMMITS" ]; then
135+
echo "No relevant commits since $LAST_RELEASE_COMMIT — nothing to release."
136+
exit 0
137+
fi
138+
139+
echo "Commits found since last release:"
140+
echo "$COMMITS"
141+
142+
TODAY=$(date +%Y-%m-%d)
143+
144+
# Read lsp-server version at the release commit and at HEAD
145+
LSP_VERSION_AT_RELEASE=$(git show \
146+
"${LAST_RELEASE_COMMIT}:packages/lsp-server/package.json" \
147+
| node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version")
148+
149+
LSP_VERSION_NOW=$(node -p "require('./packages/lsp-server/package.json').version")
150+
151+
echo "lsp-server at last release: $LSP_VERSION_AT_RELEASE"
152+
echo "lsp-server now: $LSP_VERSION_NOW"
153+
echo "vscode version now: $LAST_VERSION"
154+
155+
export TODAY LAST_VERSION COMMITS LSP_VERSION_AT_RELEASE LSP_VERSION_NOW
156+
157+
# Compute proposed vscode version based on lsp-server diff (even-minor = stable)
158+
PROPOSED_VERSION=$(node -e "
159+
const current = process.env.LAST_VERSION;
160+
const lspOld = process.env.LSP_VERSION_AT_RELEASE;
161+
const lspNew = process.env.LSP_VERSION_NOW;
162+
163+
const [curMaj, curMin, curPat] = current.split('.').map(Number);
164+
const [oldMaj, oldMin] = lspOld.split('.').map(Number);
165+
const [newMaj, newMin] = lspNew.split('.').map(Number);
166+
167+
let proposed;
168+
if (newMaj > oldMaj) {
169+
proposed = (curMaj + 1) + '.0.0';
170+
} else if (newMin > oldMin) {
171+
const nextEvenMinor = curMin % 2 === 0 ? curMin + 2 : curMin + 1;
172+
proposed = curMaj + '.' + nextEvenMinor + '.0';
173+
} else {
174+
proposed = curMaj + '.' + curMin + '.' + (curPat + 1);
175+
}
176+
177+
console.log(proposed);
178+
")
179+
180+
echo "Proposed vscode version: $PROPOSED_VERSION"
181+
export PROPOSED_VERSION
182+
183+
# Build CHANGELOG draft and insert it before the first versioned section
184+
node -e "
185+
const fs = require('fs');
186+
const today = process.env.TODAY;
187+
const proposed = process.env.PROPOSED_VERSION;
188+
const lspOld = process.env.LSP_VERSION_AT_RELEASE;
189+
const lspNew = process.env.LSP_VERSION_NOW;
190+
const commits = process.env.COMMITS.trim().split('\n')
191+
.map(l => '- ' + l).join('\n');
192+
193+
const draft = [
194+
'## [' + proposed + '] - ' + today,
195+
'',
196+
'> :pencil: Review and rewrite the entries below into user-facing language before merging.',
197+
'',
198+
'### Added',
199+
'',
200+
'### Fixed',
201+
'',
202+
'### Changed',
203+
'',
204+
commits,
205+
'',
206+
'---',
207+
'_lsp-server: ' + lspOld + ' → ' + lspNew + ' (basis for version bump)_',
208+
].join('\n');
209+
210+
const content = fs.readFileSync('packages/vscode/CHANGELOG.md', 'utf8');
211+
const lines = content.split('\n');
212+
const idx = lines.findIndex(l => /^## \[[0-9]/.test(l));
213+
if (idx === -1) {
214+
fs.writeFileSync('packages/vscode/CHANGELOG.md', content + '\n' + draft);
215+
} else {
216+
lines.splice(idx, 0, draft, '');
217+
fs.writeFileSync('packages/vscode/CHANGELOG.md', lines.join('\n'));
218+
}
219+
"
220+
221+
# Bump version in package.json
222+
node -e "
223+
const fs = require('fs');
224+
const pkg = JSON.parse(fs.readFileSync('packages/vscode/package.json', 'utf8'));
225+
pkg.version = process.env.PROPOSED_VERSION;
226+
fs.writeFileSync('packages/vscode/package.json', JSON.stringify(pkg, null, 2) + '\n');
227+
"
228+
229+
# Build VSIX with the updated version baked in
230+
pnpm --filter @agentscript/vscode prepackage
231+
pnpm --filter @agentscript/vscode package
232+
233+
VSIX_SRC=$(find packages/vscode/publish -maxdepth 1 -name '*.vsix' | head -1)
234+
VSIX_DEST="packages/vscode/vsix-versions/$(basename "$VSIX_SRC")"
235+
cp "$VSIX_SRC" "$VSIX_DEST"
236+
echo "VSIX staged at: $VSIX_DEST"
237+
238+
# Commit and open PR
239+
BRANCH="release-draft-${PROPOSED_VERSION}"
240+
git config --local user.name "github-actions[bot]"
241+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
242+
git checkout -b "$BRANCH"
243+
git add \
244+
packages/vscode/package.json \
245+
packages/vscode/CHANGELOG.md \
246+
"$VSIX_DEST"
247+
git commit -m "chore: draft release ${PROPOSED_VERSION} (was ${LAST_VERSION})"
248+
git push origin "$BRANCH" --force-with-lease
249+
250+
# Write PR body
251+
node -e "
252+
const { writeFileSync } = require('fs');
253+
const proposed = process.env.PROPOSED_VERSION;
254+
const lastVersion = process.env.LAST_VERSION;
255+
const lspOld = process.env.LSP_VERSION_AT_RELEASE;
256+
const lspNew = process.env.LSP_VERSION_NOW;
257+
const commitLines = process.env.COMMITS.trim().split('\n').map(l => '- ' + l).join('\n');
258+
const body = [
259+
'## Release draft: ' + lastVersion + ' → ' + proposed,
260+
'',
261+
'**Proposed version:** \`' + proposed + '\`',
262+
'**lsp-server changed:** ' + lspOld + ' → ' + lspNew,
263+
'',
264+
'### Before merging',
265+
'- [ ] Confirm the proposed version is correct; if not, edit \`packages/vscode/package.json\` and \`packages/vscode/CHANGELOG.md\`, then run \`/rebuild-vsix\` to regenerate the VSIX with the final version.',
266+
'- [ ] Polish the CHANGELOG entries into user-facing language (run \`/polish-changelog\` for an AI-assisted draft).',
267+
'- [ ] Get approval from the agentscript team.',
268+
'',
269+
'> Once merged, the publish workflow will build the final VSIX (with the merged CHANGELOG) and release to the VS Code Marketplace and Open VSX.',
270+
'',
271+
'---',
272+
'**Commits included in this release:**',
273+
commitLines,
274+
].join('\n');
275+
writeFileSync('/tmp/pr-body.md', body);
276+
"
277+
278+
gh pr create \
279+
--title "chore: release ${PROPOSED_VERSION} (was ${LAST_VERSION})" \
280+
--body-file /tmp/pr-body.md \
281+
--base main \
282+
--head "$BRANCH" \
283+
|| echo "PR already exists for branch $BRANCH"

packages/vscode/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
"private": true,
77
"publisher": "Salesforce",
88
"license": "Apache-2.0",
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/salesforce/agentscript"
12+
},
913
"o11yUploadEndpoint": "https://794testsite.my.site.com/byolwr/webruntime/log/metrics",
1014
"enableO11y": "true",
1115
"productFeatureId": "aJCEE0000007rcH4AQ",

packages/vscode/vsix-versions/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)