-
Notifications
You must be signed in to change notification settings - Fork 0
354 lines (349 loc) · 15.7 KB
/
Copy pathcodeql.yml
File metadata and controls
354 lines (349 loc) · 15.7 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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
name: Code Foundry CodeQL
on:
workflow_call:
inputs:
runtime-repository:
description: Repository containing the Code Foundry runtime.
required: false
type: string
default: 0xPlayerOne/code-foundry
runtime-ref:
description: Code Foundry runtime tag or ref.
required: false
type: string
default: v1.0.5
runner:
description: Runner used by CodeQL jobs.
required: false
type: string
default: ubuntu-latest
rust-shards:
description: JSON array of Rust scope sharding values. Use ["all"] for single-pass behavior.
required: false
type: string
default: '["all"]'
rust-threads:
description: Threads used for Rust extraction and analysis. Values above 1 opt into local parallelism.
required: false
type: string
default: '1'
rust-max-parallel:
description: Maximum Rust shard jobs allowed to run concurrently.
required: false
type: number
default: 1
# Retained for caller compatibility. Rust shards share the single
# analysis matrix, so no separate throttle applies.
permissions:
actions: read
contents: read
packages: read
security-events: write
# Analyzers run only for detected languages: a single analysis matrix is
# built from detection (Rust shards expand inline), so repositories never
# see checks — not even skipped ones — for languages they do not use. An
# empty matrix while analysis is enabled fails the build closed.
env:
REPO_FOUNDRY_PROFILE: ${{ vars.REPO_FOUNDRY_PROFILE }}
REPO_FOUNDRY_LANGUAGES: ${{ vars.REPO_FOUNDRY_LANGUAGES }}
REPO_FOUNDRY_FEATURES: ${{ vars.REPO_FOUNDRY_FEATURES }}
concurrency:
group: code-foundry-codeql-${{ github.event_name }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
# Superseded pull-request runs are cancelled so the newest head owns the
# group; push and scheduled runs queue instead, because cancelling a main
# push can drop default-branch analysis or release-adjacent work.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
detect:
name: Detect
if: vars.CI_BILLING_PAUSED != 'true'
runs-on: ${{ inputs.runner }}
timeout-minutes: 10
outputs:
languages: ${{ steps.languages.outputs.languages }}
enabled: ${{ steps.languages.outputs.enabled }}
matrix: ${{ steps.matrix.outputs.matrix }}
code_security: ${{ steps.security.outputs.status }}
actions_available: ${{ steps.languages.outputs.actions_available }}
actions_changed: ${{ steps.languages.outputs.actions_changed }}
actions_build_mode: ${{ steps.languages.outputs.actions_build_mode }}
javascript_available: ${{ steps.languages.outputs.javascript_available }}
javascript_changed: ${{ steps.languages.outputs.javascript_changed }}
javascript_build_mode: ${{ steps.languages.outputs.javascript_build_mode }}
python_available: ${{ steps.languages.outputs.python_available }}
python_changed: ${{ steps.languages.outputs.python_changed }}
python_build_mode: ${{ steps.languages.outputs.python_build_mode }}
rust_available: ${{ steps.languages.outputs.rust_available }}
rust_changed: ${{ steps.languages.outputs.rust_changed }}
rust_build_mode: ${{ steps.languages.outputs.rust_build_mode }}
steps:
- name: Detect Code Security
id: security
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY_VISIBILITY: ${{ github.event.repository.visibility }}
run: |
status="disabled"
if [ "$REPOSITORY_VISIBILITY" = "public" ]; then
status="enabled"
else
status="$(gh api "repos/${GITHUB_REPOSITORY}" --jq '.security_and_analysis.code_security.status // "disabled"' 2>/dev/null || printf 'disabled')"
fi
printf 'status=%s\n' "$status" >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
fetch-depth: 2
filter: blob:none
sparse-checkout: |
.github
.mise.toml
mise.lock
package.json
bun.lock
bun.lockb
pnpm-lock.yaml
yarn.lock
package-lock.json
sparse-checkout-cone-mode: false
- name: Runtime
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
repository: ${{ inputs.runtime-repository }}
ref: ${{ inputs.runtime-ref }}
path: .github/.code-foundry
sparse-checkout: |
.github/actions
src/lib
src/runtime-core.mjs
src/runtime.mjs
- name: Install runtime
run: |
mkdir -p .github/actions
mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
cp -R "$RUNNER_TEMP/code-foundry/.github/actions/." .github/actions/
- name: List pull request files
id: changed
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
# Newline-separated changed paths for the runtime's change
# detection. A failed listing leaves the file absent and the
# runtime fails open toward analyzing every language and shard.
gh api "repos/$GITHUB_REPOSITORY/pulls/$PULL_REQUEST_NUMBER/files?per_page=100" \
--paginate --jq '.[].filename' > "$RUNNER_TEMP/changed-files.txt" || true
echo "file=$RUNNER_TEMP/changed-files.txt" >> "$GITHUB_OUTPUT"
- name: Detect code scanning merge gate
id: gate
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# The code-scanning merge gate waits for results in every tracked
# category, so change detection must never skip an upload while it
# is active. Any ruleset API failure also disables change
# detection: full analysis is the safe default.
gated=false
for id in $(gh api "repos/$GITHUB_REPOSITORY/rulesets" --paginate \
-q '.[] | select(.enforcement == "active") | .id' 2>/dev/null); do
if gh api "repos/$GITHUB_REPOSITORY/rulesets/$id" \
-q 'any(.rules[]?; .type == "code_scanning")' 2>/dev/null | grep -q true; then
gated=true
break
fi
done
echo "gated=$gated" >> "$GITHUB_OUTPUT"
- id: languages
name: Detect languages
env:
CODEQL_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
FOUNDRY_CODEQL_CHANGED_FILES_FILE: ${{ steps.changed.outputs.file }}
FOUNDRY_CODEQL_MERGE_GATE: ${{ steps.gate.outputs.gated }}
REPO_FOUNDRY_CODE_SECURITY: ${{ steps.security.outputs.status }}
REPO_FOUNDRY_PRIVATE: ${{ github.event.repository.private }}
REPO_FOUNDRY_VISIBILITY: ${{ github.event.repository.visibility }}
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" codeql
- id: matrix
name: Build analysis matrix
env:
MATRIX_ENABLED: ${{ steps.languages.outputs.enabled }}
MATRIX_LANGUAGES: ${{ steps.languages.outputs.languages }}
MATRIX_SHARDS: ${{ inputs.rust-shards }}
run: |
node -e '
const fs = require("node:fs");
const out = process.env.GITHUB_OUTPUT;
function append(name, value) { fs.appendFileSync(out, name + "=" + value + "\n"); }
const enabled = process.env.MATRIX_ENABLED === "true";
if (!enabled) {
append("matrix", "[]");
} else {
const languages = JSON.parse(process.env.MATRIX_LANGUAGES || "[]");
const shards = JSON.parse(process.env.MATRIX_SHARDS || "[\"all\"]");
const display = { actions: "Actions", "javascript-typescript": "TypeScript", python: "Python" };
// SARIF categories are the code-scanning baseline keys: they must
// stay byte-identical to the pre-matrix values (/language:actions,
// /language:javascript-typescript, /language:python,
// /language:rust/<scope>). Any rename orphans the main-branch
// baseline and breaks alert comparison on every pull request.
const matrix = languages
.filter((entry) => entry.language !== "rust")
.map((entry) => ({
display: display[entry.language] || entry.language,
language: entry.language,
category: "/language:" + entry.language,
build_mode: entry["build-mode"] || "none",
changed: entry.changed === true,
}));
const rust = languages.find((entry) => entry.language === "rust");
// The runtime emits per-shard change flags when the event is a
// pull request with a usable changed-file list; fall back to the
// shard list of the caller with the language-level flag
// otherwise. No single quotes: this script runs through node -e.
const rustShards =
rust && Array.isArray(rust.shards) && rust.shards.length
? rust.shards
: (rust
? shards.map((shard) => ({ shard: String(shard), changed: rust.changed === true }))
: []);
for (const entry of rustShards) {
matrix.push({
display: entry.shard === "all" ? "Rust" : "Rust (" + entry.shard + ")",
language: "rust",
category: "/language:rust",
build_mode: "none",
changed: entry.changed === true,
shard: String(entry.shard),
});
}
if (!matrix.length) {
console.error("CodeQL analysis matrix is empty while analysis is enabled.");
process.exit(1);
}
append("matrix", JSON.stringify(matrix));
}
'
analyze:
name: Analyze (${{ matrix.entry.display }})
needs: detect
if: vars.CI_BILLING_PAUSED != 'true' && needs.detect.outputs.enabled == 'true'
strategy:
fail-fast: false
matrix:
entry: ${{ fromJson(needs.detect.outputs.matrix || '[]') }}
runs-on: ${{ inputs.runner }}
timeout-minutes: 30
steps:
# Analyze the pull request head, not the ephemeral merge ref. The
# codeql-action records commit_oid from `git rev-parse HEAD` on the
# checkout — the `sha` upload input is only a fallback when git is
# unavailable — so checking out the head is what actually keeps the
# recorded commit stable across mergeability evaluations.
- name: Checkout
if: ${{ matrix.entry.changed == true }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Runtime
if: ${{ matrix.entry.changed == true }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
repository: ${{ inputs.runtime-repository }}
ref: ${{ inputs.runtime-ref }}
path: .github/.code-foundry
sparse-checkout: |
.github/actions
src/lib
src/runtime-core.mjs
src/runtime.mjs
- name: Install runtime
if: ${{ matrix.entry.changed == true }}
run: |
mkdir -p .github/actions
mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
cp -R "$RUNNER_TEMP/code-foundry/.github/actions/." .github/actions/
- name: Analyze
if: ${{ matrix.entry.changed == true && matrix.entry.language != 'rust' }}
uses: ./.github/actions/codeql
with:
language: ${{ matrix.entry.language }}
build-mode: ${{ matrix.entry.build_mode }}
category: ${{ matrix.entry.category }}
- name: Configure Rust scope
if: ${{ matrix.entry.changed == true && matrix.entry.language == 'rust' }}
id: scope
env:
RUST_SCOPE: ${{ matrix.entry.shard }}
run: |
set -euo pipefail
scope="$RUST_SCOPE"
scope_id="$(node -e 'process.stdout.write(require("node:crypto").createHash("sha256").update(process.argv[1]).digest("hex").slice(0, 12))' "$scope")"
config_file="$GITHUB_WORKSPACE/.github/codeql-rust-$scope_id.yml"
cat > "$config_file" <<'EOF'
name: code-foundry-rust
EOF
if [ "$scope" != "all" ]; then
IFS=',' read -ra paths <<< "$scope"
if [ "${#paths[@]}" -eq 0 ]; then
echo "Empty Rust scope shard for CodeQL"
exit 1
fi
echo "paths:" >> "$config_file"
for path in "${paths[@]}"; do
path="${path#"${path%%[![:space:]]*}"}"
path="${path%"${path##*[![:space:]]}"}"
if [ -z "$path" ] ||
[[ "$path" = /* ]] ||
[[ "/$path/" = *"/../"* ]] ||
[[ ! "$path" =~ ^[A-Za-z0-9._/@+\ -]+$ ]]; then
echo "Invalid Rust CodeQL shard path: $path" >&2
exit 1
fi
if ! git ls-files -- "$path" | grep -Eq '(^|/)(Cargo\.toml|[^/]+\.rs)$'; then
echo "Rust CodeQL shard path contains no tracked Rust source: $path" >&2
exit 1
fi
printf " - '%s'\n" "$path" >> "$config_file"
done
fi
if [ -f Cargo.toml ] && grep -q '^paths:' "$config_file"; then
printf " - 'Cargo.toml'\n" >> "$config_file"
fi
if [ -f Cargo.lock ] && grep -q '^paths:' "$config_file"; then
printf " - 'Cargo.lock'\n" >> "$config_file"
fi
if [ -f rust-toolchain.toml ] && grep -q '^paths:' "$config_file"; then
printf " - 'rust-toolchain.toml'\n" >> "$config_file"
fi
echo "config_file=$config_file" >> "$GITHUB_OUTPUT"
echo "scope_id=$scope_id" >> "$GITHUB_OUTPUT"
- name: Initialize
if: ${{ matrix.entry.changed == true && matrix.entry.language == 'rust' }}
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4
with:
languages: rust
build-mode: ${{ needs.detect.outputs.rust_build_mode }}
config-file: ${{ steps.scope.outputs.config_file }}
threads: ${{ inputs.rust-threads }}
- name: Analyze Rust
if: ${{ matrix.entry.changed == true && matrix.entry.language == 'rust' }}
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4
with:
category: /language:rust/${{ steps.scope.outputs.scope_id }}
# Keep uploads attached to the PR head rather than the mutable
# merge ref used by GitHub's pull_request event.
ref: ${{ github.event_name == 'pull_request' && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }}
sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
upload-database: false
wait-for-processing: false
- name: Not applicable
if: ${{ matrix.entry.changed != true }}
run: echo "CodeQL ${{ matrix.entry.display }} analysis is not applicable to this change."