@sweny-ai/core 0.1.46 → 0.1.48
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.
|
@@ -26,9 +26,10 @@ function isHistorical(filePath) {
|
|
|
26
26
|
* Use git grep for fast, indexed search across the repo.
|
|
27
27
|
* Returns array of "file:line:content" strings.
|
|
28
28
|
*/
|
|
29
|
-
function gitGrep(pattern,
|
|
29
|
+
function gitGrep(pattern, paths = ["."]) {
|
|
30
30
|
try {
|
|
31
|
-
const
|
|
31
|
+
const pathArgs = paths.map((p) => `"${p}"`).join(" ");
|
|
32
|
+
const result = execSync(`git grep -n "${pattern}" -- ${pathArgs}`, {
|
|
32
33
|
cwd: ROOT,
|
|
33
34
|
encoding: "utf-8",
|
|
34
35
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -196,4 +197,116 @@ describe("docs action code blocks", () => {
|
|
|
196
197
|
allIssues.map((i) => ` ${i}`).join("\n"));
|
|
197
198
|
}
|
|
198
199
|
});
|
|
200
|
+
it("every swenyai/sweny@v5 code block has a workflow: input", () => {
|
|
201
|
+
const issues = [];
|
|
202
|
+
for (const f of ACTION_DOC_FILES) {
|
|
203
|
+
const filePath = resolve(ROOT, f);
|
|
204
|
+
if (!existsSync(filePath))
|
|
205
|
+
continue;
|
|
206
|
+
const content = readFileSync(filePath, "utf-8");
|
|
207
|
+
const blocks = content.match(/```ya?ml[\s\S]*?```/g) || [];
|
|
208
|
+
for (const block of blocks) {
|
|
209
|
+
if (!block.includes(`swenyai/sweny@${CURRENT_ACTION_VERSION}`))
|
|
210
|
+
continue;
|
|
211
|
+
if (!block.includes("workflow:")) {
|
|
212
|
+
issues.push(`${relative(ROOT, filePath)}: code block uses swenyai/sweny@${CURRENT_ACTION_VERSION} without workflow: input`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (issues.length > 0) {
|
|
217
|
+
expect.fail(`The generic runner requires a workflow: input.\n` + issues.map((i) => ` ${i}`).join("\n"));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
// ─── 5. Internal doc links resolve ──────────────────────────────────
|
|
222
|
+
describe("docs internal links", () => {
|
|
223
|
+
const DOCS_ROOT = resolve(ROOT, "packages/web/src/content/docs");
|
|
224
|
+
/**
|
|
225
|
+
* Extract markdown links like [text](/path/) and verify the target exists.
|
|
226
|
+
* Only checks internal absolute links (starting with /).
|
|
227
|
+
*/
|
|
228
|
+
function findBrokenLinks(filePath) {
|
|
229
|
+
if (!existsSync(filePath))
|
|
230
|
+
return [];
|
|
231
|
+
const content = readFileSync(filePath, "utf-8");
|
|
232
|
+
const broken = [];
|
|
233
|
+
// Match [text](/path/) and [text](/path/#anchor) but skip external URLs
|
|
234
|
+
const linkPattern = /\[([^\]]*)\]\(\/([^)#]*?)\/?\)/g;
|
|
235
|
+
let match;
|
|
236
|
+
while ((match = linkPattern.exec(content)) !== null) {
|
|
237
|
+
const linkPath = match[2];
|
|
238
|
+
// Map /action/ → action/index.md, /action/inputs/ → action/inputs.md
|
|
239
|
+
const candidates = [
|
|
240
|
+
resolve(DOCS_ROOT, linkPath, "index.md"),
|
|
241
|
+
resolve(DOCS_ROOT, linkPath, "index.mdx"),
|
|
242
|
+
resolve(DOCS_ROOT, `${linkPath}.md`),
|
|
243
|
+
resolve(DOCS_ROOT, `${linkPath}.mdx`),
|
|
244
|
+
];
|
|
245
|
+
if (!candidates.some((c) => existsSync(c))) {
|
|
246
|
+
broken.push(`${relative(ROOT, filePath)}: broken link [${match[1]}](/${linkPath}/)`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return broken;
|
|
250
|
+
}
|
|
251
|
+
const KEY_DOCS = [
|
|
252
|
+
"packages/web/src/content/docs/action/index.md",
|
|
253
|
+
"packages/web/src/content/docs/action/inputs.md",
|
|
254
|
+
"packages/web/src/content/docs/action/examples.md",
|
|
255
|
+
"packages/web/src/content/docs/action/scheduling.md",
|
|
256
|
+
"packages/web/src/content/docs/action/service-map.md",
|
|
257
|
+
"packages/web/src/content/docs/index.mdx",
|
|
258
|
+
"packages/web/src/content/docs/getting-started/quick-start.md",
|
|
259
|
+
];
|
|
260
|
+
it("key action docs have no broken internal links", () => {
|
|
261
|
+
const allBroken = KEY_DOCS.flatMap((f) => findBrokenLinks(resolve(ROOT, f)));
|
|
262
|
+
if (allBroken.length > 0) {
|
|
263
|
+
expect.fail(`Found ${allBroken.length} broken link(s):\n` + allBroken.map((b) => ` ${b}`).join("\n"));
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
// ─── 6. Three-action architecture documented ────────────────────────
|
|
268
|
+
describe("three-action architecture", () => {
|
|
269
|
+
it("action/index.md documents all three actions", () => {
|
|
270
|
+
const content = readFileSync(resolve(ROOT, "packages/web/src/content/docs/action/index.md"), "utf-8");
|
|
271
|
+
expect(content).toContain("swenyai/triage@v1");
|
|
272
|
+
expect(content).toContain("swenyai/e2e@v1");
|
|
273
|
+
expect(content).toContain(`swenyai/sweny@${CURRENT_ACTION_VERSION}`);
|
|
274
|
+
});
|
|
275
|
+
it("action/inputs.md has sections for all three actions", () => {
|
|
276
|
+
const content = readFileSync(resolve(ROOT, "packages/web/src/content/docs/action/inputs.md"), "utf-8");
|
|
277
|
+
expect(content).toMatch(/## Generic runner inputs/);
|
|
278
|
+
expect(content).toMatch(/## Triage action inputs/);
|
|
279
|
+
expect(content).toMatch(/## E2E action inputs/);
|
|
280
|
+
});
|
|
281
|
+
it("README Actions table lists all three actions", () => {
|
|
282
|
+
const readme = readFileSync(resolve(ROOT, "README.md"), "utf-8");
|
|
283
|
+
expect(readme).toContain("swenyai/triage@v1");
|
|
284
|
+
expect(readme).toContain("swenyai/e2e@v1");
|
|
285
|
+
expect(readme).toContain(`swenyai/sweny@${CURRENT_ACTION_VERSION}`);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
// ─── 7. Input naming consistency ────────────────────────────────────
|
|
289
|
+
describe("input naming consistency", () => {
|
|
290
|
+
const DOCS_DIR = resolve(ROOT, "packages/web/src/content/docs");
|
|
291
|
+
it("docs use issue-override, not linear-issue", () => {
|
|
292
|
+
const matches = gitGrep("linear-issue", ["packages/web/src/content/docs/"]);
|
|
293
|
+
if (matches.length > 0) {
|
|
294
|
+
expect.fail(`Found "linear-issue" in docs — the triage action input is "issue-override":\n` +
|
|
295
|
+
matches.map((m) => ` ${m.split(":").slice(0, 2).join(":")}`).join("\n"));
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
it("docs use mcp-servers-json, not bare mcp-servers as an input name", () => {
|
|
299
|
+
// Match `mcp-servers:` or `mcp-servers |` in YAML (the input name), but NOT
|
|
300
|
+
// `mcp-servers-json` or prose text mentioning "MCP servers" as a concept
|
|
301
|
+
const matches = gitGrep("mcp-servers[^-]", ["packages/web/src/content/docs/"]);
|
|
302
|
+
const inputRefs = matches.filter((m) => {
|
|
303
|
+
const content = m.split(":").slice(2).join(":");
|
|
304
|
+
// Only flag YAML input usage, not prose text
|
|
305
|
+
return /^\s*(mcp-servers:|mcp-servers\s*\|)/.test(content);
|
|
306
|
+
});
|
|
307
|
+
if (inputRefs.length > 0) {
|
|
308
|
+
expect.fail(`Found bare "mcp-servers" as an input — the triage action input is "mcp-servers-json":\n` +
|
|
309
|
+
inputRefs.map((m) => ` ${m.split(":").slice(0, 2).join(":")}`).join("\n"));
|
|
310
|
+
}
|
|
311
|
+
});
|
|
199
312
|
});
|