@ivorycanvas/qamap 0.3.0
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/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/README.md +636 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +879 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +114 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +1 -0
- package/dist/context.js +126 -0
- package/dist/context.js.map +1 -0
- package/dist/doctor.d.ts +32 -0
- package/dist/doctor.js +200 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain-language.d.ts +25 -0
- package/dist/domain-language.js +655 -0
- package/dist/domain-language.js.map +1 -0
- package/dist/domains.d.ts +33 -0
- package/dist/domains.js +258 -0
- package/dist/domains.js.map +1 -0
- package/dist/e2e.d.ts +326 -0
- package/dist/e2e.js +6869 -0
- package/dist/e2e.js.map +1 -0
- package/dist/eval.d.ts +40 -0
- package/dist/eval.js +374 -0
- package/dist/eval.js.map +1 -0
- package/dist/flows.d.ts +32 -0
- package/dist/flows.js +246 -0
- package/dist/flows.js.map +1 -0
- package/dist/fs.d.ts +7 -0
- package/dist/fs.js +132 -0
- package/dist/fs.js.map +1 -0
- package/dist/github.d.ts +34 -0
- package/dist/github.js +183 -0
- package/dist/github.js.map +1 -0
- package/dist/history.d.ts +173 -0
- package/dist/history.js +247 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-suggestions.d.ts +67 -0
- package/dist/manifest-suggestions.js +673 -0
- package/dist/manifest-suggestions.js.map +1 -0
- package/dist/manifest.d.ts +212 -0
- package/dist/manifest.js +1607 -0
- package/dist/manifest.js.map +1 -0
- package/dist/qa.d.ts +53 -0
- package/dist/qa.js +361 -0
- package/dist/qa.js.map +1 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +164 -0
- package/dist/report.js.map +1 -0
- package/dist/review.d.ts +31 -0
- package/dist/review.js +383 -0
- package/dist/review.js.map +1 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.js +797 -0
- package/dist/scanner.js.map +1 -0
- package/dist/severity.d.ts +3 -0
- package/dist/severity.js +9 -0
- package/dist/severity.js.map +1 -0
- package/dist/test-evidence.d.ts +39 -0
- package/dist/test-evidence.js +303 -0
- package/dist/test-evidence.js.map +1 -0
- package/dist/test-plan.d.ts +35 -0
- package/dist/test-plan.js +573 -0
- package/dist/test-plan.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +27 -0
- package/dist/verify.js +244 -0
- package/dist/verify.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/docs/adoption.md +166 -0
- package/docs/agent-skill.md +94 -0
- package/docs/api-contracts.md +30 -0
- package/docs/assets/qamap-30s-demo.gif +0 -0
- package/docs/configuration.md +277 -0
- package/docs/e2e-output-examples.md +464 -0
- package/docs/ecosystem.md +41 -0
- package/docs/eval.md +52 -0
- package/docs/github-action.md +89 -0
- package/docs/manifest.md +350 -0
- package/docs/quickstart-demo.md +268 -0
- package/docs/release-validation.md +247 -0
- package/docs/releasing.md +112 -0
- package/docs/roadmap.md +67 -0
- package/docs/rules.md +28 -0
- package/docs/verify.md +44 -0
- package/package.json +80 -0
- package/schema/qamap-manifest.schema.json +323 -0
- package/schema/qamap.schema.json +58 -0
- package/skills/qamap-pr-qa/SKILL.md +81 -0
package/dist/flows.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import YAML from "yaml";
|
|
4
|
+
import { pathExists, toPosixPath } from "./fs.js";
|
|
5
|
+
export const defaultFlowManifestPath = ".qamap/flows.yml";
|
|
6
|
+
const flowManifestCandidates = [
|
|
7
|
+
".qamap/flows.yml",
|
|
8
|
+
".qamap/flows.yaml",
|
|
9
|
+
".qamap/flows.json",
|
|
10
|
+
];
|
|
11
|
+
const priorityWeights = {
|
|
12
|
+
critical: 3,
|
|
13
|
+
recommended: 2,
|
|
14
|
+
optional: 1,
|
|
15
|
+
};
|
|
16
|
+
export async function loadCoreFlowManifest(rootInput) {
|
|
17
|
+
const root = path.resolve(rootInput);
|
|
18
|
+
const manifestPath = await findFlowManifestPath(root);
|
|
19
|
+
if (!manifestPath) {
|
|
20
|
+
return { flows: [] };
|
|
21
|
+
}
|
|
22
|
+
const raw = await fs.readFile(manifestPath, "utf8");
|
|
23
|
+
const parsed = parseFlowManifest(raw, manifestPath);
|
|
24
|
+
return {
|
|
25
|
+
path: toPosixPath(path.relative(root, manifestPath)),
|
|
26
|
+
flows: normalizeCoreFlows(parsed, manifestPath),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function matchCoreFlows(manifest, changedFiles) {
|
|
30
|
+
if (manifest.flows.length === 0 || changedFiles.length === 0) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const files = changedFiles.map((file) => file.path);
|
|
34
|
+
return manifest.flows
|
|
35
|
+
.map((flow) => matchCoreFlow(flow, files))
|
|
36
|
+
.filter((flow) => Boolean(flow))
|
|
37
|
+
.sort((left, right) => {
|
|
38
|
+
const priorityDiff = priorityWeights[right.priority] - priorityWeights[left.priority];
|
|
39
|
+
if (priorityDiff !== 0) {
|
|
40
|
+
return priorityDiff;
|
|
41
|
+
}
|
|
42
|
+
return right.matchedFiles.length + right.matchedSignals.length - (left.matchedFiles.length + left.matchedSignals.length);
|
|
43
|
+
})
|
|
44
|
+
.slice(0, 10);
|
|
45
|
+
}
|
|
46
|
+
export async function writeDefaultCoreFlowManifest(rootInput, fileName = defaultFlowManifestPath, force = false) {
|
|
47
|
+
const root = path.resolve(rootInput);
|
|
48
|
+
const outputPath = path.resolve(root, fileName);
|
|
49
|
+
if (!force && (await pathExists(outputPath))) {
|
|
50
|
+
throw new Error(`Refusing to overwrite ${outputPath}. Pass --force to replace it.`);
|
|
51
|
+
}
|
|
52
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
53
|
+
await fs.writeFile(outputPath, defaultCoreFlowManifestText(), "utf8");
|
|
54
|
+
return outputPath;
|
|
55
|
+
}
|
|
56
|
+
function matchCoreFlow(flow, changedFiles) {
|
|
57
|
+
const matchedFiles = new Set();
|
|
58
|
+
const matchedSignals = new Set();
|
|
59
|
+
for (const file of changedFiles) {
|
|
60
|
+
if (flow.files.some((pattern) => matchesPathPattern(file, pattern))) {
|
|
61
|
+
matchedFiles.add(file);
|
|
62
|
+
matchedSignals.add("file pattern");
|
|
63
|
+
}
|
|
64
|
+
const fileTokens = pathTokens(file);
|
|
65
|
+
for (const domain of flow.domains) {
|
|
66
|
+
const normalizedDomain = normalizeToken(domain);
|
|
67
|
+
if (normalizedDomain && fileTokens.includes(normalizedDomain)) {
|
|
68
|
+
matchedFiles.add(file);
|
|
69
|
+
matchedSignals.add(`domain:${domain}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const route of flow.routes) {
|
|
73
|
+
const routeSignal = normalizeRoute(route);
|
|
74
|
+
if (routeSignal && normalizePathForMatch(file).includes(routeSignal)) {
|
|
75
|
+
matchedFiles.add(file);
|
|
76
|
+
matchedSignals.add(`route:${route}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const tag of flow.tags) {
|
|
80
|
+
const normalizedTag = normalizeToken(tag);
|
|
81
|
+
if (normalizedTag && fileTokens.includes(normalizedTag)) {
|
|
82
|
+
matchedFiles.add(file);
|
|
83
|
+
matchedSignals.add(`tag:${tag}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (matchedFiles.size === 0) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const signals = [...matchedSignals];
|
|
91
|
+
return {
|
|
92
|
+
id: flow.id,
|
|
93
|
+
name: flow.name,
|
|
94
|
+
priority: flow.priority,
|
|
95
|
+
reason: `Changed files match ${signals.slice(0, 4).join(", ")} for this declared core flow.`,
|
|
96
|
+
matchedFiles: [...matchedFiles].slice(0, 20),
|
|
97
|
+
matchedSignals: signals.slice(0, 12),
|
|
98
|
+
routes: flow.routes.slice(0, 10),
|
|
99
|
+
checks: flow.checks.slice(0, 10),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function parseFlowManifest(raw, manifestPath) {
|
|
103
|
+
try {
|
|
104
|
+
if (/\.json$/i.test(manifestPath)) {
|
|
105
|
+
return JSON.parse(raw);
|
|
106
|
+
}
|
|
107
|
+
return YAML.parse(raw);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
111
|
+
throw new Error(`Could not parse QAMap flow manifest at ${manifestPath}: ${message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function normalizeCoreFlows(value, manifestPath) {
|
|
115
|
+
const record = asRecord(value, `QAMap flow manifest must be an object: ${manifestPath}`);
|
|
116
|
+
const rawFlows = record.flows;
|
|
117
|
+
if (!Array.isArray(rawFlows)) {
|
|
118
|
+
throw new Error(`QAMap flow manifest flows must be an array: ${manifestPath}`);
|
|
119
|
+
}
|
|
120
|
+
return rawFlows.map((flow, index) => normalizeCoreFlow(flow, manifestPath, index));
|
|
121
|
+
}
|
|
122
|
+
function normalizeCoreFlow(value, manifestPath, index) {
|
|
123
|
+
const record = asRecord(value, `QAMap flow manifest flow at index ${index} must be an object: ${manifestPath}`);
|
|
124
|
+
const id = readRequiredString(record, "id", manifestPath, index);
|
|
125
|
+
const name = readOptionalString(record, "name") ?? readOptionalString(record, "title") ?? id;
|
|
126
|
+
const priority = normalizePriority(readOptionalString(record, "priority") ?? "recommended", manifestPath, index);
|
|
127
|
+
const files = readStringArray(record, "files");
|
|
128
|
+
const domains = readStringArray(record, "domains");
|
|
129
|
+
const routes = readStringArray(record, "routes");
|
|
130
|
+
const tags = readStringArray(record, "tags");
|
|
131
|
+
const checks = readStringArray(record, "checks");
|
|
132
|
+
if (files.length + domains.length + routes.length + tags.length === 0) {
|
|
133
|
+
throw new Error(`QAMap flow manifest flow ${id} must include at least one of files, domains, routes, or tags: ${manifestPath}`);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
id,
|
|
137
|
+
name,
|
|
138
|
+
description: readOptionalString(record, "description"),
|
|
139
|
+
owner: readOptionalString(record, "owner"),
|
|
140
|
+
priority,
|
|
141
|
+
domains,
|
|
142
|
+
files,
|
|
143
|
+
routes,
|
|
144
|
+
tags,
|
|
145
|
+
checks,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async function findFlowManifestPath(root) {
|
|
149
|
+
for (const candidate of flowManifestCandidates) {
|
|
150
|
+
const absolutePath = path.join(root, candidate);
|
|
151
|
+
if (await pathExists(absolutePath)) {
|
|
152
|
+
return absolutePath;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
function matchesPathPattern(file, pattern) {
|
|
158
|
+
const normalizedFile = normalizePathForMatch(file);
|
|
159
|
+
const normalizedPattern = normalizePathForMatch(pattern);
|
|
160
|
+
if (!normalizedPattern) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (!normalizedPattern.includes("*")) {
|
|
164
|
+
return normalizedFile === normalizedPattern || normalizedFile.startsWith(`${normalizedPattern}/`);
|
|
165
|
+
}
|
|
166
|
+
const regex = new RegExp(`^${normalizedPattern
|
|
167
|
+
.split("**")
|
|
168
|
+
.map((part) => part.split("*").map(escapeRegex).join("[^/]*"))
|
|
169
|
+
.join(".*")}$`);
|
|
170
|
+
return regex.test(normalizedFile);
|
|
171
|
+
}
|
|
172
|
+
function pathTokens(file) {
|
|
173
|
+
return file
|
|
174
|
+
.replace(/\.[^.\/]+$/g, "")
|
|
175
|
+
.split("/")
|
|
176
|
+
.flatMap((part) => part.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/))
|
|
177
|
+
.map(normalizeToken)
|
|
178
|
+
.filter(Boolean);
|
|
179
|
+
}
|
|
180
|
+
function normalizeRoute(route) {
|
|
181
|
+
return normalizePathForMatch(route).replace(/^\//, "");
|
|
182
|
+
}
|
|
183
|
+
function normalizePathForMatch(value) {
|
|
184
|
+
return toPosixPath(value).replace(/^\.\/+/, "").replace(/\/+$/g, "").toLowerCase();
|
|
185
|
+
}
|
|
186
|
+
function normalizeToken(value) {
|
|
187
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
188
|
+
}
|
|
189
|
+
function escapeRegex(value) {
|
|
190
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
191
|
+
}
|
|
192
|
+
function asRecord(value, message) {
|
|
193
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
194
|
+
throw new Error(message);
|
|
195
|
+
}
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
function readRequiredString(record, key, manifestPath, index) {
|
|
199
|
+
const value = readOptionalString(record, key);
|
|
200
|
+
if (!value) {
|
|
201
|
+
throw new Error(`QAMap flow manifest flow at index ${index} is missing ${key}: ${manifestPath}`);
|
|
202
|
+
}
|
|
203
|
+
return value;
|
|
204
|
+
}
|
|
205
|
+
function readOptionalString(record, key) {
|
|
206
|
+
const value = record[key];
|
|
207
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
208
|
+
}
|
|
209
|
+
function readStringArray(record, key) {
|
|
210
|
+
const value = record[key];
|
|
211
|
+
if (value === undefined) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
215
|
+
return [value.trim()];
|
|
216
|
+
}
|
|
217
|
+
if (!Array.isArray(value)) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
return [...new Set(value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean))];
|
|
221
|
+
}
|
|
222
|
+
function normalizePriority(value, manifestPath, index) {
|
|
223
|
+
if (value === "critical" || value === "recommended" || value === "optional") {
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
throw new Error(`QAMap flow manifest flow at index ${index} has invalid priority ${value}; expected critical, recommended, or optional: ${manifestPath}`);
|
|
227
|
+
}
|
|
228
|
+
function defaultCoreFlowManifestText() {
|
|
229
|
+
return [
|
|
230
|
+
"# Commit this file when your team wants QAMap to know the flows humans care about.",
|
|
231
|
+
"flows:",
|
|
232
|
+
" - id: primary-success-path",
|
|
233
|
+
" name: Primary success path",
|
|
234
|
+
" priority: critical",
|
|
235
|
+
" domains: []",
|
|
236
|
+
" files:",
|
|
237
|
+
" - src/**",
|
|
238
|
+
" routes: []",
|
|
239
|
+
" tags: []",
|
|
240
|
+
" checks:",
|
|
241
|
+
" - Verify the happy path from entry point to completion.",
|
|
242
|
+
" - Verify one realistic failure, empty, or blocked state.",
|
|
243
|
+
"",
|
|
244
|
+
].join("\n");
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=flows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.js","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":"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"}
|
package/dist/fs.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProjectFile } from "./types.js";
|
|
2
|
+
export declare function toPosixPath(value: string): string;
|
|
3
|
+
export declare function pathExists(value: string): Promise<boolean>;
|
|
4
|
+
export declare function collectProjectFiles(root: string, maxFiles: number): Promise<ProjectFile[]>;
|
|
5
|
+
export declare function shouldReadTextFile(relativePath: string, size: number): boolean;
|
|
6
|
+
export declare function getFile(files: ProjectFile[], relativePath: string): ProjectFile | undefined;
|
|
7
|
+
export declare function getFilesUnder(files: ProjectFile[], directory: string): ProjectFile[];
|
package/dist/fs.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const ignoredDirectories = new Set([
|
|
4
|
+
".git",
|
|
5
|
+
".hg",
|
|
6
|
+
".svn",
|
|
7
|
+
"node_modules",
|
|
8
|
+
"dist",
|
|
9
|
+
"build",
|
|
10
|
+
"coverage",
|
|
11
|
+
".next",
|
|
12
|
+
".nuxt",
|
|
13
|
+
".turbo",
|
|
14
|
+
".cache",
|
|
15
|
+
"vendor",
|
|
16
|
+
]);
|
|
17
|
+
const textExtensions = new Set([
|
|
18
|
+
".bash",
|
|
19
|
+
".cjs",
|
|
20
|
+
".conf",
|
|
21
|
+
".cts",
|
|
22
|
+
".env",
|
|
23
|
+
".go",
|
|
24
|
+
".java",
|
|
25
|
+
".js",
|
|
26
|
+
".json",
|
|
27
|
+
".jsx",
|
|
28
|
+
".kt",
|
|
29
|
+
".md",
|
|
30
|
+
".mdc",
|
|
31
|
+
".mjs",
|
|
32
|
+
".mts",
|
|
33
|
+
".php",
|
|
34
|
+
".ps1",
|
|
35
|
+
".py",
|
|
36
|
+
".rb",
|
|
37
|
+
".rs",
|
|
38
|
+
".sh",
|
|
39
|
+
".swift",
|
|
40
|
+
".toml",
|
|
41
|
+
".ts",
|
|
42
|
+
".tsx",
|
|
43
|
+
".txt",
|
|
44
|
+
".yaml",
|
|
45
|
+
".yml",
|
|
46
|
+
".zsh",
|
|
47
|
+
]);
|
|
48
|
+
const textBasenames = new Set([
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"CLAUDE.md",
|
|
51
|
+
"GEMINI.md",
|
|
52
|
+
"LICENSE",
|
|
53
|
+
"SECURITY.md",
|
|
54
|
+
"CONTRIBUTING.md",
|
|
55
|
+
"CODE_OF_CONDUCT.md",
|
|
56
|
+
"Dockerfile",
|
|
57
|
+
"Makefile",
|
|
58
|
+
]);
|
|
59
|
+
const maxReadableBytes = 256 * 1024;
|
|
60
|
+
export function toPosixPath(value) {
|
|
61
|
+
return value.split(path.sep).join("/");
|
|
62
|
+
}
|
|
63
|
+
export async function pathExists(value) {
|
|
64
|
+
try {
|
|
65
|
+
await fs.access(value);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export async function collectProjectFiles(root, maxFiles) {
|
|
73
|
+
const files = [];
|
|
74
|
+
const normalizedRoot = path.resolve(root);
|
|
75
|
+
async function walk(directory) {
|
|
76
|
+
if (files.length >= maxFiles) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
80
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (files.length >= maxFiles) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (entry.isDirectory() && ignoredDirectories.has(entry.name)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const absolutePath = path.join(directory, entry.name);
|
|
89
|
+
const relativePath = toPosixPath(path.relative(normalizedRoot, absolutePath));
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
await walk(absolutePath);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (!entry.isFile()) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const stat = await fs.stat(absolutePath);
|
|
98
|
+
const file = {
|
|
99
|
+
path: relativePath,
|
|
100
|
+
absolutePath,
|
|
101
|
+
size: stat.size,
|
|
102
|
+
};
|
|
103
|
+
if (shouldReadTextFile(relativePath, stat.size)) {
|
|
104
|
+
file.text = await fs.readFile(absolutePath, "utf8");
|
|
105
|
+
}
|
|
106
|
+
files.push(file);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
await walk(normalizedRoot);
|
|
110
|
+
return files;
|
|
111
|
+
}
|
|
112
|
+
export function shouldReadTextFile(relativePath, size) {
|
|
113
|
+
if (size > maxReadableBytes) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const basename = path.basename(relativePath);
|
|
117
|
+
if (textBasenames.has(basename)) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (basename.startsWith(".env") && basename !== ".env.example") {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return textExtensions.has(path.extname(relativePath));
|
|
124
|
+
}
|
|
125
|
+
export function getFile(files, relativePath) {
|
|
126
|
+
return files.find((file) => file.path === relativePath);
|
|
127
|
+
}
|
|
128
|
+
export function getFilesUnder(files, directory) {
|
|
129
|
+
const prefix = directory.endsWith("/") ? directory : `${directory}/`;
|
|
130
|
+
return files.filter((file) => file.path.startsWith(prefix));
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=fs.js.map
|
package/dist/fs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,MAAM;IACN,KAAK;IACL,MAAM;IACN,cAAc;IACd,MAAM;IACN,OAAO;IACP,UAAU;IACV,OAAO;IACP,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,OAAO;IACP,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,OAAO;IACP,KAAK;IACL,OAAO;IACP,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,QAAQ;IACR,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,WAAW;IACX,WAAW;IACX,WAAW;IACX,SAAS;IACT,aAAa;IACb,iBAAiB;IACjB,oBAAoB;IACpB,YAAY;IACZ,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,GAAG,GAAG,IAAI,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAa;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAAgB;IACtE,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C,KAAK,UAAU,IAAI,CAAC,SAAiB;QACnC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAEnE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC7B,OAAO;YACT,CAAC;YAED,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9D,SAAS;YACX,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACtD,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC;YAE9E,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,SAAS;YACX,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACpB,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzC,MAAM,IAAI,GAAgB;gBACxB,IAAI,EAAE,YAAY;gBAClB,YAAY;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC;YAEF,IAAI,kBAAkB,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,IAAI,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACtD,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,YAAoB,EAAE,IAAY;IACnE,IAAI,IAAI,GAAG,gBAAgB,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC7C,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;QAC/D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAoB,EAAE,YAAoB;IAChE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,SAAiB;IACnE,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,CAAC;IACrE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9D,CAAC"}
|
package/dist/github.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ScanOptions, Severity } from "./types.js";
|
|
2
|
+
export type GitHubActionMode = "auto" | "scan" | "review";
|
|
3
|
+
export interface GitHubActionOptions {
|
|
4
|
+
mode?: GitHubActionMode;
|
|
5
|
+
base?: string;
|
|
6
|
+
head?: string;
|
|
7
|
+
scanOptions?: ScanOptions;
|
|
8
|
+
failOn?: Severity;
|
|
9
|
+
reportFile?: string;
|
|
10
|
+
commentFile?: string;
|
|
11
|
+
annotations?: boolean;
|
|
12
|
+
stepSummary?: boolean;
|
|
13
|
+
stepSummaryPath?: string;
|
|
14
|
+
testPlan?: boolean;
|
|
15
|
+
testPlanFile?: string;
|
|
16
|
+
evaluation?: boolean;
|
|
17
|
+
evalFile?: string;
|
|
18
|
+
prBody?: string;
|
|
19
|
+
prBodyFile?: string;
|
|
20
|
+
includeWorkingTree?: boolean;
|
|
21
|
+
validationCommands?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface GitHubActionResult {
|
|
24
|
+
mode: Exclude<GitHubActionMode, "auto">;
|
|
25
|
+
reportFile: string;
|
|
26
|
+
commentFile: string;
|
|
27
|
+
testPlanFile?: string;
|
|
28
|
+
evalFile?: string;
|
|
29
|
+
findingCount: number;
|
|
30
|
+
failedFindingCount: number;
|
|
31
|
+
exitCode: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function runGitHubAction(rootInput: string, options?: GitHubActionOptions): Promise<GitHubActionResult>;
|
|
34
|
+
export declare function githubCommentMarker(): string;
|
package/dist/github.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { evaluateChangeReadiness, formatMarkdownEvalReport } from "./eval.js";
|
|
4
|
+
import { formatMarkdownReport } from "./report.js";
|
|
5
|
+
import { formatMarkdownReviewReport, reviewProject } from "./review.js";
|
|
6
|
+
import { scanProject } from "./scanner.js";
|
|
7
|
+
import { isAtLeastSeverity } from "./severity.js";
|
|
8
|
+
import { formatMarkdownTestPlan, generateTestPlan } from "./test-plan.js";
|
|
9
|
+
const annotationLimit = 50;
|
|
10
|
+
const commentMarker = "<!-- qamap-pr-comment -->";
|
|
11
|
+
export async function runGitHubAction(rootInput, options = {}) {
|
|
12
|
+
const mode = resolveMode(options.mode);
|
|
13
|
+
const reportFile = path.resolve(options.reportFile ?? "qamap-report.md");
|
|
14
|
+
const commentFile = path.resolve(options.commentFile ?? "qamap-pr-comment.md");
|
|
15
|
+
const testPlanFile = options.testPlan ? path.resolve(options.testPlanFile ?? "qamap-test-plan.md") : undefined;
|
|
16
|
+
const evalFile = options.evaluation ? path.resolve(options.evalFile ?? "qamap-eval.md") : undefined;
|
|
17
|
+
const result = mode === "review"
|
|
18
|
+
? await runReviewAction(rootInput, options)
|
|
19
|
+
: await runScanAction(rootInput, options);
|
|
20
|
+
const testPlanMarkdown = options.testPlan
|
|
21
|
+
? formatMarkdownTestPlan(await generateTestPlan(rootInput, {
|
|
22
|
+
base: options.base,
|
|
23
|
+
head: options.head,
|
|
24
|
+
workspaceRoot: options.scanOptions?.workspaceRoot,
|
|
25
|
+
includeWorkingTree: options.includeWorkingTree,
|
|
26
|
+
validationCommands: options.validationCommands,
|
|
27
|
+
}))
|
|
28
|
+
: undefined;
|
|
29
|
+
const evalMarkdown = options.evaluation
|
|
30
|
+
? formatMarkdownEvalReport(await evaluateChangeReadiness(rootInput, {
|
|
31
|
+
base: options.base,
|
|
32
|
+
head: options.head,
|
|
33
|
+
workspaceRoot: options.scanOptions?.workspaceRoot,
|
|
34
|
+
includeWorkingTree: options.includeWorkingTree,
|
|
35
|
+
prBody: options.prBody ?? (await readPullRequestBody()),
|
|
36
|
+
prBodyFile: options.prBodyFile,
|
|
37
|
+
validationCommands: options.validationCommands,
|
|
38
|
+
}))
|
|
39
|
+
: undefined;
|
|
40
|
+
const markdown = [result.markdown, testPlanMarkdown, evalMarkdown]
|
|
41
|
+
.filter((section) => Boolean(section))
|
|
42
|
+
.map((section) => section.trim())
|
|
43
|
+
.join("\n\n---\n\n")
|
|
44
|
+
.concat("\n");
|
|
45
|
+
await fs.writeFile(reportFile, markdown, "utf8");
|
|
46
|
+
await fs.writeFile(commentFile, buildCommentBody(markdown), "utf8");
|
|
47
|
+
if (testPlanFile && testPlanMarkdown) {
|
|
48
|
+
await fs.writeFile(testPlanFile, testPlanMarkdown, "utf8");
|
|
49
|
+
}
|
|
50
|
+
if (evalFile && evalMarkdown) {
|
|
51
|
+
await fs.writeFile(evalFile, evalMarkdown, "utf8");
|
|
52
|
+
}
|
|
53
|
+
if (options.stepSummary !== false) {
|
|
54
|
+
await appendStepSummary(markdown, options.stepSummaryPath ?? process.env.GITHUB_STEP_SUMMARY);
|
|
55
|
+
}
|
|
56
|
+
if (options.annotations !== false) {
|
|
57
|
+
writeAnnotations(result.annotations);
|
|
58
|
+
}
|
|
59
|
+
console.log(`QAMap ${mode} report: ${reportFile}`);
|
|
60
|
+
console.log(`QAMap PR comment body: ${commentFile}`);
|
|
61
|
+
if (testPlanFile) {
|
|
62
|
+
console.log(`QAMap test plan: ${testPlanFile}`);
|
|
63
|
+
}
|
|
64
|
+
if (evalFile) {
|
|
65
|
+
console.log(`QAMap eval: ${evalFile}`);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mode,
|
|
69
|
+
reportFile,
|
|
70
|
+
commentFile,
|
|
71
|
+
testPlanFile,
|
|
72
|
+
evalFile,
|
|
73
|
+
findingCount: result.findings.length,
|
|
74
|
+
failedFindingCount: result.failedFindings.length,
|
|
75
|
+
exitCode: result.failedFindings.length > 0 ? 1 : 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function runScanAction(rootInput, options) {
|
|
79
|
+
const result = await scanProject(rootInput, options.scanOptions);
|
|
80
|
+
const failedFindings = options.failOn
|
|
81
|
+
? result.findings.filter((finding) => isAtLeastSeverity(finding.severity, options.failOn))
|
|
82
|
+
: [];
|
|
83
|
+
return {
|
|
84
|
+
markdown: formatMarkdownReport(result),
|
|
85
|
+
findings: result.findings,
|
|
86
|
+
failedFindings,
|
|
87
|
+
annotations: result.findings.map((finding) => ({
|
|
88
|
+
finding,
|
|
89
|
+
failed: failedFindings.includes(finding),
|
|
90
|
+
})),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function runReviewAction(rootInput, options) {
|
|
94
|
+
const result = await reviewProject(rootInput, {
|
|
95
|
+
base: options.base,
|
|
96
|
+
head: options.head,
|
|
97
|
+
scanOptions: options.scanOptions,
|
|
98
|
+
});
|
|
99
|
+
const findings = [...result.newFindings, ...result.changedRiskyFindings];
|
|
100
|
+
const failedFindings = options.failOn
|
|
101
|
+
? findings.filter((finding) => isAtLeastSeverity(finding.severity, options.failOn))
|
|
102
|
+
: [];
|
|
103
|
+
return {
|
|
104
|
+
markdown: formatMarkdownReviewReport(result),
|
|
105
|
+
findings,
|
|
106
|
+
failedFindings,
|
|
107
|
+
annotations: [
|
|
108
|
+
...result.newFindings.map((finding) => ({
|
|
109
|
+
finding,
|
|
110
|
+
failed: failedFindings.includes(finding),
|
|
111
|
+
})),
|
|
112
|
+
...result.changedRiskyFindings.map((finding) => ({
|
|
113
|
+
finding,
|
|
114
|
+
failed: failedFindings.includes(finding),
|
|
115
|
+
prefix: "Changed risky file: ",
|
|
116
|
+
})),
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function resolveMode(mode) {
|
|
121
|
+
if (mode === "scan" || mode === "review") {
|
|
122
|
+
return mode;
|
|
123
|
+
}
|
|
124
|
+
return process.env.GITHUB_BASE_REF || process.env.GITHUB_HEAD_REF ? "review" : "scan";
|
|
125
|
+
}
|
|
126
|
+
function buildCommentBody(markdown) {
|
|
127
|
+
return [
|
|
128
|
+
commentMarker,
|
|
129
|
+
"## QAMap",
|
|
130
|
+
"",
|
|
131
|
+
markdown.trim(),
|
|
132
|
+
"",
|
|
133
|
+
"_Generated by QAMap._",
|
|
134
|
+
"",
|
|
135
|
+
].join("\n");
|
|
136
|
+
}
|
|
137
|
+
async function appendStepSummary(markdown, stepSummaryPath) {
|
|
138
|
+
if (!stepSummaryPath) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
await fs.appendFile(stepSummaryPath, `${markdown.trim()}\n`, "utf8");
|
|
142
|
+
}
|
|
143
|
+
function writeAnnotations(annotations) {
|
|
144
|
+
for (const annotation of annotations.slice(0, annotationLimit)) {
|
|
145
|
+
console.log(formatAnnotation(annotation));
|
|
146
|
+
}
|
|
147
|
+
if (annotations.length > annotationLimit) {
|
|
148
|
+
console.log(`::notice title=QAMap::${annotations.length - annotationLimit} additional QAMap findings were omitted from annotations.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function formatAnnotation(annotation) {
|
|
152
|
+
const finding = annotation.finding;
|
|
153
|
+
const level = annotation.failed ? "error" : finding.severity === "info" ? "notice" : "warning";
|
|
154
|
+
const properties = [`title=${escapeAnnotationProperty(`${finding.id} ${finding.title}`)}`];
|
|
155
|
+
if (finding.file) {
|
|
156
|
+
properties.push(`file=${escapeAnnotationProperty(finding.file)}`);
|
|
157
|
+
}
|
|
158
|
+
const message = `${annotation.prefix ?? ""}${finding.message} Fix: ${finding.recommendation}`;
|
|
159
|
+
return `::${level} ${properties.join(",")}::${escapeAnnotationMessage(message)}`;
|
|
160
|
+
}
|
|
161
|
+
function escapeAnnotationMessage(value) {
|
|
162
|
+
return value.replaceAll("%", "%25").replaceAll("\r", "%0D").replaceAll("\n", "%0A");
|
|
163
|
+
}
|
|
164
|
+
function escapeAnnotationProperty(value) {
|
|
165
|
+
return escapeAnnotationMessage(value).replaceAll(":", "%3A").replaceAll(",", "%2C");
|
|
166
|
+
}
|
|
167
|
+
async function readPullRequestBody() {
|
|
168
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
169
|
+
if (!eventPath) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const event = JSON.parse(await fs.readFile(eventPath, "utf8"));
|
|
174
|
+
return event.pull_request?.body ?? undefined;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function githubCommentMarker() {
|
|
181
|
+
return commentMarker;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"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"}
|