-
Notifications
You must be signed in to change notification settings - Fork 0
176 lines (165 loc) · 9.35 KB
/
Copy pathcodeql.yml
File metadata and controls
176 lines (165 loc) · 9.35 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
name: CodeQL
# ⚠️ THIS JOB IS CURRENTLY EXPECTED TO FAIL. Do not re-diagnose it from scratch.
#
# CodeQL's tracer injects libtrace.dylib via DYLD_INSERT_LIBRARIES, and the
# architecture-specific helpers Xcode 27 spawns then die with EBADARCH ("Bad CPU
# type"). Two of them:
#
# sandbox-exec SwiftPM manifest loading — worked around below
# swift-plugin-server Swift macro expansion — NO workaround exists
#
# So @State and #Preview fail to expand inside the KeyboardShortcuts dependency
# and the traced build dies with exit 65. The untraced Build AppStore / Build
# Direct / Test jobs are green: the app is fine, only the traced build is not.
#
# It is NOT a CodeQL version problem, and that is tested rather than assumed.
# 2.27.0 and 2.27.1 were both run against this failure and both died the same
# way, exit 65 with the same swift-plugin-server EBADARCH. The run that passed
# on main on 2026-09-23 used this same action SHA and the same CodeQL 2.27.0,
# both pinned and neither moved — the xcode-27 IMAGE moved. So no `tools:` pin
# helps, and there is deliberately no `tools:` line here: the action SHA above
# already pins the bundle through its own src/defaults.json (v4.38.1 -> 2.27.0).
#
# Do not spend a cycle re-testing CodeQL versions. That has been done.
#
# Nothing here needs changing when it is fixed upstream: the job simply starts
# passing. Full diagnosis in PR #22.
#
# Advanced setup, not default setup. CodeQL analyses Swift by BUILDING it, and
# default setup would run that build on a GitHub-hosted macos-latest — which is
# still macOS 26 and has no macOS 27 SDK (see the note on `runs-on` in ci.yml).
# The build would fail on every push. This runs on the same xcode-27 image the
# rest of CI uses, and builds the same way.
on:
push:
branches: [main]
pull_request:
schedule:
# Weekly. New queries ship regularly and find old bugs in unchanged code;
# without this, the analysis only ever sees what a push happens to touch.
- cron: '17 6 * * 1'
permissions:
contents: read
jobs:
analyze:
name: Analyze (swift)
runs-on: xcode-27
permissions:
contents: read
# Required: this is how the analysis uploads its results to the Security
# tab. Nothing else here needs write. (actions: read is only needed on
# private repositories; this one is public.)
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 (node24)
# Resolve SwiftPM dependencies BEFORE CodeQL starts tracing.
#
# The tracer sets DYLD_INSERT_LIBRARIES and injects libtrace.dylib into every
# process the build spawns. SwiftPM compiles package manifests inside
# `sandbox-exec`, and that spawn then dies with EBADARCH:
#
# posix_spawn error: Bad CPU type in executable (86), ["/usr/bin/sandbox-exec", ...]
#
# It is the tracer, not the packages: Build AppStore, Build Direct and Test run
# the same xcodebuild untraced on the same commit and pass in ~2 minutes.
# Resolving here populates SourcePackages/ and SwiftPM's manifest cache outside
# the tracer, leaving the traced build nothing to do but compile. The
# -derivedDataPath must match the build step's or the checkouts are not found.
- name: Resolve package dependencies (untraced)
run: |
xcodebuild -project NetRelish.xcodeproj -scheme "NetRelish (App Store)" \
-derivedDataPath .build/DerivedData -resolvePackageDependencies
# Proven necessary, and a real trade-off — read before deleting or copying.
#
# WHY: CodeQL's tracer injects libtrace.dylib into every spawned process.
# SwiftPM compiles package manifests inside sandbox-exec, and that spawn
# dies with EBADARCH. Four runs died here. Pre-resolving above is necessary
# but not sufficient: the traced build reloads the manifests regardless,
# most likely because SwiftPM's manifest cache key includes the environment
# and DYLD_INSERT_LIBRARIES changes it — so a cache warmed outside the
# tracer can never be hit inside it, and the sandbox itself has to go.
# With this step the build and extraction complete.
#
# WHAT IT COSTS: manifest evaluation loses its isolation, so Package.swift
# code from a dependency — or from a pull request that edits one — runs
# unsandboxed here. Three things bound that: the runner is ephemeral and
# the default dies with it; this workflow's token is `contents: read`; and
# the job already compiles the PR's own code through xcodebuild, which runs
# arbitrary build phases anyway, so the marginal exposure is small.
#
# It is still a downgrade, and it is here because the alternative is no
# Swift scanning at all. Revisit when the xcode-27 image or CodeQL's tracer
# stops requiring it; this step should not outlive that.
- name: Disable SwiftPM's manifest sandbox (see comment — trade-off)
run: defaults write com.apple.dt.Xcode IDEPackageSupportDisableManifestSandbox -bool YES
- uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
id: init
with:
languages: swift
# Adds .github/codeql on top of the default suite — it does not replace
# it. `disable-default-queries` stays false, so both run.
queries: ./.github/codeql
# Compile the custom queries against the CodeQL the runner actually uses,
# BEFORE spending eight minutes on a build.
#
# A broken query otherwise surfaces as "CodeQL job status was
# configuration error" only after the whole extraction, with the real
# message buried under a few hundred lines of extractor diagnostics. This
# turns that into a readable failure in seconds.
#
# It also prints the CodeQL version, which is NOT necessarily the one the
# queries were authored against: the action is pinned by SHA, the CLI it
# downloads is not, and qlpack.yml takes codeql/swift-all at "*". If those
# ever diverge enough to matter, this step is where it shows up.
- name: Compile the custom queries (fail fast, readable error)
run: |
"${{ steps.init.outputs.codeql-path }}" version --format=terse
"${{ steps.init.outputs.codeql-path }}" query compile --warnings=error \
--threads=0 .github/codeql/
# Not autobuild: this project builds through a named scheme and
# configuration, exactly as ci.yml does. Signing is off because CI holds
# no certificate.
#
# BOTH flavours, into one database. CodeQL only sees what the traced build
# compiles, so an AppStore-only build leaves every `#if DIRECT_BUILD` line
# out of the database entirely — and that is exactly where CLAUDE.md puts
# the direct-build licence activation, and where Pickle's GitHub OAuth
# device flow will live. A gate blind to the riskiest code in the project
# is worse than no gate, because it reads as coverage. Consecutive traced
# commands collect into the same database, so this is two invocations, not
# two jobs.
- name: Build both flavours (unsigned — CI has no certificate)
run: |
xcodebuild -project NetRelish.xcodeproj -scheme "NetRelish (App Store)" \
-configuration Release-AppStore -derivedDataPath .build/DerivedData \
-destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO -quiet build
xcodebuild -project NetRelish.xcodeproj -scheme "NetRelish (Direct)" \
-configuration Release-Direct -derivedDataPath .build/DerivedData \
-destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO -quiet build
- uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
with:
category: "/language:swift"
output: .codeql-results
# analyze uploads findings; it never fails the job on them, and there is
# no `fail-on` input. Without this step the gate is a report, not a gate —
# the CodeQL check can sit green with an alert on it, so requiring it in
# branch protection would still let a fifth endpoint merge. Gating
# repo-side works too (code scanning's check-failure severity, or a
# ruleset), but that is a console toggle a fork, a clone or a forgetful
# afternoon loses. This travels with the repo.
#
# The logic is in scripts/codeql_gate.py, not inline here. It has had
# twenty-eight defects — five before it was extracted, and twenty-three more found
# by review of the branch that extracted it — every one in its
# enforcement rather than its design, and every one failing OPEN. A
# heredoc cannot be tested, so the first five were caught by a human
# reading it, or not caught at all. It now has a suite in scripts/tests/
# that the `CodeQL gate self-test` job in ci.yml runs on every push,
# including while THIS job cannot build the project. The module header
# lists all twenty-eight; read it before adding anything here.
#
# Only the gate fails the build. netrelish/remote-capable-url-read has two
# legitimate baseline hits; failing on it would teach everyone to ignore a
# red CodeQL job, which is the opposite of what the split was for.
- name: Fail if the gate fired
run: python3 scripts/codeql_gate.py .codeql-results