-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
269 lines (250 loc) · 11.4 KB
/
action.yml
File metadata and controls
269 lines (250 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
name: "Codemap"
description: "SQL-queryable structural index of your codebase. Run any predicate as a recipe; CI gating via SARIF → Code Scanning."
author: "stainless-code"
branding:
icon: "database"
color: "blue"
inputs:
# WHERE TO RUN
working-directory:
description: "Subdirectory to run codemap in (for monorepos). Defaults to the repository root."
required: false
default: "."
package-manager:
description: "Override package-manager autodetect. Accepts npm | pnpm | yarn | yarn@berry | bun. Empty = autodetect via package-manager-detector (lockfile + packageManager field + devEngines.packageManager + install-metadata + parent-dir walk)."
required: false
default: ""
version:
description: "Pin codemap CLI version (e.g. 1.2.3). Empty = use the project's devDependency if present, else fall back to <pm> dlx codemap@latest."
required: false
default: ""
state-dir:
description: "Override codemap state directory location. Empty = .codemap/ at the working-directory root (codemap default)."
required: false
default: ""
# WHAT TO RUN — high-level (mutually exclusive; precedence: command > mode > defaults)
mode:
description: "Run shape: 'audit' | 'recipe' | 'aggregate' | 'command'. Default: 'audit' on pull_request events; ignored on other events (Action no-ops). 'aggregate' is reserved for v1.x — currently rejected."
required: false
default: "audit"
recipe:
description: "Recipe id (when mode=recipe). Use --recipes-json on the CLI to list known recipes."
required: false
default: ""
params:
description: "Recipe params for parametrised recipes (when mode=recipe). Multiline `key=value` pairs, one per line."
required: false
default: ""
baseline:
description: "Saved baseline name to diff against (when mode=recipe + a baseline was previously saved with --save-baseline)."
required: false
default: ""
audit-base:
description: "Git ref to audit against (when mode=audit). Empty (default) → falls back to `github.base_ref` on `pull_request` events; on other events the action no-ops unless an explicit `command:` is set."
required: false
default: ""
changed-since:
description: "Filter results to files changed since the given git ref (e.g. 'origin/main')."
required: false
default: ""
group-by:
description: "Bucket results by 'owner' (CODEOWNERS) | 'directory' | 'package' (workspace package). Empty = no bucketing."
required: false
default: ""
command:
description: "Raw CLI args to invoke codemap with (escape hatch). When set, overrides mode / recipe / params / baseline / audit-base / changed-since / group-by — those are silently ignored with a warning."
required: false
default: ""
# WHAT TO DO WITH OUTPUT
format:
description: "Output format: 'sarif' | 'json' | 'annotations' | 'mermaid' | 'diff' (per-mode availability varies; audit supports text/json/sarif). Default: 'sarif' (SARIF 2.1.0 → Code Scanning)."
required: false
default: "sarif"
output-path:
description: "Where to write the output file. Used as the artifact-upload source when format=sarif and upload-sarif=true."
required: false
default: "codemap.sarif"
upload-sarif:
description: "Upload the SARIF artifact to GitHub Code Scanning. Requires GitHub Advanced Security on private repos. Set 'false' if your repo can't use Code Scanning (still produces the artifact for manual download / pr-comment writer)."
required: false
default: "true"
pr-comment:
description: "Post a markdown summary comment on the PR (Slice 3 — opt-in for v1.0). Set 'true' to enable. Useful when SARIF→Code-Scanning isn't available (private repos without GHAS, or repos that haven't enabled Code Scanning)."
required: false
default: "false"
fail-on:
description: "Exit-code policy: 'any' | 'error' | 'warning' | 'never'. v1.0 ships only 'any' (fails when any finding) and 'never' (no exit code). 'error' / 'warning' deferred until per-recipe severity overrides ship."
required: false
default: "any"
token:
description: "GitHub token for SARIF upload + PR comment posting. Empty (default) → falls back to `github.token` automatically. Pass an explicit fine-grained PAT only if you need elevated permissions."
required: false
default: ""
outputs:
agent:
description: "Resolved package manager (npm / pnpm / yarn / bun)."
value: ${{ steps.detect-pm.outputs.agent }}
exec:
description: "Shell-ready command used to invoke codemap."
value: ${{ steps.detect-pm.outputs.exec }}
install_method:
description: "How codemap was located: 'project-installed' | 'dlx-pinned' | 'dlx-latest'."
value: ${{ steps.detect-pm.outputs.install_method }}
output-file:
description: "Path to the written output file (echoes inputs.output-path)."
value: ${{ inputs.output-path }}
runs:
using: "composite"
steps:
- name: Skip on non-PR events when defaulting to audit
id: gate
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
MODE: ${{ inputs.mode }}
COMMAND: ${{ inputs.command }}
AUDIT_BASE_INPUT: ${{ inputs.audit-base }}
run: |
if [ -n "$COMMAND" ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$MODE" != "audit" ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
exit 0
fi
BASE="${AUDIT_BASE_INPUT:-$BASE_REF}"
if [ -z "$BASE" ]; then
echo "codemap action: no PR context (event_name=$EVENT_NAME, base_ref empty), skipping. Pass an explicit 'command:' input to run on non-PR events."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Setup Node.js (for the package-manager-detector wrapper)
if: steps.gate.outputs.skip != 'true'
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Detect package manager + resolve CLI invocation
if: steps.gate.outputs.skip != 'true'
id: detect-pm
shell: bash
env:
PACKAGE_MANAGER: ${{ inputs.package-manager }}
VERSION: ${{ inputs.version }}
WORKING_DIRECTORY: ${{ inputs.working-directory }}
run: |
# Action runs without its own node_modules; install the detector lazily.
# Pinned to a known version so consumers get reproducible builds.
npm install --no-save --prefix "$RUNNER_TEMP/codemap-action" package-manager-detector@1.6.0 >/dev/null
cp "${{ github.action_path }}/scripts/detect-pm.mjs" "$RUNNER_TEMP/codemap-action/detect-pm.mjs"
cd "$RUNNER_TEMP/codemap-action"
node detect-pm.mjs
- name: Validate inputs (mode + flag interactions)
if: steps.gate.outputs.skip != 'true'
shell: bash
env:
MODE: ${{ inputs.mode }}
RECIPE: ${{ inputs.recipe }}
COMMAND: ${{ inputs.command }}
run: |
# Precedence: command > mode. If command is set, mode/recipe are silently
# ignored but we surface a single warning so consumers notice.
if [ -n "$COMMAND" ] && [ -n "$RECIPE" ]; then
echo "::warning::codemap action: 'command' input takes precedence; 'recipe' (and other mode-* inputs) are ignored."
fi
case "$MODE" in
audit | recipe | command) ;;
aggregate)
echo "::error::codemap action: mode='aggregate' is reserved for v1.x and not yet implemented. Use mode='audit' or pass a 'command:' input."
exit 1
;;
*)
echo "::error::codemap action: unknown mode '$MODE'. Expected: audit | recipe | aggregate | command."
exit 1
;;
esac
if [ "$MODE" = "recipe" ] && [ -z "$RECIPE" ] && [ -z "$COMMAND" ]; then
echo "::error::codemap action: mode='recipe' requires the 'recipe' input (a recipe id). Run codemap query --recipes-json to list known recipes."
exit 1
fi
if [ "$MODE" = "command" ] && [ -z "$COMMAND" ]; then
echo "::error::codemap action: mode='command' requires the 'command' input (raw CLI args)."
exit 1
fi
- name: Run codemap
if: steps.gate.outputs.skip != 'true'
id: run
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
EXEC: ${{ steps.detect-pm.outputs.exec }}
MODE: ${{ inputs.mode }}
RECIPE: ${{ inputs.recipe }}
PARAMS: ${{ inputs.params }}
BASELINE: ${{ inputs.baseline }}
AUDIT_BASE: ${{ inputs.audit-base }}
CHANGED_SINCE: ${{ inputs.changed-since }}
GROUP_BY: ${{ inputs.group-by }}
COMMAND: ${{ inputs.command }}
FORMAT: ${{ inputs.format }}
OUTPUT_PATH: ${{ inputs.output-path }}
FAIL_ON: ${{ inputs.fail-on }}
STATE_DIR: ${{ inputs.state-dir }}
BASE_REF: ${{ github.base_ref }}
run: |
set +e
if [ -n "$COMMAND" ]; then
ARGS="$COMMAND"
elif [ "$MODE" = "audit" ]; then
BASE="${AUDIT_BASE:-$BASE_REF}"
ARGS="audit --base $BASE --format $FORMAT"
elif [ "$MODE" = "recipe" ]; then
ARGS="query --recipe $RECIPE --format $FORMAT"
[ -n "$PARAMS" ] && while IFS= read -r line; do
[ -n "$line" ] && ARGS="$ARGS --params $line"
done <<< "$PARAMS"
[ -n "$BASELINE" ] && ARGS="$ARGS --baseline $BASELINE"
fi
[ -n "$CHANGED_SINCE" ] && ARGS="$ARGS --changed-since $CHANGED_SINCE"
[ -n "$GROUP_BY" ] && ARGS="$ARGS --group-by $GROUP_BY"
[ -n "$STATE_DIR" ] && ARGS="--state-dir $STATE_DIR $ARGS"
echo "+ $EXEC $ARGS"
$EXEC $ARGS > "$OUTPUT_PATH"
EXIT=$?
echo "exit_code=$EXIT" >> "$GITHUB_OUTPUT"
# `fail-on` policy. v1.0 supports 'any' (default) and 'never'. Other
# values fall back to passing the underlying exit code through.
case "$FAIL_ON" in
never) exit 0 ;;
any | *) exit "$EXIT" ;;
esac
- name: Upload SARIF to Code Scanning
if: steps.gate.outputs.skip != 'true' && inputs.upload-sarif == 'true' && inputs.format == 'sarif' && always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ inputs.working-directory }}/${{ inputs.output-path }}
token: ${{ inputs.token != '' && inputs.token || github.token }}
- name: Post PR summary comment
if: steps.gate.outputs.skip != 'true' && inputs.pr-comment == 'true' && github.event_name == 'pull_request' && always()
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
EXEC: ${{ steps.detect-pm.outputs.exec }}
OUTPUT_PATH: ${{ inputs.output-path }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ inputs.token != '' && inputs.token || github.token }}
run: |
# Render the markdown body via `codemap pr-comment`, then post via
# `gh pr comment`. The same binary that produced the SARIF / JSON
# output renders the comment — keeps the version stream coherent.
BODY=$($EXEC pr-comment "$OUTPUT_PATH" 2>/dev/null) || {
echo "::warning::codemap action: pr-comment renderer failed; skipping PR comment."
exit 0
}
if [ -z "$BODY" ]; then
echo "::notice::codemap action: pr-comment produced empty body; skipping."
exit 0
fi
echo "$BODY" | gh pr comment "$PR_NUMBER" -F -