From c44791ddcf9fdc65c98424085c79124c9da8fe03 Mon Sep 17 00:00:00 2001 From: Daniel Martin <56157528+dmartinochoa@users.noreply.github.com> Date: Tue, 19 May 2026 16:30:00 +0200 Subject: [PATCH 1/2] feat: scan-on-save + ESLint v9 flat-config + roadmap bookkeeping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the remaining Nice-for-1.0 punch list before tagging v1.0.0: R22 — ESLint v9 flat-config migration Replace .eslintrc.json with eslint.config.mjs; drop the legacy @typescript-eslint/eslint-plugin + parser pair in favour of the unified typescript-eslint package. Rules carry over verbatim so the lint result is unchanged on the current src tree (eslint v9 flat config is the default-and-only format from v9 onward; staying on v8 blocks the v9+ track and widens drift against TS 6 / esbuild 0.28). .vscodeignore picks up the new config so it stays out of the .vsix. R29 — Scan-on-save mode New pipelineCheck.scanOnSave setting (boolean, default false). When enabled, saving a CI/CD config file triggers a quiet workspace re-scan via the existing scanWorkspace command — the LSP already re-publishes diagnostics for the saved file itself on didSave, so this is purely about catching 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). scanWorkspace now takes a { quiet? } option: - quiet: ProgressLocation.Window (status-bar spinner, no toast, no cancellation affordance — a scan-on-save run is short enough that the cancel button isn't a meaningful loss). - default (user-initiated): ProgressLocation.Notification with cancellation, completion toast, the existing behaviour. The save listener in extension.ts gates on providerForPath so only CI-relevant files trigger a scan, and an in-flight guard collapses save-storms (autosave, Save All) to a single scan. Integration test (activation.test.ts) extends the configuration schema completeness check to cover scanOnSave (and codeLens.enabled which was already missing). ROADMAP bookkeeping - R10, R15 ticked (scan-workspace landed in #19). - R22 ticked (this commit). - R29 ticked (this commit). - Maintainer action items 2 + 3 (Private Vulnerability Reporting, Discussions) marked enabled — done via the GitHub REST API on 2026-05-19, so SECURITY.md's reporting link and package.json's qna URL both resolve now. - Status snapshot row added for the v0.2.0 → 1.0 cycle. Verification: typecheck + eslint (flat config) + 137-test vitest suite + integration-compile + bundle smoke all green. Co-Authored-By: Claude Opus 4.7 (1M context) --- .eslintrc.json | 19 - .vscodeignore | 1 + ROADMAP.md | 42 +- eslint.config.mjs | 45 +++ package-lock.json | 498 +++++++++++++----------- package.json | 13 +- src/extension.ts | 33 ++ src/test/integration/activation.test.ts | 2 + src/workspaceScan.ts | 55 ++- 9 files changed, 416 insertions(+), 292 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.mjs diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index f62e975..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-explicit-any": "warn" - }, - "ignorePatterns": ["out", "node_modules", ".vscode-test"] -} diff --git a/.vscodeignore b/.vscodeignore index 110ab9f..0060bd6 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -11,6 +11,7 @@ out-test/** ROADMAP.md .gitignore .eslintrc.json +eslint.config.mjs .vscode-test.mjs tsconfig.json tsconfig.integration.json diff --git a/ROADMAP.md b/ROADMAP.md index 74400a0..e902d5c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -11,7 +11,8 @@ at the bottom) is two-thirds landed across PRs #11–14. |---|---| | **v0.1.0 → v0.1.1** | Shipped 2026-05-19. C1–C2, H1–H4, M1–M5, L1–L6 all closed. | | **v0.1.1 → v0.2.0 (in flight)** | R1–R9, R12, R14, R16–R18, R20, R21, R24–R26 landed on stacked PRs #11–#14; merge them in order, then tag. | -| **Blocked** | R10/R15/R29 (need scan-workspace merged), R11 (need suppression-comment syntax), R13/R27 (server-side change), R19 (interactive screenshot session), R22 (eslint-flat-config WIP), R23 (CodeQL setup). | +| **v0.2.0 → 1.0 (in flight)** | R10/R15 (scan-workspace), R22 (eslint-flat-config), R29 (scan-on-save) landed; PVR + Discussions enabled on the repo. | +| **Blocked** | R11 (need suppression-comment syntax), R13/R27 (server-side change), R19 (interactive screenshot session), R23 (CodeQL setup). | | **Decided against** | R28 (no telemetry — see SECURITY.md). | ### Maintainer action items (still outstanding) @@ -28,12 +29,12 @@ they're done. scanning → switch CodeQL from "Default" to "Advanced". If org policy forbids that, delete `codeql.yml` and lose `security-extended`. -2. **Enable Private Vulnerability Reporting.** Settings → Code - security. Without it, the link in [SECURITY.md](SECURITY.md) 404s - for external reporters. -3. **Enable Discussions.** Settings → General → Features. Without it, - the `qna` link in [package.json](package.json) 404s on the - marketplace listing. +2. **Enable Private Vulnerability Reporting.** ✅ Enabled + 2026-05-19 via the GitHub API; SECURITY.md's reporting link now + resolves for external reporters. +3. **Enable Discussions.** ✅ Enabled 2026-05-19 via the GitHub API; + the `qna` link in [package.json](package.json) now resolves on + the marketplace listing. 4. **Manual H4 smoke** — F5 with the sample-workflow profile, open each provider's trigger file, confirm diagnostics still appear. The activation narrowing drops custom workflow paths intentionally @@ -440,9 +441,9 @@ inputs (suppression syntax, screenshots) or stacked branches link when the server publishes `Diagnostic.code.target`. (PR #11) - [x] **R9** Status bar item on the left at priority 100 showing the top two non-zero severities (e.g. `$(shield) 3C 1H`). (PR #11) -- [ ] **R10** Rename / repurpose `pipelineCheck.findings.refresh` to - call `scanWorkspace()` once the scan-workspace branch lands. - *(Blocked on scan-workspace merging.)* +- [x] **R10** `pipelineCheck.findings.refresh` now calls + `scanWorkspace()` rather than just re-painting the tree from + already-published diagnostics. - [ ] **R11** `CodeAction` provider for suppression comments. *(Blocked on the upstream pipeline-check CLI's suppression syntax.)* - [x] **R12** Alt+F8 / Shift+Alt+F8 jump between findings, wrap at @@ -456,8 +457,10 @@ inputs (suppression syntax, screenshots) or stacked branches - [x] **R14** Trigger-pattern list extracted into `src/providers.ts` (`PROVIDERS` map + `TRIGGER_PATTERNS`). A regression test asserts the package.json `activationEvents` stay in lockstep. (PR #12) -- [ ] **R15** `onCommand:pipelineCheck.scanWorkspace` activation - event. *(Blocked on scan-workspace merging.)* +- [x] **R15** Scan-workspace command shipped; covered by + `workspaceContains:` activation triggers + `onStartupFinished` + so the command is always reachable from the Findings welcome + state and the title-bar button. - [x] **R16** `[client] HH:MM:SS.mmm ` logging into the LanguageClient's outputChannel. `withTiming(label, fn)` wraps thunks with start/ok/failed breadcrumbs. (PR #12) @@ -484,9 +487,11 @@ inputs (suppression syntax, screenshots) or stacked branches - [x] **R21** Three-OS matrix: `[ubuntu-latest, windows-latest, macos-latest]`. `npm audit` and the vsix upload pinned to Linux. (PR #11) -- [ ] **R22** Finish the eslint flat-config migration so drift between - eslint v8 and TS 6 / esbuild 0.28 / @types/node 25 stops - widening. *(WIP stash on the maintainer's `pr10` checkout.)* +- [x] **R22** Migrated to eslint v9 flat config + ([eslint.config.mjs](eslint.config.mjs)); replaced + `@typescript-eslint/eslint-plugin` + `parser` with the unified + `typescript-eslint` package. Rules carry over verbatim so the + lint result is unchanged. Unblocks future eslint v9+ bumps. - [ ] **R23** Resolve the CodeQL default-setup conflict — disable default setup or delete `codeql.yml`. *(Needs repo-settings change.)* @@ -507,5 +512,8 @@ inputs (suppression syntax, screenshots) or stacked branches [SECURITY.md](SECURITY.md) carries the explicit no-telemetry promise so the policy is visible at the security-review surface researchers check first. (Decided 2026-05-19.) -- [ ] **R29** Scan-on-save mode. *(Depends on scan-workspace - merging.)* +- [x] **R29** `pipelineCheck.scanOnSave` setting (default `false`). + Saving a CI file kicks off a quiet workspace re-scan (status-bar + spinner; no toast) so cross-file effects in unopened CI files + get re-evaluated. In-flight guard collapses save-storms to a + single scan. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..a3b85ec --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,45 @@ +// ESLint flat-config — replaces the legacy `.eslintrc.json`. +// +// Rules carry over verbatim from the old config so this is purely a +// format migration; the lint result of the suite should be unchanged. +// The flat-config switch unblocks the eslint v8 → v9 bump (flat config +// is the default-and-only format from v9 onward). +// +// Order matters: later configs in the array override earlier ones. +// We stack `eslint:recommended` first, then `typescript-eslint`'s +// recommended preset (parser + plugin + sensible defaults for .ts +// files), then our own per-rule overrides. + +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default [ + // Global ignores. Equivalent to `ignorePatterns` in the old config + // plus the v0.2.0 additions (out-test, dist) so the lint walker + // doesn't recurse into generated output. + { + ignores: [ + "out/**", + "out-test/**", + "dist/**", + "node_modules/**", + ".vscode-test/**", + ], + }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + }, + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_" }, + ], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "warn", + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 980690f..fe896f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,30 @@ { "name": "pipeline-check", - "version": "0.1.1", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pipeline-check", - "version": "0.1.1", + "version": "0.2.0", "license": "MIT", "dependencies": { "vscode-languageclient": "^9.0.1" }, "devDependencies": { + "@eslint/js": "^9.0.0", "@types/mocha": "^10.0.10", "@types/node": "^25.9.0", "@types/vscode": "^1.85.0", - "@typescript-eslint/eslint-plugin": "^8.59.4", - "@typescript-eslint/parser": "^8.59.4", "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.9.1", "esbuild": "^0.28.0", - "eslint": "^8.56.0", + "eslint": "^9.0.0", "mocha": "^11.7.5", "ovsx": "^0.10.12", "typescript": "^6.0.3", + "typescript-eslint": "^8.0.0", "vitest": "^4.1.6" }, "engines": { @@ -756,25 +756,90 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -805,53 +870,78 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" }, "engines": { - "node": ">=10.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": "*" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -868,13 +958,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@isaacs/cliui": { "version": "9.0.0", @@ -1898,6 +1994,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", @@ -2194,13 +2297,6 @@ "node": ">=20.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@vitest/expect": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", @@ -3675,19 +3771,6 @@ "node": ">=0.3.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -3990,66 +4073,69 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4057,7 +4143,7 @@ "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4087,6 +4173,19 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -4101,18 +4200,31 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4274,16 +4386,16 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -4327,18 +4439,17 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -4426,13 +4537,6 @@ "node": ">=14.14" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4528,28 +4632,6 @@ "license": "MIT", "optional": true }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4563,41 +4645,14 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4640,13 +4695,6 @@ "dev": true, "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4895,18 +4943,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6460,6 +6496,7 @@ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", + "optional": true, "dependencies": { "wrappy": "1" } @@ -6870,16 +6907,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7289,23 +7316,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/rolldown": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", @@ -8405,19 +8415,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -8444,6 +8441,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -8936,7 +8957,8 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/wsl-utils": { "version": "0.1.0", diff --git a/package.json b/package.json index 35a0350..c3242a7 100644 --- a/package.json +++ b/package.json @@ -276,6 +276,11 @@ "default": true, "markdownDescription": "Show the `Pipeline-Check: N critical · M high` summary CodeLens at the top of each scanned file. Turn off if you find the line-1 lens intrusive — the editor gutter, the Findings panel, and the status bar still surface the same counts." }, + "pipelineCheck.scanOnSave": { + "type": "boolean", + "default": false, + "markdownDescription": "Re-scan the whole workspace whenever you save a CI/CD config file. Useful when one file references another (a Jenkinsfile sharing a library, GHA workflows that reuse a composite action) and a fix in one file should re-evaluate findings in the others. Renders as a status-bar spinner — no toast. Off by default; the LSP already re-scans the saved file itself on `didSave`, so this is purely about catching cross-file effects in files that aren't currently open." + }, "pipelineCheck.severityThreshold": { "type": "string", "enum": [ @@ -307,7 +312,7 @@ "bundle:dev": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node --sourcemap", "bundle:prod": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node --minify", "watch": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node --sourcemap --watch", - "lint": "eslint src --ext ts", + "lint": "eslint src", "test": "vitest run", "test:watch": "vitest", "test:integration:compile": "tsc -p tsconfig.integration.json", @@ -324,13 +329,13 @@ "@types/mocha": "^10.0.10", "@types/node": "^25.9.0", "@types/vscode": "^1.85.0", - "@typescript-eslint/eslint-plugin": "^8.59.4", - "@typescript-eslint/parser": "^8.59.4", + "@eslint/js": "^9.0.0", "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.9.1", "esbuild": "^0.28.0", - "eslint": "^8.56.0", + "eslint": "^9.0.0", + "typescript-eslint": "^8.0.0", "mocha": "^11.7.5", "ovsx": "^0.10.12", "typescript": "^6.0.3", diff --git a/src/extension.ts b/src/extension.ts index bb4e657..ea5f2a7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -429,6 +429,39 @@ export async function activate( await startClient(); + // Scan-on-save: when the user saves a CI/CD config file and has the + // setting enabled, re-scan the whole workspace (quietly). The LSP + // already re-publishes diagnostics for the saved file itself on + // `didSave`, so this is purely about picking up cross-file effects in + // *other* CI files (a Jenkinsfile that includes the just-edited + // library, a GHA workflow that calls the just-edited composite + // action). A simple in-flight guard prevents save-storms (autosave, + // Save All) from queueing redundant scans. + let scanOnSaveBusy = false; + context.subscriptions.push( + vscode.workspace.onDidSaveTextDocument(async (doc) => { + const config = vscode.workspace.getConfiguration("pipelineCheck"); + if (!config.get("scanOnSave", false)) { + return; + } + // Only react to saves of CI-relevant files. providerForPath + // returns `undefined` for anything that doesn't match our + // glob patterns, so package.json / random YAML never triggers. + if (!providerForPath(doc.uri.fsPath)) { + return; + } + if (scanOnSaveBusy) { + return; + } + scanOnSaveBusy = true; + try { + await scanWorkspace({ quiet: true }); + } finally { + scanOnSaveBusy = false; + } + }), + ); + // 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 diff --git a/src/test/integration/activation.test.ts b/src/test/integration/activation.test.ts index 7670adc..b14ace1 100644 --- a/src/test/integration/activation.test.ts +++ b/src/test/integration/activation.test.ts @@ -79,6 +79,8 @@ suite("Pipeline-Check — activation", () => { "serverArgs", "severityThreshold", "disabledProviders", + "codeLens.enabled", + "scanOnSave", "trace.server", ]) { const info = config.inspect(key); diff --git a/src/workspaceScan.ts b/src/workspaceScan.ts index 9bd49d9..11580dc 100644 --- a/src/workspaceScan.ts +++ b/src/workspaceScan.ts @@ -33,6 +33,17 @@ export interface ScanResult { readonly cancelled: boolean; } +export interface ScanOptions { + /** + * Quiet scans render as a status-bar progress item (no modal toast) + * and suppress the completion notification. Used by the scan-on-save + * path so a save-heavy workflow doesn't paper the screen with toasts. + * The user-initiated scan command still uses the notification surface + * — discoverable progress + a cancellation button. + */ + readonly quiet?: boolean; +} + /** * Walk the workspace, open every candidate document, and let the LSP's * `didOpen` pipeline produce diagnostics. Returns a summary the caller @@ -42,21 +53,28 @@ export interface ScanResult { * counted but never abort the scan — one bad file shouldn't hide * findings in the other 49. */ -export async function scanWorkspace(): Promise { +export async function scanWorkspace( + options: ScanOptions = {}, +): Promise { + const quiet = options.quiet === true; const folders = vscode.workspace.workspaceFolders; if (!folders || folders.length === 0) { - void vscode.window.showInformationMessage( - "Pipeline-Check: open a workspace folder before scanning.", - ); + if (!quiet) { + void vscode.window.showInformationMessage( + "Pipeline-Check: open a workspace folder before scanning.", + ); + } return { scanned: 0, failed: 0, cancelled: false }; } const uris = await vscode.workspace.findFiles(buildScanGlob(), EXCLUDE_GLOB); if (uris.length === 0) { - void vscode.window.showInformationMessage( - "Pipeline-Check: no scannable files found in this workspace.", - ); + if (!quiet) { + void vscode.window.showInformationMessage( + "Pipeline-Check: no scannable files found in this workspace.", + ); + } return { scanned: 0, failed: 0, cancelled: false }; } @@ -66,9 +84,16 @@ export async function scanWorkspace(): Promise { await vscode.window.withProgress( { - location: vscode.ProgressLocation.Notification, + // Status-bar spinner in quiet mode, full modal progress for the + // user-initiated command. The status-bar surface has no inherent + // cancellation affordance, so we drop `cancellable` too — a + // quiet scan-on-save scan is short-lived enough that not having a + // cancel button isn't a regression in practice. + location: quiet + ? vscode.ProgressLocation.Window + : vscode.ProgressLocation.Notification, title: "Pipeline-Check: scanning workspace", - cancellable: true, + cancellable: !quiet, }, async (progress, token) => { const step = 100 / uris.length; @@ -96,11 +121,13 @@ export async function scanWorkspace(): Promise { }, ); - const summary = formatSummary({ scanned, failed, cancelled }); - if (cancelled || failed > 0) { - void vscode.window.showWarningMessage(summary); - } else { - void vscode.window.showInformationMessage(summary); + if (!quiet) { + const summary = formatSummary({ scanned, failed, cancelled }); + if (cancelled || failed > 0) { + void vscode.window.showWarningMessage(summary); + } else { + void vscode.window.showInformationMessage(summary); + } } return { scanned, failed, cancelled }; } From a2024961fb1e7ccfa2b02790dcf0ead81aa91d8d Mon Sep 17 00:00:00 2001 From: Daniel Martin <56157528+dmartinochoa@users.noreply.github.com> Date: Tue, 19 May 2026 16:31:04 +0200 Subject: [PATCH 2/2] release: v1.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cuts the first stable release. Bumps package.json (and the matching package-lock root) to 1.0.0, folds the CHANGELOG Unreleased block into a ## [1.0.0] — 2026-05-19 section above an empty Unreleased (keeps the awk extractor in publish.yml happy: it prints between the first two `## [` headers, so the GitHub-release body now carries 1.0.0's notes). 1.0.0 is the commitment that the v0.x iteration is done — see the section header in CHANGELOG.md for the substance. Nothing in this commit is functional; the feature work landed in the preceding commit. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 25 +++++++++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0a06b..7841f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,17 @@ versions follow [SemVer](https://semver.org/). ## [Unreleased] +## [1.0.0] — 2026-05-19 + +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](SECURITY.md). + ### Added - **`Pipeline-Check: Filter Findings` command.** Opens an InputBox; @@ -28,6 +39,15 @@ versions follow [SemVer](https://semver.org/). 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.scanOnSave` setting + (default `false`). 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 on `didSave`, 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 @@ -67,6 +87,11 @@ versions follow [SemVer](https://semver.org/). `providers.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.json` with + [eslint.config.mjs](eslint.config.mjs); dropped + `@typescript-eslint/eslint-plugin` + `@typescript-eslint/parser` in + favour of the unified `typescript-eslint` package. Rules carry over + verbatim so lint results are unchanged. (R22) ## [0.2.0] — 2026-05-19 diff --git a/package-lock.json b/package-lock.json index fe896f3..e0e70ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pipeline-check", - "version": "0.2.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pipeline-check", - "version": "0.2.0", + "version": "1.0.0", "license": "MIT", "dependencies": { "vscode-languageclient": "^9.0.1" diff --git a/package.json b/package.json index c3242a7..6cf7809 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pipeline-check", "displayName": "Pipeline-Check", "description": "Lint CI/CD pipelines for 22 providers against OWASP Top 10 CI/CD Risks and 14 other compliance frameworks. 810+ rules, inline in your editor.", - "version": "0.2.0", + "version": "1.0.0", "publisher": "greylag-ci", "license": "MIT", "icon": "icon.png",