Skip to content

Commit 03e2456

Browse files
committed
add db test + gate ci behind it, modernize publish workflow
1 parent 435349a commit 03e2456

4 files changed

Lines changed: 185 additions & 9 deletions

File tree

‎.github/workflows/docker-publish.yml‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,21 @@ env:
2121
REGISTRY: ghcr.io
2222
# github.repository as <account>/<repo>
2323
IMAGE_NAME: ${{ github.repository }}
24+
IMAGE_PLATFORMS: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
2425

2526

2627
jobs:
28+
# gate: don't build or publish anything unless the tests pass
29+
test:
30+
runs-on: ubuntu-latest
31+
steps:
32+
- name: Checkout repository
33+
uses: actions/checkout@v5
34+
- name: Run tests
35+
run: ./test.sh
36+
2737
build:
38+
needs: test
2839

2940
runs-on: ubuntu-latest
3041
permissions:
@@ -36,20 +47,25 @@ jobs:
3647

3748
steps:
3849
- name: Checkout repository
39-
uses: actions/checkout@v4
50+
uses: actions/checkout@v5
4051

4152
- name: Install Cosign
4253
uses: sigstore/cosign-installer@v3.5.0
4354

55+
- name: Set up QEMU
56+
uses: docker/setup-qemu-action@v4
57+
with:
58+
platforms: ${{ env.IMAGE_PLATFORMS }}
59+
4460
# Workaround: https://github.com/docker/build-push-action/issues/461
4561
- name: Setup Docker buildx
46-
uses: docker/setup-buildx-action@v3
62+
uses: docker/setup-buildx-action@v4
4763

4864
# Login against a Docker registry except on PR
4965
# https://github.com/docker/login-action
5066
- name: Log into registry ${{ env.REGISTRY }}
5167
if: github.event_name != 'pull_request'
52-
uses: docker/login-action@v3
68+
uses: docker/login-action@v4
5369
with:
5470
registry: ${{ env.REGISTRY }}
5571
username: ${{ github.actor }}
@@ -59,12 +75,16 @@ jobs:
5975
# https://github.com/docker/metadata-action
6076
- name: Extract Docker metadata
6177
id: meta
62-
uses: docker/metadata-action@v5
78+
uses: docker/metadata-action@v6
6379
with:
6480
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
6581

6682
- name: Extract Docker Version Tag
67-
id: docker_version_tag
83+
id: docker_version_tag
84+
env:
85+
# rebuild on code changes (push / manual run); only the nightly
86+
# schedule dedups on the version tag to skip identical rebuilds
87+
FORCE_REBUILD: ${{ github.event_name != 'schedule' && '1' || '' }}
6888
run: |
6989
./get-version.sh $(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:" | tr '[:upper:]' '[:lower:]') | sed 's/^/tag=/g' >> $GITHUB_OUTPUT
7090
@@ -77,12 +97,12 @@ jobs:
7797
- name: Build and push Docker image with tag
7898
if: ${{ !endsWith(steps.docker_version_tag.outputs.tag, 'latest') }}
7999
id: build-and-push-tagged
80-
uses: docker/build-push-action@v5
100+
uses: docker/build-push-action@v7
81101
with:
82102
context: .
83103
push: ${{ github.event_name != 'pull_request' }}
84104
tags: ${{ steps.docker_version_tag.outputs.tag }}
85-
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
105+
platforms: ${{ env.IMAGE_PLATFORMS }}
86106
labels: ${{ steps.meta.outputs.labels }}
87107
cache-from: type=gha
88108
cache-to: type=gha,mode=max
@@ -109,12 +129,12 @@ jobs:
109129
- name: Build and push Docker image as latest
110130
if: ${{ !endsWith(steps.docker_version_tag.outputs.tag, 'latest') }}
111131
id: build-and-push-latest
112-
uses: docker/build-push-action@v5
132+
uses: docker/build-push-action@v7
113133
with:
114134
context: .
115135
push: ${{ github.event_name != 'pull_request' }}
116136
tags: ${{ steps.docker_latest_tag.outputs.tag }}
117-
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
137+
platforms: ${{ env.IMAGE_PLATFORMS }}
118138
labels: ${{ steps.meta.outputs.labels }}
119139
cache-from: type=gha
120140
cache-to: type=gha,mode=max

‎.github/workflows/test.yml‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout repository
12+
uses: actions/checkout@v5
13+
14+
- name: Run test suite
15+
run: ./test.sh

‎get-version.sh‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ export ALPINE_VERSION=$(docker run --rm -t get-version cat /etc/alpine-release |
66
[ -z "$ALPINE_VERSION" ] && exit 1
77

88
export IMGTAG=$(echo "$1""a$ALPINE_VERSION-m$MYSQL_VERSION")
9+
# FORCE_REBUILD (set by the workflow for push / manual runs) rebuilds even if
10+
# the versioned image already exists, so code/config changes get republished.
11+
# the nightly schedule leaves it unset and keeps deduping on the version tag.
12+
if [ -n "$FORCE_REBUILD" ]; then
13+
echo "$IMGTAG"
14+
exit 0
15+
fi
16+
917
export IMAGE_EXISTS=$(docker pull "$IMGTAG" 2>/dev/null >/dev/null; echo $?)
1018

1119
# return latest, if container is already available :)

‎test.sh‎

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/bin/sh
2+
# automated smoke test for the mysql (mariadb) container
3+
# builds the image, starts it standalone, waits for mariadb to accept
4+
# connections and then does a REAL db round-trip (create/insert/select)
5+
set -eu
6+
7+
IMAGE=mysql-test
8+
NAME=mysql-test-run
9+
10+
ADMIN_USER=admin
11+
ADMIN_PASSWORD=adminsecret
12+
DB_NAME=testdb
13+
DB_USER=testuser
14+
DB_PASSWORD=testsecret
15+
16+
FAILED=0
17+
fail() {
18+
echo "FAIL: $*" >&2
19+
FAILED=1
20+
}
21+
22+
cleanup() {
23+
echo ">> cleanup: removing container $NAME"
24+
docker rm -f "$NAME" >/dev/null 2>&1 || true
25+
}
26+
trap cleanup EXIT INT TERM
27+
28+
# run a query as the given user, printing the result (no column headers)
29+
# usage: q <user> <password> <sql> [db]
30+
q() {
31+
_u="$1"; _p="$2"; _sql="$3"; _db="${4:-}"
32+
docker exec "$NAME" mysql -u"$_u" -p"$_p" -N -B ${_db:+"$_db"} -e "$_sql" 2>/dev/null
33+
}
34+
35+
echo ">> building image $IMAGE"
36+
docker build -t "$IMAGE" .
37+
38+
echo ">> (re)starting container $NAME"
39+
docker rm -f "$NAME" >/dev/null 2>&1 || true
40+
docker run -d --name "$NAME" \
41+
-e ADMIN_USER="$ADMIN_USER" \
42+
-e ADMIN_PASSWORD="$ADMIN_PASSWORD" \
43+
-e DB_NAME="$DB_NAME" \
44+
-e DB_USER="$DB_USER" \
45+
-e DB_PASSWORD="$DB_PASSWORD" \
46+
"$IMAGE"
47+
48+
# wait until the admin user can actually run a query (mariadb init takes a while)
49+
echo ">> waiting for mariadb to accept connections (up to ~120s)"
50+
READY=0
51+
i=0
52+
while [ "$i" -lt 60 ]; do
53+
if ! docker ps --format '{{.Names}}' | grep -q "^${NAME}$"; then
54+
echo "!! container is not running anymore, dumping logs:" >&2
55+
docker logs "$NAME" >&2 2>&1 || true
56+
fail "container exited during startup"
57+
break
58+
fi
59+
if [ "$(q "$ADMIN_USER" "$ADMIN_PASSWORD" 'SELECT 1')" = "1" ]; then
60+
READY=1
61+
break
62+
fi
63+
i=$((i + 1))
64+
sleep 2
65+
done
66+
67+
if [ "$READY" -ne 1 ] && [ "$FAILED" -eq 0 ]; then
68+
echo "!! mariadb did not accept connections in time, dumping logs:" >&2
69+
docker logs "$NAME" >&2 2>&1 || true
70+
fail "timed out waiting for mariadb"
71+
fi
72+
73+
# only run the deeper assertions if the db is up
74+
if [ "$READY" -eq 1 ]; then
75+
76+
echo ">> assert: admin user works"
77+
if [ "$(q "$ADMIN_USER" "$ADMIN_PASSWORD" 'SELECT 42')" = "42" ]; then
78+
echo "ok - admin user can query"
79+
else
80+
fail "admin user could not run a query"
81+
fi
82+
83+
# the auto-created db/user is provisioned asynchronously by the entrypoint,
84+
# so wait until the app user can connect to its database
85+
echo ">> waiting for auto-created db '$DB_NAME' + user '$DB_USER'"
86+
DBREADY=0
87+
j=0
88+
while [ "$j" -lt 30 ]; do
89+
if [ "$(q "$DB_USER" "$DB_PASSWORD" 'SELECT 1' "$DB_NAME")" = "1" ]; then
90+
DBREADY=1
91+
break
92+
fi
93+
j=$((j + 1))
94+
sleep 2
95+
done
96+
if [ "$DBREADY" -eq 1 ]; then
97+
echo "ok - db user can connect to '$DB_NAME'"
98+
else
99+
fail "db user '$DB_USER' could not connect to '$DB_NAME'"
100+
fi
101+
102+
if [ "$DBREADY" -eq 1 ]; then
103+
echo ">> assert: real round-trip (CREATE TABLE / INSERT / SELECT) as db user"
104+
q "$DB_USER" "$DB_PASSWORD" \
105+
'CREATE TABLE roundtrip (id INT PRIMARY KEY, name VARCHAR(32));' "$DB_NAME"
106+
q "$DB_USER" "$DB_PASSWORD" \
107+
"INSERT INTO roundtrip (id, name) VALUES (1, 'servercontainers');" "$DB_NAME"
108+
GOT=$(q "$DB_USER" "$DB_PASSWORD" \
109+
'SELECT name FROM roundtrip WHERE id = 1;' "$DB_NAME")
110+
if [ "$GOT" = "servercontainers" ]; then
111+
echo "ok - round-trip returned expected row ('$GOT')"
112+
else
113+
fail "round-trip did not return expected row (got: '$GOT')"
114+
fi
115+
116+
echo ">> assert: admin can see the app database"
117+
if q "$ADMIN_USER" "$ADMIN_PASSWORD" 'SHOW DATABASES;' | grep -q "^${DB_NAME}$"; then
118+
echo "ok - admin sees database '$DB_NAME'"
119+
else
120+
fail "admin does not see database '$DB_NAME'"
121+
fi
122+
fi
123+
124+
fi
125+
126+
echo
127+
if [ "$FAILED" -eq 0 ]; then
128+
echo "ALL TESTS PASSED"
129+
exit 0
130+
else
131+
echo "SOME TESTS FAILED"
132+
exit 1
133+
fi

0 commit comments

Comments
 (0)