@mnci/cli 4.12.4 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1479,10 +1479,18 @@ extension:
1479
1479
  Linux binaries. Unix file modes survive into the package, so the binary stays
1480
1480
  executable. Resolve it at runtime from `context.extensionPath` + `bin/`.
1481
1481
  - **`nx-release-publish`** runs `vsce publish --packagePath <every vsix>
1482
- --skip-duplicate` when `VSCE_PAT` is set (a Marketplace personal access token, as
1483
- a CI secret) and skips with a message otherwise. It depends on `package`, so it
1484
- ships the version `nx release` just wrote. The CI release step passes `VSCE_PAT`
1485
- through in both providers.
1482
+ --skip-duplicate` with a Marketplace credential, and skips with a message without
1483
+ one. It depends on `package`, so it ships the version `nx release` just wrote. Two
1484
+ credentials work, and Entra ID wins when both are configured:
1485
+ - **Microsoft Entra ID, no secret (GitHub Actions).** When the `AZURE_CLIENT_ID`
1486
+ and `AZURE_TENANT_ID` repository variables exist, the release job signs in with
1487
+ `azure/login` through OIDC (`id-token: write`) and publishes with
1488
+ `vsce publish --azure-credential`. Nothing is stored and nothing expires. Setup
1489
+ is below.
1490
+ - **`VSCE_PAT`**, a Marketplace personal access token as a CI secret, in both
1491
+ providers. Creating one needs an Azure DevOps organization, which now needs an
1492
+ Azure subscription, and global PATs are reported to retire on 2026-12-01. Azure
1493
+ Pipelines has only this route for now.
1486
1494
  - **Debugging**: an `<name>: debug` launch entry (`extensionHost`) opens a second
1487
1495
  VS Code window with the extension loaded, after the `<name>: build (development)`
1488
1496
  task, which keeps source maps.
@@ -1493,6 +1501,35 @@ Both targets run `tools/vscode-extension.cjs`, a workspace file mnci owns (like
1493
1501
  `package.json` `bin` and runs them with `node`, with no shell, so a workspace path
1494
1502
  containing spaces works on Windows.
1495
1503
 
1504
+ ### Publishing with Microsoft Entra ID (one-time setup)
1505
+
1506
+ Think of it as a guest list rather than a key. GitHub vouches for "a job in
1507
+ `<owner>/<repo>` on `main`", Entra ID checks that against the federated credential
1508
+ and issues a short-lived token, and the Marketplace accepts the token because the
1509
+ identity is a member of the publisher.
1510
+
1511
+ 1. **A tenant.** A free Azure account creates one. No billable resource is needed.
1512
+ 2. **An app registration** (Entra admin center → App registrations → New
1513
+ registration, single tenant, no redirect URI). Prefer it to a managed identity:
1514
+ it lives in the tenant, not a subscription, so a lapsed trial does not stop
1515
+ publishing. Note its Application (client) ID and Directory (tenant) ID.
1516
+ 3. **A federated credential** on it (Certificates & secrets → Federated
1517
+ credentials → GitHub Actions deploying Azure resources): your owner and
1518
+ repository, entity type *Branch*, branch `main`. Repositories created after
1519
+ 2026-07-15 (or opted in) send GitHub's immutable subject, which carries the
1520
+ numeric IDs: `repo:<owner>@<owner_id>/<repo>@<repo_id>:ref:refs/heads/main`.
1521
+ The form asks for both IDs; `GET /repos/<owner>/<repo>` returns them as
1522
+ `owner.id` and `id`.
1523
+ 4. **Repository variables** (Settings → Secrets and variables → Actions →
1524
+ Variables, not Secrets): `AZURE_CLIENT_ID` and `AZURE_TENANT_ID`.
1525
+ 5. **The identity as a publisher member.** The Marketplace's Members page wants
1526
+ the identity's Azure DevOps profile ID, which only the identity can read, so read
1527
+ it from a workflow run on `main` after `azure/login`:
1528
+ `az rest -u https://app.vssps.visualstudio.com/_apis/profile/profiles/me
1529
+ --resource 499b84ac-1321-427f-aa17-267ca6975798 --query id -o tsv`. Add that ID
1530
+ under Members with the **Contributor** role, then confirm with
1531
+ `npx @vscode/vsce verify-pat --azure-credential <publisher>` in the same job.
1532
+
1496
1533
  Not built yet: integration tests through `@vscode/test-cli` (they download VS Code
1497
1534
  and need a display, so they would be gated like the Go and Flutter sections).
1498
1535
 
package/dist/cli.js CHANGED
@@ -1020,7 +1020,8 @@ var ACTION_VERSIONS = {
1020
1020
  "actions/checkout": "v7",
1021
1021
  "actions/setup-node": "v7",
1022
1022
  "actions/setup-dotnet": "v6",
1023
- "actions/upload-artifact": "v7"
1023
+ "actions/upload-artifact": "v7",
1024
+ "azure/login": "v3"
1024
1025
  };
1025
1026
  var NPM_VERSION = "11";
1026
1027
  var DOTNET_SDK_VERSION = "10.0.x";
@@ -1433,6 +1434,11 @@ permissions:
1433
1434
  # Lets the release step push the version tag nx release creates back to main,
1434
1435
  # and (github-only provider) create the GitHub Release itself.
1435
1436
  contents: write
1437
+ # Lets azure/login exchange this job's OIDC token for a Microsoft Entra ID one,
1438
+ # which is how a VS Code extension reaches the Marketplace with no stored secret.
1439
+ # A token minted on a pull request cannot use it: the federated credential is
1440
+ # pinned to refs/heads/main.
1441
+ id-token: write
1436
1442
 
1437
1443
  jobs:
1438
1444
  ci:
@@ -1634,14 +1640,31 @@ ${npmAuthPreflight(registryKind) ? `
1634
1640
  # which is why 'nx release' pushes the tag itself here \u2014 see
1635
1641
  # releaseConfig's remarks for why that is safe on this Nx version and
1636
1642
  # why every other provider combination keeps the explicit push step below.` : ""}
1643
+ # Signs in to Microsoft Entra ID as the Marketplace publishing identity (an
1644
+ # app registration with a federated credential for this repository's main
1645
+ # branch), so the release step publishes VS Code extensions with
1646
+ # 'vsce publish --azure-credential' and no token. Runs only when the
1647
+ # AZURE_CLIENT_ID and AZURE_TENANT_ID repository variables exist and the
1648
+ # workspace has an extension (every one ships a .vscodeignore). No Azure
1649
+ # subscription is needed. Setup: README, 'VS Code extensions'.
1650
+ - uses: azure/login@${ACTION_VERSIONS["azure/login"]}
1651
+ name: Sign in to Microsoft Entra ID (VS Code Marketplace)
1652
+ if: \${{ ${onMain} && vars.AZURE_CLIENT_ID != '' && hashFiles('apps/*/.vscodeignore') != '' }}
1653
+ with:
1654
+ client-id: \${{ vars.AZURE_CLIENT_ID }}
1655
+ tenant-id: \${{ vars.AZURE_TENANT_ID }}
1656
+ allow-no-subscriptions: true
1657
+
1637
1658
  - run: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2, registryKind), nugetPublishEnvFragment(nugetFeedUrl2))}
1638
1659
  name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python + C# + VS Code)
1639
1660
  if: \${{ ${onMain} }}
1640
1661
  env:
1641
1662
  ${npmAuthName}: ${npmAuthValue}${pypiTokenEnvLine(registryKind, (name) => `\${{ secrets.${name} }}`, " ".repeat(10))}
1642
1663
  RELEASE_SPECIFIER: \${{ vars.RELEASE_SPECIFIER }}
1643
- # A VS Code extension publishes with it; tools/vscode-extension.cjs
1644
- # skips the Marketplace when it is unset.
1664
+ # A VS Code extension publishes with Entra ID when the sign-in step
1665
+ # above ran (VSCE_AUTH=entra), else with the VSCE_PAT token;
1666
+ # tools/vscode-extension.cjs skips the Marketplace with neither.
1667
+ VSCE_AUTH: \${{ vars.AZURE_CLIENT_ID != '' && 'entra' || '' }}
1645
1668
  VSCE_PAT: \${{ secrets.VSCE_PAT }}${githubReleases ? `
1646
1669
  GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}` : ""}
1647
1670
  ${githubReleases ? "" : `
@@ -3890,16 +3913,17 @@ function publishExtension (manifest, sidecar, dryRun) {
3890
3913
 
3891
3914
  return
3892
3915
  }
3916
+ const entra = process.env.VSCE_AUTH === 'entra'
3893
3917
  const token = process.env.VSCE_PAT ?? ''
3894
- if (token === '' || /^\$\(.*\)$/.test(token)) {
3895
- console.log('VSCE_PAT is not set - skipping the Marketplace publish of ' + manifest.name + '. Add it as a CI secret to publish.')
3918
+ if (!entra && (token === '' || /^\$\(.*\)$/.test(token))) {
3919
+ console.log('No Marketplace credential - skipping the Marketplace publish of ' + manifest.name + '. Set the AZURE_CLIENT_ID and AZURE_TENANT_ID variables (Microsoft Entra ID) or the VSCE_PAT secret to publish.')
3896
3920
 
3897
3921
  return
3898
3922
  }
3899
3923
  const files = vsixFiles(project, sidecar)
3900
3924
  const missing = files.filter(file => !existsSync(file))
3901
3925
  if (missing.length > 0) fail('Nothing to publish: ' + missing.join(', ') + ' not found - run the package target first.')
3902
- run('@vscode/vsce', 'vsce', ['publish', '--packagePath', ...files, '--skip-duplicate'])
3926
+ run('@vscode/vsce', 'vsce', ['publish', ...(entra ? ['--azure-credential'] : []), '--packagePath', ...files, '--skip-duplicate'])
3903
3927
  }
3904
3928
 
3905
3929
  const [command, projectRoot, ...rest] = process.argv.slice(2)