Instruction file imported from miguelcolmenares/css-js-minifier (
.github/instructions/publish-update-extension.instructions.md). Copyright stays with the author.
VS Code Extension Publishing Instructions
This document is the runbook for publishing updates to the CSS & JS Minifier extension on the Visual Studio Code Marketplace.
As of v1.3.3 the entire release flow — build, tag, GitHub Release, Marketplace publish — is orchestrated from a single GitHub Actions workflow (Build & Release, .github/workflows/release.yml). There is no local vsce publish in the primary path anymore. See "Emergency procedures" at the end for the offline fallback.
Why this workflow-driven flow
Two hard constraints shape everything below:
-
Tag immutability. The repository ruleset on
refs/tags/v*blocks deletion and force-push. Once av1.x.ytag exists it is permanent, and any release tied to it is permanent too. This is a feature (immutable audit trail), but it means a mistake that pushes a bad tag burns that version number forever. -
VSCE_PAT expires. Azure DevOps PATs have a maximum lifespan of one year. If the PAT expires between two releases and nobody notices, a naïve "push tag → workflow publishes" flow would burn the tag on GitHub while failing to reach the Marketplace, leaving the two out of sync.
The preflight-first workflow solves both: the tag is created from inside the workflow, and only after vsce verify-pat confirms the PAT is still valid and correctly scoped. If the PAT is broken, nothing gets created on GitHub, no version is burned, and the maintainer can rotate the PAT and re-run the workflow with the same version number.
Prerequisites (one-time setup)
1. VSCE_PAT repository secret
⚠️ Hard deadline: 2026-12-01. Microsoft is retiring global PATs on this date — see Retirement of Global Personal Access Tokens in Azure DevOps. The PAT rotation instructions below still work today (2026-07-22), but on 2026-12-01 the token stops working regardless of its own expiration. Migration to Microsoft Entra ID federated auth (
azure/login@v2+vsce publish --azure-credential) is tracked in issue #180. Once that lands, this section is replaced with the Entra setup steps andVSCE_PATis deleted from the repository secrets.
🔥 CRITICAL: If the migration in #180 is not merged before 2026-12-01, do not attempt a release. The publish step will fail after the tag is created, placing the release in the same broken state described in "Publish fails after tag was created". Complete the Entra migration first.
The workflow reads the PAT from secrets.VSCE_PAT. To create or rotate it:
- Sign in at https://dev.azure.com with the account that owns the
miguel-colmenaresMarketplace publisher. - User Settings → Personal Access Tokens → New Token.
- Configure:
- Name:
VS Code Marketplace — miguel-colmenares (GitHub Actions) - Organization: All accessible organizations (do NOT scope to a single org — Marketplace lookups fail otherwise; this is precisely the setting Microsoft is retiring, hence the deadline above).
- Scopes: Custom defined → Marketplace (Manage).
- Expiration: 1 year (the maximum allowed).
- Name:
- Copy the token immediately (it is shown only once).
- In GitHub: Settings → Secrets and variables → Actions → New repository secret.
- Name:
VSCE_PAT - Secret: paste the PAT verbatim, no wrapping quotes.
- Name:
2. Local tooling (only if you plan to run the emergency fallback)
npm install -g @vscode/vsce
Verifying VSCE_PAT any time (without publishing)
Before every release, and any time you rotate the PAT, run the standalone verification workflow. It uses vsce verify-pat, which authenticates against the Marketplace API without publishing:
# From your local checkout, on any branch (workflow file must exist on master).
gh workflow run verify-marketplace-auth.yml
# Poll the latest run:
RUN_ID=$(gh run list --workflow=verify-marketplace-auth.yml --limit 1 --json databaseId --jq '.[0].databaseId')
gh run watch "$RUN_ID" --exit-status
A green run confirms the PAT is present, unexpired, correctly scoped and matches the miguel-colmenares publisher. No version is burned; the Marketplace state is untouched.
The release workflow (Build & Release) also runs this check in its preflight step, so even if you skip the standalone verification the release is still safe. Running it manually is a fast smoke test after rotating the PAT.
The release runbook
Step 1 — Prepare the release commit (on a feature branch → PR)
-
Bump the version in
package.jsonfollowing SemVer:- PATCH (1.3.4): bug fixes only, no user-visible feature changes.
- MINOR (1.4.0): new features, backwards compatible.
- MAJOR (2.0.0): breaking changes.
-
If
@types/vscodewas updated, keepengines.vscodein sync with it (^1.X.Yon both) and update the matching entry in.github/workflows/test-vscode-minimum.ymland.vscode-test.mjs. -
Add a new entry at the top of
CHANGELOG.mdfollowing the existing style:## [X.Y.Z] - YYYY-MM-DDwith### Added/### Fixed/### Changedsections describing user-visible impact. The release workflow verifies this entry exists before creating the tag — a missing entry aborts the release. -
Run the full local check:
npm test npm run lint -
Commit:
git checkout -b chore/release-X.Y.Z git add package.json CHANGELOG.md git commit -m "chore: Release version X.Y.Z" git push -u origin chore/release-X.Y.Z gh pr create --title "chore: Release X.Y.Z" --base master | cat -
Merge the PR (squash). The 6-platform build matrix runs automatically on the PR because
package.jsonis in the workflow's path filter.
Step 2 — Verify VSCE_PAT is healthy
gh workflow run verify-marketplace-auth.yml
Wait for the run to complete green. If it fails, rotate the PAT (see Prerequisites) before continuing.
Step 3 — Trigger the release workflow
The release is dispatched manually with the version number as input. The version must match package.json on master exactly.
Via the GitHub UI:
- Repository → Actions → Build & Release → Run workflow.
- Branch:
master. version: the exact version frompackage.json(e.g.1.3.4), without the leadingv.- Run workflow.
Via the CLI:
gh workflow run release.yml -f version=X.Y.Z
Step 4 — Wait for the workflow to finish
The workflow runs four jobs in order:
- Preflight (~1 min). Validates:
- The
versioninput matchespackage.jsononmaster. CHANGELOG.mdhas a## [X.Y.Z]entry.- The tag
vX.Y.Zdoes not already exist onorigin. VSCE_PATis present.vsce verify-pat miguel-colmenaressucceeds.
- The
- Build matrix (~10–15 min). Packages one
.vsixper platform (darwin-x64,darwin-arm64,linux-x64,linux-arm64,win32-x64,win32-arm64) and runsscripts/verify-vsix-activation.mjsagainst each. - Tag and release (~1 min). Re-verifies
master'spackage.jsonhasn't drifted, then creates the annotated tagvX.Y.Z, pushes it, and creates the GitHub Release with all six.vsixfiles attached and auto-generated release notes. - Publish (~2 min). Uploads all six
.vsixfiles to the Marketplace viavsce publish --packagePath dist/*.vsix.
If any step in preflight, build or tag-and-release fails, the workflow stops before the tag is created. The version number remains available and you can fix the issue and re-run.
If publish fails (e.g. Marketplace outage, PAT revoked mid-run), the tag and GitHub Release stay in place, and users can install manually from the release assets via code --install-extension while the maintainer investigates. Re-running just the publish job is not currently supported — a manual vsce publish from the maintainer's machine using the already-attached .vsix files is the recovery path (see Emergency procedures below).
Note: If the
.vsixfiles are missing from the GitHub Release, rebuild them locally withvsce package --target <platform>for each of the six platforms before running the emergency publish steps.
Step 5 — Verify publication
-
Wait 5–10 minutes for the Marketplace CDN to update.
-
Confirm the new version is live: https://marketplace.visualstudio.com/items?itemName=miguel-colmenares.css-js-minifier.
-
Install and smoke-test:
code --install-extension miguel-colmenares.css-js-minifier -
If any GitHub issues were fixed in this release, close them referencing the release tag.
Local safety nets (Husky hooks)
The repository ships with Husky-managed Git hooks that catch common mistakes locally, before they turn into CI failures or burned tag versions. See AGENTS.md for the full list, including the reasoning behind the "no pre-tag" design choice. The ones relevant to publishing:
pre-push— refuses to push anyrefs/tags/v*from a local machine. Tags belong to the release workflow. If you need to release, dispatchrelease.ymlinstead.pre-commit— runsnpm run linton the wholesrc/tree, and if the commit changespackage.jsonversion, requiresCHANGELOG.mdto be staged in the same commit with a matching## [X.Y.Z]heading. This mirrors the workflow's preflight check exactly, so a bump commit that passes locally is guaranteed to pass in CI.commit-msg— enforces Conventional Commit format on the subject line.
Troubleshooting
Preflight fails on "Input version does not match package.json"
You dispatched the workflow with a version that doesn't match what's on master. Either you forgot to merge the bump PR, or you typed the wrong number. Confirm with git show master:package.json | jq -r .version and dispatch again with the matching value.
Preflight fails on "CHANGELOG.md is missing an entry"
Open a follow-up PR that adds ## [X.Y.Z] - YYYY-MM-DD at the top of CHANGELOG.md, merge, and re-dispatch.
Preflight fails on "Tag vX.Y.Z already exists"
The tag was created by a previous run and cannot be reused (immutability policy). Bump package.json to the next available version on a new PR, merge, and dispatch with the new number.
Preflight fails on vsce verify-pat
The PAT is expired, has the wrong scope, or does not belong to the miguel-colmenares publisher. Rotate the PAT (see Prerequisites) and re-dispatch. No tag was created, so nothing is burned.
Build matrix fails on a specific platform
Look at the failing job's log. Common causes:
- Native binding regression:
scripts/verify-vsix-activation.mjswill report exactly whichrequire()failed. Usually points at a missing entry inoptionalDependenciesor.vscodeignore. - Test flake: retry the job. If it fails twice, treat it as a real failure.
Publish fails after tag was created
The tag and GitHub Release are in place; the Marketplace is missing the version. Recovery:
- Investigate the failure (
vsce publishoutput is in the job log). If it's a transient Marketplace error, wait and re-run manually (see Emergency procedures). - If the PAT was revoked mid-run, rotate it, then run the emergency manual publish using the
.vsixfiles from the GitHub Release. - Do not try to re-cut the release under a different tag — the tag/release pair is immutable and users may already have installed from the GitHub assets.
Emergency procedures
Manual publish from a local machine
Use this only when the workflow's publish step failed and you cannot re-run it (e.g. GitHub Actions outage, or Marketplace requires interactive confirmation). It assumes the tag and GitHub Release already exist.
# 1. Authenticate vsce locally with a fresh PAT.
export VSCE_PAT=<your-pat>
# 2. Download all six .vsix files from the GitHub Release.
mkdir -p /tmp/release-X.Y.Z
gh release download vX.Y.Z --repo miguelcolmenares/css-js-minifier --dir /tmp/release-X.Y.Z --pattern '*.vsix'
# 3. Publish them all in one shot.
cd /tmp/release-X.Y.Z
npx @vscode/vsce publish --packagePath *.vsix
Rotating a compromised PAT
- Revoke the PAT at https://dev.azure.com immediately.
- Create a fresh PAT (see Prerequisites).
- Update the
VSCE_PATsecret in the repository. - Run
gh workflow run verify-marketplace-auth.ymlto confirm the new PAT works.
Unpublishing (extreme caution)
Unpublishing breaks every existing installation of the affected version. Use only for security-critical bugs.
# Unpublish a specific version.
npx @vscode/vsce unpublish miguel-colmenares.css-js-minifier@X.Y.Z
# Unpublish the entire extension (last resort).
npx @vscode/vsce unpublish miguel-colmenares.css-js-minifier
The tag and GitHub Release remain untouched by unpublishing — they must be dealt with separately if the intent is to fully erase a version, but the immutability ruleset blocks tag deletion.
Reference
.github/workflows/release.yml— the release workflow..github/workflows/verify-marketplace-auth.yml— the standalone PAT check.AGENTS.md— agent-oriented summary of the release flow and Husky hooks.CONTRIBUTING.md— contributor onboarding and CI matrix explanation.- VS Code Extension Publishing Guide
- vsce CLI documentation
- Azure DevOps PAT management
- Marketplace Publisher Portal
- Semantic Versioning
Last Updated: 2026-07-22 (v1.3.3 introduced the workflow-driven release flow).