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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ versions follow [SemVer](https://semver.org/).

### Added

- **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 to `statusBarItem.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 Workspace` command.** Walks every CI/config
file in the workspace (matching the same patterns the LSP's
`documentSelector` uses), opens each via
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![VS Code Marketplace](https://vsmarketplacebadges.dev/version-short/greylag-ci.pipeline-check.svg)](https://marketplace.visualstudio.com/items?itemName=greylag-ci.pipeline-check)
[![Open VSX](https://img.shields.io/open-vsx/v/greylag-ci/pipeline-check?label=open%20vsx)](https://open-vsx.org/extension/greylag-ci/pipeline-check)
[![Installs](https://vsmarketplacebadges.dev/installs-short/greylag-ci.pipeline-check.svg)](https://marketplace.visualstudio.com/items?itemName=greylag-ci.pipeline-check)
[![Socket](https://socket.dev/api/badge/openvsx/package/greylag-ci.pipeline-check)](https://socket.dev/openvsx/package/greylag-ci.pipeline-check/overview)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![CodeRabbit](https://img.shields.io/coderabbit/prs/github/greylag-ci/pipeline-check-vscode?labelColor=171717&color=FF570A&label=CodeRabbit+Reviews)](https://coderabbit.ai)

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"homepage": "https://github.com/greylag-ci/pipeline-check-vscode#readme",
"main": "./dist/extension.js",
"activationEvents": [
"onStartupFinished",
"workspaceContains:**/.github/workflows/*.yml",
"workspaceContains:**/.github/workflows/*.yaml",
"workspaceContains:**/.gitlab-ci.yml",
Expand All @@ -59,7 +58,7 @@
"capabilities": {
"untrustedWorkspaces": {
"supported": "limited",
"description": "Pipeline-Check spawns the configured Python interpreter to analyze workflow files. In untrusted workspaces the extension stays inactive until the workspace is trusted."
"description": "Pipeline-Check spawns the configured Python interpreter (defaults to `python -m pipeline_check.lsp`) to analyze workflow files. The `serverCommand` and `serverArgs` settings β€” the only inputs that influence what gets executed β€” are scoped `machine-overridable`, so a workspace cannot silently override them; VS Code surfaces an explicit prompt before any workspace-level value is applied. No other workspace-controlled input reaches a process-spawning code path."
},
"virtualWorkspaces": false
},
Expand Down
8 changes: 8 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { filterByThreshold } from "./severityFilter";
import { registerStatusBar } from "./statusBar";
import { scanWorkspace } from "./workspaceScan";
import { showWhatsNewIfUpgraded } from "./whatsNew";

// Group-mode options offered by the Findings panel's "Change
// Grouping" button. Labels are user-facing; descriptions are the
Expand Down Expand Up @@ -376,6 +377,13 @@ export async function activate(
);

await startClient();

// Fire-and-forget the one-time "what's new" toast for users who
// just upgraded. Detached so a not-yet-dismissed notification never
// blocks activation (same lesson as the LSP-failure toast). The
// function persists the seen-version before showing, so a missed
// notification doesn't repeat next launch.
void showWhatsNewIfUpgraded(context, context.extension.packageJSON.version);
}

export async function deactivate(): Promise<void> {
Expand Down
96 changes: 89 additions & 7 deletions src/statusBar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { describe, it, expect, vi } from "vitest";

// statusBar.ts imports `vscode` for the runtime wiring; the pure
// helpers (formatStatusBarText, formatStatusBarTooltip,
// countDiagnostics) don't touch it but the module-level import has to
// resolve. Tiny stub covers it.
vi.mock("vscode", () => ({
StatusBarAlignment: { Left: 1, Right: 2 },
window: {},
languages: {},
}));
// countDiagnostics, pickBackgroundColor) don't touch it but the
// module-level import has to resolve. Tiny stub covers it; ThemeColor
// is a class so `new vscode.ThemeColor(id)` works and tests can read
// `.id` off the result.
vi.mock("vscode", () => {
class ThemeColor {
constructor(public readonly id: string) {}
}
return {
ThemeColor,
StatusBarAlignment: { Left: 1, Right: 2 },
window: {},
languages: {},
};
});

import {
countDiagnostics,
Expand Down Expand Up @@ -218,3 +226,77 @@ describe("formatStatusBarTooltip", () => {
expect(tip).not.toContain("Alt+F8");
});
});

describe("pickBackgroundColor", () => {
// The stub vscode module returns `{ id }` as the ThemeColor β€” the
// tests check the colour by id rather than relying on identity.
// We need ThemeColor to be available in the stub for this test;
// statusBar.test.ts's existing minimal stub doesn't include it.
// Below we re-import the function through the same minimal stub
// (vi.mock at the top of this file maps `vscode` to the inline
// object), so we read .id off whatever shape it returns.

// Pull the function lazily so the vi.mock at the top is already in
// place when it resolves.
async function pick(c: import("./statusBar").SeverityCounts) {
const mod = await import("./statusBar");
return mod.pickBackgroundColor(c);
}

it("returns the error-background token when CRITICAL is present", async () => {
const bg = (await pick({
CRITICAL: 1,
HIGH: 0,
MEDIUM: 0,
LOW: 0,
INFO: 0,
})) as { id: string } | undefined;
expect(bg?.id).toBe("statusBarItem.errorBackground");
});

it("CRITICAL outranks HIGH for the colour choice", async () => {
const bg = (await pick({
CRITICAL: 1,
HIGH: 5,
MEDIUM: 0,
LOW: 0,
INFO: 0,
})) as { id: string } | undefined;
expect(bg?.id).toBe("statusBarItem.errorBackground");
});

it("returns the warning-background token when HIGH (but no CRITICAL) is present", async () => {
const bg = (await pick({
CRITICAL: 0,
HIGH: 3,
MEDIUM: 0,
LOW: 0,
INFO: 0,
})) as { id: string } | undefined;
expect(bg?.id).toBe("statusBarItem.warningBackground");
});

it("returns undefined when only MEDIUM / LOW / INFO are present", async () => {
expect(
await pick({
CRITICAL: 0,
HIGH: 0,
MEDIUM: 4,
LOW: 9,
INFO: 2,
}),
).toBeUndefined();
});

it("returns undefined on a clean workspace", async () => {
expect(
await pick({
CRITICAL: 0,
HIGH: 0,
MEDIUM: 0,
LOW: 0,
INFO: 0,
}),
).toBeUndefined();
});
});
25 changes: 25 additions & 0 deletions src/statusBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ function readSeverity(diag: vscode.Diagnostic): SeverityName {
}
}

/**
* Pick the status bar's background color from the per-severity tally.
*
* - any CRITICAL β†’ `statusBarItem.errorBackground` (red)
* - any HIGH β†’ `statusBarItem.warningBackground` (yellow)
* - everything else β†’ `undefined` (default fg, blends with the bar)
*
* The two named ThemeColor tokens are VS Code's standard status-bar
* severity colors β€” ESLint and Error Lens use the same ones, so the
* visual language reads correctly to existing VS Code users without
* any per-theme custom CSS.
*/
export function pickBackgroundColor(
c: SeverityCounts,
): vscode.ThemeColor | undefined {
if (c.CRITICAL > 0) {
return new vscode.ThemeColor("statusBarItem.errorBackground");
}
if (c.HIGH > 0) {
return new vscode.ThemeColor("statusBarItem.warningBackground");
}
return undefined;
}

// File patterns that suggest the current workspace is worth showing
// the status bar in. Mirrors providers.ts's TRIGGER_PATTERNS β€” kept
// inline here so the status bar can ship without a circular import
Expand Down Expand Up @@ -198,6 +222,7 @@ export function registerStatusBar(
item.accessibilityInformation = {
label: formatStatusBarAccessibilityLabel(counts),
};
item.backgroundColor = pickBackgroundColor(counts);
const total =
counts.CRITICAL + counts.HIGH + counts.MEDIUM + counts.LOW + counts.INFO;
if (total > 0) relevant = true;
Expand Down
Loading