-
Notifications
You must be signed in to change notification settings - Fork 15.4k
Add independent VS Code extension releases #64139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Jake Bailey (jakebailey)
merged 26 commits into
microsoft:main
from
jakebailey:vscode-typescript-release-pipeline
Sep 8, 2026
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
f0e99fe
Add independent VS Code extension releases
jakebailey c600717
Require real signing for extension releases
jakebailey b99e410
Open a PR for extension version bumps
jakebailey 9e5a0e2
Isolate extension bump credentials
jakebailey 980acb4
Make extension bump workflow resumable
jakebailey 7225576
Refine extension release workflow inputs
jakebailey 4844ad5
Automate VS Code extension release tagging
jakebailey 986fbb6
Restrict manual release tags to main
jakebailey 7a8559f
Simplify automatic extension release tagging
jakebailey 5dfbe43
Make automatic release tagging idempotent
jakebailey e6a159b
Serialize automatic release tagging
jakebailey 810aa9b
Remove redundant Marketplace version checks
jakebailey f4a7b90
Remove obsolete placeholder version checks
jakebailey da43566
Add manual extension publish approval
jakebailey fd480d6
Use existing TypeScript approval team
jakebailey 4e2c401
Remove stale Marketplace auth precheck
jakebailey bbdce34
Use DevDiv Key Vault for GitHub releases
jakebailey 7464a7a
Use central npm package proxy in Azure
jakebailey 73da4b2
Share pinned vsce release setup
jakebailey 4aa9f3e
Check release pipelines use pinned vsce
jakebailey fa6e27e
Validate active vsce pipeline configuration
jakebailey 744e7e3
Harden VS Code extension releases
jakebailey 51383b7
Preserve existing extension release assets
jakebailey 71e5a2d
Reject noncanonical extension versions
jakebailey 7554472
Defer the first extension release
jakebailey 636b1fa
Guard the initial extension version
jakebailey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| name: Bump vscode-typescript | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| bump: | ||
| description: Version component to bump | ||
| required: true | ||
| type: choice | ||
| options: | ||
| - patch | ||
| - minor | ||
| - major | ||
|
|
||
| run-name: Bump vscode-typescript (${{ inputs.bump }}) | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| jobs: | ||
| prepare: | ||
| if: github.repository == 'microsoft/TypeScript' | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| source-sha: ${{ steps.source.outputs.sha }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: main | ||
| filter: blob:none | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Record source commit | ||
| id: source | ||
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: 'lts/*' | ||
|
|
||
| - run: npm ci | ||
|
|
||
| - name: Update extension version | ||
| env: | ||
| BUMP: ${{ inputs.bump }} | ||
| run: | | ||
| set -euo pipefail | ||
| currentVersion="$(jq -r '.version' packages/vscode-typescript/package.json)" | ||
| if [ "$currentVersion" = "0.0.0" ] && [ "$BUMP" != "major" ]; then | ||
| echo "The first extension release must use a major bump from 0.0.0 to 1.0.0." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| npm version "$BUMP" \ | ||
| --workspace native-preview \ | ||
| --no-git-tag-version \ | ||
|
|
||
| packageVersion="$(jq -r '.version' packages/vscode-typescript/package.json)" | ||
| lockVersion="$(jq -r '.packages["packages/vscode-typescript"].version' package-lock.json)" | ||
| if [ "$packageVersion" != "$lockVersion" ]; then | ||
| echo "package.json version $packageVersion does not match package-lock.json version $lockVersion." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| - run: npm test -w native-preview | ||
|
|
||
| - name: Package extension | ||
| run: npx hereby vscode-typescript:pack --forRelease --vscodeTypescriptRelease | ||
|
|
||
| - name: Create version bump patch | ||
| run: git diff --binary -- packages/vscode-typescript/package.json package-lock.json > vscode-typescript-bump.patch | ||
|
|
||
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: vscode-typescript-bump | ||
| path: vscode-typescript-bump.patch | ||
| if-no-files-found: error | ||
|
|
||
| create-pr: | ||
| needs: prepare | ||
| if: github.repository == 'microsoft/TypeScript' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| environment: | ||
| name: azure | ||
| deployment: false | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ needs.prepare.outputs.source-sha }} | ||
| filter: blob:none | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 | ||
| with: | ||
| name: vscode-typescript-bump | ||
| path: ${{ runner.temp }} | ||
|
|
||
| - name: Apply version bump | ||
| run: | | ||
| set -euo pipefail | ||
| git apply --index "$RUNNER_TEMP/vscode-typescript-bump.patch" | ||
|
|
||
| mapfile -t changedFiles < <(git diff --cached --name-only) | ||
| expectedFiles=("package-lock.json" "packages/vscode-typescript/package.json") | ||
| if [ "${changedFiles[*]}" != "${expectedFiles[*]}" ]; then | ||
| echo "Unexpected files in version bump: ${changedFiles[*]}" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 | ||
| with: | ||
| client-id: ${{ vars.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ vars.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Create GitHub App token | ||
| id: app-token | ||
| uses: microsoft/create-github-app-token-via-key-vault@5ba0d436e9c3cac52feff4d1f2f66f9698ce4a2d # v1 | ||
| with: | ||
| client-id: ${{ vars.TYPESCRIPT_AUTOMATION_GITHUB_APP_CLIENT_ID }} | ||
| key-id: ${{ vars.TYPESCRIPT_AUTOMATION_GITHUB_APP_KEY_ID }} | ||
| owner: microsoft | ||
| repositories: TypeScript | ||
| permission-contents: write | ||
| permission-pull-requests: write | ||
|
|
||
| - name: Commit, push, and open pull request | ||
| env: | ||
| SOURCE_SHA: ${{ needs.prepare.outputs.source-sha }} | ||
| GITHUB_APP_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| EXTENSION_VERSION="$(jq -r '.version' packages/vscode-typescript/package.json)" | ||
| branch="vscode-typescript-release/v$EXTENSION_VERSION" | ||
| git switch -c "$branch" | ||
| git config user.email "290192711+typescript-automation[bot]@users.noreply.github.com" | ||
| git config user.name "typescript-automation[bot]" | ||
| git config core.hooksPath /dev/null | ||
| git commit -m "Bump vscode-typescript to $EXTENSION_VERSION" | ||
|
|
||
| basic_auth="$(node -e 'process.stdout.write(Buffer.from("x-access-token:" + process.env.GITHUB_APP_TOKEN).toString("base64"))')" | ||
| echo "::add-mask::$basic_auth" | ||
| git config --local http.https://github.com/.extraheader "AUTHORIZATION: basic ${basic_auth}" | ||
|
|
||
| if git ls-remote --exit-code --heads origin "$branch" >/dev/null; then | ||
| git fetch origin "refs/heads/$branch:refs/remotes/origin/$branch" | ||
| existingCommit="$(git rev-parse "origin/$branch")" | ||
| existingParent="$(git rev-parse "origin/$branch^")" | ||
| if [ "$existingParent" != "$SOURCE_SHA" ] || ! git diff --quiet HEAD "$existingCommit"; then | ||
| echo "Existing branch $branch does not match this release bump." >&2 | ||
| exit 1 | ||
| fi | ||
| else | ||
| git push --set-upstream origin "$branch" | ||
| fi | ||
|
|
||
| existingPr="$(gh pr list \ | ||
| --repo microsoft/TypeScript \ | ||
| --base main \ | ||
| --head "$branch" \ | ||
| --state open \ | ||
| --json url \ | ||
| --jq '.[0].url // empty')" | ||
| if [ -n "$existingPr" ]; then | ||
| echo "Pull request already exists: $existingPr" | ||
| else | ||
| gh pr create \ | ||
| --repo microsoft/TypeScript \ | ||
| --base main \ | ||
| --head "$branch" \ | ||
| --title "Bump vscode-typescript to $EXTENSION_VERSION" \ | ||
| --body "Updates the vscode-typescript extension to $EXTENSION_VERSION." | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| name: Tag vscode-typescript release | ||
|
|
||
| on: | ||
| pull_request_target: | ||
| types: [closed] | ||
| branches: [main] | ||
| paths: | ||
| - packages/vscode-typescript/package.json | ||
|
|
||
| run-name: Tag vscode-typescript release | ||
|
|
||
| concurrency: | ||
| group: tag-vscode-typescript-${{ github.event.pull_request.merge_commit_sha }} | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| jobs: | ||
| tag: | ||
| if: >- | ||
| github.repository == 'microsoft/TypeScript' && | ||
| github.event.pull_request.merged == true | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: azure | ||
| deployment: false | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ github.event.pull_request.merge_commit_sha }} | ||
| filter: blob:none | ||
| fetch-depth: 2 | ||
| persist-credentials: false | ||
|
|
||
| - name: Check for extension version bump | ||
| id: version | ||
| run: | | ||
| set -euo pipefail | ||
| packagePath="packages/vscode-typescript/package.json" | ||
| previousVersion="$(git show "HEAD^:$packagePath" | jq -r '.version')" | ||
| version="$(jq -r '.version' "$packagePath")" | ||
|
|
||
| if [ "$version" = "0.0.0" ]; then | ||
| echo "$packagePath is still at the unreleased initialization version." | ||
| echo "changed=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| if [ "$previousVersion" = "$version" ]; then | ||
| echo "$packagePath changed without changing its version." | ||
| echo "changed=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
|
|
||
| if [ "$previousVersion" = "0.0.0" ] && [ "$version" != "1.0.0" ]; then | ||
| echo "The first extension release must change 0.0.0 to 1.0.0." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! [[ "$version" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then | ||
| echo "Invalid extension version: $version" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| lockVersion="$(jq -r '.packages["packages/vscode-typescript"].version' package-lock.json)" | ||
| if [ "$version" != "$lockVersion" ]; then | ||
| echo "package.json version $version does not match package-lock.json version $lockVersion." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "changed=true" >> "$GITHUB_OUTPUT" | ||
| echo "version=$version" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 | ||
| if: steps.version.outputs.changed == 'true' | ||
| with: | ||
| client-id: ${{ vars.AZURE_CLIENT_ID }} | ||
| tenant-id: ${{ vars.AZURE_TENANT_ID }} | ||
| subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} | ||
|
|
||
| - name: Create GitHub App token | ||
| if: steps.version.outputs.changed == 'true' | ||
| id: app-token | ||
| uses: microsoft/create-github-app-token-via-key-vault@5ba0d436e9c3cac52feff4d1f2f66f9698ce4a2d # v1 | ||
| with: | ||
| client-id: ${{ vars.TYPESCRIPT_AUTOMATION_GITHUB_APP_CLIENT_ID }} | ||
| key-id: ${{ vars.TYPESCRIPT_AUTOMATION_GITHUB_APP_KEY_ID }} | ||
| owner: microsoft | ||
| repositories: TypeScript | ||
| permission-contents: write | ||
|
|
||
| - name: Create release tag | ||
| if: steps.version.outputs.changed == 'true' | ||
| env: | ||
| EXTENSION_VERSION: ${{ steps.version.outputs.version }} | ||
| GITHUB_APP_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| tag="vscode-typescript/v$EXTENSION_VERSION" | ||
| git config user.email "290192711+typescript-automation[bot]@users.noreply.github.com" | ||
| git config user.name "typescript-automation[bot]" | ||
| git config core.hooksPath /dev/null | ||
|
|
||
| basic_auth="$(node -e 'process.stdout.write(Buffer.from("x-access-token:" + process.env.GITHUB_APP_TOKEN).toString("base64"))')" | ||
| echo "::add-mask::$basic_auth" | ||
| git config --local http.https://github.com/.extraheader "AUTHORIZATION: basic ${basic_auth}" | ||
|
|
||
| if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null; then | ||
| git fetch origin "refs/tags/$tag:refs/tags/$tag" | ||
| existingCommit="$(git rev-list -n 1 "$tag")" | ||
| if [ "$existingCommit" != "$(git rev-parse HEAD)" ]; then | ||
| echo "Tag $tag already exists at $existingCommit." >&2 | ||
| exit 1 | ||
| fi | ||
| echo "Tag $tag already exists at the release commit." | ||
| exit 0 | ||
| fi | ||
|
|
||
| git tag --annotate "$tag" --message "vscode-typescript $EXTENSION_VERSION" | ||
| git push origin "refs/tags/$tag" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.