Repository navigation
456 lines (424 loc) · 21.5 KB
/
Copy pathgo.yml
File metadata and controls
456 lines (424 loc) · 21.5 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
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
name: codefly core
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Cancel an in-progress run when a new push to the same ref lands.
# A commit that's been superseded doesn't need to finish CI — saves
# minutes and shortens the feedback loop. Group by workflow+ref so
# main-branch pushes only cancel each other (not, say, a PR run).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Read by default, and no job that runs code under review names a secret of any
# kind -- the Slack webhook moved into `notify`, which checks out nothing. A
# repository secret is not governed by `permissions:` at all, so narrowing the
# built-in token would have left a credential in the job that runs the suite
# exactly as reachable as it was before.
#
# On a pull request every job here checks out that pull
# request's head and then runs its code -- the suite, the race detector, the
# guards, a pnpm install -- and `permissions:` is job-wide, so a write at this
# scope would be a write held by all of it. The one thing in this workflow that
# writes (the coverage badge) is a job of its own, gated to a push of the
# default branch. See docs/ci-credentials.md;
# internal/ciguard/untrusted_code_test.go holds it.
permissions:
contents: read
jobs:
# Separate from build deliberately. The gate needs full history and a network
# round trip to the BSR, and neither belongs in the job that runs the test
# suite: inside build, a buf.build hiccup would abort before `go test` ever
# ran and report as a schema break, and a red Build would say nothing about
# which half failed. Split, the two answer independently and in parallel.
proto:
name: Proto
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# buf reads the baseline out of this checkout's own .git, and the
# default depth-1 fetch populates no refs/remotes/origin/*, so there
# would be no main to diff against.
fetch-depth: 0
- name: set up buf
uses: bufbuild/buf-setup-action@a47c93e0b1648d5651a065437926377d060baa99 # v1.50.0
with:
# Must match the buf the proto companion bakes
# (companions/proto/Dockerfile); internal/ciguard's
# TestWorkflowBufMatchesTheCompanionImage fails when the two drift.
# Left unset, the action installs 1.50.0 — its own default, not the
# latest.
version: 1.73.0
github_token: ${{ github.token }}
- name: proto breaking-change guard
# proto/ is the schema source of truth for the whole ecosystem, and
# nothing in CI read it — a wire-breaking edit and its regenerated
# bindings were verified only on the contributor's machine.
#
# The script is the gate, and `make buf-breaking` runs the same one, so
# what CI checks and what a developer can run before pushing cannot
# drift into two programs. It owns the baseline (the merge base), the
# BSR retry, and the one way a reviewed schema deletion may land: a
# commit touching proto/ that declares the break in Conventional
# Commits. An undeclared break is still exit 100, and the findings are
# printed either way. internal/ciguard holds all three of those.
run: ./scripts/check_proto_breaking.sh
- name: proto lint guard
# proto/buf.yaml has selected BASIC + COMMENTS since the module landed
# and proto/README.md has always said to run `buf lint` before
# committing, but no workflow ran it. The obligation rotted into 363
# standing COMMENTS findings, which is what an undocumented field costs
# once the next real one is indistinguishable from them (#567).
#
# Exit 100 and the retry loop mean what they mean in the step above.
working-directory: proto
run: |
for attempt in 1 2 3; do
if buf lint; then
exit 0
else
code=$?
fi
if [ "$code" -eq 100 ]; then
exit 100
fi
echo "buf exited $code (attempt $attempt/3), not a rule violation; retrying in 15s" >&2
sleep 15
done
echo "buf lint could not complete after 3 attempts" >&2
exit 1
build:
name: Build
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Pull Alpine Image
run: docker pull alpine:latest
# We use it for tests
- name: set up
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.27.3"
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: agent contract release notes
run: python3 -m unittest discover -s scripts -p "test_*.py"
- name: version/tag drift guard
# version/info.codefly.yaml is embedded into the binary and gates every
# package install through composition's minimum-codefly-version check,
# but no test reads it — so it has silently rotted (0.3.8 while v0.3.18
# shipped; 0.3.20 while v0.3.24 shipped). Fail here instead of at a
# user's install. Reads tag names via ls-remote, so the shallow
# checkout above is fine.
run: ./scripts/check_version_tag.sh
- name: Cache Go Modules
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
continue-on-error: true
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go
- name: Install Dependencies
run: go mod tidy && go mod download
- name: CGO-free surface guard
# cgo-ness is a link-time property of the import graph: a new import can
# silently drag a cgo-only package (the tree-sitter grammars under
# code/semantic) into a surface that must stay CGO-free, and nothing
# fails until a downstream CGO_ENABLED=0 build (companion publish, an
# alpine image) tries to link — weeks later, one repo away. This builds
# every package except the documented cgo allowlist with CGO_ENABLED=0
# so creep fails HERE, at the PR that introduces it. See docs/cgo.md.
run: ./scripts/check_cgo_free.sh
- name: Darwin surface guard
# Every runner in this repository is ubuntu, so nothing in CI ever
# compiles the GOOS=darwin files: runners/base's process-identity and
# postgres-IPC implementations, plus the darwin-only tests under
# runners/ and toolbox/launch. A type error in one of them merges green
# and breaks only on a developer's machine. Type-check that surface from
# this runner so it fails HERE instead.
#
# Mirrors scripts/check_cgo_free.sh: the tree-sitter grammars under
# code/semantic cannot cross-compile without cgo, so they are excluded
# and the supported CGO-free constructor is selected with
# codefly_nosemantic. vet covers test files, which `go build` does not.
#
# This type-checks only. RUNNING the darwin tests still needs a macOS
# runner, which would add a required status check — see
# docs/runbooks/branch-protection.md.
run: |
set -euo pipefail
pkgs=()
while IFS= read -r line; do
pkgs+=("$line")
done < <(GOOS=darwin go list -f '{{if .GoFiles}}{{.ImportPath}}{{end}}' ./... \
| grep -vxF -e "github.com/codefly-dev/core/code/semantic")
echo "Type-checking the darwin surface: ${#pkgs[@]} packages"
CGO_ENABLED=0 GOOS=darwin go vet -tags codefly_nosemantic "${pkgs[@]}"
- name: Install apt deps (bubblewrap, ripgrep)
# bubblewrap → core/runners/sandbox tests (default + sandbox_e2e)
# ripgrep → core/code search tests (real-repo grep coverage)
# Both are part of the strict signal — codefly's security
# stack and code-search depend on them, so a broken install
# MUST fail CI. The retry loop handles transient mirror flakes
# (azure.archive.ubuntu.com periodically hiccups for ~5-10
# minutes); sustained outages still fail loud as intended.
run: |
for i in 1 2 3; do
sudo apt-get update && sudo apt-get install -y bubblewrap ripgrep && exit 0
echo "apt failed (attempt $i / 3); waiting 30s before retry"
sleep 30
done
echo "apt install failed after 3 attempts" >&2
exit 1
- name: Install uv
# Strict — uv drives core/runners/python's formula runner tests
# (formula_run_test.go), which exercise the REAL uv+python toolchain
# and fail-loud (requireUv never skips) when uv is absent. astral-sh's
# setup-uv is the de-facto standard installer for GH Actions: pinned,
# deterministic, adds uv to PATH. uv provisions its own Python on
# demand, so no separate python setup is needed.
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
- name: Python agent contract SDK
env:
PYTHONPATH: generated/python/codefly-cli/codefly_cli
run: >-
uv run --no-project --with protobuf==7.36.1 --with grpcio==1.83.1
--with googleapis-common-protos==1.75.3
python -m unittest discover
-s generated/python/codefly-cli/codefly_cli/tests
-p "test_*_contract.py"
- name: proto companion Python plugins
# The bindings, facade and strip plugins the proto companion bakes
# (companions/proto/facades/python): map_entry kept through the strip,
# each streaming RPC on its own gateway seam, bindings importing only
# what their types need. Run on the protobuf and grpcio-tools the
# companion's /venv installs (companions/proto/Dockerfile), grpcio held
# to grpcio-tools' release; internal/ciguard's
# TestFacadePluginSuiteRunsOnTheCompanionsPins fails when they drift.
# googleapis-common-protos is the suite's own (the image carries none):
# it lets the google.rpc import case run. `protoc` is the pinned
# grpc_tools' compiler. A skipped case fails the step: here it would be
# an unverified plugin, not an absent optional tool.
run: |
set -euo pipefail
shim="$RUNNER_TEMP/grpc-tools-protoc"
mkdir -p "$shim"
printf '#!/bin/sh\nexec python -m grpc_tools.protoc "$@"\n' > "$shim/protoc"
chmod +x "$shim/protoc"
PATH="$shim:$PATH" uv run --no-project --python 3.12 \
--with protobuf==5.29.3 --with grpcio-tools==1.71.0 --with grpcio==1.71.0 \
--with googleapis-common-protos==1.70.0 --with pytest==9.1.1 \
python -m pytest -p no:cacheprovider -rs companions/proto/testdata/facade \
| tee "$RUNNER_TEMP/facade-pytest.txt"
if grep -qE '[0-9]+ skipped' "$RUNNER_TEMP/facade-pytest.txt"; then
echo "a proto companion Python plugin case was skipped; every case must run here" >&2
exit 1
fi
- name: Install Nix
# Strict — nix is core to the platform; if it doesn't install,
# CI must fail loud rather than silently skipping nix tests.
# DeterminateSystems' installer is the de-facto standard for
# GH Actions: deterministic, no daemon issues on ephemeral
# runners.
#
# This ref tracked the `main` BRANCH before it was pinned. The pinned
# commit is also tagged v23, so Dependabot's github-actions updater can
# still move it (it bumps SHA pins to newer releases, and would have
# nothing to move an untagged branch commit to).
uses: DeterminateSystems/nix-installer-action@3138316df39ed29be04236d7ffc686fa525866aa # v23
- name: Allow unprivileged user namespaces (for bwrap)
# Ubuntu 24.04 ships with kernel.apparmor_restrict_unprivileged_userns=1
# which blocks bubblewrap's user-namespace setup ("bwrap: setting
# up uid map: Permission denied"). Disabling the restriction is
# the standard CI workaround — bwrap then works in unprivileged
# mode, which is what our sandbox layer expects.
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
#
# - name: golangci-lint
# uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
# with:
# version: v1.55
- name: govulncheck audit
# scripts/govulncheck.sh mirrors `codefly audit go` / `codefly
# agent build`'s audit logic: runs govulncheck, partitions
# findings against .govulncheck.yaml suppressions + staleness,
# fails only when an unsuppressed vuln has an upstream fix
# available (unpatched-upstream issues can't be cleared by a
# rebuild).
#
# This deliberately does NOT shell out to the codefly CLI:
# github.com/codefly-dev/cli is a PRIVATE module, and core's CI
# checks out core alone with no credentials for sibling repos, so
# `go install github.com/codefly-dev/cli@latest` fails with
# "could not read Username" / "Authentication failed". The script
# only needs govulncheck (public, go-installable) and is fully
# self-contained — no private-repo dependency.
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
./scripts/govulncheck.sh
- name: generate test generate coverage
# Single strict signal. -tags=sandbox_e2e,nix_required opts
# into the integration tests that depend on bubblewrap (the
# OS sandbox layer) and the nix binary. Both are installed
# strict above; if either install failed, CI already failed
# before reaching this step.
#
# NOT included: proto_companion_required. Those tests
# (grpc_test.go, openapi_test.go) need the ghcr.io/codefly-dev/proto
# Docker image which can't be built from core's CI alone
# (it cross-builds the codefly CLI from a sibling checkout).
# Run locally with `go test -tags=proto_companion_required`,
# OR — once codefly CLI is installable in CI — bring the
# image build into the workflow as `codefly companion build
# proto` and add the tag here.
run: go test ./... -tags=sandbox_e2e,nix_required -coverprofile=cover.out -covermode=atomic -coverpkg=./...
- name: check test coverage
id: coverage
# The THRESHOLD check, on every pull request and every push. No
# `git-token:`: writing the badge from here would need this job's token
# to carry `contents: write`, and this job has just run the suite from
# the head of whatever branch is under review. The badge is written by
# the coverage-badge job below, from the profile this one uploads.
uses: vladopajic/go-test-coverage@f484eec846448c97c777a26f8b58c5a69a327e12 # v2.20.0
with:
config: ./.testcoverage.yaml
- name: upload the coverage profile
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: coverage-profile
path: cover.out
retention-days: 1
- name: test with race detection
# Strict, separate from coverage. Races are real bugs (we
# found a bytes.Buffer concurrent-write race in NativeProc
# that flaked Linux CI for months), so the race detector
# has to run alongside the regular suite — not as a
# commented-out optional. CLI already does this; bring
# core to parity.
#
# We skip sandbox_e2e + nix_required tags here: the race
# detector roughly doubles test runtime, and the integration
# suites also run in the regular step above with full
# coverage. Race-detector signal lands fastest by sticking
# to the unit + light-integration scope.
run: go test -race ./... -timeout 10m
# The notifications were two steps inside Build, skipped on pull requests by
# an `if:`. That leaves the webhook named in the job that runs the suite, and
# a step condition decides whether a STEP runs -- it is not a boundary around
# the job's credential, and a reader has to follow step order to see what is
# safe. As a job of its own the webhook is simply not in the job that runs
# unreviewed code, which is both true and legible.
# internal/ciguard/credential_isolation_test.go holds that.
#
# Still push-and-main only, and for the original reason as well as this one: a
# run triggered by Dependabot (or a fork) is handled as though it came from a
# fork, so `secrets.*` resolves to the empty string, and slack-github-action
# rejects an empty webhook outright -- `throw new Error('Need to provide at
# least one botToken or webhookUrl')` -- which turned otherwise green runs
# red. These alerts are about main's health anyway.
#
# `always()` so a failed Build still reports, and the outcome is read from
# `needs` rather than from `failure()`, which would describe this job instead.
notify:
name: Notify Slack
needs: build
if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
# Nothing to read and nothing to write: no checkout, no repository content,
# one webhook call.
permissions: {}
steps:
- name: Notify Slack on Failure
if: needs.build.result == 'failure'
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
# The action resolves the webhook URL from SLACK_WEBHOOK_URL below,
# but reads the type from this input alone -- there is no environment
# fallback for it -- and throws before sending when it is absent.
webhook-type: incoming-webhook
payload: |
{
"text": "Build Failed in ${{ github.repository }} on ${{ github.ref }}. See GitHub Actions for more details."
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Notify Slack on Success
if: needs.build.result == 'success'
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
# See the failure notification above.
webhook-type: incoming-webhook
payload: |
{
"text": "Build Succeeded in ${{ github.repository }} on ${{ github.ref }}. Great job!"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
# README.md renders the coverage badge from the `badges` branch, so something
# has to push to it -- and pushing is the only write this workflow performs.
# Split out, it is a job that holds `contents: write` and runs no code under
# review: it reads the profile the Build job produced as an artifact and the
# threshold config from the default branch, and runs the same pinned action.
#
# Gated to a push of the default branch, so no pull request run reaches it.
# That gate also keeps it out of the required status checks -- a job with an
# `if:` is never derived as safe to require -- see
# docs/runbooks/branch-protection.md.
coverage-badge:
name: Coverage badge
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Named, not inherited. An absent ref means the triggering ref, and a
# trusted trigger is not a statement about which code is checked out;
# this job holds a write token, so it says which tree it wants.
ref: main
# The action authenticates with the `git-token:` input below; nothing
# here pushes over git, so the write credential stays out of
# .git/config.
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: coverage-profile
- name: publish the coverage badge
# Same pinned action and same config as the gate in Build, so the badge
# cannot report a number the gate did not decide on.
# internal/ciguard/coverage_gate_test.go checks every use of it against
# the Makefile's pin.
uses: vladopajic/go-test-coverage@f484eec846448c97c777a26f8b58c5a69a327e12 # v2.20.0
with:
config: ./.testcoverage.yaml
git-token: ${{ github.token }}
## Name of the branch where badges are stored; ideally an orphan
## branch. README.md reads coverage.svg from it.
git-branch: badges
pnpm-source-evidence:
name: pnpm source evidence
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '26'
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
with:
package_json_file: agents/services/sbom/testdata/pnpm/package.json
- run: go test -race -tags=pnpm_required ./agents/services/audit ./agents/services/sbom