Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ permissions:

jobs:
check:
runs-on: ubuntu-latest
# 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@v6

Expand All @@ -31,6 +41,15 @@ jobs:
- 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

Expand All @@ -44,17 +63,31 @@ jobs:
# 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: 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
ls -lh pipeline-check.vsix
run: npx vsce package --out pipeline-check.vsix

- uses: actions/upload-artifact@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
Expand Down
38 changes: 34 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ name: Publish
# VS Code Marketplace and Open VSX, then attaches the .vsix to a
# GitHub release.
#
# Tag-naming convention:
# - `v0.1.0` (stable) → stable marketplace channel.
# - `v0.1.0-rc.1` (pre-release) → pre-release marketplace channel,
# GitHub release marked `prerelease`.
# Detection is by the presence of a `-` after the semver core; see
# the "Detect pre-release tag" step below.
#
# Required repo secrets:
# - VSCE_PAT — Azure DevOps Personal Access Token, scope
# `Marketplace > Acquire and Manage`. Bound to the
Expand Down Expand Up @@ -109,37 +116,60 @@ jobs:
- name: Bundle smoke
run: npm run smoke

- name: Detect pre-release tag
# A tag like `v0.2.0-rc.1` (anything with a `-` after the
# semver core) ships to the marketplace's pre-release channel
# and the GitHub release is marked prerelease. Stable tags
# (`v0.2.0`) ship to the stable channel. Detection is purely
# by the version string — keeps the convention discoverable
# via `git tag`.
run: |
set -euo pipefail
version=$(node -p "require('./package.json').version")
if [[ "$version" == *-* ]]; then
echo "Pre-release tag detected: $version"
echo "PRERELEASE_FLAG=--pre-release" >> "$GITHUB_ENV"
echo "GH_PRERELEASE=--prerelease" >> "$GITHUB_ENV"
else
echo "Stable tag: $version"
echo "PRERELEASE_FLAG=" >> "$GITHUB_ENV"
echo "GH_PRERELEASE=" >> "$GITHUB_ENV"
fi

- name: Package vsix
# vsce and ovsx are pinned devDependencies in package.json, so
# `npm ci` above installed the exact versions and Dependabot
# bumps them via the standard npm config. `npx` here resolves
# the local binary — no fresh fetch with PATs in env.
run: |
version=$(node -p "require('./package.json').version")
npx vsce package --out "pipeline-check-${version}.vsix"
npx vsce package $PRERELEASE_FLAG --out "pipeline-check-${version}.vsix"
ls -lh "pipeline-check-${version}.vsix"
echo "VSIX_PATH=pipeline-check-${version}.vsix" >> $GITHUB_ENV

- name: Publish to VS Code Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: |
npx vsce publish \
npx vsce publish $PRERELEASE_FLAG \
--packagePath "$VSIX_PATH" \
--pat "$VSCE_PAT"

- name: Publish to Open VSX
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
npx ovsx publish "$VSIX_PATH" \
# Open VSX (ovsx >= 0.10) honours --pre-release the same
# way vsce does. Older versions ignore the flag silently,
# so this stays safe across minor bumps.
npx ovsx publish $PRERELEASE_FLAG "$VSIX_PATH" \
--pat "$OVSX_PAT"

- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
version=$(node -p "require('./package.json').version")
gh release create "v${version}" "$VSIX_PATH" \
gh release create "v${version}" "$VSIX_PATH" $GH_PRERELEASE \
--title "v${version}" \
--notes-file <(awk '/^## \[/{n++} n==2{exit} n==1{print}' CHANGELOG.md)
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ web_modules/
# Next.js build output
.next
out
out-test

# Nuxt.js build / generate output
.nuxt
Expand Down
29 changes: 29 additions & 0 deletions .vscode-test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Configuration for `@vscode/test-cli`, which boots a real VS Code
// extension host so we can verify the contracts that unit tests can
// only approximate: that activation actually fires, commands really
// register, the view appears in the activity bar. The compiled tests
// live in `out-test/` (separate from the esbuild bundle in `dist/`)
// and use mocha-bdd-ui — same convention every official VS Code
// extension uses.

import { defineConfig } from "@vscode/test-cli";

export default defineConfig({
// Compiled test files; `tsconfig.integration.json` emits them.
files: "out-test/test/integration/**/*.test.js",
// Open the deliberately-vulnerable sample workflow as the workspace
// root so activation events fire (workspaceContains:**/.github/workflows/*).
workspaceFolder: "test-fixtures/sample-workflow",
// Tests need the extension's commands and views registered before
// they run; without a sane timeout, mocha may fail before the
// extension finishes activating in slow CI environments.
mocha: {
// TDD ui exports `suite` / `test` (matching what every official
// VS Code extension uses). BDD's `describe` / `it` would need a
// re-write of the test files; staying with TDD keeps the
// convention familiar.
ui: "tdd",
timeout: 20000,
color: true,
},
});
4 changes: 4 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ test-fixtures/**
scripts/**
docs/**
out/**
out-test/**
ROADMAP.md
.gitignore
.eslintrc.json
.vscode-test.mjs
tsconfig.json
tsconfig.integration.json
vitest.config.ts
**/*.map
**/*.ts
**/tsconfig.json
Expand Down
157 changes: 157 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,163 @@ All notable changes to the Pipeline-Check VS Code extension. The
format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
versions follow [SemVer](https://semver.org/).

> ⚠ **Release note for publish.yml:** the release-notes extractor (an
> `awk` script 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.

## [0.2.0] — 2026-05-19

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.

### Added

- **Inline CodeLens summary.** Each scanned file carries a
`Pipeline-Check: 2 critical · 1 high` lens 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+F8` jump between
Pipeline-Check findings in editor order (fsPath ascending, then by
line); wraps at both ends. Mirrors VS Code's `F8` muscle memory
for the global "Next Problem" command. (R12)
- **Per-provider toggles.** New `pipelineCheck.disabledProviders`
setting silences whole providers. `dockerfile` covers both
`Dockerfile` and `Containerfile` (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.target` URL, the Findings tree's
leaf tooltip appends a clickable
`$(book) <rule-id> documentation` link below the message body. (R8)
- **Client-side structured logging.** The extension's output channel
now interleaves `[client] HH:MM:SS.mmm` lines around activation
and command invocations with the LSP's `window/logMessage`
traffic. 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.1` ship to the
marketplace pre-release channel; the matching GitHub release is
marked `prerelease`. 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.target` in 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.enabled` setting.** Defaults to `true`.
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.copyInstallCommand` command.** Copies
`pip 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.

### Changed

- **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.
- **`onStartupFinished` activation 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 a
`workspaceContains:` match. The LSP child process still only
spawns when the `documentSelector` matches 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 `findFiles` for 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) clean` cluttering 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-electron` integration suite** now runs in CI
(Linux only, via `xvfb-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 (the `documentSelector` uses the same patterns). Opening an
unrelated `package.json` or `mkdocs.yml` no longer wakes up the
extension. (H4)
- **Trigger-pattern list lives in one place.** Extracted into
`src/providers.ts` as a single `PROVIDERS` map; the `documentSelector`,
`activationEvents`, and the LSP middleware's per-provider filter
all read from it. A regression test asserts the manifest's
`activationEvents` stay in lockstep with the patterns. (R14)
- **Shared `vi.mock("vscode")` factory** under `src/__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)

### Fixed

- **`Restart language server` toast 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)
- **`stopClient` has 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)
- **`groupByFile` no longer round-trips Uri through string** for
every group node. Bucket value carries the original Uri. (R4)
- **`compareByLocation` sorts on `fsPath`** instead of the full URI
string. Cross-scheme entries (file:// vs untitled://) no longer
bunch at one end. (R5)
- **`collectFindings` is memoised per refresh** — buildRoot and
updateBadge used to walk the global diagnostic store twice per
refresh. (R6)
- **`onDidChangeDiagnostics` skips 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)

## [0.1.1] — 2026-05-19

Production-readiness pass. v0.1.0 was effectively unusable on a clean
Expand Down
Loading
Loading