@htekdev/actions-debugger 1.0.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/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/db/loader.d.ts +12 -0
- package/dist/db/loader.d.ts.map +1 -0
- package/dist/db/loader.js +76 -0
- package/dist/db/loader.js.map +1 -0
- package/dist/db/search.d.ts +19 -0
- package/dist/db/search.d.ts.map +1 -0
- package/dist/db/search.js +123 -0
- package/dist/db/search.js.map +1 -0
- package/dist/db/types.d.ts +61 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +5 -0
- package/dist/db/types.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +164 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/diagnose-workflow.d.ts +13 -0
- package/dist/tools/diagnose-workflow.d.ts.map +1 -0
- package/dist/tools/diagnose-workflow.js +53 -0
- package/dist/tools/diagnose-workflow.js.map +1 -0
- package/dist/tools/list-categories.d.ts +13 -0
- package/dist/tools/list-categories.d.ts.map +1 -0
- package/dist/tools/list-categories.js +51 -0
- package/dist/tools/list-categories.js.map +1 -0
- package/dist/tools/lookup-error.d.ts +18 -0
- package/dist/tools/lookup-error.d.ts.map +1 -0
- package/dist/tools/lookup-error.js +67 -0
- package/dist/tools/lookup-error.js.map +1 -0
- package/dist/tools/search-errors.d.ts +17 -0
- package/dist/tools/search-errors.d.ts.map +1 -0
- package/dist/tools/search-errors.js +33 -0
- package/dist/tools/search-errors.js.map +1 -0
- package/dist/tools/suggest-fix.d.ts +13 -0
- package/dist/tools/suggest-fix.d.ts.map +1 -0
- package/dist/tools/suggest-fix.js +62 -0
- package/dist/tools/suggest-fix.js.map +1 -0
- package/dist/utils/pattern-matcher.d.ts +15 -0
- package/dist/utils/pattern-matcher.d.ts.map +1 -0
- package/dist/utils/pattern-matcher.js +50 -0
- package/dist/utils/pattern-matcher.js.map +1 -0
- package/dist/utils/yaml-parser.d.ts +10 -0
- package/dist/utils/yaml-parser.d.ts.map +1 -0
- package/dist/utils/yaml-parser.js +142 -0
- package/dist/utils/yaml-parser.js.map +1 -0
- package/errors/_schema.json +89 -0
- package/errors/caching-artifacts/cache-miss.yml +56 -0
- package/errors/caching-artifacts/upload-artifact-v4-breaking.yml +67 -0
- package/errors/concurrency-timing/jobs-cancelled-unexpectedly.yml +60 -0
- package/errors/known-unsolved/no-step-retry.yml +53 -0
- package/errors/permissions-auth/github-token-403.yml +64 -0
- package/errors/permissions-auth/oidc-aws-failure.yml +85 -0
- package/errors/runner-environment/disk-space.yml +57 -0
- package/errors/runner-environment/node-runtime-deprecation.yml +56 -0
- package/errors/silent-failures/github-token-no-trigger.yml +57 -0
- package/errors/silent-failures/scheduled-workflow-disabled.yml +59 -0
- package/errors/triggers/cron-schedule-late.yml +59 -0
- package/errors/triggers/workflow-not-triggering.yml +60 -0
- package/errors/yaml-syntax/if-always-true.yml +52 -0
- package/errors/yaml-syntax/secrets-in-if.yml +55 -0
- package/errors/yaml-syntax/unexpected-yaml-key.yml +69 -0
- package/package.json +67 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow YAML analysis utilities.
|
|
3
|
+
* Parses GitHub Actions workflow YAML and checks for common mistakes.
|
|
4
|
+
*/
|
|
5
|
+
import type { DiagnosticFinding } from "../db/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Analyze a workflow YAML string for common issues.
|
|
8
|
+
*/
|
|
9
|
+
export declare function analyzeWorkflow(workflowYaml: string): DiagnosticFinding[];
|
|
10
|
+
//# sourceMappingURL=yaml-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-parser.d.ts","sourceRoot":"","sources":["../../src/utils/yaml-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA4BxD;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAiJzE"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow YAML analysis utilities.
|
|
3
|
+
* Parses GitHub Actions workflow YAML and checks for common mistakes.
|
|
4
|
+
*/
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
|
+
/**
|
|
7
|
+
* Analyze a workflow YAML string for common issues.
|
|
8
|
+
*/
|
|
9
|
+
export function analyzeWorkflow(workflowYaml) {
|
|
10
|
+
const findings = [];
|
|
11
|
+
// Check for tab characters (before parsing — YAML rejects tabs)
|
|
12
|
+
if (workflowYaml.includes("\t")) {
|
|
13
|
+
findings.push({
|
|
14
|
+
severity: "critical",
|
|
15
|
+
message: "Tab characters detected — GitHub Actions requires spaces for indentation.",
|
|
16
|
+
fix: "Replace all tabs with spaces (2-space indentation recommended).",
|
|
17
|
+
fix_code: "# Use .editorconfig:\nindent_style = space\nindent_size = 2",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
let workflow;
|
|
21
|
+
try {
|
|
22
|
+
workflow = yaml.load(workflowYaml);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
findings.push({
|
|
26
|
+
severity: "critical",
|
|
27
|
+
message: `YAML parse error: ${err.message}`,
|
|
28
|
+
fix: "Fix the YAML syntax error before proceeding.",
|
|
29
|
+
});
|
|
30
|
+
return findings;
|
|
31
|
+
}
|
|
32
|
+
if (!workflow || typeof workflow !== "object") {
|
|
33
|
+
findings.push({
|
|
34
|
+
severity: "critical",
|
|
35
|
+
message: "Workflow is empty or not a valid YAML object.",
|
|
36
|
+
fix: "Ensure the file contains a valid GitHub Actions workflow.",
|
|
37
|
+
});
|
|
38
|
+
return findings;
|
|
39
|
+
}
|
|
40
|
+
// Check jobs
|
|
41
|
+
if (!workflow.jobs || Object.keys(workflow.jobs).length === 0) {
|
|
42
|
+
findings.push({
|
|
43
|
+
severity: "critical",
|
|
44
|
+
message: "No jobs defined in workflow.",
|
|
45
|
+
fix: "Add at least one job under the `jobs:` key.",
|
|
46
|
+
});
|
|
47
|
+
return findings;
|
|
48
|
+
}
|
|
49
|
+
const hasTopLevelPermissions = !!workflow.permissions;
|
|
50
|
+
for (const [jobName, job] of Object.entries(workflow.jobs)) {
|
|
51
|
+
// Missing runs-on
|
|
52
|
+
if (!job["runs-on"]) {
|
|
53
|
+
findings.push({
|
|
54
|
+
severity: "critical",
|
|
55
|
+
message: `Job '${jobName}' is missing 'runs-on'.`,
|
|
56
|
+
fix: "Add runs-on: ubuntu-latest (or another runner label).",
|
|
57
|
+
fix_code: `jobs:\n ${jobName}:\n runs-on: ubuntu-latest`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// Missing permissions
|
|
61
|
+
if (!hasTopLevelPermissions && !job.permissions) {
|
|
62
|
+
findings.push({
|
|
63
|
+
severity: "high",
|
|
64
|
+
message: `Job '${jobName}' has no 'permissions:' block — defaults are read-only since Feb 2023.`,
|
|
65
|
+
fix: "Add explicit permissions for the GITHUB_TOKEN.",
|
|
66
|
+
fix_code: `permissions:\n contents: read`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Missing timeout
|
|
70
|
+
if (!job["timeout-minutes"]) {
|
|
71
|
+
findings.push({
|
|
72
|
+
severity: "medium",
|
|
73
|
+
message: `Job '${jobName}' has no 'timeout-minutes' — default is 6 hours.`,
|
|
74
|
+
fix: "Add timeout-minutes to prevent runaway jobs.",
|
|
75
|
+
fix_code: `jobs:\n ${jobName}:\n timeout-minutes: 30`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Check steps
|
|
79
|
+
for (const step of job.steps ?? []) {
|
|
80
|
+
// Deprecated set-output
|
|
81
|
+
if (step.run?.includes("::set-output")) {
|
|
82
|
+
findings.push({
|
|
83
|
+
severity: "high",
|
|
84
|
+
message: `Step '${step.name ?? "unnamed"}' uses deprecated set-output command.`,
|
|
85
|
+
fix: "Use $GITHUB_OUTPUT instead.",
|
|
86
|
+
fix_code: 'echo "key=value" >> $GITHUB_OUTPUT',
|
|
87
|
+
relatedError: "runner-environment-005",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Deprecated set-env
|
|
91
|
+
if (step.run?.includes("::set-env")) {
|
|
92
|
+
findings.push({
|
|
93
|
+
severity: "high",
|
|
94
|
+
message: `Step '${step.name ?? "unnamed"}' uses deprecated set-env command.`,
|
|
95
|
+
fix: "Use $GITHUB_ENV instead.",
|
|
96
|
+
fix_code: 'echo "KEY=value" >> $GITHUB_ENV',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// Deprecated action versions
|
|
100
|
+
if (step.uses) {
|
|
101
|
+
// Node 16 actions
|
|
102
|
+
if (step.uses.match(/actions\/upload-artifact@v3/)) {
|
|
103
|
+
findings.push({
|
|
104
|
+
severity: "high",
|
|
105
|
+
message: `Step uses actions/upload-artifact@v3 — v4 is current and uses a different backend.`,
|
|
106
|
+
fix: "Upgrade to actions/upload-artifact@v4. Note: v3 and v4 artifacts are incompatible.",
|
|
107
|
+
relatedError: "caching-artifacts-004",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (step.uses.match(/actions\/download-artifact@v3/)) {
|
|
111
|
+
findings.push({
|
|
112
|
+
severity: "high",
|
|
113
|
+
message: `Step uses actions/download-artifact@v3 — v4 is current.`,
|
|
114
|
+
fix: "Upgrade to actions/download-artifact@v4. Ensure upload and download versions match.",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// if: with pipe scalar
|
|
119
|
+
if (step.if && typeof step.if === "string") {
|
|
120
|
+
if (step.if.trim().startsWith("|\n") || step.if.trim().startsWith("|")) {
|
|
121
|
+
findings.push({
|
|
122
|
+
severity: "medium",
|
|
123
|
+
message: `Step '${step.name ?? "unnamed"}' if: condition uses pipe scalar — will always be true.`,
|
|
124
|
+
fix: "Remove the pipe character. Write the condition on a single line.",
|
|
125
|
+
relatedError: "yaml-syntax-007",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Secrets in if conditions
|
|
129
|
+
if (step.if.includes("secrets.")) {
|
|
130
|
+
findings.push({
|
|
131
|
+
severity: "medium",
|
|
132
|
+
message: `Step '${step.name ?? "unnamed"}' uses secrets.* in if: condition — secrets context is not available in if:.`,
|
|
133
|
+
fix: "Pass the secret to an env var first, then check the env var in the if: condition.",
|
|
134
|
+
relatedError: "yaml-syntax-005",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return findings;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=yaml-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-parser.js","sourceRoot":"","sources":["../../src/utils/yaml-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,SAAS,CAAC;AA6B3B;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,QAAQ,GAAwB,EAAE,CAAC;IAEzC,gEAAgE;IAChE,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,2EAA2E;YACpF,GAAG,EAAE,iEAAiE;YACtE,QAAQ,EAAE,6DAA6D;SACxE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAa,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,qBAAsB,GAAa,CAAC,OAAO,EAAE;YACtD,GAAG,EAAE,8CAA8C;SACpD,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,+CAA+C;YACxD,GAAG,EAAE,2DAA2D;SACjE,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,aAAa;IACb,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,8BAA8B;YACvC,GAAG,EAAE,6CAA6C;SACnD,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,sBAAsB,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;IAEtD,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,kBAAkB;QAClB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,QAAQ,OAAO,yBAAyB;gBACjD,GAAG,EAAE,uDAAuD;gBAC5D,QAAQ,EAAE,YAAY,OAAO,+BAA+B;aAC7D,CAAC,CAAC;QACL,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAC,sBAAsB,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,QAAQ,OAAO,wEAAwE;gBAChG,GAAG,EAAE,gDAAgD;gBACrD,QAAQ,EAAE,gCAAgC;aAC3C,CAAC,CAAC;QACL,CAAC;QAED,kBAAkB;QAClB,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE,QAAQ,OAAO,kDAAkD;gBAC1E,GAAG,EAAE,8CAA8C;gBACnD,QAAQ,EAAE,YAAY,OAAO,4BAA4B;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,cAAc;QACd,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACnC,wBAAwB;YACxB,IAAI,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,uCAAuC;oBAC/E,GAAG,EAAE,6BAA6B;oBAClC,QAAQ,EAAE,oCAAoC;oBAC9C,YAAY,EAAE,wBAAwB;iBACvC,CAAC,CAAC;YACL,CAAC;YAED,qBAAqB;YACrB,IAAI,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACpC,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,oCAAoC;oBAC5E,GAAG,EAAE,0BAA0B;oBAC/B,QAAQ,EAAE,iCAAiC;iBAC5C,CAAC,CAAC;YACL,CAAC;YAED,6BAA6B;YAC7B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,kBAAkB;gBAClB,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,6BAA6B,CAAC,EAAE,CAAC;oBACnD,QAAQ,CAAC,IAAI,CAAC;wBACZ,QAAQ,EAAE,MAAM;wBAChB,OAAO,EAAE,oFAAoF;wBAC7F,GAAG,EAAE,oFAAoF;wBACzF,YAAY,EAAE,uBAAuB;qBACtC,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,+BAA+B,CAAC,EAAE,CAAC;oBACrD,QAAQ,CAAC,IAAI,CAAC;wBACZ,QAAQ,EAAE,MAAM;wBAChB,OAAO,EAAE,yDAAyD;wBAClE,GAAG,EAAE,qFAAqF;qBAC3F,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,uBAAuB;YACvB,IAAI,IAAI,CAAC,EAAE,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBAC3C,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvE,QAAQ,CAAC,IAAI,CAAC;wBACZ,QAAQ,EAAE,QAAQ;wBAClB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,yDAAyD;wBACjG,GAAG,EAAE,kEAAkE;wBACvE,YAAY,EAAE,iBAAiB;qBAChC,CAAC,CAAC;gBACL,CAAC;gBAED,2BAA2B;gBAC3B,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;oBACjC,QAAQ,CAAC,IAAI,CAAC;wBACZ,QAAQ,EAAE,QAAQ;wBAClB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,IAAI,SAAS,8EAA8E;wBACtH,GAAG,EAAE,mFAAmF;wBACxF,YAAY,EAAE,iBAAiB;qBAChC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Actions Debugger Error Entry",
|
|
4
|
+
"description": "Schema for GitHub Actions error database entries",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "title", "category", "severity", "patterns", "root_cause", "fix"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^[a-z-]+-\\d{3}$",
|
|
11
|
+
"description": "Unique ID: {category}-{number}"
|
|
12
|
+
},
|
|
13
|
+
"title": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Human-readable error title"
|
|
16
|
+
},
|
|
17
|
+
"category": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": [
|
|
20
|
+
"yaml-syntax",
|
|
21
|
+
"silent-failures",
|
|
22
|
+
"runner-environment",
|
|
23
|
+
"permissions-auth",
|
|
24
|
+
"caching-artifacts",
|
|
25
|
+
"triggers",
|
|
26
|
+
"concurrency-timing",
|
|
27
|
+
"known-unsolved"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"severity": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["error", "warning", "silent-failure", "limitation"]
|
|
33
|
+
},
|
|
34
|
+
"tags": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": { "type": "string" }
|
|
37
|
+
},
|
|
38
|
+
"patterns": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["regex"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"regex": { "type": "string" },
|
|
45
|
+
"flags": { "type": "string", "default": "i" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"error_messages": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": { "type": "string" },
|
|
52
|
+
"description": "Exact error message strings for full-text search"
|
|
53
|
+
},
|
|
54
|
+
"root_cause": { "type": "string" },
|
|
55
|
+
"fix": { "type": "string" },
|
|
56
|
+
"fix_code": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"language": { "type": "string" },
|
|
62
|
+
"label": { "type": "string" },
|
|
63
|
+
"code": { "type": "string" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"prevention": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"items": { "type": "string" }
|
|
70
|
+
},
|
|
71
|
+
"docs": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"items": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"url": { "type": "string", "format": "uri" },
|
|
77
|
+
"label": { "type": "string" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"source": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"properties": {
|
|
84
|
+
"article": { "type": "string", "format": "uri" },
|
|
85
|
+
"section": { "type": "string" }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
id: caching-artifacts-001
|
|
2
|
+
title: "Cache Miss Despite Recent Save"
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- cache
|
|
7
|
+
- artifacts
|
|
8
|
+
- restore-keys
|
|
9
|
+
- branch-scope
|
|
10
|
+
- performance
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Cache not found for input keys: .*"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "Failed to restore: .*"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "Received 429 response while trying to reserve cache"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Cache not found for input keys"
|
|
20
|
+
- "Received 429 response while trying to reserve cache"
|
|
21
|
+
root_cause: |
|
|
22
|
+
GitHub Actions caches are scoped by key, version, and branch. A cache saved on one branch
|
|
23
|
+
is not always visible from another branch, and a small key mismatch can force a full miss.
|
|
24
|
+
High-volume repositories can also hit cache service throttling, which makes cache restore
|
|
25
|
+
or save behavior look flaky.
|
|
26
|
+
|
|
27
|
+
This is especially confusing when a previous run clearly saved a cache but the next run
|
|
28
|
+
still reports a miss.
|
|
29
|
+
fix: |
|
|
30
|
+
Design cache keys intentionally, add `restore-keys` for partial matches, and understand the
|
|
31
|
+
branch visibility rules. Prime important caches on the default branch and avoid creating an
|
|
32
|
+
overly specific key for data that should be shared more broadly.
|
|
33
|
+
fix_code:
|
|
34
|
+
- language: yaml
|
|
35
|
+
label: "Use restore keys and stable cache key prefixes"
|
|
36
|
+
code: |
|
|
37
|
+
- uses: actions/cache@v4
|
|
38
|
+
with:
|
|
39
|
+
path: |
|
|
40
|
+
~/.npm
|
|
41
|
+
node_modules
|
|
42
|
+
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
|
43
|
+
restore-keys: |
|
|
44
|
+
${{ runner.os }}-npm-
|
|
45
|
+
prevention:
|
|
46
|
+
- "Keep cache keys stable enough to be reusable, but specific enough to avoid stale restores."
|
|
47
|
+
- "Use `restore-keys` for partial fallback instead of a single exact key."
|
|
48
|
+
- "Prime caches on the default branch for the widest reuse."
|
|
49
|
+
docs:
|
|
50
|
+
- url: "https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows"
|
|
51
|
+
label: "Caching dependencies to speed up workflows"
|
|
52
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
|
|
53
|
+
label: "Workflow syntax for GitHub Actions"
|
|
54
|
+
source:
|
|
55
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
56
|
+
section: "Cache misses despite recent saves"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
id: caching-artifacts-004
|
|
2
|
+
title: "upload-artifact v3 → v4 Breaking Changes"
|
|
3
|
+
category: caching-artifacts
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- artifacts
|
|
7
|
+
- upload-artifact
|
|
8
|
+
- download-artifact
|
|
9
|
+
- v4
|
|
10
|
+
- compatibility
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Artifact not found for name: .*"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "Unable to find any artifacts for the associated workflow"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "The requested artifact was not found"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Artifact not found for name: build-output"
|
|
20
|
+
- "Unable to find any artifacts for the associated workflow"
|
|
21
|
+
- "The requested artifact was not found"
|
|
22
|
+
root_cause: |
|
|
23
|
+
The v4 artifact actions use a different backend and behavior than v3. Mixing
|
|
24
|
+
`actions/upload-artifact@v3` with `actions/download-artifact@v4` can produce confusing
|
|
25
|
+
artifact-not-found failures even when the earlier upload step seemed successful.
|
|
26
|
+
|
|
27
|
+
The safest rule is to keep upload and download artifact actions on the same major version,
|
|
28
|
+
and preferably migrate both sides to v4 together.
|
|
29
|
+
fix: |
|
|
30
|
+
Upgrade both upload and download steps to v4 in the same workflow or workflow chain.
|
|
31
|
+
Re-test artifact names exactly as written, because name mismatches compound the version
|
|
32
|
+
mismatch problem.
|
|
33
|
+
fix_code:
|
|
34
|
+
- language: yaml
|
|
35
|
+
label: "Use matching artifact action versions"
|
|
36
|
+
code: |
|
|
37
|
+
jobs:
|
|
38
|
+
build:
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
- run: npm run build
|
|
43
|
+
- uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: build-output
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
consume:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: build
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/download-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: build-output
|
|
55
|
+
path: dist/
|
|
56
|
+
prevention:
|
|
57
|
+
- "Upgrade upload and download artifact actions together, not one side at a time."
|
|
58
|
+
- "Keep artifact names consistent and explicit across jobs."
|
|
59
|
+
- "Retest cross-workflow artifact usage after action major-version upgrades."
|
|
60
|
+
docs:
|
|
61
|
+
- url: "https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts"
|
|
62
|
+
label: "Store and share data with workflow artifacts"
|
|
63
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
|
|
64
|
+
label: "Workflow syntax for GitHub Actions"
|
|
65
|
+
source:
|
|
66
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
67
|
+
section: "Artifact v4 breaking changes"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
id: concurrency-timing-001
|
|
2
|
+
title: "Jobs Cancelled Unexpectedly"
|
|
3
|
+
category: concurrency-timing
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- concurrency
|
|
7
|
+
- cancellation
|
|
8
|
+
- matrix
|
|
9
|
+
- timing
|
|
10
|
+
- branch-isolation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Canceling since a higher priority waiting request for '.+' exists"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "The workflow run was canceled because another workflow run with the same concurrency group was queued"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "Operation was canceled\\."
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Canceling since a higher priority waiting request for 'deploy' exists"
|
|
20
|
+
- "Operation was canceled."
|
|
21
|
+
root_cause: |
|
|
22
|
+
Concurrency groups are global to the repository unless you scope them yourself. If every
|
|
23
|
+
branch uses the same group name, a push on one branch can cancel a run on a completely
|
|
24
|
+
different branch. Matrix jobs can make this harder to spot because only some legs appear to
|
|
25
|
+
vanish, even though the concurrency rule is what canceled them.
|
|
26
|
+
|
|
27
|
+
The workflow is behaving exactly as configured, but the group name is too broad.
|
|
28
|
+
fix: |
|
|
29
|
+
Include branch or ref information in the concurrency group so only related runs cancel one
|
|
30
|
+
another. Keep `cancel-in-progress: true` only when you truly want a new run to replace the
|
|
31
|
+
older run for the same ref.
|
|
32
|
+
fix_code:
|
|
33
|
+
- language: yaml
|
|
34
|
+
label: "Scope concurrency groups by workflow and ref"
|
|
35
|
+
code: |
|
|
36
|
+
concurrency:
|
|
37
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
38
|
+
cancel-in-progress: true
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
test:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
strategy:
|
|
44
|
+
matrix:
|
|
45
|
+
node: [18, 20]
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- run: npm test
|
|
49
|
+
prevention:
|
|
50
|
+
- "Never use a bare group like `deploy` or `ci` unless cross-branch cancellation is intentional."
|
|
51
|
+
- "Include `${{ github.ref }}` or `${{ github.head_ref || github.ref }}` in concurrency groups."
|
|
52
|
+
- "Review concurrency settings any time runs are mysteriously disappearing."
|
|
53
|
+
docs:
|
|
54
|
+
- url: "https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency"
|
|
55
|
+
label: "Control the concurrency of workflows and jobs"
|
|
56
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
|
|
57
|
+
label: "Workflow syntax for GitHub Actions"
|
|
58
|
+
source:
|
|
59
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
60
|
+
section: "Unexpected cancellations from concurrency"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
id: known-unsolved-002
|
|
2
|
+
title: "No Step-Level Retry"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- retry
|
|
7
|
+
- steps
|
|
8
|
+
- limitation
|
|
9
|
+
- flakiness
|
|
10
|
+
- resilience
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Process completed with exit code [0-9]+"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: 'curl: \(28\) Operation timed out'
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "npm ERR! network"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "Process completed with exit code 1"
|
|
20
|
+
- "curl: (28) Operation timed out"
|
|
21
|
+
root_cause: |
|
|
22
|
+
GitHub Actions can rerun jobs and whole workflows, but it does not provide native retry
|
|
23
|
+
semantics for a single arbitrary step. That means flaky network calls, package registry
|
|
24
|
+
hiccups, and transient shell commands fail the step immediately unless you build a retry
|
|
25
|
+
wrapper yourself.
|
|
26
|
+
|
|
27
|
+
This is a platform limitation rather than a YAML mistake.
|
|
28
|
+
fix: |
|
|
29
|
+
Wrap the flaky command in a retry helper or use a marketplace action such as
|
|
30
|
+
`nick-fields/retry` when retry semantics are needed for one step only.
|
|
31
|
+
fix_code:
|
|
32
|
+
- language: yaml
|
|
33
|
+
label: "Retry a flaky step with nick-fields/retry"
|
|
34
|
+
code: |
|
|
35
|
+
- name: Retry npm install
|
|
36
|
+
uses: nick-fields/retry@v3
|
|
37
|
+
with:
|
|
38
|
+
timeout_minutes: 15
|
|
39
|
+
max_attempts: 3
|
|
40
|
+
retry_wait_seconds: 20
|
|
41
|
+
command: npm ci
|
|
42
|
+
prevention:
|
|
43
|
+
- "Assume external network calls are flaky and wrap them in retries if they are business-critical."
|
|
44
|
+
- "Prefer idempotent commands for steps that may need retry wrappers."
|
|
45
|
+
- "Document step-level retry strategy because GitHub Actions does not provide it natively."
|
|
46
|
+
docs:
|
|
47
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/re-running-workflows-and-jobs"
|
|
48
|
+
label: "Re-running workflows and jobs"
|
|
49
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
|
|
50
|
+
label: "Workflow syntax for GitHub Actions"
|
|
51
|
+
source:
|
|
52
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
53
|
+
section: "Platform limitations: no step-level retry"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
id: permissions-auth-001
|
|
2
|
+
title: "GITHUB_TOKEN Permission Denied (403)"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- github-token
|
|
7
|
+
- permissions
|
|
8
|
+
- 403
|
|
9
|
+
- auth
|
|
10
|
+
- push
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "remote: Permission to .+\\.git denied to github-actions\\[bot\\]"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "fatal: unable to access 'https://github\\.com/.+': The requested URL returned error: 403"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "Resource not accessible by integration"
|
|
17
|
+
flags: "i"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "remote: Permission to org/repo.git denied to github-actions[bot]."
|
|
20
|
+
- "fatal: unable to access 'https://github.com/org/repo.git/': The requested URL returned error: 403"
|
|
21
|
+
- "Resource not accessible by integration"
|
|
22
|
+
root_cause: |
|
|
23
|
+
Since February 2023, newly created repositories default `GITHUB_TOKEN` to read-only
|
|
24
|
+
permissions in many cases. A workflow that tries to push commits, create releases, or
|
|
25
|
+
modify pull requests without an explicit `permissions:` block can suddenly fail with 403
|
|
26
|
+
or integration access errors.
|
|
27
|
+
|
|
28
|
+
The token exists, but it does not have the scopes the job assumed it had.
|
|
29
|
+
fix: |
|
|
30
|
+
Add the minimum required `permissions:` block at the workflow or job level. For pushes,
|
|
31
|
+
tags, or release creation, that usually means `contents: write`. For pull request comments
|
|
32
|
+
or checks, grant the specific write permission those APIs require.
|
|
33
|
+
fix_code:
|
|
34
|
+
- language: yaml
|
|
35
|
+
label: "Grant explicit write permissions to GITHUB_TOKEN"
|
|
36
|
+
code: |
|
|
37
|
+
name: Release
|
|
38
|
+
|
|
39
|
+
on:
|
|
40
|
+
push:
|
|
41
|
+
branches:
|
|
42
|
+
- main
|
|
43
|
+
|
|
44
|
+
permissions:
|
|
45
|
+
contents: write
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
release:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- run: git push origin HEAD:main
|
|
53
|
+
prevention:
|
|
54
|
+
- "Assume `GITHUB_TOKEN` is read-only unless you explicitly grant the needed permission."
|
|
55
|
+
- "Set least-privilege `permissions:` blocks on every workflow instead of relying on defaults."
|
|
56
|
+
- "Match API usage to the smallest write scope required."
|
|
57
|
+
docs:
|
|
58
|
+
- url: "https://docs.github.com/en/actions/security-guides/automatic-token-authentication"
|
|
59
|
+
label: "Automatic token authentication"
|
|
60
|
+
- url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions"
|
|
61
|
+
label: "permissions"
|
|
62
|
+
source:
|
|
63
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
64
|
+
section: "GITHUB_TOKEN 403 and read-only defaults"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
id: permissions-auth-002
|
|
2
|
+
title: "OIDC Federation Failures with AWS"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- oidc
|
|
7
|
+
- aws
|
|
8
|
+
- sts
|
|
9
|
+
- iam
|
|
10
|
+
- federation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "Not authorized to perform sts:AssumeRoleWithWebIdentity"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "InvalidIdentityToken"
|
|
15
|
+
flags: "i"
|
|
16
|
+
- regex: "No OpenIDConnect provider found in your account"
|
|
17
|
+
flags: "i"
|
|
18
|
+
- regex: "audience.*sts\\.amazonaws\\.com"
|
|
19
|
+
flags: "i"
|
|
20
|
+
error_messages:
|
|
21
|
+
- "Not authorized to perform sts:AssumeRoleWithWebIdentity"
|
|
22
|
+
- "InvalidIdentityToken"
|
|
23
|
+
- "No OpenIDConnect provider found in your account"
|
|
24
|
+
root_cause: |
|
|
25
|
+
GitHub's OIDC token must match the AWS IAM trust policy exactly. Failures usually come
|
|
26
|
+
from one of three mismatches: the workflow is missing `id-token: write`, the IAM role
|
|
27
|
+
trust policy expects the wrong `aud` value, or the `sub` claim does not match the repo,
|
|
28
|
+
branch, environment, or tag that is actually requesting the token.
|
|
29
|
+
|
|
30
|
+
Any one of those mismatches is enough for AWS STS to reject the federation request.
|
|
31
|
+
fix: |
|
|
32
|
+
Grant `id-token: write`, verify the GitHub OIDC provider exists in AWS, and align the IAM
|
|
33
|
+
trust policy's `aud` and `sub` conditions with the workflow that is assuming the role.
|
|
34
|
+
fix_code:
|
|
35
|
+
- language: yaml
|
|
36
|
+
label: "Grant OIDC permission in the workflow"
|
|
37
|
+
code: |
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write
|
|
40
|
+
contents: read
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
deploy:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
48
|
+
with:
|
|
49
|
+
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
|
|
50
|
+
aws-region: us-east-1
|
|
51
|
+
- language: json
|
|
52
|
+
label: "Example AWS trust policy for GitHub OIDC"
|
|
53
|
+
code: |
|
|
54
|
+
{
|
|
55
|
+
"Version": "2012-10-17",
|
|
56
|
+
"Statement": [
|
|
57
|
+
{
|
|
58
|
+
"Effect": "Allow",
|
|
59
|
+
"Principal": {
|
|
60
|
+
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
|
|
61
|
+
},
|
|
62
|
+
"Action": "sts:AssumeRoleWithWebIdentity",
|
|
63
|
+
"Condition": {
|
|
64
|
+
"StringEquals": {
|
|
65
|
+
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
|
|
66
|
+
},
|
|
67
|
+
"StringLike": {
|
|
68
|
+
"token.actions.githubusercontent.com:sub": "repo:owner/repo:ref:refs/heads/main"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
prevention:
|
|
75
|
+
- "Treat the OIDC `aud` and `sub` claims as part of your contract and document them with the role."
|
|
76
|
+
- "Test branch, tag, and environment-specific subjects before rolling OIDC out broadly."
|
|
77
|
+
- "Always include `id-token: write` when using cloud federation from GitHub Actions."
|
|
78
|
+
docs:
|
|
79
|
+
- url: "https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"
|
|
80
|
+
label: "Configuring OpenID Connect in Amazon Web Services"
|
|
81
|
+
- url: "https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect"
|
|
82
|
+
label: "About security hardening with OpenID Connect"
|
|
83
|
+
source:
|
|
84
|
+
article: "https://htek.dev/articles/github-actions-debugging-guide"
|
|
85
|
+
section: "OIDC federation with AWS"
|