From 37561c5a2d5065dadc7350027212504d66d51970 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 08:55:12 +0000 Subject: [PATCH 1/9] chore: publish VS Code extension with managed identity --- .github/workflows/release.yml | 18 ++++++-- docs/ci-cd.md | 79 ++++++++++++++++++++++++++++++++--- packages/vscode/.env_sample | 1 - 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c73b28f631..e95be19e79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,7 @@ jobs: id: params env: EVENT_NAME: ${{ github.event_name }} + WORKFLOW_REF: ${{ github.ref }} PUSH_SHA: ${{ github.sha }} INPUT_CHANNEL: ${{ inputs.channel }} INPUT_REF: ${{ inputs.ref }} @@ -82,6 +83,10 @@ jobs: CHANNEL=insider REF="$PUSH_SHA" else + if [ "$WORKFLOW_REF" != "refs/heads/main" ]; then + echo "Refusing to release from workflow ref '$WORKFLOW_REF'. Marketplace authentication is federated to main." >&2 + exit 1 + fi CHANNEL="$INPUT_CHANNEL" REF="${INPUT_REF:-main}" case "$REF" in @@ -260,6 +265,9 @@ jobs: needs: [plan, release] runs-on: ubuntu-latest timeout-minutes: 10 + permissions: + contents: read + id-token: write env: ASSET_FILE: ${{ needs.plan.outputs.asset_name }}-${{ needs.plan.outputs.version }}.vsix steps: @@ -280,10 +288,14 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vsix + - name: Log in to Azure + uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Publish vsix to marketplace - env: - VSCE_PAT: ${{ secrets.AZURE_DEVOPS_PERSONAL_ACCESS_TOKEN }} - run: cd packages/vscode && npx vsce publish --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" + run: cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" publish-open-vsx: name: Publish to open-vsx diff --git a/docs/ci-cd.md b/docs/ci-cd.md index b276007622..2b771325a1 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -54,14 +54,83 @@ graph TD ### Permissions -The workflow default is `contents: read`. Only two jobs are granted more: -`release` (`contents: write`, to create the tag and GitHub release) and -`publish-language-server` (`id-token: write`, for npm Trusted Publishers). -Every checkout sets `persist-credentials: false`, so no job has a git +The workflow default is `contents: read`. Three jobs are granted additional +permissions: `release` (`contents: write`, to create the tag and GitHub +release), `publish-language-server` (`id-token: write`, for npm Trusted +Publishers), and `publish-marketplace` (`id-token: write`, to authenticate to +Azure). Every checkout sets `persist-credentials: false`, so no job has a git credential in its config while running dependency code. Releases only run from `main` or an `x.y.x` patch branch; `plan` rejects any -other `ref`. +other source `ref`. The release workflow itself must be launched from `main` +so its OIDC subject matches the Azure federated credential. A patch release +can still select an `x.y.x` source branch with the `ref` input. + +### VS Code Marketplace authentication + +`publish-marketplace` uses GitHub OIDC to sign in as a user-assigned Azure +managed identity, then runs `vsce publish --azure-credential`. It has no +Personal Access Token fallback: an OIDC, Azure login, or Marketplace +permissions failure stops the job. Open VSX is separate and continues to use +`OPEN_VSX_ACCESS_TOKEN`. + +The Azure and GitHub setup is external to this repository: + +1. Create a user-assigned managed identity in the Azure tenant used for + Marketplace publishing. Give it Reader access at the narrowest practical + Azure scope so `azure/login` can select the configured subscription. +2. Add a federated credential to that identity with these exact values: + + | Field | Value | + | -------- | ------------------------------------------------ | + | Issuer | `https://token.actions.githubusercontent.com` | + | Subject | `repo:prisma/language-tools:ref:refs/heads/main` | + | Audience | `api://AzureADTokenExchange` | + + For example: + + ```bash + az identity federated-credential create \ + --name language-tools-github-main \ + --identity-name \ + --resource-group \ + --issuer https://token.actions.githubusercontent.com \ + --subject repo:prisma/language-tools:ref:refs/heads/main \ + --audiences api://AzureADTokenExchange + ``` + +3. Authenticate as the managed identity and query its Azure DevOps profile to + obtain the Marketplace resource ID (the `id` field, which is not the Azure + client ID or object ID): + + ```bash + az rest \ + --url https://app.vssps.visualstudio.com/_apis/profile/profiles/me \ + --resource 499b84ac-1321-427f-aa17-267ca6975798 + ``` + + One way to run this command is a temporary GitHub Actions step immediately + after the `azure/login` step. Do not print or retain access tokens. + +4. On the [Visual Studio Marketplace publisher management + page](https://marketplace.visualstudio.com/manage), add that resource ID as + a member of the Prisma publisher and assign the **Contributor** role. +5. In **GitHub → prisma/language-tools → Settings → Secrets and variables → + Actions → Variables**, create these repository variables: + + | Variable | Value | + | ----------------------- | -------------------------- | + | `AZURE_CLIENT_ID` | Managed identity client ID | + | `AZURE_TENANT_ID` | Microsoft Entra tenant ID | + | `AZURE_SUBSCRIPTION_ID` | Azure subscription ID | + +6. Launch `release.yml` from `main` and verify an insider release. After it + publishes successfully, delete the obsolete + `AZURE_DEVOPS_PERSONAL_ACCESS_TOKEN` Actions secret. + +Microsoft's [VS Code extension publishing +instructions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#secure-automated-publishing-to-visual-studio-marketplace) +describe the managed-identity Marketplace authorization flow. ### Channels diff --git a/packages/vscode/.env_sample b/packages/vscode/.env_sample index 5ed3b0b1ae..9d74d8752f 100644 --- a/packages/vscode/.env_sample +++ b/packages/vscode/.env_sample @@ -1,2 +1 @@ ENVIRONMENT=<1> # Default to dry-run, to actually publish, use ENVIRONMENT=PRODUCTION -AZURE_DEVOPS_PERSONAL_ACCESS_TOKEN= # https://code.visualstudio.com/api/working-with-extensions/publishing-extension#get-a-personal-access-token \ No newline at end of file From e1b91b531b56b9fe2188bbbb1fbe037b176c7c33 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 12:31:09 +0000 Subject: [PATCH 2/9] chore: add Marketplace identity bootstrap workflow --- .../workflows/get-marketplace-identity.yml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/get-marketplace-identity.yml diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml new file mode 100644 index 0000000000..bd0ca615b9 --- /dev/null +++ b/.github/workflows/get-marketplace-identity.yml @@ -0,0 +1,51 @@ +name: Get Marketplace identity + +# Temporary bootstrap workflow. Delete it after adding the managed identity to +# the Visual Studio Marketplace publisher. +on: + workflow_dispatch: + +permissions: + contents: read + id-token: write + +jobs: + get-marketplace-identity: + name: Get Marketplace identity + if: github.repository == 'prisma/language-tools' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Log in to Azure + uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Get Marketplace identity ID + shell: bash + run: | + set -euo pipefail + + MARKETPLACE_ID="$(az rest \ + --url https://app.vssps.visualstudio.com/_apis/profile/profiles/me \ + --resource 499b84ac-1321-427f-aa17-267ca6975798 \ + --query id \ + --output tsv)" + + if [[ ! "$MARKETPLACE_ID" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]]; then + echo "Azure DevOps returned an invalid Marketplace identity ID." >&2 + exit 1 + fi + + echo "::notice title=Marketplace identity ID::$MARKETPLACE_ID" + { + echo "## Marketplace identity ID" + echo + echo '```text' + echo "$MARKETPLACE_ID" + echo '```' + echo + echo "Add this ID as a Contributor to the Prisma publisher, then delete this workflow." + } >> "$GITHUB_STEP_SUMMARY" From 6431d05f7258081d48b8f8295725999ac166f21d Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 12:32:38 +0000 Subject: [PATCH 3/9] chore: run identity bootstrap from PR branch --- .github/workflows/get-marketplace-identity.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml index bd0ca615b9..ff50550ef9 100644 --- a/.github/workflows/get-marketplace-identity.yml +++ b/.github/workflows/get-marketplace-identity.yml @@ -1,8 +1,13 @@ name: Get Marketplace identity -# Temporary bootstrap workflow. Delete it after adding the managed identity to -# the Visual Studio Marketplace publisher. +# Temporary bootstrap workflow. Delete it after retrieving the identity ID and +# before granting the managed identity access to the Marketplace publisher. on: + push: + branches: + - azure-managed-identioty + paths: + - '.github/workflows/get-marketplace-identity.yml' workflow_dispatch: permissions: @@ -12,7 +17,7 @@ permissions: jobs: get-marketplace-identity: name: Get Marketplace identity - if: github.repository == 'prisma/language-tools' && github.ref == 'refs/heads/main' + if: github.repository == 'prisma/language-tools' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/azure-managed-identioty') runs-on: ubuntu-latest timeout-minutes: 5 steps: From fb8c4d2edfdc483984408919438a6e09cf5adda8 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 12:43:23 +0000 Subject: [PATCH 4/9] chore: read Azure identity IDs from secrets --- .github/workflows/get-marketplace-identity.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- docs/ci-cd.md | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml index ff50550ef9..2a13624d1b 100644 --- a/.github/workflows/get-marketplace-identity.yml +++ b/.github/workflows/get-marketplace-identity.yml @@ -24,8 +24,8 @@ jobs: - name: Log in to Azure uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 with: - client-id: ${{ vars.AZURE_CLIENT_ID }} - tenant-id: ${{ vars.AZURE_TENANT_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Get Marketplace identity ID diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e95be19e79..f76f2e4c09 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -291,8 +291,8 @@ jobs: - name: Log in to Azure uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 with: - client-id: ${{ vars.AZURE_CLIENT_ID }} - tenant-id: ${{ vars.AZURE_TENANT_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - name: Publish vsix to marketplace run: cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 2b771325a1..07fe863eda 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -116,13 +116,14 @@ The Azure and GitHub setup is external to this repository: page](https://marketplace.visualstudio.com/manage), add that resource ID as a member of the Prisma publisher and assign the **Contributor** role. 5. In **GitHub → prisma/language-tools → Settings → Secrets and variables → - Actions → Variables**, create these repository variables: - - | Variable | Value | - | ----------------------- | -------------------------- | - | `AZURE_CLIENT_ID` | Managed identity client ID | - | `AZURE_TENANT_ID` | Microsoft Entra tenant ID | - | `AZURE_SUBSCRIPTION_ID` | Azure subscription ID | + Actions**, create the identity and tenant values as repository secrets and + the subscription value as a repository variable: + + | Name | Storage | Value | + | ----------------------- | ------------------- | -------------------------- | + | `AZURE_CLIENT_ID` | Repository secret | Managed identity client ID | + | `AZURE_TENANT_ID` | Repository secret | Microsoft Entra tenant ID | + | `AZURE_SUBSCRIPTION_ID` | Repository variable | Azure subscription ID | 6. Launch `release.yml` from `main` and verify an insider release. After it publishes successfully, delete the obsolete From ffbc2fb4c6a747f4d70a4bebe674c6b1104c0d91 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 12:44:27 +0000 Subject: [PATCH 5/9] chore: read Azure subscription ID from secrets --- .github/workflows/get-marketplace-identity.yml | 2 +- .github/workflows/release.yml | 2 +- docs/ci-cd.md | 15 +++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml index 2a13624d1b..6050da4beb 100644 --- a/.github/workflows/get-marketplace-identity.yml +++ b/.github/workflows/get-marketplace-identity.yml @@ -26,7 +26,7 @@ jobs: with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Get Marketplace identity ID shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f76f2e4c09..9a595f2c04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -293,7 +293,7 @@ jobs: with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Publish vsix to marketplace run: cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 07fe863eda..7fba4d0cdf 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -116,14 +116,13 @@ The Azure and GitHub setup is external to this repository: page](https://marketplace.visualstudio.com/manage), add that resource ID as a member of the Prisma publisher and assign the **Contributor** role. 5. In **GitHub → prisma/language-tools → Settings → Secrets and variables → - Actions**, create the identity and tenant values as repository secrets and - the subscription value as a repository variable: - - | Name | Storage | Value | - | ----------------------- | ------------------- | -------------------------- | - | `AZURE_CLIENT_ID` | Repository secret | Managed identity client ID | - | `AZURE_TENANT_ID` | Repository secret | Microsoft Entra tenant ID | - | `AZURE_SUBSCRIPTION_ID` | Repository variable | Azure subscription ID | + Actions → Secrets**, create all three values as repository secrets: + + | Name | Storage | Value | + | ----------------------- | ----------------- | -------------------------- | + | `AZURE_CLIENT_ID` | Repository secret | Managed identity client ID | + | `AZURE_TENANT_ID` | Repository secret | Microsoft Entra tenant ID | + | `AZURE_SUBSCRIPTION_ID` | Repository secret | Azure subscription ID | 6. Launch `release.yml` from `main` and verify an insider release. After it publishes successfully, delete the obsolete From ef349d375f74d6e7e68d6e98097944bcf6b95573 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 13:00:26 +0000 Subject: [PATCH 6/9] fix: allow Azure login without subscriptions --- .github/workflows/get-marketplace-identity.yml | 2 +- .github/workflows/release.yml | 2 +- docs/ci-cd.md | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml index 6050da4beb..31786c3d9d 100644 --- a/.github/workflows/get-marketplace-identity.yml +++ b/.github/workflows/get-marketplace-identity.yml @@ -26,7 +26,7 @@ jobs: with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true - name: Get Marketplace identity ID shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a595f2c04..175d823b62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -293,7 +293,7 @@ jobs: with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true - name: Publish vsix to marketplace run: cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 7fba4d0cdf..7be9203b5f 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -77,8 +77,9 @@ permissions failure stops the job. Open VSX is separate and continues to use The Azure and GitHub setup is external to this repository: 1. Create a user-assigned managed identity in the Azure tenant used for - Marketplace publishing. Give it Reader access at the narrowest practical - Azure scope so `azure/login` can select the configured subscription. + Marketplace publishing. The workflow authenticates with + `allow-no-subscriptions: true`, so the identity does not need an Azure RBAC + role assignment. 2. Add a federated credential to that identity with these exact values: | Field | Value | @@ -116,13 +117,12 @@ The Azure and GitHub setup is external to this repository: page](https://marketplace.visualstudio.com/manage), add that resource ID as a member of the Prisma publisher and assign the **Contributor** role. 5. In **GitHub → prisma/language-tools → Settings → Secrets and variables → - Actions → Secrets**, create all three values as repository secrets: + Actions → Secrets**, create both values as repository secrets: - | Name | Storage | Value | - | ----------------------- | ----------------- | -------------------------- | - | `AZURE_CLIENT_ID` | Repository secret | Managed identity client ID | - | `AZURE_TENANT_ID` | Repository secret | Microsoft Entra tenant ID | - | `AZURE_SUBSCRIPTION_ID` | Repository secret | Azure subscription ID | + | Name | Storage | Value | + | ----------------- | ----------------- | -------------------------- | + | `AZURE_CLIENT_ID` | Repository secret | Managed identity client ID | + | `AZURE_TENANT_ID` | Repository secret | Microsoft Entra tenant ID | 6. Launch `release.yml` from `main` and verify an insider release. After it publishes successfully, delete the obsolete From fc8cc0b453a94f46b7afb9f48823baabddb0519a Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 13:06:10 +0000 Subject: [PATCH 7/9] chore: remove Marketplace identity bootstrap workflow --- .../workflows/get-marketplace-identity.yml | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 .github/workflows/get-marketplace-identity.yml diff --git a/.github/workflows/get-marketplace-identity.yml b/.github/workflows/get-marketplace-identity.yml deleted file mode 100644 index 31786c3d9d..0000000000 --- a/.github/workflows/get-marketplace-identity.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Get Marketplace identity - -# Temporary bootstrap workflow. Delete it after retrieving the identity ID and -# before granting the managed identity access to the Marketplace publisher. -on: - push: - branches: - - azure-managed-identioty - paths: - - '.github/workflows/get-marketplace-identity.yml' - workflow_dispatch: - -permissions: - contents: read - id-token: write - -jobs: - get-marketplace-identity: - name: Get Marketplace identity - if: github.repository == 'prisma/language-tools' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/azure-managed-identioty') - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Log in to Azure - uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - allow-no-subscriptions: true - - - name: Get Marketplace identity ID - shell: bash - run: | - set -euo pipefail - - MARKETPLACE_ID="$(az rest \ - --url https://app.vssps.visualstudio.com/_apis/profile/profiles/me \ - --resource 499b84ac-1321-427f-aa17-267ca6975798 \ - --query id \ - --output tsv)" - - if [[ ! "$MARKETPLACE_ID" =~ ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ]]; then - echo "Azure DevOps returned an invalid Marketplace identity ID." >&2 - exit 1 - fi - - echo "::notice title=Marketplace identity ID::$MARKETPLACE_ID" - { - echo "## Marketplace identity ID" - echo - echo '```text' - echo "$MARKETPLACE_ID" - echo '```' - echo - echo "Add this ID as a Contributor to the Prisma publisher, then delete this workflow." - } >> "$GITHUB_STEP_SUMMARY" From 610ba82217fca1be64a965f7679a84c81f5dd306 Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 13:09:51 +0000 Subject: [PATCH 8/9] fix: isolate Marketplace publishing credentials --- .github/workflows/release.yml | 55 +++++++++++++++++++++++++++-------- docs/ci-cd.md | 15 +++++++--- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 175d823b62..9612b4156c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -260,34 +260,61 @@ jobs: --target "$SHA" \ $PRERELEASE + prepare-marketplace-publisher: + name: Prepare VS Code Marketplace publisher + needs: release + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: {} + steps: + - name: Use Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Install Marketplace publisher + run: | + npm install \ + --prefix "$RUNNER_TEMP/marketplace-publisher" \ + --no-save \ + --no-audit \ + --no-fund \ + @vscode/vsce@2.29.0 + tar -czf "$RUNNER_TEMP/marketplace-publisher.tar.gz" \ + -C "$RUNNER_TEMP" marketplace-publisher + - name: Upload Marketplace publisher + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: marketplace-publisher + path: ${{ runner.temp }}/marketplace-publisher.tar.gz + if-no-files-found: error + retention-days: 1 + publish-marketplace: name: Publish to VS Code Marketplace - needs: [plan, release] + needs: [plan, release, prepare-marketplace-publisher] runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: read - id-token: write + id-token: write # Required by azure/login and vsce --azure-credential. env: ASSET_FILE: ${{ needs.plan.outputs.asset_name }}-${{ needs.plan.outputs.version }}.vsix steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ needs.plan.outputs.sha }} - persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - name: Use Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' - - name: Install Dependencies - run: pnpm install - name: Download vsix artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vsix + - name: Download Marketplace publisher + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: marketplace-publisher + path: ${{ runner.temp }} + - name: Extract Marketplace publisher + run: tar -xzf "$RUNNER_TEMP/marketplace-publisher.tar.gz" -C "$RUNNER_TEMP" - name: Log in to Azure uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 with: @@ -295,7 +322,11 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} allow-no-subscriptions: true - name: Publish vsix to marketplace - run: cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" + run: | + node "$RUNNER_TEMP/marketplace-publisher/node_modules/@vscode/vsce/vsce" \ + publish \ + --azure-credential \ + --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" publish-open-vsx: name: Publish to open-vsx diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 7be9203b5f..014425856e 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -35,7 +35,8 @@ graph TD TEST --> LS[publish-language-server:
npm publish with dist-tag dev or latest] TEST --> PKG[package: build vsix, upload artifact] PKG --> REL[release: download artifact,
create GitHub release + tag] - REL --> MKT[publish-marketplace: vsce publish] + REL --> PREP[prepare-marketplace-publisher:
install and archive vsce without OIDC permission] + PREP --> MKT[publish-marketplace: download artifacts,
authenticate and publish] REL --> OVSX[publish-open-vsx: ovsx publish] end ``` @@ -49,6 +50,11 @@ graph TD release and published to both marketplaces (passed as a workflow artifact). - **release** only downloads that artifact and creates the tag and GitHub release. It does not check out, install or build anything. +- **prepare-marketplace-publisher** installs and archives the pinned `vsce` + version without any GitHub token permissions. **publish-marketplace** then + downloads only the VSIX and publisher artifacts before requesting an Azure + OIDC credential; it does not check out or execute release-source dependency + lifecycle scripts while holding `id-token: write`. - Insider GitHub releases are marked as pre-releases, so the repository's "latest release" always points to a stable version. @@ -58,8 +64,9 @@ The workflow default is `contents: read`. Three jobs are granted additional permissions: `release` (`contents: write`, to create the tag and GitHub release), `publish-language-server` (`id-token: write`, for npm Trusted Publishers), and `publish-marketplace` (`id-token: write`, to authenticate to -Azure). Every checkout sets `persist-credentials: false`, so no job has a git -credential in its config while running dependency code. +Azure). `prepare-marketplace-publisher` explicitly has no GitHub token +permissions. Every checkout sets `persist-credentials: false`, so no job has a +git credential in its config while running dependency code. Releases only run from `main` or an `x.y.x` patch branch; `plan` rejects any other source `ref`. The release workflow itself must be launched from `main` @@ -106,7 +113,7 @@ The Azure and GitHub setup is external to this repository: ```bash az rest \ - --url https://app.vssps.visualstudio.com/_apis/profile/profiles/me \ + --url 'https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1' \ --resource 499b84ac-1321-427f-aa17-267ca6975798 ``` From c32bdecdd4d2c402146d24b76630ef328488e3da Mon Sep 17 00:00:00 2001 From: Steven McClankerton Date: Fri, 4 Sep 2026 13:13:49 +0000 Subject: [PATCH 9/9] refactor: keep Marketplace publishing workflow direct --- .github/workflows/release.yml | 54 ++++++++--------------------------- docs/ci-cd.md | 13 ++------- 2 files changed, 15 insertions(+), 52 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9612b4156c..4062524555 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -260,38 +260,9 @@ jobs: --target "$SHA" \ $PRERELEASE - prepare-marketplace-publisher: - name: Prepare VS Code Marketplace publisher - needs: release - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: {} - steps: - - name: Use Node.js - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Install Marketplace publisher - run: | - npm install \ - --prefix "$RUNNER_TEMP/marketplace-publisher" \ - --no-save \ - --no-audit \ - --no-fund \ - @vscode/vsce@2.29.0 - tar -czf "$RUNNER_TEMP/marketplace-publisher.tar.gz" \ - -C "$RUNNER_TEMP" marketplace-publisher - - name: Upload Marketplace publisher - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: marketplace-publisher - path: ${{ runner.temp }}/marketplace-publisher.tar.gz - if-no-files-found: error - retention-days: 1 - publish-marketplace: name: Publish to VS Code Marketplace - needs: [plan, release, prepare-marketplace-publisher] + needs: [plan, release] runs-on: ubuntu-latest timeout-minutes: 10 permissions: @@ -300,21 +271,23 @@ jobs: env: ASSET_FILE: ${{ needs.plan.outputs.asset_name }}-${{ needs.plan.outputs.version }}.vsix steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.plan.outputs.sha }} + persist-credentials: false + - name: Install pnpm + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 - name: Use Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} + cache: 'pnpm' + - name: Install Dependencies + run: pnpm install - name: Download vsix artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vsix - - name: Download Marketplace publisher - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: marketplace-publisher - path: ${{ runner.temp }} - - name: Extract Marketplace publisher - run: tar -xzf "$RUNNER_TEMP/marketplace-publisher.tar.gz" -C "$RUNNER_TEMP" - name: Log in to Azure uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2 with: @@ -322,11 +295,8 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} allow-no-subscriptions: true - name: Publish vsix to marketplace - run: | - node "$RUNNER_TEMP/marketplace-publisher/node_modules/@vscode/vsce/vsce" \ - publish \ - --azure-credential \ - --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" + run: >- + cd packages/vscode && npx vsce publish --azure-credential --packagePath "$GITHUB_WORKSPACE/$ASSET_FILE" publish-open-vsx: name: Publish to open-vsx diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 014425856e..c642a0bec0 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -35,8 +35,7 @@ graph TD TEST --> LS[publish-language-server:
npm publish with dist-tag dev or latest] TEST --> PKG[package: build vsix, upload artifact] PKG --> REL[release: download artifact,
create GitHub release + tag] - REL --> PREP[prepare-marketplace-publisher:
install and archive vsce without OIDC permission] - PREP --> MKT[publish-marketplace: download artifacts,
authenticate and publish] + REL --> MKT[publish-marketplace: vsce publish] REL --> OVSX[publish-open-vsx: ovsx publish] end ``` @@ -50,11 +49,6 @@ graph TD release and published to both marketplaces (passed as a workflow artifact). - **release** only downloads that artifact and creates the tag and GitHub release. It does not check out, install or build anything. -- **prepare-marketplace-publisher** installs and archives the pinned `vsce` - version without any GitHub token permissions. **publish-marketplace** then - downloads only the VSIX and publisher artifacts before requesting an Azure - OIDC credential; it does not check out or execute release-source dependency - lifecycle scripts while holding `id-token: write`. - Insider GitHub releases are marked as pre-releases, so the repository's "latest release" always points to a stable version. @@ -64,9 +58,8 @@ The workflow default is `contents: read`. Three jobs are granted additional permissions: `release` (`contents: write`, to create the tag and GitHub release), `publish-language-server` (`id-token: write`, for npm Trusted Publishers), and `publish-marketplace` (`id-token: write`, to authenticate to -Azure). `prepare-marketplace-publisher` explicitly has no GitHub token -permissions. Every checkout sets `persist-credentials: false`, so no job has a -git credential in its config while running dependency code. +Azure). Every checkout sets `persist-credentials: false`, so no job has a git +credential in its config while running dependency code. Releases only run from `main` or an `x.y.x` patch branch; `plan` rejects any other source `ref`. The release workflow itself must be launched from `main`