@stsepelin/checktrail 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +42 -0
- package/LICENSE +21 -0
- package/README.md +355 -0
- package/SECURITY.md +46 -0
- package/dist/src/actionlint-evidence.d.ts +2 -0
- package/dist/src/actionlint-evidence.js +166 -0
- package/dist/src/actionlint-inputs.d.ts +5 -0
- package/dist/src/actionlint-inputs.js +123 -0
- package/dist/src/actionlint-runner.d.ts +1 -0
- package/dist/src/actionlint-runner.js +122 -0
- package/dist/src/actionlint.d.ts +19 -0
- package/dist/src/actionlint.js +80 -0
- package/dist/src/adapters.d.ts +49 -0
- package/dist/src/adapters.js +430 -0
- package/dist/src/architecture.d.ts +107 -0
- package/dist/src/architecture.js +230 -0
- package/dist/src/clang-evidence.d.ts +2 -0
- package/dist/src/clang-evidence.js +194 -0
- package/dist/src/clang-protocol.d.ts +3 -0
- package/dist/src/clang-protocol.js +25 -0
- package/dist/src/clang-runner.d.ts +1 -0
- package/dist/src/clang-runner.js +159 -0
- package/dist/src/clang.d.ts +44 -0
- package/dist/src/clang.js +197 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +338 -0
- package/dist/src/config.d.ts +33 -0
- package/dist/src/config.js +140 -0
- package/dist/src/contract-schema.d.ts +85 -0
- package/dist/src/contract-schema.js +56 -0
- package/dist/src/contract-worker.d.ts +1 -0
- package/dist/src/contract-worker.js +123 -0
- package/dist/src/contracts.d.ts +22 -0
- package/dist/src/contracts.js +123 -0
- package/dist/src/django-evidence.d.ts +2 -0
- package/dist/src/django-evidence.js +100 -0
- package/dist/src/django-runner.d.ts +1 -0
- package/dist/src/django-runner.js +80 -0
- package/dist/src/django.d.ts +9 -0
- package/dist/src/django.js +50 -0
- package/dist/src/dotnet-compiler.d.ts +1 -0
- package/dist/src/dotnet-compiler.js +87 -0
- package/dist/src/dotnet-evidence.d.ts +2 -0
- package/dist/src/dotnet-evidence.js +127 -0
- package/dist/src/dotnet-runner.d.ts +1 -0
- package/dist/src/dotnet-runner.js +167 -0
- package/dist/src/dotnet.d.ts +65 -0
- package/dist/src/dotnet.js +120 -0
- package/dist/src/engine.d.ts +19 -0
- package/dist/src/engine.js +239 -0
- package/dist/src/environment.d.ts +6 -0
- package/dist/src/environment.js +36 -0
- package/dist/src/eslint-evidence.d.ts +2 -0
- package/dist/src/eslint-evidence.js +98 -0
- package/dist/src/eslint-runner.d.ts +1 -0
- package/dist/src/eslint-runner.js +61 -0
- package/dist/src/eslint.d.ts +2 -0
- package/dist/src/eslint.js +40 -0
- package/dist/src/evidence.d.ts +2 -0
- package/dist/src/evidence.js +313 -0
- package/dist/src/external-adapter.d.ts +164 -0
- package/dist/src/external-adapter.js +265 -0
- package/dist/src/external-evidence.d.ts +2 -0
- package/dist/src/external-evidence.js +127 -0
- package/dist/src/external-runner.d.ts +1 -0
- package/dist/src/external-runner.js +122 -0
- package/dist/src/fastapi-evidence.d.ts +2 -0
- package/dist/src/fastapi-evidence.js +126 -0
- package/dist/src/fastapi-runner.d.ts +1 -0
- package/dist/src/fastapi-runner.js +84 -0
- package/dist/src/fastapi.d.ts +10 -0
- package/dist/src/fastapi.js +52 -0
- package/dist/src/fetch-pack.d.ts +16 -0
- package/dist/src/fetch-pack.js +154 -0
- package/dist/src/finding-policy-schema.d.ts +146 -0
- package/dist/src/finding-policy-schema.js +70 -0
- package/dist/src/finding-policy.d.ts +62 -0
- package/dist/src/finding-policy.js +206 -0
- package/dist/src/git-selection.d.ts +3 -0
- package/dist/src/git-selection.js +229 -0
- package/dist/src/go-directives.d.ts +1 -0
- package/dist/src/go-directives.js +32 -0
- package/dist/src/go-scope.d.ts +11 -0
- package/dist/src/go-scope.js +122 -0
- package/dist/src/golangci-evidence.d.ts +2 -0
- package/dist/src/golangci-evidence.js +92 -0
- package/dist/src/golangci-runner.d.ts +1 -0
- package/dist/src/golangci-runner.js +124 -0
- package/dist/src/golangci.d.ts +2 -0
- package/dist/src/golangci.js +35 -0
- package/dist/src/guidance.d.ts +98 -0
- package/dist/src/guidance.js +247 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.js +16 -0
- package/dist/src/inventory.d.ts +4 -0
- package/dist/src/inventory.js +94 -0
- package/dist/src/java-compiler.d.ts +1 -0
- package/dist/src/java-compiler.js +110 -0
- package/dist/src/java-evidence.d.ts +2 -0
- package/dist/src/java-evidence.js +114 -0
- package/dist/src/java-runner.d.ts +1 -0
- package/dist/src/java-runner.js +79 -0
- package/dist/src/java.d.ts +25 -0
- package/dist/src/java.js +102 -0
- package/dist/src/jest-evidence.d.ts +2 -0
- package/dist/src/jest-evidence.js +117 -0
- package/dist/src/jest-runner.d.ts +1 -0
- package/dist/src/jest-runner.js +46 -0
- package/dist/src/jest.d.ts +2 -0
- package/dist/src/jest.js +36 -0
- package/dist/src/junit.d.ts +18 -0
- package/dist/src/junit.js +157 -0
- package/dist/src/laravel-evidence.d.ts +2 -0
- package/dist/src/laravel-evidence.js +175 -0
- package/dist/src/laravel-runner.d.ts +1 -0
- package/dist/src/laravel-runner.js +168 -0
- package/dist/src/laravel.d.ts +8 -0
- package/dist/src/laravel.js +50 -0
- package/dist/src/local-tool.d.ts +1 -0
- package/dist/src/local-tool.js +19 -0
- package/dist/src/mcp.d.ts +14 -0
- package/dist/src/mcp.js +362 -0
- package/dist/src/mutation.d.ts +118 -0
- package/dist/src/mutation.js +370 -0
- package/dist/src/mypy-evidence.d.ts +2 -0
- package/dist/src/mypy-evidence.js +63 -0
- package/dist/src/mypy.d.ts +2 -0
- package/dist/src/mypy.js +59 -0
- package/dist/src/node-reporter.d.ts +1 -0
- package/dist/src/node-reporter.js +28 -0
- package/dist/src/nuxt-evidence.d.ts +2 -0
- package/dist/src/nuxt-evidence.js +160 -0
- package/dist/src/nuxt-protocol.d.ts +52 -0
- package/dist/src/nuxt-protocol.js +29 -0
- package/dist/src/nuxt-runner.d.ts +1 -0
- package/dist/src/nuxt-runner.js +165 -0
- package/dist/src/nuxt.d.ts +24 -0
- package/dist/src/nuxt.js +105 -0
- package/dist/src/output.d.ts +3 -0
- package/dist/src/output.js +48 -0
- package/dist/src/phpstan-evidence.d.ts +2 -0
- package/dist/src/phpstan-evidence.js +81 -0
- package/dist/src/phpstan.d.ts +2 -0
- package/dist/src/phpstan.js +51 -0
- package/dist/src/phpunit-evidence.d.ts +2 -0
- package/dist/src/phpunit-evidence.js +53 -0
- package/dist/src/phpunit.d.ts +2 -0
- package/dist/src/phpunit.js +58 -0
- package/dist/src/pint-evidence.d.ts +2 -0
- package/dist/src/pint-evidence.js +79 -0
- package/dist/src/pint-runner.d.ts +1 -0
- package/dist/src/pint-runner.js +53 -0
- package/dist/src/pint.d.ts +2 -0
- package/dist/src/pint.js +37 -0
- package/dist/src/playwright-evidence.d.ts +2 -0
- package/dist/src/playwright-evidence.js +127 -0
- package/dist/src/playwright-reporter.d.ts +10 -0
- package/dist/src/playwright-reporter.js +37 -0
- package/dist/src/playwright-runner.d.ts +1 -0
- package/dist/src/playwright-runner.js +61 -0
- package/dist/src/playwright.d.ts +2 -0
- package/dist/src/playwright.js +35 -0
- package/dist/src/policy-pack.d.ts +21 -0
- package/dist/src/policy-pack.js +41 -0
- package/dist/src/pytest-evidence.d.ts +2 -0
- package/dist/src/pytest-evidence.js +116 -0
- package/dist/src/pytest-runner.d.ts +1 -0
- package/dist/src/pytest-runner.js +48 -0
- package/dist/src/pytest.d.ts +2 -0
- package/dist/src/pytest.js +25 -0
- package/dist/src/report-validation.d.ts +129 -0
- package/dist/src/report-validation.js +27 -0
- package/dist/src/review.d.ts +270 -0
- package/dist/src/review.js +349 -0
- package/dist/src/ruby.d.ts +2 -0
- package/dist/src/ruby.js +25 -0
- package/dist/src/ruff-evidence.d.ts +2 -0
- package/dist/src/ruff-evidence.js +99 -0
- package/dist/src/ruff.d.ts +2 -0
- package/dist/src/ruff.js +40 -0
- package/dist/src/runner.d.ts +8 -0
- package/dist/src/runner.js +117 -0
- package/dist/src/runtime-inventory.d.ts +112 -0
- package/dist/src/runtime-inventory.js +200 -0
- package/dist/src/rust-dep-info.d.ts +1 -0
- package/dist/src/rust-dep-info.js +46 -0
- package/dist/src/rust-evidence.d.ts +2 -0
- package/dist/src/rust-evidence.js +157 -0
- package/dist/src/rust-runner.d.ts +1 -0
- package/dist/src/rust-runner.js +161 -0
- package/dist/src/rust.d.ts +7 -0
- package/dist/src/rust.js +39 -0
- package/dist/src/sarif.d.ts +86 -0
- package/dist/src/sarif.js +113 -0
- package/dist/src/schemas.d.ts +393 -0
- package/dist/src/schemas.js +222 -0
- package/dist/src/staticcheck-evidence.d.ts +2 -0
- package/dist/src/staticcheck-evidence.js +75 -0
- package/dist/src/swift.d.ts +2 -0
- package/dist/src/swift.js +20 -0
- package/dist/src/task-store.d.ts +37 -0
- package/dist/src/task-store.js +349 -0
- package/dist/src/tool-versions.d.ts +3 -0
- package/dist/src/tool-versions.js +260 -0
- package/dist/src/types.d.ts +157 -0
- package/dist/src/types.js +37 -0
- package/dist/src/typescript-build-evidence.d.ts +2 -0
- package/dist/src/typescript-build-evidence.js +86 -0
- package/dist/src/typescript-build-runner.d.ts +1 -0
- package/dist/src/typescript-build-runner.js +132 -0
- package/dist/src/typescript-build.d.ts +2 -0
- package/dist/src/typescript-build.js +41 -0
- package/dist/src/typescript.d.ts +2 -0
- package/dist/src/typescript.js +54 -0
- package/dist/src/validation-task-worker.d.ts +1 -0
- package/dist/src/validation-task-worker.js +205 -0
- package/dist/src/validation-tasks-protocol.d.ts +69 -0
- package/dist/src/validation-tasks-protocol.js +57 -0
- package/dist/src/validation-tasks.d.ts +11 -0
- package/dist/src/validation-tasks.js +144 -0
- package/dist/src/vitest-evidence.d.ts +2 -0
- package/dist/src/vitest-evidence.js +112 -0
- package/dist/src/vitest-runner.d.ts +1 -0
- package/dist/src/vitest-runner.js +48 -0
- package/dist/src/vitest.d.ts +2 -0
- package/dist/src/vitest.js +36 -0
- package/dist/src/vue-router-capture.d.ts +42 -0
- package/dist/src/vue-router-capture.js +87 -0
- package/dist/src/vue-router-evidence.d.ts +2 -0
- package/dist/src/vue-router-evidence.js +126 -0
- package/dist/src/vue-router-protocol.d.ts +63 -0
- package/dist/src/vue-router-protocol.js +44 -0
- package/dist/src/vue-router-runner.d.ts +1 -0
- package/dist/src/vue-router-runner.js +88 -0
- package/dist/src/vue-router.d.ts +23 -0
- package/dist/src/vue-router.js +108 -0
- package/dist/src/vue-tsc-runner.d.ts +1 -0
- package/dist/src/vue-tsc-runner.js +24 -0
- package/dist/src/workspace.d.ts +7 -0
- package/dist/src/workspace.js +53 -0
- package/docs/ACCEPTANCE.md +56 -0
- package/docs/ACTIONLINT.md +112 -0
- package/docs/ARCHITECTURE-POLICY.md +82 -0
- package/docs/ARCHITECTURE.md +122 -0
- package/docs/CLANG.md +113 -0
- package/docs/CLIENTS.md +109 -0
- package/docs/CONTRACTS.md +73 -0
- package/docs/DEPENDENCIES.md +42 -0
- package/docs/DJANGO.md +70 -0
- package/docs/DOTNET.md +114 -0
- package/docs/ENVIRONMENTS.md +52 -0
- package/docs/ESLINT.md +68 -0
- package/docs/EVALUATION.md +124 -0
- package/docs/EXECUTION.md +250 -0
- package/docs/EXTERNAL-ADAPTERS.md +170 -0
- package/docs/EXTERNAL-EVALUATION.md +155 -0
- package/docs/EXTERNAL-RUFF-EVALUATION.md +85 -0
- package/docs/FASTAPI.md +73 -0
- package/docs/FINDING-POLICY.md +67 -0
- package/docs/GO-RACE.md +19 -0
- package/docs/GO-SCOPE.md +63 -0
- package/docs/GOLANGCI-LINT.md +64 -0
- package/docs/GUIDANCE.md +46 -0
- package/docs/IMPACT-MEASUREMENT.md +97 -0
- package/docs/INSTALLATION.md +102 -0
- package/docs/JAVA.md +79 -0
- package/docs/JEST.md +36 -0
- package/docs/JUNIT.md +31 -0
- package/docs/LANGUAGES.md +113 -0
- package/docs/LARAVEL.md +111 -0
- package/docs/MCP-COMPATIBILITY.md +68 -0
- package/docs/MUTATIONS.md +93 -0
- package/docs/MYPY.md +37 -0
- package/docs/NATIVE-CI.md +96 -0
- package/docs/NUXT.md +123 -0
- package/docs/PACK-DISTRIBUTION.md +98 -0
- package/docs/PERFORMANCE.md +71 -0
- package/docs/PEST.md +36 -0
- package/docs/PHPSTAN.md +48 -0
- package/docs/PHPUNIT.md +32 -0
- package/docs/PINT.md +41 -0
- package/docs/PLAN.md +265 -0
- package/docs/PLAYWRIGHT.md +73 -0
- package/docs/POLICY-PACKS.md +106 -0
- package/docs/PRIOR-WORKFLOW-EVALUATION.md +116 -0
- package/docs/PUBLICATION.md +28 -0
- package/docs/PYTEST.md +51 -0
- package/docs/RELEASE.md +115 -0
- package/docs/RENAMING.md +55 -0
- package/docs/REVIEW-EXCHANGE.md +136 -0
- package/docs/RUBY.md +50 -0
- package/docs/RUFF.md +39 -0
- package/docs/RUNTIME-INVENTORY.md +56 -0
- package/docs/RUST.md +74 -0
- package/docs/SARIF.md +64 -0
- package/docs/STATUS.md +530 -0
- package/docs/SWIFT.md +48 -0
- package/docs/TASK-STORAGE.md +141 -0
- package/docs/TOOL-IDENTITY.md +30 -0
- package/docs/TYPESCRIPT-BUILD.md +55 -0
- package/docs/VALIDATION-TASKS.md +119 -0
- package/docs/VITEST.md +36 -0
- package/docs/VUE-ROUTER.md +130 -0
- package/docs/VUE-TSC.md +26 -0
- package/docs/WORKSPACES.md +81 -0
- package/docs/licenses/nodable-entities-3.0.0.txt +21 -0
- package/docs/measurements/client-claude-before-schema-fix-darwin-arm64-node26.json +60 -0
- package/docs/measurements/client-claude-darwin-arm64-node26.json +55 -0
- package/docs/measurements/client-codex-darwin-arm64-node26.json +175 -0
- package/docs/measurements/evaluation-corpus-repo-verifier.json +332 -0
- package/docs/measurements/evaluation-darwin-arm64-node26.json +1236 -0
- package/docs/measurements/evaluation-linux-arm64-node22.json +1236 -0
- package/docs/measurements/external-eslint-darwin-arm64-node26.json +11866 -0
- package/docs/measurements/external-eslint-linux-arm64-node22.json +11866 -0
- package/docs/measurements/external-eslint-preservation.json +1527 -0
- package/docs/measurements/external-ruff-darwin-arm64-node26.json +1031 -0
- package/docs/measurements/external-ruff-linux-arm64-node22.json +1031 -0
- package/docs/measurements/hosted-ci-52ba415.json +1005 -0
- package/docs/measurements/impact-darwin-arm64-node26.json +6685 -0
- package/docs/measurements/impact-linux-arm64-node22.json +6690 -0
- package/docs/measurements/performance-darwin-arm64-node26.json +845 -0
- package/docs/measurements/performance-linux-arm64-node22.json +845 -0
- package/docs/measurements/required-native-profiles.json +500 -0
- package/package.json +72 -0
- package/packs/actionlint.json +8 -0
- package/packs/cpp.json +8 -0
- package/packs/dotnet.json +8 -0
- package/packs/go.json +7 -0
- package/packs/java.json +8 -0
- package/packs/javascript-node.json +7 -0
- package/packs/nuxt.json +7 -0
- package/packs/php-pest.json +7 -0
- package/packs/php-phpunit.json +7 -0
- package/packs/python-pytest.json +7 -0
- package/packs/ruby.json +7 -0
- package/packs/rust.json +7 -0
- package/packs/swift.json +7 -0
- package/packs/vue-router.json +7 -0
- package/packs/vue.json +11 -0
- package/schemas/actionlint-config.schema.json +19 -0
- package/schemas/architecture-policy.schema.json +51 -0
- package/schemas/architecture-report.schema.json +123 -0
- package/schemas/architecture-summary.schema.json +82 -0
- package/schemas/clang-database.schema.json +23 -0
- package/schemas/config.schema.json +69 -0
- package/schemas/contract-bundle.schema.json +78 -0
- package/schemas/contract-report.schema.json +114 -0
- package/schemas/contract-summary.schema.json +72 -0
- package/schemas/dependency-graph.schema.json +65 -0
- package/schemas/django-config.schema.json +16 -0
- package/schemas/dotnet-config.schema.json +60 -0
- package/schemas/external-manifest.schema.json +97 -0
- package/schemas/external-reference.schema.json +10 -0
- package/schemas/external-request.schema.json +56 -0
- package/schemas/external-result.schema.json +111 -0
- package/schemas/fastapi-config.schema.json +27 -0
- package/schemas/finding-baseline.schema.json +92 -0
- package/schemas/finding-comparison-summary.schema.json +95 -0
- package/schemas/finding-comparison.schema.json +121 -0
- package/schemas/guidance-context.schema.json +34 -0
- package/schemas/guidance-report.schema.json +176 -0
- package/schemas/guidance-summary.schema.json +68 -0
- package/schemas/java-config.schema.json +24 -0
- package/schemas/junit.schema.json +69 -0
- package/schemas/laravel-config.schema.json +11 -0
- package/schemas/mutation-recipe.schema.json +31 -0
- package/schemas/mutation-report.schema.json +238 -0
- package/schemas/mutation-summary.schema.json +98 -0
- package/schemas/nuxt-config.schema.json +51 -0
- package/schemas/plan-summary.schema.json +63 -0
- package/schemas/plan.schema.json +244 -0
- package/schemas/policy-pack.schema.json +41 -0
- package/schemas/report-summary.schema.json +100 -0
- package/schemas/report.schema.json +463 -0
- package/schemas/review-assessment.schema.json +133 -0
- package/schemas/review-context-summary.schema.json +31 -0
- package/schemas/review-context.schema.json +254 -0
- package/schemas/review-receipt-summary.schema.json +104 -0
- package/schemas/review-receipt.schema.json +271 -0
- package/schemas/review-selection.schema.json +30 -0
- package/schemas/runtime-comparison-summary.schema.json +68 -0
- package/schemas/runtime-comparison.schema.json +127 -0
- package/schemas/runtime-inventory.schema.json +99 -0
- package/schemas/vue-router-config.schema.json +67 -0
- package/server.json +47 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { workflowPath } from "./actionlint.js";
|
|
3
|
+
import { parseDocument, visit, isScalar } from "yaml";
|
|
4
|
+
function record(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
6
|
+
? value
|
|
7
|
+
: {};
|
|
8
|
+
}
|
|
9
|
+
export function actionlintInputs(files, scope) {
|
|
10
|
+
const findings = [];
|
|
11
|
+
const visited = new Set();
|
|
12
|
+
const queue = scope.map((file) => ({ file, action: false }));
|
|
13
|
+
const requireFile = (file) => {
|
|
14
|
+
if (!files.has(file))
|
|
15
|
+
throw new Error("A local workflow, action or runtime asset is missing or excluded from the inventory");
|
|
16
|
+
};
|
|
17
|
+
const localPath = (base, value) => {
|
|
18
|
+
if (!workflowPath(value) ||
|
|
19
|
+
path.posix.isAbsolute(value) ||
|
|
20
|
+
value.includes("${{"))
|
|
21
|
+
throw new Error("Local action paths must be literal relative paths");
|
|
22
|
+
const file = path.posix.normalize(path.posix.join(base, value));
|
|
23
|
+
if (file === ".." || file.startsWith("../"))
|
|
24
|
+
throw new Error("A local action path escapes the project");
|
|
25
|
+
return file;
|
|
26
|
+
};
|
|
27
|
+
const action = (value) => {
|
|
28
|
+
if (typeof value !== "string" || !value.startsWith("./"))
|
|
29
|
+
return;
|
|
30
|
+
const directory = localPath(".", value);
|
|
31
|
+
const metadata = ["action.yml", "action.yaml"]
|
|
32
|
+
.map((name) => path.posix.join(directory, name))
|
|
33
|
+
.filter((file) => files.has(file));
|
|
34
|
+
if (metadata.length !== 1)
|
|
35
|
+
throw new Error("A local action requires exactly one inventoried metadata file");
|
|
36
|
+
queue.push({ file: metadata[0], action: true });
|
|
37
|
+
};
|
|
38
|
+
const steps = (value) => {
|
|
39
|
+
if (Array.isArray(value))
|
|
40
|
+
for (const step of value)
|
|
41
|
+
action(record(step).uses);
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
for (let index = 0; index < queue.length; index++) {
|
|
45
|
+
const entry = queue[index];
|
|
46
|
+
if (visited.has(entry.file))
|
|
47
|
+
continue;
|
|
48
|
+
if (visited.size >= 512)
|
|
49
|
+
throw new Error("Local action dependency closure exceeds 512 YAML files");
|
|
50
|
+
visited.add(entry.file);
|
|
51
|
+
requireFile(entry.file);
|
|
52
|
+
const bytes = files.get(entry.file);
|
|
53
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
54
|
+
const doc = parseDocument(text, {
|
|
55
|
+
prettyErrors: false,
|
|
56
|
+
strict: true,
|
|
57
|
+
uniqueKeys: true,
|
|
58
|
+
});
|
|
59
|
+
if (doc.errors.length) {
|
|
60
|
+
findings.push(...doc.errors.map((error) => ({
|
|
61
|
+
ruleId: `yaml/${error.code}`,
|
|
62
|
+
level: "error",
|
|
63
|
+
message: error.message,
|
|
64
|
+
file: entry.file,
|
|
65
|
+
line: text.slice(0, error.pos[0]).split("\n").length,
|
|
66
|
+
})));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (doc.warnings.length)
|
|
70
|
+
throw new Error("YAML warnings prevent reliable local dependency inspection");
|
|
71
|
+
visit(doc, {
|
|
72
|
+
Alias() {
|
|
73
|
+
throw new Error("YAML aliases require a separate verified workflow profile");
|
|
74
|
+
},
|
|
75
|
+
Pair(_key, pair) {
|
|
76
|
+
if (isScalar(pair.key) && pair.key.value === "<<")
|
|
77
|
+
throw new Error("YAML merge keys require a separate verified workflow profile");
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
const data = record(doc.toJS({ maxAliasCount: 0 }));
|
|
81
|
+
if (entry.action) {
|
|
82
|
+
const runs = record(data.runs);
|
|
83
|
+
for (const key of [
|
|
84
|
+
"main",
|
|
85
|
+
"pre",
|
|
86
|
+
"post",
|
|
87
|
+
"image",
|
|
88
|
+
"entrypoint",
|
|
89
|
+
"pre-entrypoint",
|
|
90
|
+
"post-entrypoint",
|
|
91
|
+
]) {
|
|
92
|
+
const value = runs[key];
|
|
93
|
+
if (typeof value === "string" &&
|
|
94
|
+
value &&
|
|
95
|
+
!(key === "image" && value.startsWith("docker://")))
|
|
96
|
+
requireFile(localPath(path.posix.dirname(entry.file), value));
|
|
97
|
+
}
|
|
98
|
+
steps(runs.steps);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
for (const job of Object.values(record(data.jobs))) {
|
|
102
|
+
const item = record(job);
|
|
103
|
+
if (typeof item.uses === "string" && item.uses.startsWith("./")) {
|
|
104
|
+
const file = localPath(".", item.uses);
|
|
105
|
+
requireFile(file);
|
|
106
|
+
if (!scope.includes(file))
|
|
107
|
+
throw new Error("Local reusable workflows must belong to the planned workflow scope");
|
|
108
|
+
}
|
|
109
|
+
steps(item.steps);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { findings };
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
findings,
|
|
118
|
+
unavailable: error instanceof Error
|
|
119
|
+
? error.message
|
|
120
|
+
: "Workflow dependencies could not be inspected",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, realpath, rm, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { actionlintInputs } from "./actionlint-inputs.js";
|
|
6
|
+
import { actionlintInvocationSchema, workflowRoot, workflowPath, } from "./actionlint.js";
|
|
7
|
+
import { inventory, withinRoot } from "./inventory.js";
|
|
8
|
+
let nativeBytes = 0;
|
|
9
|
+
function invoke(args, cwd) {
|
|
10
|
+
const result = spawnSync("actionlint", args, {
|
|
11
|
+
cwd,
|
|
12
|
+
env: { PATH: process.env.PATH ?? "" },
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
maxBuffer: 1024 * 1024,
|
|
15
|
+
});
|
|
16
|
+
nativeBytes +=
|
|
17
|
+
Buffer.byteLength(result.stdout ?? "") +
|
|
18
|
+
Buffer.byteLength(result.stderr ?? "");
|
|
19
|
+
if (result.error ||
|
|
20
|
+
result.signal ||
|
|
21
|
+
result.status === null ||
|
|
22
|
+
nativeBytes > 4 * 1024 * 1024)
|
|
23
|
+
throw new Error("Actionlint evidence exceeded process limits");
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
async function main() {
|
|
27
|
+
const temporary = await realpath(await mkdtemp(path.join(tmpdir(), "checktrail-actionlint-")));
|
|
28
|
+
try {
|
|
29
|
+
try {
|
|
30
|
+
const result = invoke(["-version"], temporary);
|
|
31
|
+
if (result.status !== 0 ||
|
|
32
|
+
result.stderr ||
|
|
33
|
+
!/^1\.7\.12\ninstalled by downloading from release page\nbuilt with go[^\r\n]+ compiler for (?:darwin|linux)\/(?:arm64|amd64)\n$/.test(result.stdout))
|
|
34
|
+
throw new Error("Unverified actionlint toolchain");
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
process.stdout.write(JSON.stringify({ unavailable: "actionlint-toolchain" }));
|
|
38
|
+
process.exitCode = 3;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (process.argv[2] === "--version") {
|
|
42
|
+
process.stdout.write("1.7.12\n");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const root = await realpath(process.argv[2]);
|
|
46
|
+
const projectRoot = await realpath(process.cwd());
|
|
47
|
+
const project = path.relative(root, projectRoot).split(path.sep).join("/") || ".";
|
|
48
|
+
const invocation = actionlintInvocationSchema.parse(JSON.parse(process.argv[3]));
|
|
49
|
+
const source = await inventory(root);
|
|
50
|
+
if (source.fingerprint !== invocation.fingerprint)
|
|
51
|
+
throw new Error("Workflow source changed after planning");
|
|
52
|
+
const prefix = project === "." ? "" : `${project}/`;
|
|
53
|
+
const scope = source.files
|
|
54
|
+
.filter((file) => workflowRoot(file) === project)
|
|
55
|
+
.map((file) => file.slice(prefix.length));
|
|
56
|
+
if (JSON.stringify(scope) !== JSON.stringify(invocation.scope))
|
|
57
|
+
throw new Error("Workflow scope changed after planning");
|
|
58
|
+
const files = new Map();
|
|
59
|
+
let bytes = 0;
|
|
60
|
+
for (const file of source.files.filter((file) => file.startsWith(prefix))) {
|
|
61
|
+
const relative = file.slice(prefix.length);
|
|
62
|
+
if (relative.split("/").some((part) => part.toLowerCase() === ".git") ||
|
|
63
|
+
/(?:^|\/)\.github\/actionlint\.ya?ml$/i.test(relative))
|
|
64
|
+
continue;
|
|
65
|
+
if (!workflowPath(relative))
|
|
66
|
+
throw new Error("Workflow input paths contain unsupported characters");
|
|
67
|
+
const resolved = await withinRoot(root, file);
|
|
68
|
+
if (resolved !== path.resolve(root, file))
|
|
69
|
+
throw new Error("Workflow inputs must not traverse symbolic links");
|
|
70
|
+
const content = await readFile(resolved);
|
|
71
|
+
bytes += content.length;
|
|
72
|
+
if (content.length > 8 * 1024 * 1024 || bytes > 64 * 1024 * 1024)
|
|
73
|
+
throw new Error("Workflow copy exceeds inventory bounds");
|
|
74
|
+
files.set(relative, content);
|
|
75
|
+
}
|
|
76
|
+
const preflight = actionlintInputs(files, scope);
|
|
77
|
+
const results = [];
|
|
78
|
+
if (!preflight.unavailable && !preflight.findings.length) {
|
|
79
|
+
const snapshot = path.join(temporary, "project");
|
|
80
|
+
await mkdir(path.join(snapshot, ".git"), { recursive: true });
|
|
81
|
+
for (const [file, content] of files) {
|
|
82
|
+
const output = path.join(snapshot, file);
|
|
83
|
+
await mkdir(path.dirname(output), { recursive: true });
|
|
84
|
+
await writeFile(output, content, { flag: "wx" });
|
|
85
|
+
}
|
|
86
|
+
const configFile = path.join(temporary, "actionlint.json");
|
|
87
|
+
await writeFile(configFile, JSON.stringify({
|
|
88
|
+
"self-hosted-runner": { labels: invocation.config.runnerLabels },
|
|
89
|
+
"config-variables": invocation.config.variables,
|
|
90
|
+
}));
|
|
91
|
+
for (const file of scope) {
|
|
92
|
+
const result = invoke([
|
|
93
|
+
"-verbose",
|
|
94
|
+
"-format",
|
|
95
|
+
"{{json .}}",
|
|
96
|
+
"-shellcheck=",
|
|
97
|
+
"-pyflakes=",
|
|
98
|
+
"-no-color",
|
|
99
|
+
"-config-file",
|
|
100
|
+
configFile,
|
|
101
|
+
file,
|
|
102
|
+
], snapshot);
|
|
103
|
+
results.push({
|
|
104
|
+
file,
|
|
105
|
+
exitCode: result.status,
|
|
106
|
+
stdout: result.stdout.replaceAll(snapshot, "<project>"),
|
|
107
|
+
stderr: result.stderr.replaceAll(snapshot, "<project>"),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if ((await inventory(root)).fingerprint !== invocation.fingerprint)
|
|
112
|
+
throw new Error("Workflow inputs changed during validation");
|
|
113
|
+
process.stdout.write(JSON.stringify({ version: "1.7.12", ...invocation, preflight, results }));
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await rm(temporary, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
main().catch(() => {
|
|
120
|
+
process.stderr.write("Workflow evidence collection could not complete\n");
|
|
121
|
+
process.exitCode = 2;
|
|
122
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Check, Inventory, Project } from "./types.js";
|
|
3
|
+
export declare function workflowPath(value: string): boolean;
|
|
4
|
+
export declare const actionlintConfigSchema: z.ZodObject<{
|
|
5
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
6
|
+
runnerLabels: z.ZodArray<z.ZodString>;
|
|
7
|
+
variables: z.ZodArray<z.ZodString>;
|
|
8
|
+
}, z.core.$strict>;
|
|
9
|
+
export declare const actionlintInvocationSchema: z.ZodObject<{
|
|
10
|
+
config: z.ZodObject<{
|
|
11
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
12
|
+
runnerLabels: z.ZodArray<z.ZodString>;
|
|
13
|
+
variables: z.ZodArray<z.ZodString>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
scope: z.ZodArray<z.ZodString>;
|
|
16
|
+
fingerprint: z.ZodString;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export declare function workflowRoot(file: string): string | undefined;
|
|
19
|
+
export declare function actionlintCheck(source: Inventory, project: Project): Promise<Check>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { readProjectFile } from "./inventory.js";
|
|
4
|
+
export function workflowPath(value) {
|
|
5
|
+
return [...value].every((character) => character.charCodeAt(0) >= 32 &&
|
|
6
|
+
character.charCodeAt(0) !== 127 &&
|
|
7
|
+
character !== "\\");
|
|
8
|
+
}
|
|
9
|
+
export const actionlintConfigSchema = z.strictObject({
|
|
10
|
+
schemaVersion: z.literal(1),
|
|
11
|
+
runnerLabels: z
|
|
12
|
+
.array(z.string().regex(/^[A-Za-z0-9_-]{1,128}$/))
|
|
13
|
+
.max(128)
|
|
14
|
+
.refine((items) => new Set(items.map((item) => item.toLowerCase())).size === items.length),
|
|
15
|
+
variables: z
|
|
16
|
+
.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/))
|
|
17
|
+
.max(256)
|
|
18
|
+
.refine((items) => new Set(items.map((item) => item.toLowerCase())).size === items.length),
|
|
19
|
+
});
|
|
20
|
+
export const actionlintInvocationSchema = z.strictObject({
|
|
21
|
+
config: actionlintConfigSchema,
|
|
22
|
+
scope: z
|
|
23
|
+
.array(z
|
|
24
|
+
.string()
|
|
25
|
+
.regex(/^\.github\/workflows\/[^/]+\.ya?ml$/)
|
|
26
|
+
.refine(workflowPath))
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(128),
|
|
29
|
+
fingerprint: z.string().regex(/^[a-f0-9]{64}$/),
|
|
30
|
+
});
|
|
31
|
+
export function workflowRoot(file) {
|
|
32
|
+
const match = /^(.*?)(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/.exec(file);
|
|
33
|
+
return match ? match[1] || "." : undefined;
|
|
34
|
+
}
|
|
35
|
+
export async function actionlintCheck(source, project) {
|
|
36
|
+
const prefix = project.path === "." ? "" : `${project.path}/`;
|
|
37
|
+
const scope = source.files
|
|
38
|
+
.filter((file) => workflowRoot(file) === project.path)
|
|
39
|
+
.map((file) => file.slice(prefix.length));
|
|
40
|
+
const check = {
|
|
41
|
+
id: "infrastructure.actionlint",
|
|
42
|
+
adapter: project.adapter,
|
|
43
|
+
project: project.path,
|
|
44
|
+
scope,
|
|
45
|
+
kind: "analysis",
|
|
46
|
+
parser: "actionlint-json",
|
|
47
|
+
commands: [],
|
|
48
|
+
reason: "Statically check every inventoried workflow with native completion accounting and explicit local dependencies.",
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
const file = `${prefix}checktrail.actionlint.json`;
|
|
52
|
+
if (!source.files.includes(file))
|
|
53
|
+
throw new Error("Prepare checktrail.actionlint.json with explicit runnerLabels and variables");
|
|
54
|
+
const config = actionlintConfigSchema.parse(JSON.parse(await readProjectFile(source.root, file)));
|
|
55
|
+
const invocation = JSON.stringify(actionlintInvocationSchema.parse({
|
|
56
|
+
config,
|
|
57
|
+
scope,
|
|
58
|
+
fingerprint: source.fingerprint,
|
|
59
|
+
}));
|
|
60
|
+
if (Buffer.byteLength(invocation) > 100 * 1024)
|
|
61
|
+
throw new Error("Actionlint invocation exceeds 100 KiB");
|
|
62
|
+
check.commands.push({
|
|
63
|
+
executable: process.execPath,
|
|
64
|
+
args: [
|
|
65
|
+
fileURLToPath(new URL("./actionlint-runner.js", import.meta.url)),
|
|
66
|
+
source.root,
|
|
67
|
+
invocation,
|
|
68
|
+
],
|
|
69
|
+
cwd: project.path,
|
|
70
|
+
env: { PATH: process.env.PATH ?? "" },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
check.unavailableReason =
|
|
75
|
+
error instanceof Error
|
|
76
|
+
? error.message
|
|
77
|
+
: "Workflow profile could not be prepared";
|
|
78
|
+
}
|
|
79
|
+
return check;
|
|
80
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ExternalAdapter } from "./external-adapter.js";
|
|
2
|
+
import type { Check, Inventory, Project } from "./types.js";
|
|
3
|
+
export declare const adapters: readonly [{
|
|
4
|
+
readonly id: "javascript";
|
|
5
|
+
readonly markers: readonly ["package.json"];
|
|
6
|
+
readonly checks: readonly ["javascript.node-test", "javascript.typescript", "javascript.typescript-build", "javascript.vue-tsc", "javascript.vue-router", "javascript.nuxt-runtime", "javascript.eslint", "javascript.vitest", "javascript.jest", "javascript.playwright"];
|
|
7
|
+
}, {
|
|
8
|
+
readonly id: "python";
|
|
9
|
+
readonly markers: readonly ["pyproject.toml", "setup.py", "requirements.txt"];
|
|
10
|
+
readonly checks: readonly ["python.unittest", "python.pytest", "python.ruff", "python.mypy", "python.fastapi-routes", "python.django-routes"];
|
|
11
|
+
}, {
|
|
12
|
+
readonly id: "go";
|
|
13
|
+
readonly markers: readonly ["go.mod"];
|
|
14
|
+
readonly checks: readonly ["go.format", "go.vet", "go.test", "go.test-race", "go.staticcheck", "go.golangci-lint"];
|
|
15
|
+
}, {
|
|
16
|
+
readonly id: "php";
|
|
17
|
+
readonly markers: readonly ["composer.json"];
|
|
18
|
+
readonly checks: readonly ["php.syntax", "php.phpstan", "php.phpunit", "php.pest", "php.pint", "php.laravel-runtime"];
|
|
19
|
+
}, {
|
|
20
|
+
readonly id: "rust";
|
|
21
|
+
readonly markers: readonly ["Cargo.toml"];
|
|
22
|
+
readonly checks: readonly ["rust.cargo-check"];
|
|
23
|
+
}, {
|
|
24
|
+
readonly id: "jvm";
|
|
25
|
+
readonly markers: readonly ["pom.xml", "build.gradle", "build.gradle.kts"];
|
|
26
|
+
readonly checks: readonly ["jvm.javac"];
|
|
27
|
+
}, {
|
|
28
|
+
readonly id: "dotnet";
|
|
29
|
+
readonly markers: readonly [];
|
|
30
|
+
readonly checks: readonly ["dotnet.csharp"];
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "ruby";
|
|
33
|
+
readonly markers: readonly ["Gemfile"];
|
|
34
|
+
readonly checks: readonly ["ruby.syntax"];
|
|
35
|
+
}, {
|
|
36
|
+
readonly id: "swift";
|
|
37
|
+
readonly markers: readonly ["Package.swift"];
|
|
38
|
+
readonly checks: readonly ["swift.syntax"];
|
|
39
|
+
}, {
|
|
40
|
+
readonly id: "cpp";
|
|
41
|
+
readonly markers: readonly ["CMakeLists.txt", "meson.build", "compile_commands.json"];
|
|
42
|
+
readonly checks: readonly ["cpp.clang-check"];
|
|
43
|
+
}, {
|
|
44
|
+
readonly id: "infrastructure";
|
|
45
|
+
readonly markers: readonly ["Chart.yaml", "kustomization.yaml"];
|
|
46
|
+
readonly checks: readonly ["infrastructure.actionlint"];
|
|
47
|
+
}];
|
|
48
|
+
export declare function discover(source: Inventory, external?: ExternalAdapter[]): Project[];
|
|
49
|
+
export declare function checksFor(source: Inventory, project: Project, requested?: readonly string[]): Promise<Check[]>;
|