Repository navigation
301 lines (273 loc) · 13.4 KB
/
Copy pathcode-map.yml
File metadata and controls
301 lines (273 loc) · 13.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
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
name: Code map
# The merge gate: the code map, the intent manifest against it, and every
# requirement row's evidence pin (specs/requirements/INTENT_PINS.tsv). It runs
# on every pull request, with no path condition (owner, 2026-09-28): a change
# to CONFORMANCE §8 alone can move a row's status and so its pin, and a
# required check skipped by a path filter would read as passed. Its jobs have
# no layer condition for the same reason.
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
inputs:
base:
description: "The commit this one's pins are compared with (pin evidence)"
type: string
default: origin/main
permissions:
contents: read
# A newer push to the same pull request supersedes this run: it is cancelled
# rather than finished for a result nobody reads. A run for anything else (a
# push to main, a dispatch) is its own group and is never cancelled: each
# merge commit is checked by its own result.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# The code map (make requirement-map): every source file hashed with BLAKE3;
# each shipped build's call graph from a rust-analyzer index of that build
# (Android: the real aarch64-linux-android target with the NDK Gradle pins;
# the storage node: Linux, which only this job builds), and the host test
# build. Every definition is reached, dead or indeterminate in each build,
# with a reason code; the map refuses an index whose analyzer log shows a
# failure or a line of no known kind, and definitions that do not add up.
# The page and its tables are kept as an artifact.
requirement-map:
name: Code map
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
# The pins' tests read history: a commit's parent, and the first
# commit, which holds no pins.
fetch-depth: 0
- name: Free disk space
# Three indexes build their build scripts for three targets. The
# Android SDK stays: the NDK comes from it.
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL \
/usr/local/.ghcup /usr/local/share/boost /usr/share/swift
df -h /
- uses: dtolnay/rust-toolchain@1.98.0
with:
components: rust-analyzer
targets: aarch64-linux-android
- uses: Swatinem/rust-cache@v2
with:
key: requirement-map
- name: Install CI system deps
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler
- name: Install the NDK version Gradle pins
# `yes` is ended by a broken pipe once sdkmanager has read enough;
# `|| :` covers yes alone, so sdkmanager's own status still decides.
run: |
NDK_VERSION="$(sed -n 's/.*ndkVersion = "\([^"]*\)".*/\1/p' dsm_client/android/app/build.gradle.kts | head -1)"
test -n "$NDK_VERSION"
(yes || :) | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses > /dev/null
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install "ndk;$NDK_VERSION" > /dev/null
test -d "$ANDROID_HOME/ndk/$NDK_VERSION/toolchains/llvm/prebuilt"
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION" >> "$GITHUB_ENV"
# The expensive half of this job (the fixture run, the three indexes and
# the mutation cases, about seventeen of its twenty minutes) reads code,
# configuration and this workflow: never a pin and never prose. The
# other half (the map's tables, its check, the intent manifest, every
# pin, the pins' own tests) runs on every commit. So the expensive half
# is kept under a key of everything it can read: every tracked file
# except `*.md` and specs/requirements/INTENT_PINS.tsv, and the runner
# image. The intent manifest stays in the key, because the mutation
# cases read the map its root questions shape. A commit that changes
# only pins or prose finds the indexes and fixture run of a commit whose
# every other byte is the same, verified there, and skips that half.
# Any other change misses and rebuilds it. The key matches exactly or
# not at all: there are no restore-keys, so a near match is a miss.
- name: The key of what the expensive half reads
id: key
run: |
inputs="$(git ls-tree -r HEAD | awk -F'\t' '$2 !~ /\.md$/ && $2 != "specs/requirements/INTENT_PINS.tsv"' | sha256sum | cut -d' ' -f1)"
echo "key=code-map-v1-${ImageOS}-${ImageVersion}-${inputs}" >> "$GITHUB_OUTPUT"
# Besides the indexes and the tables beside them, the map reads one
# thing from ./target: each build script's `output` (what it printed to
# cargo), which the indexes' cargo runs leave under target/debug/build
# and target/<triple>/debug/build. A shipped crate's build script that
# sets a cfg is refused from it, so it is kept with them.
- name: The indexes and fixture run of a commit with the same key
id: kept
uses: actions/cache/restore@v4
with:
key: ${{ steps.key.outputs.key }}
path: |
target/requirement-map/android.scip
target/requirement-map/android.log
target/requirement-map/node.scip
target/requirement-map/node.log
target/requirement-map/tests.scip
target/requirement-map/tests.log
target/requirement-map/android-features.txt
target/requirement-map/android-features-indexed.txt
target/requirement-map/android-packages.txt
target/requirement-map/node-packages.txt
target/requirement-map/node-features.txt
target/requirement-map/node-features-indexed.txt
target/requirement-map/fixture
target/debug/build/*/output
target/*/debug/build/*/output
- name: The map reads its fixture as expected
# Dispatch through a type argument, a qualified path and a value, and
# a type only named: tools/requirement_map/fixture/expected.tsv.
id: fixture
if: steps.kept.outputs.cache-hit != 'true'
run: make requirement-map-fixture MAP=target/requirement-map
- name: Index
id: indexes
if: steps.kept.outputs.cache-hit != 'true'
run: make requirement-map-indexes MAP=target/requirement-map
- name: Map
# From the indexes, built above or kept; it refuses an index whose
# analyzer log shows a failure either way.
id: map
run: make requirement-map-tables MAP=target/requirement-map
- name: The map holds no contradiction and keeps its committed facts
# Contradictions in its own tables; sentinels, entry points and
# counts against ci/requirement_map.*.tsv. committed.tsv (kept below)
# is what this map reads, for a person to review and commit.
id: check
if: ${{ !cancelled() && steps.map.outcome == 'success' }}
run: make requirement-map-check MAP=target/requirement-map
- name: The intent manifest holds against the map
# specs/requirements/INTENT_MANIFEST.tsv: each row's outcome, and a
# failure for every gap a Met requirement has; both builds must be
# indexed. intent.tsv and unspecified.tsv are kept below.
if: ${{ !cancelled() && steps.map.outcome == 'success' }}
run: make requirement-map-intent MAP=target/requirement-map INTENT_BUILT=android,node
- name: The evidence pins refuse what they must
# ci/test_intent_pins.py: each pin state planted in copies of the
# fixture's manifest, requirements and pins; the repin and bootstrap
# rules; board evidence; test names over this map.
if: ${{ !cancelled() && steps.map.outcome == 'success' }}
run: make requirement-map-pin-tests MAP=target/requirement-map
- name: Every mutation case moves what it names and nothing else
# tools/requirement_map/fixture/mutations.toml: fixture changes, the
# app's declarations, a real-tree re-index, and faults planted in
# copies of the maps, each in a temporary copy. With the indexes kept,
# every input of every case is what it was where they passed.
id: mutations
if: ${{ !cancelled() && steps.map.outcome == 'success' && steps.kept.outputs.cache-hit != 'true' }}
run: make requirement-map-mutations MAP=target/requirement-map
- name: Keep the indexes and fixture run for a commit with the same key
# Only once this run built them and every step that verifies them
# passed: the fixture, the map, its check and every mutation case. The
# intent manifest and the pins need not hold: a commit re-pinning the
# rows this one left stale is exactly the one that reuses them.
if: >-
${{ !cancelled() && steps.kept.outputs.cache-hit != 'true'
&& steps.fixture.outcome == 'success' && steps.indexes.outcome == 'success'
&& steps.map.outcome == 'success' && steps.check.outcome == 'success'
&& steps.mutations.outcome == 'success' }}
uses: actions/cache/save@v4
with:
key: ${{ steps.key.outputs.key }}
path: |
target/requirement-map/android.scip
target/requirement-map/android.log
target/requirement-map/node.scip
target/requirement-map/node.log
target/requirement-map/tests.scip
target/requirement-map/tests.log
target/requirement-map/android-features.txt
target/requirement-map/android-features-indexed.txt
target/requirement-map/android-packages.txt
target/requirement-map/node-packages.txt
target/requirement-map/node-features.txt
target/requirement-map/node-features-indexed.txt
target/requirement-map/fixture
target/debug/build/*/output
target/*/debug/build/*/output
- name: Keep the map
if: always()
uses: actions/upload-artifact@v7
with:
name: code-map
path: |
target/requirement-map/code-map.html
target/requirement-map/files.tsv
target/requirement-map/defs.tsv
target/requirement-map/reach.tsv
target/requirement-map/edges.tsv
target/requirement-map/accounting.tsv
target/requirement-map/health.tsv
target/requirement-map/token-read.tsv
target/requirement-map/artifacts.tsv
target/requirement-map/committed.tsv
target/requirement-map/intent.tsv
target/requirement-map/unspecified.tsv
target/requirement-map/root-queries.tsv
target/requirement-map/pins.tsv
target/requirement-map/tree
target/requirement-map/sources.txt
target/requirement-map/inputs.txt
target/requirement-map/analyzer.txt
target/requirement-map/*.log
# The half of the gate that runs tests. A pin binds its row's code and its
# evidence tests' code, so the map job needs to run no test; what it cannot
# know is that a pin this change adds or alters stands on evidence that
# passed. This job runs, on this commit, the evidence of every requirement
# row whose pin is new, changed or missing against the base, and fails
# unless each test passed. Where no pins are committed yet, that is every
# row: its log is what the one-time bootstrap pins from.
pin-evidence:
name: Pin evidence
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dsm_node_test
ports:
- 5432:5432
# Without a health check the job races the container's first accept().
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 20
# The storage node's store is Postgres, and the SDK suites run their nodes
# on it: every node-backed test needs this server.
env:
DSM_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/dsm_node_test?sslmode=disable
steps:
- uses: actions/checkout@v7
with:
# The base commit's pins are read from history.
fetch-depth: 0
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL /usr/local/.ghcup \
/usr/local/share/boost /usr/share/swift
df -h /
- uses: dtolnay/rust-toolchain@1.98.0
- uses: Swatinem/rust-cache@v2
with:
key: pin-evidence
- name: Install CI system deps
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler
- name: Every new, changed or missing pin's evidence passes on this commit
env:
BASE: ${{ github.event.pull_request.base.sha || inputs.base || github.event.before }}
run: python3 ci/intent_pins.py evidence --base "$BASE" --out pin-evidence.log
- name: Keep the evidence log
if: always()
uses: actions/upload-artifact@v7
with:
name: pin-evidence-log
path: pin-evidence.log