Skip to content

Commit 81cb9e2

Browse files
authored
Merge pull request #144 from simplerisk/feature/testing-image-promote
ci: publish testing-channel minimal image + promote image tags into dedicated-hosting
2 parents 6746ec8 + e92766b commit 81cb9e2

2 files changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Promote latest image tag (release)
2+
3+
# On a release (push to master that bumps the version), promote the customers-cdk
4+
# `latest` channel: write SSM /simplerisk/customers/image-tag/latest = <VERSION> in
5+
# the customers account via OIDC, so the image-updater Lambda rolls tier=latest
6+
# (production) services onto the just-published release image.
7+
#
8+
# The release IMAGE itself is built + pushed (:latest + :<VERSION>) by the existing
9+
# push-to-dockerhub workflow on the same master push — this workflow ONLY does the
10+
# cross-account SSM promote (the missing automation link). Path-filtered to the
11+
# minimal Dockerfile so a docs-only master push does not roll production.
12+
#
13+
# See design docs/superpowers/specs/2026-07-01-testing-image-promote (customers-cdk).
14+
15+
on:
16+
push:
17+
branches: [master]
18+
paths:
19+
- simplerisk-minimal/Dockerfile
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: read
24+
id-token: write
25+
26+
concurrency:
27+
group: promote-latest
28+
cancel-in-progress: false
29+
30+
env:
31+
AWS_REGION: us-east-1
32+
SSM_PARAM: /simplerisk/customers/image-tag/latest
33+
34+
jobs:
35+
promote:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v6
40+
41+
- name: Read release version from the minimal Dockerfile
42+
id: ver
43+
run: |
44+
set -euo pipefail
45+
# The committed simplerisk-minimal/Dockerfile carries `ENV version=<VERSION>`
46+
# (set by generate_dockerfile.sh at release cut), same source create_new_tag
47+
# uses. Guard the shape before it reaches the promote.
48+
VERSION=$(grep -E '^ENV version=' simplerisk-minimal/Dockerfile | head -1 | cut -d '=' -f 2 | tr -d '[:space:]')
49+
if ! printf '%s' "$VERSION" | grep -qE '^[0-9]{8}-[0-9]{3}$'; then
50+
echo "::error::release version '$VERSION' from simplerisk-minimal/Dockerfile is missing or malformed"; exit 1
51+
fi
52+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
53+
54+
- name: Configure AWS credentials (OIDC → customers account)
55+
uses: aws-actions/configure-aws-credentials@v4
56+
with:
57+
role-to-assume: ${{ vars.IMAGE_PROMOTER_LATEST_ROLE_ARN }}
58+
aws-region: ${{ env.AWS_REGION }}
59+
60+
- name: Promote — SSM /image-tag/latest = <VERSION>
61+
env:
62+
VERSION: ${{ steps.ver.outputs.version }}
63+
run: |
64+
set -euo pipefail
65+
aws ssm put-parameter --name "$SSM_PARAM" \
66+
--value "$VERSION" --type String --overwrite \
67+
--region "$AWS_REGION"
68+
echo "promoted $SSM_PARAM = $VERSION" >> "$GITHUB_STEP_SUMMARY"
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
name: Publish simplerisk-minimal testing image + promote
2+
3+
# Publishes a TESTING-channel simplerisk-minimal image and promotes it into the
4+
# dedicated-hosting (customers-cdk) account so tier=testing customers auto-update.
5+
#
6+
# Trigger: a push to this repo's `testing` branch (code-development mirrors the
7+
# current testing version here on each testing publish — see the code-development
8+
# `sync_docker_testing` workflow), or a manual dispatch.
9+
#
10+
# Build: the CURRENT testing bundle from bundles-test (the built testing-branch
11+
# code) + the database/testing schema, via `generate_dockerfile.sh testing`
12+
# (COPYs the app from the context) — the same recipe the code-development
13+
# `test_docker_deploy` smoke uses, but pushed multi-arch to Docker Hub.
14+
#
15+
# Tags (see design 2026-07-01-testing-image-promote): an IMMUTABLE per-version
16+
# tag `<VERSION>-testing` plus the floating `:testing` alias. The bare `<VERSION>`
17+
# and `:latest` tags are RESERVED for the release build (master) and are NOT
18+
# touched here — the testing and release images are different builds (testing
19+
# bundle vs finalized public bundle), so they must not share the bare version tag.
20+
#
21+
# Promote: writes SSM /simplerisk/customers/image-tag/testing = <VERSION>-testing
22+
# in the customers account via OIDC; the image-updater Lambda there rolls every
23+
# tier=testing service (new image + fresh extras together).
24+
25+
on:
26+
push:
27+
branches: [testing]
28+
workflow_dispatch:
29+
30+
permissions:
31+
contents: read
32+
id-token: write # OIDC: assume the cross-account image-tag promoter role
33+
34+
concurrency:
35+
group: publish-testing
36+
cancel-in-progress: false
37+
38+
env:
39+
IMAGE_NAME: simplerisk/simplerisk-minimal
40+
AWS_REGION: us-east-1
41+
SSM_PARAM: /simplerisk/customers/image-tag/testing
42+
43+
jobs:
44+
publish:
45+
runs-on: ubuntu-latest
46+
timeout-minutes: 40
47+
steps:
48+
- name: Checkout (docker@testing)
49+
uses: actions/checkout@v6
50+
51+
- name: Resolve current testing version + fetch bundle/schema
52+
id: fetch
53+
run: |
54+
set -euo pipefail
55+
# The testing channel keeps exactly one current bundle; list it and
56+
# derive VERSION from its name (self-sufficient — no version file needed
57+
# in this repo). `|| true` so a zero-match reaches the COUNT guard.
58+
BUNDLE=$(curl -fsSL "https://bundles-test.simplerisk.com/?list-type=2" \
59+
| grep -oE 'simplerisk-[0-9]{8}-[0-9]{3}\.tgz' | sort -u || true)
60+
COUNT=$(printf '%s\n' "$BUNDLE" | grep -c . || true)
61+
if [ "$COUNT" -ne 1 ]; then
62+
echo "::error::expected exactly one testing bundle, found $COUNT: [$BUNDLE]"; exit 1
63+
fi
64+
VERSION=$(printf '%s' "$BUNDLE" | sed -n 's/^simplerisk-\([0-9]\{8\}-[0-9]\{3\}\)\.tgz$/\1/p')
65+
if ! printf '%s' "$VERSION" | grep -qE '^[0-9]{8}-[0-9]{3}$'; then
66+
echo "::error::could not derive VERSION from bundle '$BUNDLE'"; exit 1
67+
fi
68+
echo "testing bundle: $BUNDLE (version $VERSION)"
69+
curl -fsSL -o /tmp/testing-bundle.tgz "https://bundles-test.simplerisk.com/$BUNDLE"
70+
# Integrity: verify the bundle against the sha256 published in the served
71+
# updates-test feed (publish-bundle writes the hash on the same push).
72+
# VERSION is regex-guarded, so it is safe in the sed pattern.
73+
EXPECTED_SHA=$(curl -fsSL "https://updates-test.simplerisk.com/releases.xml" \
74+
| sed -n "/<release version=\"${VERSION}\">/,/<\/release>/p" \
75+
| grep -oE '<bundle_sha256>[a-f0-9]{64}</bundle_sha256>' | head -1 | grep -oE '[a-f0-9]{64}')
76+
if ! printf '%s' "$EXPECTED_SHA" | grep -qE '^[a-f0-9]{64}$'; then
77+
echo "::error::no bundle_sha256 for $VERSION in updates-test releases.xml"; exit 1
78+
fi
79+
ACTUAL_SHA=$(sha256sum /tmp/testing-bundle.tgz | cut -d' ' -f1)
80+
if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then
81+
echo "::error::bundle sha256 mismatch for $VERSION (expected $EXPECTED_SHA, got $ACTUAL_SHA)"; exit 1
82+
fi
83+
echo "bundle sha256 verified"
84+
SQL_URL="https://raw.githubusercontent.com/simplerisk/database/testing/simplerisk-en-${VERSION}.sql"
85+
curl -fsSL -o /tmp/testing.sql "$SQL_URL" \
86+
|| { echo "::error::testing schema not found: $SQL_URL"; exit 1; }
87+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
88+
89+
- name: Assemble the testing build context
90+
env:
91+
VERSION: ${{ steps.fetch.outputs.version }}
92+
run: |
93+
set -euo pipefail
94+
cd simplerisk-minimal
95+
# generate_dockerfile.sh testing -> a Dockerfile that COPYs simplerisk/
96+
# (app) + common/simplerisk.sql (schema) from this context.
97+
./generate_dockerfile.sh testing
98+
tar xzf /tmp/testing-bundle.tgz -C .
99+
cp /tmp/testing.sql common/simplerisk.sql
100+
test -d simplerisk || { echo "::error::bundle did not extract a simplerisk/ dir"; exit 1; }
101+
102+
- name: Set up QEMU
103+
uses: docker/setup-qemu-action@v4
104+
105+
- name: Set up Docker Buildx
106+
uses: docker/setup-buildx-action@v4
107+
108+
- name: Log in to Docker Hub
109+
uses: docker/login-action@v4
110+
with:
111+
username: ${{ secrets.DOCKER_USERNAME }}
112+
password: ${{ secrets.DOCKER_TOKEN }}
113+
114+
- name: Build and push (multi-arch) — <VERSION>-testing + :testing
115+
uses: docker/build-push-action@v7
116+
with:
117+
context: simplerisk-minimal
118+
file: simplerisk-minimal/Dockerfile
119+
push: true
120+
platforms: linux/amd64,linux/arm64
121+
tags: |
122+
${{ env.IMAGE_NAME }}:${{ steps.fetch.outputs.version }}-testing
123+
${{ env.IMAGE_NAME }}:testing
124+
cache-from: type=gha,scope=minimal-testing
125+
cache-to: type=gha,mode=max,scope=minimal-testing
126+
127+
- name: Configure AWS credentials (OIDC → customers account)
128+
uses: aws-actions/configure-aws-credentials@v4
129+
with:
130+
role-to-assume: ${{ vars.IMAGE_PROMOTER_TESTING_ROLE_ARN }}
131+
aws-region: ${{ env.AWS_REGION }}
132+
133+
- name: Promote — SSM /image-tag/testing = <VERSION>-testing
134+
env:
135+
VERSION: ${{ steps.fetch.outputs.version }}
136+
run: |
137+
set -euo pipefail
138+
aws ssm put-parameter --name "$SSM_PARAM" \
139+
--value "${VERSION}-testing" --type String --overwrite \
140+
--region "$AWS_REGION"
141+
echo "promoted $SSM_PARAM = ${VERSION}-testing" >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)