-
Notifications
You must be signed in to change notification settings - Fork 219
135 lines (116 loc) · 4.43 KB
/
Copy pathdocker.yml
File metadata and controls
135 lines (116 loc) · 4.43 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
name: Docker
on:
push:
branches:
- main
tags:
- "meridian-v*"
pull_request:
branches:
- main
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
# Runs BEFORE build-push, which gates on it. The gap that let #884 ship was
# not just "CI never started the container" — it was that publication did not
# depend on the container working. A runtime image with no /etc/machine-id
# builds perfectly, fails EVERY /v1/messages with a 500, and still reports
# /health healthy, so nothing downstream notices.
smoke:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build runtime image
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: meridian:smoke
cache-from: type=gha
# Publishes the amd64 layers that build-push then reuses, so gating on
# this job costs far less than a second full build.
cache-to: type=gha,mode=max
- name: Machine identity is well formed
run: |
set -euo pipefail
# Assert what the runtime actually asserts. linuxLocalBootIdentity()
# reads the WHOLE file, trim()s it, and tests /^[0-9a-fA-F]{32}$/ — so
# a stray banner line would pass a line-scoped `grep -q` in the build
# and still 500 at runtime.
docker run --rm meridian:smoke sh -c '
id=$(cat /etc/machine-id)
case "$id" in
*[!0-9a-f]* | "") echo "machine-id is not 32 lowercase hex: [$id]" >&2; exit 1 ;;
esac
[ "${#id}" -eq 32 ] || { echo "machine-id length ${#id} != 32: [$id]" >&2; exit 1; }
'
- name: Serve a request
run: |
set -euo pipefail
docker run -d --name smoke \
-e CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-smoke-not-a-real-token \
-p 3456:3456 meridian:smoke
for _ in $(seq 1 60); do
curl -sf localhost:3456/health >/dev/null 2>&1 && break
sleep 1
done
curl -sf localhost:3456/health >/dev/null
status=$(curl -s -o /tmp/body.json -w '%{http_code}' -X POST localhost:3456/v1/messages \
-H 'content-type: application/json' \
-d '{"model":"claude-sonnet-4-6","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}')
echo "status=$status"
cat /tmp/body.json
# Pin to exactly 401. The dummy token cannot authenticate, so 401 is
# proof the request reached the auth layer with the image intact.
# Asserting merely "< 500" would let a 404 from a renamed route pass,
# and would also turn an upstream Anthropic incident (which
# classifyError maps to 502/503/504) into a red build on every PR.
if [ "$status" != "401" ]; then
echo "::error::/v1/messages returned $status, expected 401 — the runtime image cannot serve requests"
exit 1
fi
- name: Container logs
if: failure()
run: docker logs smoke || true
build-push:
needs: smoke
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=match,pattern=meridian-v(.*),group=1
type=match,pattern=meridian-v(\d+\.\d+),group=1
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max