-
Notifications
You must be signed in to change notification settings - Fork 0
257 lines (239 loc) · 10.7 KB
/
Copy pathci.yml
File metadata and controls
257 lines (239 loc) · 10.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
name: CI
on:
push:
branches: [master]
# CI-minute savings (2026-05-21): skip CI on docs-only commits.
paths-ignore:
- '**.md'
- 'docs/**'
- 'CLAUDE.md'
- '.gitignore'
- 'LICENSE'
- 'BUGBASH-*/**'
pull_request:
branches: [master]
paths-ignore:
- '**.md'
- 'docs/**'
- 'CLAUDE.md'
- '.gitignore'
- 'LICENSE'
- 'BUGBASH-*/**'
concurrency:
# CI-minute savings (2026-05-21): cancel prior in-flight CI for the same
# branch/PR when a new commit lands.
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Stale-green guard. A PR can show a green CI run that was executed BEFORE a
# breaking commit landed on the base branch — merging it would ship a broken
# master. This job FAILS if the PR branch does not contain origin/<base> as
# an ancestor, forcing an "Update branch" before the PR can merge.
up-to-date-with-base:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Fail if PR branch is behind its base branch
run: |
BASE="${{ github.event.pull_request.base.ref }}"
git fetch origin "${BASE}" --depth=1
if git merge-base --is-ancestor "origin/${BASE}" HEAD; then
echo "PR branch contains origin/${BASE} — up to date."
else
echo "::error::PR branch is behind origin/${BASE}. Update the branch (merge/rebase ${BASE}) and re-run CI so it validates against current base."
exit 1
fi
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Checkout proto sibling (replace ../proto)
uses: actions/checkout@v7
with:
repository: ${{ vars.PROTO_REPO || format('{0}/proto', github.repository_owner) }}
# REPO_ACCESS_TOKEN is a fine-grained PAT with read on the private
# sibling repos; GITHUB_TOKEN is scoped to THIS repo only and 404s.
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _proto_ci
- run: mv _proto_ci ../proto
- name: Checkout common sibling (replace ../common)
uses: actions/checkout@v7
with:
repository: ${{ vars.COMMON_REPO || format('{0}/common', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _common_ci
- run: mv _common_ci ../common
# Cross-repo registry-iterating tests (CLAUDE.md rule 18) text-walk
# api/internal/models/audit_kinds.go and api/e2e/reliability_contract_test.go
# to assert worker auditKind* wire values match the api source. Without
# this checkout the tests SKIP in CI, leaving cross-repo drift detection
# to developer machines only. INSTANT_API_REPO is set on the test step
# below so findApiRepoRoot() locates the sibling deterministically.
- name: Checkout api sibling (for cross-repo registry-iterating tests)
uses: actions/checkout@v7
with:
repository: ${{ vars.API_REPO || format('{0}/api', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _api_ci
fetch-depth: 1
- run: mv _api_ci ../api
- uses: actions/setup-go@v6
with:
go-version: '1.25'
- run: go build ./...
- run: go vet ./...
- name: Run tests (with INSTANT_API_REPO for cross-repo gate)
env:
INSTANT_API_REPO: ${{ github.workspace }}/../api
run: go test ./... -v -race -count=1
# Wave 5 — push the gated-test result to New Relic so a red run is
# studyable from an NR dashboard, not just the GitHub Actions log.
# if: always() records a FAILED `go test` as InstantCITestRun result=fail
# + InstantCITestFailure. No-ops cleanly without the NR secret.
- name: Emit CI result to New Relic
if: always()
uses: ./.github/actions/nr-ci-event
with:
license-key: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
account-id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
result: ${{ job.status == 'success' && 'pass' || 'fail' }}
suite: build-and-test
pr-number: ${{ github.event.pull_request.number }}
failed-step: ${{ job.status != 'success' && 'go build / vet / test (-race)' || '' }}
repo: ${{ github.repository }}
workflow: ${{ github.workflow }}
branch: ${{ github.ref_name }}
commit-sha: ${{ github.sha }}
log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
event-name: ${{ github.event_name }}
actor: ${{ github.actor }}
# ------------------------------------------------------------------
# GATING integration job (W2-T1).
#
# WHY: the worker's real-DB integration tests
# (internal/jobs/*_integration_test.go) skip under `-short` AND when
# TEST_DATABASE_URL is unset. The fast `build-and-test` job above runs
# `-race` with NO DB service and the deploy.yml test step runs
# `-short` — so in BOTH the integration tests SKIP and gate NOTHING.
# This job runs them WITHOUT `-short` against a real Postgres so the
# trigger→DB round-trip is actually asserted in CI. It mirrors
# coverage.yml's proven Postgres+Redis service block. Per the two-gate
# rule (root CLAUDE.md rule 15 / project_api_two_test_gates_ci_and_deploy),
# the same job is mirrored in deploy.yml — a worker test-infra change
# must land in BOTH or the deploy wedges while PR CI stays green.
integration:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: instant_dev_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
# Integration tests read TEST_DATABASE_URL and run the real-DB path
# (they SKIP when it's unset). TEST_REDIS_URL is wired for parity
# with coverage.yml and for the redis-touching jobs as they land.
TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/instant_dev_test?sslmode=disable
TEST_REDIS_URL: redis://localhost:6379/15
steps:
- uses: actions/checkout@v7
- name: Checkout proto sibling (replace ../proto)
uses: actions/checkout@v7
with:
repository: ${{ vars.PROTO_REPO || format('{0}/proto', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _proto_ci
- run: mv _proto_ci ../proto
- name: Checkout common sibling (replace ../common)
uses: actions/checkout@v7
with:
repository: ${{ vars.COMMON_REPO || format('{0}/common', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _common_ci
- run: mv _common_ci ../common
- name: Checkout api sibling (for migrations + cross-repo tests)
uses: actions/checkout@v7
with:
repository: ${{ vars.API_REPO || format('{0}/api', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _api_ci
fetch-depth: 1
- run: mv _api_ci ../api
- uses: actions/setup-go@v6
with:
go-version: '1.25'
- name: Apply DB migrations to the test database
# Mirrors coverage.yml: api owns the platform_db schema
# (teams / resources / deployments / pending_propagations /
# audit_log) the integration tests round-trip against; apply those
# first, then any worker-local SQL on top (IF NOT EXISTS-guarded).
env:
PGPASSWORD: postgres
run: |
if [ -d ../api/internal/db/migrations ]; then
for f in $(ls ../api/internal/db/migrations/*.sql | sort); do
echo "→ applying api migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null
done
echo "all api migrations applied to instant_dev_test"
else
echo "::error::no api migrations directory found at ../api/internal/db/migrations — integration tests would skip"
exit 1
fi
if [ -d sql ]; then
for f in $(ls sql/*.sql 2>/dev/null | sort); do
echo "→ applying worker migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null || echo "::warning::worker migration $(basename "$f") failed — likely overlap with api schema; continuing"
done
fi
- name: Run real-DB integration tests (NO -short, so they do not skip)
# `-run Integration` scopes the run to the _integration_test.go
# funcs (all carry "Integration" in their name); `-p 1` because
# every package shares the single instant_dev_test DB and default
# parallelism corrupts shared state. Dropping `-short` is what
# makes testhelpers.SetupTestDB's testing.Short() guard pass and
# the real-DB path run instead of skipping.
env:
INSTANT_API_REPO: ${{ github.workspace }}/../api
run: go test ./internal/... -run Integration -count=1 -p 1 -v
# Wave 5 — record the real-DB integration-gate outcome in NR
# (suite=integration) so a red here is visible alongside the unit gate on
# the CI-health dashboard. No-ops without the NR secret.
- name: Emit integration result to New Relic
if: always()
uses: ./.github/actions/nr-ci-event
with:
license-key: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
account-id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }}
result: ${{ job.status == 'success' && 'pass' || 'fail' }}
suite: integration
pr-number: ${{ github.event.pull_request.number }}
failed-step: ${{ job.status != 'success' && 'real-DB integration tests' || '' }}
repo: ${{ github.repository }}
workflow: ${{ github.workflow }}
branch: ${{ github.ref_name }}
commit-sha: ${{ github.sha }}
log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
event-name: ${{ github.event_name }}
actor: ${{ github.actor }}