All notable changes to the Pipeline-Check VS Code extension. The format follows Keep a Changelog; versions follow SemVer.
⚠ Release note for publish.yml: the release-notes extractor (an
awkscript in publish.yml) prints every line between the first and second## [headers. When cutting a release, fold the entries under## [Unreleased]into the new## [X.Y.Z] — <date>section above Unreleased, or remove the Unreleased block for the release commit. Otherwise the GitHub release ships boilerplate.
Stability batch on top of v1.0.0 — three rounds of edge-case
hardening covered by 57 new tests (194 → 251), plus supply-chain
hardening on the publish pipeline (CycloneDX SBOM + signed SLSA
provenance attached to each release). No new features; no behavior
change for users on the golden path. Highlights: the LSP install
command now uses the universal python -m pip form so the official
Windows Python installer + corporate ExecutionPolicy combo stops
blocking first-run install; the welcome panel and status bar no
longer go stale after an LSP crash or workspace-folder removal;
Scan workspace against a dead LSP surfaces a real error instead of
a false-success toast; the rc → ga "What's new" toast actually fires
this time.
- CycloneDX SBOM attached to each GitHub release. The publish
workflow now scans
package-lock.jsonviaanchore/sbom-actionand uploadspipeline-check-<version>-sbom.cdx.jsonalongside the.vsix. Downstream consumers can ingest it into their existing vuln-management tooling without re-deriving the dep set from the bundle. - Signed SLSA build provenance for each
.vsix. Emitted byactions/attest-build-provenanceusing GitHub's OIDC token and Sigstore's keyless flow. Consumers verify withgh attestation verify pipeline-check-<version>.vsix --owner greylag-ci. Covers signing (no separate cosign step) and provenance in one attestation. npm audit --omit=dev --audit-level=highgate on the publish workflow. CI already runs this on every push; the publish-side gate catches advisories that land between the merge tomainand the tag push, preventing a known-vulnerable build from shipping during the window between merge and release.
- Install command now uses
python -m pip install. Switched from the barepip installform so the install path works under two conditions that previously broke it: a corporate Windows PowerShell ExecutionPolicy that allowspython.exebut blockspip.exe, and the case wherepythonis onPATHbutpipis not (common with the official Windows Python installer when the Scripts directory wasn't added). Matches PyPA's own recommendation. README and welcome-panel copy updated.
- Status-bar relevance latch releases on folder removal. A
multi-root workspace user who removed the last CI folder used to
see the bar item pinned to "clean" for the rest of the session.
The latch now re-evaluates on
onDidChangeWorkspaceFolders: when the workspace has no CI candidate files AND no current findings, the item hides again. Re-adding a CI folder re-shows.
- Two scan-workspace runs in parallel no longer spawn two
notifications. A module-level in-flight guard collapses every
concurrent
scanWorkspacecaller to one scan. A second call (from a double-clicked button, or scan + refresh) returns immediately withskippedAsBusy: trueand, in noisy mode, surfaces a "scan already in progress" info toast. installInTerminalreuses the existing "Pipeline-Check install" terminal. Repeated clicks on the welcome-panel CTA used to stack identical terminals in the dropdown. Now the second click reuses the live terminal; an exited terminal is treated as dead and a fresh one takes its place.scan-on-saveshort-circuits when the saved file's provider is disabled. Saving a Dockerfile in a workspace that hasdockerfileinpipelineCheck.disabledProvidersused to trigger a full workspace re-open even though every published diagnostic would be dropped by the middleware. The handler now consults the livedisabledProviderssetting and skips the scan.whatsNewrc → ga transition is no longer silently swallowed. Per semver §11 a pre-release version is LOWER precedence than the corresponding release. The previous version-compare stripped the suffix and treated1.0.0-rc.1and1.0.0as equal, so pre-release testers never saw the "What's New" toast for the GA.
- Welcome panel stops lying after an LSP crash. Subscribe to
client.onDidChangeStateso a mid-session server crash (or the LanguageClient's auto-restart exhausting) flipspipelineCheck.lspReadyback tofalse. Previously the panel kept showing "Scan workspace" after the LSP died — the button still worked, it just opened files against a dead server. disabledProvidersnow silences lowercasedockerfile/jenkinsfile. The internal glob matcher inproviderForPathwas case-sensitive against**/Dockerfile, so files written in lowercase (common on Windows case-preserving filesystems) classified asundefinedand slipped past the user's disable filter.- Activation no longer hangs on a misconfigured
serverArgs.client.start()is now raced against a 30-second timeout. An emptypipelineCheck.serverArgsused to drop the Python child into the REPL where it waited on stdin forever; activation would stay half-pending and the welcome panel would never leave the install prompt. On timeout we kill the stranded subprocess and surface the same "Install in terminal / Open server log" toast the LSP-failure path uses. Scan workspaceno longer claims success against a dead LSP. The scan command now gates onisLspReady()and surfaces a warning toast with Install in terminal / Restart language server / Open server log when the LSP is down. Quiet mode (scan-on-save) stays silent. Previously the scan wouldopenTextDocumentevery candidate file, publish no diagnostics, and finish with a "scanned N files" toast even though no LSP was alive to produce findings.
First stable release. Closes the v0.x line: the Findings tree has its
remaining affordances (filter, non-preview open, scan-on-save), the
release-tooling and repo-security work is fully landed (SHAs pinned on
every action, GITHUB_TOKEN locked out of .git/config, Private
Vulnerability Reporting + Discussions enabled, production environment
gate, npm audit + dependency-review + CodeQL on every push), and the
internal eslint stack is on v9 flat config so future toolchain bumps
have a clean ramp. No telemetry — see SECURITY.md.
Pipeline-Check: Filter Findingscommand. Opens an InputBox; matches against rule ID, message body, and file path (case-insensitive substring). Re-invoking the command pre-fills the current filter so users can edit or clear (empty input clears). New$(filter)button on the Findings view title bar. The badge updates to reflect the filtered count; thelastFindingUrisset still tracks the unfiltered universe so a publish for a currently-hidden finding still wakes the tree up.Pipeline-Check: Open Findingcontext-menu entry on Findings tree leaves. Opens the finding as a permanent (non-preview) tab — useful when triaging multiple findings side-by-side. The default click-to-reveal still uses preview-style so the common "click through to scan" flow doesn't create tab clutter.- Scan-on-save mode. New
pipelineCheck.scanOnSavesetting (defaultfalse). When enabled, saving a CI/CD config file triggers a quiet workspace re-scan — the LSP already re-publishes diagnostics for the saved file itself ondidSave, so this picks up cross-file effects in other CI files that aren't currently open (a Jenkinsfile that includes the just-edited shared library, a GHA workflow that calls the just-edited composite action). Renders as a status-bar spinner with no completion toast; an in-flight guard collapses save-storms (autosave, Save All) to a single scan. (R29) - Status bar background colour reflects severity. A workspace with
any CRITICAL finding tints the bar to
statusBarItem.errorBackground(red in the default themes); a workspace with HIGH but no CRITICAL tints tostatusBarItem.warningBackground(yellow). MEDIUM / LOW / INFO keep the default fg colour so a "1 medium" workspace doesn't shout. Same ThemeColor tokens ESLint and Error Lens use, so the visual language reads correctly to any existing VS Code user. - "What's new" notification on upgrade. First activation after a version bump shows a one-time toast — "Pipeline-Check 0.X.Y is here …" — with a "See release notes" button that opens the matching GitHub release. Persists the seen-version before showing so a missed dismissal doesn't loop next launch. Suppressed when the stored version equals the manifest version (same launch).
Pipeline-Check: Scan Workspacecommand. Walks every CI/config file in the workspace (matching the same patterns the LSP'sdocumentSelectoruses), opens each viavscode.workspace.openTextDocumentso the LSP'sdidOpenpipeline picks them up, and lets the Findings panel re-render from the diagnostic stream as scans complete. Progress toast with cancellation; partial failures (read errors, unsupported encodings) are counted but don't abort the scan. Surfaced from a$(play)button on the Findings view title bar, the Command Palette, and a link in the Findings welcome state. (R10, R15)
- Quieter clipboard confirmations. Copy Rule ID and Copy LSP Install Command now write a 2-second status-bar message instead of firing a modal information toast. The copy still succeeded silently 95% of the time anyway; this confirms the action without stealing focus.
- "Refresh Findings" now triggers a real scan instead of just re-painting the tree from already-published diagnostics. Matches the user's mental model: clicking a refresh icon should fetch new data, not re-render stale data. (R10)
SCAN_PATTERNSremoved in favour ofTRIGGER_PATTERNSfromproviders.ts. The single source of truth for which files are CI-relevant now drives the documentSelector, the activationEvents, and the workspace scan — three surfaces that used to drift apart.- ESLint migrated to v9 flat config. Replaced
.eslintrc.jsonwith eslint.config.mjs; dropped@typescript-eslint/eslint-plugin+@typescript-eslint/parserin favour of the unifiedtypescript-eslintpackage. Rules carry over verbatim so lint results are unchanged. (R22)
Closes 24 of 29 items from the 2026-05-19 in-depth UX/code review.
Adds the activity-bar Findings tree's missing affordances (status bar,
CodeLens, navigation, context menus, per-provider toggles), the
release-tooling polish (production environment gate, pre-release
channel, three-OS CI, integration tests), and the discovery /
accessibility pass.
Heads-up for users with non-standard workflow paths: the
extension's activationEvents now match only the
workspaceContains: patterns shared with the LSP documentSelector
(plus onStartupFinished so the activity-bar slot is always
visible). If your repo keeps CI definitions outside the standard
locations (e.g. pipelines/build.yml instead of
.github/workflows/*.yml), the extension still activates on
onStartupFinished, but the LSP only scans files matching the
document selector. Use pipelineCheck.serverArgs to point the LSP
at a different path or symlink your custom config into a standard
location.
- Inline CodeLens summary. Each scanned file carries a
Pipeline-Check: 2 critical · 1 highlens at line 1. Click reveals the Findings panel. Re-emits on every diagnostic publish so the text tracks the latest scan. (R26) - Status bar item. Bottom-left of the window, shows the top two
non-zero severities (e.g.
$(shield) 3C 1H) with a tooltip that breaks down the full per-severity tally. Click reveals the Findings panel. (R9) - Keyboard navigation.
Alt+F8/Shift+Alt+F8jump between Pipeline-Check findings in editor order (fsPath ascending, then by line); wraps at both ends. Mirrors VS Code'sF8muscle memory for the global "Next Problem" command. (R12) - Per-provider toggles. New
pipelineCheck.disabledProviderssetting silences whole providers.dockerfilecovers bothDockerfileandContainerfile(same syntax). Useful in a monorepo where Pipeline-Check would otherwise lint a sub-project's Dockerfile that has its own lint pipeline. (R25) - Rule documentation link in leaf tooltip. When the server
publishes a
Diagnostic.code.targetURL, the Findings tree's leaf tooltip appends a clickable$(book) <rule-id> documentationlink below the message body. (R8) - Client-side structured logging. The extension's output channel
now interleaves
[client] HH:MM:SS.mmmlines around activation and command invocations with the LSP'swindow/logMessagetraffic. Easier to triage bug reports — start/ok/failed breadcrumbs land in the same surface users already focus via Show language server output. (R16) - Pre-release channel. Tags like
v0.2.0-rc.1ship to the marketplace pre-release channel; the matching GitHub release is markedprerelease. Detection is by the presence of a-after the semver core. (R24) - Right-click context menu on Findings tree leaves. Open Rule
Documentation opens the URL the server published via
Diagnostic.code.targetin the system browser; Copy Rule ID writes the rule's identifier to the clipboard. Same data the leaf tooltip already surfaces, now available without keeping the tooltip open. pipelineCheck.codeLens.enabledsetting. Defaults totrue. Hides the line-1 file-summary CodeLens for users who find it intrusive without disabling CodeLens globally. Toggle takes effect on the next render — no extension restart.pipelineCheck.copyInstallCommandcommand. Copiespip install "pipeline-check[lsp]"to the clipboard. Surfaced from the Findings welcome state and from the Command Palette so users can re-find it after dismissing the first-run error toast.
-
Welcome state of the Findings panel teaches. Now leads with what Pipeline-Check does + a Copy install command link for the Python
[lsp]extra, then onboarding ("open a workflow…"), then the Alt+F8 / Shift+Alt+F8 keyboard hint, then a---separator and the recovery actions (Restart, Open Log) demoted below. -
onStartupFinishedactivation event. The extension now wakes up after VS Code's start-up barrier so the activity-bar slot is visible in every workspace — not just ones with aworkspaceContains:match. The LSP child process still only spawns when thedocumentSelectormatches an open document, so there's no idle-Python-process cost. -
Status bar item hides in non-CI workspaces. On activation we do a one-shot
findFilesfor any of the trigger patterns; the status bar item only shows once we've seen evidence the workspace is CI-relevant (either a match or an actual diagnostic publish). Stops$(shield) cleancluttering the bottom-left in frontend projects that happen to have Pipeline-Check installed alongside other linters. -
Status bar accessibility label. Screen readers now hear "Pipeline-Check: 3 critical, 1 high" instead of the codicon shortcode + letter-by-letter abbreviation.
-
Status bar tooltip teaches Alt+F8. The trailing line of the tooltip ("Alt+F8 / Shift+Alt+F8 to step through findings") is the primary discovery surface for the navigation keybindings.
-
Command titles use title case for VS Code's convention: "Restart Language Server", "Show Language Server Output", "Refresh Findings". Existing "Go to Next Finding" and "Change Grouping" stay the same. Command IDs are unchanged — settings, keybindings, and automation continue to work.
-
@vscode/test-electronintegration suite now runs in CI (Linux only, viaxvfb-run -a). Five tests pin activation, the command-registration contract, the Findings view registration, the configuration schema completeness, and the workspace-trust capability declarations. Catches what unit tests can only approximate. (R17) -
Three-OS test matrix —
[ubuntu-latest, windows-latest, macos-latest]. The LSP child-process spawn path is Windows-sensitive; matrix CI catches the LF/CRLF and path-separator bugs single-OS CI silently misses. (R21) -
Activation surface narrowed. Triggers are
workspaceContains:patterns matching the providers we actually scan (thedocumentSelectoruses the same patterns). Opening an unrelatedpackage.jsonormkdocs.ymlno longer wakes up the extension. (H4) -
Trigger-pattern list lives in one place. Extracted into
src/providers.tsas a singlePROVIDERSmap; thedocumentSelector,activationEvents, and the LSP middleware's per-provider filter all read from it. A regression test asserts the manifest'sactivationEventsstay in lockstep with the patterns. (R14) -
Shared
vi.mock("vscode")factory undersrc/__testStubs__/. Unit tests now share a single stub instead of redefining the surface per file. (R18) -
Marketplace description length gated in CI at the 145-character truncation point so future edits don't blow it. (R20)
Restart language servertoast no longer fires on failure — if the server failed to come up, the error notification already carries the install hint; the success toast used to fire too, giving the user contradictory signals. (R2)stopClienthas a 2-second hard ceiling on the LSP child's shutdown. A deadlocked server used to hold the deactivate path indefinitely; VS Code reported "Window not responding" until the user force-quit. (R3)groupByFileno longer round-trips Uri through string for every group node. Bucket value carries the original Uri. (R4)compareByLocationsorts onfsPathinstead of the full URI string. Cross-scheme entries (file:// vs untitled://) no longer bunch at one end. (R5)collectFindingsis memoised per refresh — buildRoot and updateBadge used to walk the global diagnostic store twice per refresh. (R6)onDidChangeDiagnosticsskips refreshes from unrelated publishers. ESLint / mypy / redhat.yaml keystroke chatter no longer rebuilds the tree. The skip-check also catches clears (a stale leaf can't outlive a cleared file). (R7)
Production-readiness pass. v0.1.0 was effectively unusable on a clean install (see Fixed below); v0.1.1 is the first release that actually loads in VS Code. Also lands the Findings panel and the security hardening from the pre-marketplace review.
-
Findings panel. A dedicated activity-bar slot ("Pipeline-Check" — custom inverted-Y pipeline glyph at
media/pipeline-check.svg) carries aFindingstree that re-groups the diagnostics the LSP server has already published. Strictly a re-presentation: never triggers its own scan, so the thin-transport-adapter promise inextension.tsstays intact. The activity-bar icon carries a live count badge so "how many findings does this workspace have right now?" is answerable without expanding the panel. Three group modes — severity (default), file, rule — are switched via aChange GroupingQuick Pick that marks the active mode with$(check). Each leaf renders as the rule title plus aRULE · file:LINEdescription that drops whichever component is already implied by the parent group; clicking opens the file at the diagnostic range. CRITICAL is rendered asflameand HIGH aserrorso the two distinguish in the severity-grouped tree without breaking parity with the editor gutter (which has no "more red than red" state); INFO usescircle-outlinethemed todescriptionForegroundso it is visibly the quietest row instead of inheriting the default foreground. The welcome state leads with what the extension does rather than what is missing; the diagnostic recovery links sit on a secondary "Not seeing findings?" line. -
pipelineCheck.severityThresholdsetting. A new enum knob (low/medium/high/critical, defaultlow) that mirrors the CLI's--severity-threshold. Drives a client-sidehandleDiagnosticsmiddleware that filters out diagnostics whose upstream pipeline-check severity falls below the threshold before they reach the gutter or Problems panel, so the editor surface can be tuned independently of the CLI's report. The filter readsDiagnostic.data["severity"](set by the v1.0.6 server) so it can distinguishCRITICALfromHIGH(both map to LSPError). Diagnostics without thedata.severitymetadata pass through unconditionally, so an older server (or a non-pipeline-check publish) is never hidden.
pipelineCheck.serverCommandandpipelineCheck.serverArgsare nowmachine-overridable. Workspace overrides require an explicit prompt, so a malicious.vscode/settings.jsoncan't silently swap the interpreter or inject-c "<code>"once the user trusts the workspace.- Declared
capabilities.untrustedWorkspaces: "limited"andvirtualWorkspaces: false. The extension stays inactive in untrusted workspaces until the user trusts them, so the LSP child process never spawns from a freshly-cloned, untrusted repo. - Hardened the publish workflow. Pinned
@vscode/vsceandovsxto specific versions (no more@latestwith PATs in env), added agit merge-basecheck that refuses to publish a tag that isn't onmain, added a CHANGELOG-fold check, and narrowed workflow-level permissions tocontents: readwith the publish job opting up tocontents: write. The publish job is gated on theproductionGitHub Environment soVSCE_PAT/OVSX_PATare only readable from a run that has cleared required reviewers. - Added SECURITY.md with GitHub Private Vulnerability Reporting as the disclosure channel, response SLAs, and a published threat model.
- Vitest unit suite added. 25 tests covering the severity threshold
filter (extracted into src/severityFilter.ts)
and the Findings tree's pure logic (collection from
diagnostics, group-by-severity / file / rule, severity normalisation,
no-refresh-storm contract).
npm testruns the suite; both ci.yml and publish.yml gate on it. Test files live next to the code they cover and are stripped from the .vsix.
- The published
.vsixwas missing its runtime dependency. The previous build emittedout/extension.jsviatscbut excludednode_modules/from the package, sorequire("vscode-languageclient/node")threw on activation in a clean install. Now bundled with esbuild into a singledist/extension.js(the only JS in the.vsix); a CI smoke step (scripts/smoke.js) stubs thevscodemodule, loads the bundle, and assertsactivate/deactivateare exported so this regression class fails the build instead of the user.
-
npm audit --omit=dev --audit-level=highnow runs on every push tomainso advisories filed after a PR has merged still surface. -
Activation tightened. The extension used to activate on every YAML / JSON / Dockerfile / Terraform / Groovy file in any workspace, then rely on the server's content filter to drop unrelated documents.
activationEventsis now aworkspaceContains:list of the trigger files we actually scan (.github/workflows/*,.gitlab-ci.yml,azure-pipelines.yml, etc.). The LSP'sdocumentSelectoris switched from language IDs to matching file-path globs, so the server only sees candidate documents — no more spurious activations onpackage.json,mkdocs.yml, or a Helmvalues.yaml. -
@vscode/vsceandovsxare pinned devDependencies. Workflows invoke them via the locally-installed binaries (npx vsce,npx ovsx) afternpm ci. Versions live inpackage-lock.jsonand Dependabot's existing npm config keeps them current. -
Marketplace metadata polish. Added
Othertocategories, pointedqnaat the repo Discussions page. -
Marketplace polish pass. The
package.jsondescriptionis rewritten so the numbers that differentiate this extension (22 providers, 14 compliance frameworks beyond OWASP Top 10 CI/CD, 810+ rules) land in the first 100 characters and survive the ~145-character truncation the marketplace search results impose. Command titles renamed to Restart language server and Show language server output so the palette entries are self-disambiguating without leaning on thePipeline-Checkcategory prefix. Every configuration setting switched fromdescriptiontomarkdownDescriptionso backtick'd literals (python,python -m pipeline_check.lsp) render as code in the Settings UI rather than plain quoted text. -
README restructured for the marketplace listing. The first paragraph carries the same numbers as the marketplace description, followed by a What it scans provider table (the differentiating content that prospective users land on the listing to see). Dropped the
## Statussection (the marketplace already shows the version in the listing's metadata bar). Collapsed the architecture diagram behind a<details>block near the bottom of the page; it's a useful engineering reference but not the first scroll of a marketplace listing. -
Start-failure notification carries two actions, not one. When
python -m pipeline_check.lspfails to launch (Python missing fromPATH,[lsp]extra not installed, server crash on import), the notification now exposes Copy install command alongside Open server log. The user can act on either without re-reading the message body. Notification copy stripped of the redundantPipeline-Check:prefix that the notification chrome already shows.
First public release. Wires the editor surface to the upstream
pipeline_check.lsp
server.
- TypeScript LSP client that spawns
python -m pipeline_check.lspover stdio (vscode-languageclientv9). Document selector covers YAML, JSON, Dockerfile, Terraform, and Groovy files; the server filters further by path and content so unrelated documents are not analyzed. - Pilot provider coverage: GitHub Actions, GitLab CI, Azure Pipelines, Bitbucket Pipelines, CircleCI, Google Cloud Build, Buildkite, Drone, Jenkins, and Dockerfile. Multi-file / context-heavy providers (Kubernetes, Helm, Terraform plans, live AWS, CloudFormation, SCM posture) follow in a later release.
- Three configuration knobs:
pipelineCheck.serverCommand,pipelineCheck.serverArgs, andpipelineCheck.trace.serverfor overriding the Python interpreter, module path, and LSP trace level. - Two commands under the
Pipeline-Checkcategory:- Restart server — stops the running client and respawns.
- Show server log — focuses the
Pipeline-Checkoutput channel wherewindow/logMessagetraffic lands.
- Graceful start-failure handling: when
python -m pipeline_check.lspfails (Python not onPATH,[lsp]extra not installed, server crash on import), the editor surfaces a notification with the install hint plus anOpen server logbutton. - Two F5 debug profiles in
.vscode/launch.json:- Run Extension — fresh extension-host window, no workspace.
- Run Extension (sample workflow) — opens
test-fixtures/sample-workflow/with a deliberately-vulnerable GitHub Actions workflow that fires GHA-001, GHA-004, GHA-015, and GHA-016 on open.
- VS Code 1.85+.
- Python 3.11+ with
pipeline-check[lsp]installed:pip install "pipeline-check[lsp]"