Skip to content

Commit 6d2bf8b

Browse files
committed
separate smoke build from build.
1 parent 29a26fe commit 6d2bf8b

1 file changed

Lines changed: 71 additions & 67 deletions

File tree

.github/workflows/build-and-check.yml

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -65,69 +65,93 @@ jobs:
6565
username: ${{ github.actor }}
6666
password: ${{ secrets.GITHUB_TOKEN }}
6767

68-
# Single-arch first, loaded into the local daemon so the next step can
69-
# actually run it. The multi-arch build below reuses this layer cache, so
70-
# the extra build is close to free.
71-
#
72-
# A boot smoke test is the only thing that exercises the real CMD, the
73-
# real migrations and the real build args. Two production incidents got
74-
# past a green build here: config/runtime.exs is evaluated at container
75-
# start, not at build time, and a build arg renamed in the Dockerfile but
76-
# not here is silently ignored - which shipped a SQLite build under the
77-
# -postgres tag.
78-
- name: Build image for the smoke test (amd64, loaded locally)
68+
- name: Build and (conditionally) push multi-arch image
7969
uses: docker/build-push-action@v4
8070
with:
8171
context: .
8272
file: Dockerfile
8373
build-args: |
8474
GAMEND_CONTENT_APP_VERSION=${{ env.APP_VERSION }}
8575
GAMEND_DB_ADAPTER=${{ matrix.adapter }}
76+
platforms: linux/amd64,linux/arm64
77+
# amd64 reuses the smoke build's layers; arm64 keeps its own scope.
78+
# Without this every run rebuilt the Rust toolchain, every dependency
79+
# and every NIF from scratch.
80+
cache-from: type=gha,scope=${{ matrix.adapter }}
81+
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}
82+
push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
83+
tags: |
84+
${{ env.IMAGE }}:latest${{ matrix.tags_suffix }}
85+
${{ env.IMAGE }}:${{ github.sha }}${{ matrix.extra_tag_suffix }}
86+
87+
smoke:
88+
name: Boot the image (${{ matrix.adapter }})
89+
runs-on: ubuntu-latest
90+
91+
# Deliberately independent of `build`: this proves the image starts and
92+
# serves traffic, which unit tests cannot. config/runtime.exs is evaluated
93+
# at container start, not at build time, so a green build says nothing
94+
# about whether the app boots. It does not gate publishing.
95+
strategy:
96+
fail-fast: false
97+
matrix:
98+
adapter: [sqlite, postgres]
99+
100+
steps:
101+
- uses: actions/checkout@v4
102+
103+
- uses: docker/setup-buildx-action@v2
104+
105+
- name: Build the image (amd64, loaded locally)
106+
uses: docker/build-push-action@v4
107+
with:
108+
context: .
109+
file: Dockerfile
110+
build-args: |
111+
GAMEND_DB_ADAPTER=${{ matrix.adapter }}
86112
platforms: linux/amd64
87113
load: true
88114
tags: gamend-smoke:${{ matrix.adapter }}
89-
cache-from: type=gha,scope=${{ matrix.adapter }}-amd64
90-
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}-amd64
91-
92-
- name: Start a Postgres for the smoke test
93-
if: matrix.adapter == 'postgres'
94-
run: |
95-
docker run -d --name smoke-db \
96-
-e POSTGRES_PASSWORD=postgres \
97-
-e POSTGRES_USER=postgres \
98-
-e POSTGRES_DB=game_server_prod \
99-
-p 5432:5432 postgres:15
100-
for _ in $(seq 1 30); do
101-
docker exec smoke-db pg_isready -U postgres && break
102-
sleep 2
103-
done
115+
cache-from: type=gha,scope=${{ matrix.adapter }}
116+
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}
104117

105-
- name: Boot the image and check it serves traffic
118+
- name: Boot it and check it serves traffic
106119
run: |
107120
set -euo pipefail
108121
109-
# An env file keeps this out of word-splitting, and keeps the
110-
# docker run line readable whichever adapter is being smoked.
111-
: > /tmp/smoke.env
112-
echo "GAMEND_AUTH_SECRET_KEY_BASE=$(head -c 48 /dev/urandom | base64 | tr -d '\n')" >> /tmp/smoke.env
113-
echo "GAMEND_HTTP_HOST=localhost" >> /tmp/smoke.env
114-
echo "GAMEND_OBSERVABILITY_LOG_LEVEL=info" >> /tmp/smoke.env
122+
docker network create smoke-net
123+
124+
# POSTGRES_* are inherited names — platforms provision them, so they
125+
# are the only database variables the server reads. Using a derived
126+
# GAMEND_DB_POSTGRES_* name here silently configures nothing.
127+
{
128+
echo "GAMEND_AUTH_SECRET_KEY_BASE=$(head -c 48 /dev/urandom | base64 | tr -d '\n')"
129+
echo "GAMEND_HTTP_HOST=localhost"
130+
echo "GAMEND_OBSERVABILITY_LOG_LEVEL=info"
131+
} > smoke.env
115132
116133
if [ "${{ matrix.adapter }}" = "postgres" ]; then
134+
docker run -d --name smoke-db --network smoke-net \
135+
-e POSTGRES_PASSWORD=postgres \
136+
-e POSTGRES_USER=postgres \
137+
-e POSTGRES_DB=game_server_prod \
138+
postgres:15
139+
140+
for _ in $(seq 1 30); do
141+
docker exec smoke-db pg_isready -U postgres >/dev/null 2>&1 && break
142+
sleep 2
143+
done
144+
117145
{
118-
echo "GAMEND_DB_POSTGRES_HOST=host.docker.internal"
119-
echo "GAMEND_DB_POSTGRES_USER=postgres"
120-
echo "GAMEND_DB_POSTGRES_PASSWORD=postgres"
121-
echo "GAMEND_DB_POSTGRES_DB=game_server_prod"
122-
} >> /tmp/smoke.env
146+
echo "POSTGRES_HOST=smoke-db"
147+
echo "POSTGRES_USER=postgres"
148+
echo "POSTGRES_PASSWORD=postgres"
149+
echo "POSTGRES_DB=game_server_prod"
150+
} >> smoke.env
123151
fi
124152
125-
# A real production environment: enough to enter the branches in
126-
# config/host_runtime.exs that only prod takes.
127-
docker run -d --name gamend-smoke \
128-
--add-host=host.docker.internal:host-gateway \
129-
-p 4000:4000 \
130-
--env-file /tmp/smoke.env \
153+
docker run -d --name gamend-smoke --network smoke-net \
154+
-p 4000:4000 --env-file smoke.env \
131155
gamend-smoke:${{ matrix.adapter }}
132156
133157
for _ in $(seq 1 60); do
@@ -148,7 +172,7 @@ jobs:
148172
exit 1
149173
150174
# The build arg has to reach the compiled code, not just the shell: an
151-
# ignored GAMEND_DB_ADAPTER used to publish a SQLite build as -postgres.
175+
# ignored GAMEND_DB_ADAPTER once published a SQLite build as -postgres.
152176
- name: Verify the image compiled against the expected adapter
153177
run: |
154178
set -euo pipefail
@@ -160,32 +184,12 @@ jobs:
160184
sqlite) echo "$adapter" | grep -q "Ecto.Adapters.SQLite3" ;;
161185
esac
162186
163-
- name: Smoke test teardown
187+
- name: Teardown
164188
if: always()
165189
run: |
166190
docker logs gamend-smoke 2>&1 | tail -50 || true
167191
docker rm -f gamend-smoke smoke-db 2>/dev/null || true
168-
169-
- name: Build and (conditionally) push multi-arch image
170-
uses: docker/build-push-action@v4
171-
with:
172-
context: .
173-
file: Dockerfile
174-
build-args: |
175-
GAMEND_CONTENT_APP_VERSION=${{ env.APP_VERSION }}
176-
GAMEND_DB_ADAPTER=${{ matrix.adapter }}
177-
platforms: linux/amd64,linux/arm64
178-
# amd64 reuses the smoke build's layers; arm64 keeps its own scope.
179-
# Without this every run rebuilt the Rust toolchain, every dependency
180-
# and every NIF from scratch.
181-
cache-from: |
182-
type=gha,scope=${{ matrix.adapter }}-amd64
183-
type=gha,scope=${{ matrix.adapter }}-arm64
184-
cache-to: type=gha,mode=max,scope=${{ matrix.adapter }}-arm64
185-
push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
186-
tags: |
187-
${{ env.IMAGE }}:latest${{ matrix.tags_suffix }}
188-
${{ env.IMAGE }}:${{ github.sha }}${{ matrix.extra_tag_suffix }}
192+
docker network rm smoke-net 2>/dev/null || true
189193
190194
lint:
191195
name: Elixir lint

0 commit comments

Comments
 (0)