Skip to content

ci(publish): don't strand the GitHub release on transient registry failure #81

ci(publish): don't strand the GitHub release on transient registry failure

ci(publish): don't strand the GitHub release on transient registry failure #81

Workflow file for this run

name: CI
# Gate every PR and every push to main on the same three checks the
# publish workflow runs before shipping: lint, type-compile, and vsix
# pack. If a contributor's PR can't be packaged, the marketplace
# upload would have failed anyway, so failing fast here saves a
# release-day fire-drill.
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
# The dogfood step's SARIF upload (github/codeql-action/upload-sarif)
# writes findings to the repo's Security tab. PR triggers degrade
# gracefully without this (GitHub strips write tokens for forks), so
# the gap only surfaced on the first push: main run.
security-events: write
# attest-build-provenance needs an OIDC token (id-token) and write
# access to the repo's attestations store. Same fork caveat as
# security-events — declared here for push:main runs, gated by
# `if:` on the attest step so fork-PR runs don't fail trying to use
# them.
id-token: write
attestations: write
jobs:
check:
timeout-minutes: 30
# The extension is cross-platform; the LSP child-process spawn
# path, the bundle loader, and several file-path helpers are all
# Windows-sensitive. Running the gate on three OSes catches the
# LF/CRLF and path-separator bugs that single-OS CI silently
# ignores. The matrix shares the same step list — only the vsix
# upload and the network-bound npm audit are pinned to Linux.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# `persist-credentials: false` keeps the GITHUB_TOKEN out of
# `.git/config`'s extraheader. Without it any later `run:` step
# can `git config --get http.https://github.com/.extraheader`
# to read the token (GHA-037).
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Lint
run: npm run lint
- name: Marketplace description length
# The marketplace truncates listing descriptions around 145
# characters in search results. Anything longer loses signal
# before users click through. The current copy hugs the limit
# deliberately — this step keeps future edits honest.
if: matrix.os == 'ubuntu-latest'
run: |
node -e 'const d=require("./package.json").description; if(d.length>145){console.error("description is "+d.length+" chars (max 145):",d);process.exit(1)}'
- name: TypeScript compile
run: npm run compile
- name: Unit tests
run: npm test
- name: Bundle smoke
# Catches the "vsix packages but won't load" failure mode — a
# successful vsce package doesn't prove the bundle has all its
# runtime deps. Loads dist/extension.js with a vscode stub and
# asserts activate/deactivate are exported.
run: npm run smoke
- name: Integration tests (real VS Code)
# @vscode/test-electron boots a real extension host and runs
# the mocha suite under src/test/integration/. Verifies the
# activation / command / view contracts that unit tests can
# only approximate. Linux-only — VS Code needs an X server
# (xvfb-run) on headless runners; Windows/macOS in this
# matrix already exercise the platform-specific paths via the
# unit suite + bundle smoke.
if: matrix.os == 'ubuntu-latest'
run: xvfb-run -a npm run test:integration
- name: npm audit (prod deps, high+)
# Network-bound and platform-independent; one run is enough.
if: matrix.os == 'ubuntu-latest'
run: npm audit --omit=dev --audit-level=high
- name: Dogfood — pipeline-check on our own workflows
# Run the published pipeline-check action against this repo's
# `.github/workflows/`. Catches GHA-rule regressions in our own
# CI/release pipeline before they ship, and exercises the tool
# end-to-end from the same surface marketplace users invoke.
# Linux-only — the action runs once per workspace, not once per
# matrix leg. `fail-on: HIGH` gates: HIGH+ findings break the
# build; LOW/MEDIUM stay informational so a single style nit
# doesn't block contributors.
if: matrix.os == 'ubuntu-latest'
# Pinned to a specific tag — the upstream action repo doesn't
# publish a floating `v1` major tag, so a bare `@v1` reference
# fails the actions resolver. Dependabot's github-actions
# updater bumps this line on new releases.
uses: dmartinochoa/pipeline-check@1df5e768503c886134c70fb517f3234dbfc43aab # v1.0.5
with:
pipeline: github
fail-on: HIGH
- name: Verify vsix packs cleanly
# vsce is a pinned devDependency (see package.json) — Dependabot
# bumps it via the npm config. `npm ci` has already installed it.
run: npx vsce package --out pipeline-check.vsix
- name: Generate SBOM (CycloneDX)
# Linux-only — one SBOM per run is enough; the matrix legs
# would produce identical content from the same lockfile.
# Mirrors the publish workflow's SBOM step so a release SBOM
# can be diffed against a CI SBOM if the trees diverge.
if: matrix.os == 'ubuntu-latest'
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
format: cyclonedx-json
output-file: pipeline-check-sbom.cdx.json
artifact-name: sbom
- name: Attest build provenance
# Produces a signed SLSA build provenance attestation for the
# CI .vsix via OIDC + Sigstore keyless signing. Gated on
# `push` because fork-PR runs get read-only tokens that
# cannot mint OIDC tokens or write attestations. Linux-only
# — same one-per-run rationale as the SBOM step.
if: github.event_name == 'push' && matrix.os == 'ubuntu-latest'
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: pipeline-check.vsix
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
# Single artefact upload from the Linux job; identical-name
# uploads from the matrix would collide.
if: matrix.os == 'ubuntu-latest'
with:
name: vsix
path: pipeline-check.vsix
retention-days: 14