-
Notifications
You must be signed in to change notification settings - Fork 15
361 lines (341 loc) · 12.4 KB
/
Copy pathci.yml
File metadata and controls
361 lines (341 loc) · 12.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
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
name: CI
on:
push:
branches:
- "**"
tags:
- "v*.*.*"
pull_request:
workflow_dispatch:
inputs:
confirm_publish:
description: "Type true to publish Docker images to GHCR"
required: true
default: "false"
permissions:
contents: read
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: Ruff
run: uv run ruff check .
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: Format
run: uv run ruff format --check .
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: Mypy
run: uv run mypy src tests
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: Coverage
run: uv run pytest --cov=base --cov-report=term-missing --cov-fail-under=80
production-policy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: Runtime production policy tests
run: >-
uv run pytest
tests/unit/test_config_template_security.py
tests/unit/test_db_and_models.py
tests/unit/test_registry_extended.py
tests/unit/test_admin_registry_proxy.py
-q
postgres-orm:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: base
POSTGRES_PASSWORD: base
POSTGRES_DB: base_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U base -d base_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
BASE_TEST_DATABASE_URL: postgresql+asyncpg://base:base@localhost:5432/base_test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install dependencies
run: uv sync --extra dev --extra master
- name: PostgreSQL ORM integration tests
run: uv run pytest tests/integration -m postgres -q
docker-build:
needs:
- ruff
- format
- mypy
- coverage
- production-policy
- postgres-orm
runs-on: ubuntu-latest
strategy:
matrix:
include:
- image: base
dockerfile: docker/Dockerfile.validator
build_args: ""
- image: base-master
dockerfile: docker/Dockerfile.master
build_args: ""
- image: base-validator-runtime
dockerfile: docker/Dockerfile.validator-runtime
build_args: |
AGENT_CHALLENGE_REF=d02f7329b17dbc3b663bcd518c746022bbc0afe8
PRISM_REF=680440d59411fa578ba564b0b04bf437a78c7f66
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build ${{ matrix.image }} image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
build-args: ${{ matrix.build_args }}
push: false
tags: ghcr.io/baseintelligence/${{ matrix.image }}:ci-${{ github.sha }}
docker-publish:
if: >-
github.event_name != 'pull_request' &&
(github.ref == 'refs/heads/main' ||
startsWith(github.ref, 'refs/tags/v') ||
(github.event_name == 'workflow_dispatch' && inputs.confirm_publish == 'true'))
needs:
- docker-build
- postgres-orm
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
include:
- image: base
dockerfile: docker/Dockerfile.validator
build_args: ""
- image: base-master
dockerfile: docker/Dockerfile.master
build_args: ""
- image: base-validator-runtime
dockerfile: docker/Dockerfile.validator-runtime
build_args: |
AGENT_CHALLENGE_REF=d02f7329b17dbc3b663bcd518c746022bbc0afe8
PRISM_REF=680440d59411fa578ba564b0b04bf437a78c7f66
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Verify release tag belongs to main
if: startsWith(github.ref, 'refs/tags/v')
run: |
git fetch --no-tags origin main
git merge-base --is-ancestor "${GITHUB_SHA}" origin/main
- uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/baseintelligence/${{ matrix.image }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{raw}}
type=sha,prefix=sha-
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and publish ${{ matrix.image }} image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
build-args: ${{ matrix.build_args }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
publish-self-update-manifest:
# On every merge to main, AFTER the images publish, overwrite the base
# self-update manifest at a STABLE url (raw file on the dedicated `release`
# branch). The base-supervisor http_manifest_detector polls this JSON and
# advances the on-disk `base` release whenever `version` changes (architecture
# sec 9 / research G-A2). `version` is MONOTONIC + filesystem-safe and
# `source_url` is the codeload tarball for this exact ref.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs:
- docker-publish
- postgres-orm
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Publish self-update manifest to the release branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Retry wrapper: the github-actions[bot] force-push to `release`
# intermittently 403s on the first attempt (a transient GitHub
# permission hiccup a re-run clears), so retry with backoff to keep the
# self-update manifest publish durable instead of needing a manual re-run.
retry() {
local n=0
until "$@"; do
n=$((n + 1))
if [ "$n" -ge 5 ]; then
echo "command failed after ${n} attempts: $*" >&2
return 1
fi
echo "attempt ${n} failed (transient); retrying in $((n * 5))s..." >&2
sleep "$((n * 5))"
done
}
# MONOTONIC, filesystem-safe version: the strictly-increasing GitHub
# run number (advances on every merge to main) plus the merged commit
# sha for traceability. No "/", never "." / ".." -> accepted by
# AvailableRelease.__post_init__ (src/base/supervisor/self_update.py).
version="r${{ github.run_number }}-sha-${{ github.sha }}"
# Codeload tarball for THIS ref; tarball_stager strips the single
# top-level dir then runs `uv sync` on the staged release.
source_url="https://codeload.github.com/${{ github.repository }}/tar.gz/${{ github.sha }}"
work="$(mktemp -d)"
printf '{\n "version": "%s",\n "source_url": "%s"\n}\n' \
"${version}" "${source_url}" > "${work}/self-update-manifest.json"
echo "publishing self-update manifest version=${version}"
cat "${work}/self-update-manifest.json"
cd "${work}"
git init -q
git checkout -q -b release
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add self-update-manifest.json
git commit -q -m "self-update manifest ${version}"
retry git push -q --force \
"https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" \
release
github-release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs:
- docker-publish
- postgres-orm
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86
- name: Verify tag belongs to main
run: |
git fetch --no-tags origin main
git merge-base --is-ancestor "${GITHUB_SHA}" origin/main
- name: Prepare release metadata
id: release
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
python - "${version}" <<'PY'
import json
import pathlib
import sys
import tomllib
expected = sys.argv[1]
project = tomllib.loads(pathlib.Path("pyproject.toml").read_text())
manifest = json.loads(
pathlib.Path(
"src/base/challenge_sdk/release_manifest.json"
).read_text()
)
assert project["project"]["version"] == expected
assert manifest["artifact_version"] == expected
assert manifest["release_id"] == f"v{expected}"
PY
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "SOURCE_DATE_EPOCH=1704067200" >> "$GITHUB_ENV"
- name: Build immutable Python release artifacts
run: |
uv sync --locked --extra dev
uv build --no-build-isolation --wheel --sdist --out-dir dist
sha256sum dist/* | tee dist/SHA256SUMS
- name: Create GitHub release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65
with:
tag_name: ${{ github.ref_name }}
name: BASE ${{ steps.release.outputs.version }}
generate_release_notes: true
append_body: true
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
make_latest: ${{ !contains(github.ref_name, '-') }}
files: |
dist/base-${{ steps.release.outputs.version }}-py3-none-any.whl
dist/base-${{ steps.release.outputs.version }}.tar.gz
dist/SHA256SUMS
body: |
## Container Images
- `ghcr.io/baseintelligence/base:${{ steps.release.outputs.version }}`
- `ghcr.io/baseintelligence/base:${{ github.ref_name }}`
- `ghcr.io/baseintelligence/base:sha-${{ github.sha }}`
- `ghcr.io/baseintelligence/base-master:${{ steps.release.outputs.version }}`
- `ghcr.io/baseintelligence/base-master:${{ github.ref_name }}`
- `ghcr.io/baseintelligence/base-master:sha-${{ github.sha }}`
## Deployment Notes
Production deployments should pin the SemVer image tag plus the immutable digest. The `latest` tag is published only from `main`, not from release tags.
## Documentation
See `docs/versioning.md` for the SemVer, Git tag, GHCR tag, GitHub Release, and production image policy.