Skip to content

Commit ea2d92d

Browse files
committed
Merge branch 'dev'
2 parents 90ec084 + 6996f96 commit ea2d92d

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,31 +124,75 @@ jobs:
124124
id: login-ecr
125125
uses: aws-actions/amazon-ecr-login@v2
126126

127+
- name: Extract project version
128+
id: project_version
129+
run: |
130+
VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
131+
SHORT_SHA="${GITHUB_SHA::7}"
132+
echo "version=v${VERSION}" >> $GITHUB_OUTPUT
133+
echo "build=v${VERSION}-${SHORT_SHA}" >> $GITHUB_OUTPUT
134+
127135
- name: Build, tag, and push images to Amazon ECR
128136
id: build-image
129137
env:
130138
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
131139
IMAGE_TAG: ${{ github.sha }}
140+
VERSION_TAG: ${{ steps.project_version.outputs.version }}
141+
BUILD_TAG: ${{ steps.project_version.outputs.build }}
132142
run: |
133143
docker compose -f $COMPOSE_FILE build
134144
145+
# Re-tag the freshly built images with the meaningful version-anchored
146+
# names used for emergency rollback. BUILD_TAG (vX.Y.Z-<sha7>) is
147+
# always unique; VERSION_TAG (vX.Y.Z) is published only on main, where
148+
# commitizen guarantees a unique version per release.
149+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
150+
$ECR_REGISTRY/$ECR_REPOSITORY:$BUILD_TAG
151+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$IMAGE_TAG \
152+
$ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$BUILD_TAG
153+
135154
# Push Django image
136155
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
156+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$BUILD_TAG
137157
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
138158
139159
# Push Traefik image
140160
docker push $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$IMAGE_TAG
161+
docker push $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$BUILD_TAG
141162
docker push $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:latest
142163
143164
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
144165
166+
- name: Tag and push canonical version tag (main only)
167+
if: github.ref_name == 'main'
168+
env:
169+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
170+
IMAGE_TAG: ${{ github.sha }}
171+
VERSION_TAG: ${{ steps.project_version.outputs.version }}
172+
run: |
173+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
174+
$ECR_REGISTRY/$ECR_REPOSITORY:$VERSION_TAG
175+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$IMAGE_TAG \
176+
$ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$VERSION_TAG
177+
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$VERSION_TAG
178+
docker push $ECR_REGISTRY/$ECR_REPOSITORY_TRAEFIK:$VERSION_TAG
179+
145180
- name: Build, tag, and push monitor image
146181
if: github.ref_name == 'main'
147182
env:
148183
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
149184
IMAGE_TAG: ${{ github.sha }}
185+
VERSION_TAG: ${{ steps.project_version.outputs.version }}
186+
BUILD_TAG: ${{ steps.project_version.outputs.build }}
150187
run: |
188+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$IMAGE_TAG \
189+
$ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$BUILD_TAG
190+
docker tag $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$IMAGE_TAG \
191+
$ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$VERSION_TAG
192+
151193
docker push $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$IMAGE_TAG
194+
docker push $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$BUILD_TAG
195+
docker push $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:$VERSION_TAG
152196
docker push $ECR_REGISTRY/$ECR_REPOSITORY_MONITOR:latest
153197
154198
- name: Force deployment

docs/TAGGING.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Container image tagging
2+
3+
Every successful CI deploy publishes one or more tags to the relevant
4+
ECR repository (`kcprofiles` / `kcprofiles-dev`,
5+
`kcprofiles-traefik` / `kcprofiles-traefik-dev`, and `kcprofiles-monitor`
6+
on `main`). This document describes which tags are pushed when, what
7+
each one means, and how to use them to roll back in an emergency.
8+
9+
## Tags pushed on every deploy
10+
11+
| Tag | Mutability | Pushed on | Purpose |
12+
| ------------------------- | ---------- | ----------- | ------------------------------------ |
13+
| `<git_sha>` | Immutable | main, dev | Exact commit identity (40-char SHA). |
14+
| `v<version>-<sha7>` | Immutable | main, dev | Human-readable, version-anchored, unique per build. **Use this for rollback names.** |
15+
| `v<version>` | Mutable | main only | Pointer to the latest build of a given release. |
16+
| `latest` | Mutable | main, dev | Pointer to the most recent build on the branch. |
17+
18+
`<version>` is read from the `[project]` table of `pyproject.toml` at
19+
build time. `<sha7>` is the first 7 characters of the commit SHA.
20+
21+
## Why the `-<sha7>` suffix?
22+
23+
Disambiguation. On `dev`, commitizen never bumps the version (the
24+
`Bump version` workflow only runs on pushes to `main`), so every dev
25+
deploy would otherwise compete for the same `v<version>` tag and the
26+
previous build would be untaggable by name. Appending the short SHA
27+
gives every build its own immutable, version-anchored handle.
28+
29+
On `main` the same suffix is still pushed for two reasons:
30+
31+
1. Between a feature merge and the commitizen bump that follows, two
32+
commits can deploy with the same `v<version>` value, so the suffix
33+
keeps each build addressable.
34+
2. Symmetry: rollback procedures are the same on both environments.
35+
36+
## Why `v<version>` is `main`-only
37+
38+
`main` runs commitizen on every push and guarantees a fresh version per
39+
release, so `v<version>` is a stable name for "the build that
40+
corresponds to release X.Y.Z." On `dev` the same tag would change
41+
meaning every push, which is misleading — so we don't publish it there.
42+
43+
## Example
44+
45+
For commit `1cd0a566` deployed on `dev` while `pyproject.toml` has
46+
`version = "4.36.1"`, the dev ECR repository receives:
47+
48+
- `kcprofiles-dev:1cd0a566fa...` (full SHA)
49+
- `kcprofiles-dev:v4.36.1-1cd0a56`
50+
- `kcprofiles-dev:latest`
51+
52+
The same commit, if it were on `main`, would additionally produce:
53+
54+
- `kcprofiles:v4.36.1`
55+
56+
## Rolling back
57+
58+
In an emergency, prefer the immutable, named tag:
59+
60+
```bash
61+
aws ecs update-service \
62+
--cluster kcprofiles \
63+
--service kcprofiles-blue \
64+
--task-definition $(aws ecs register-task-definition \
65+
--family kcprofiles \
66+
--container-definitions '[{"name":"django","image":"<account>.dkr.ecr.us-east-1.amazonaws.com/kcprofiles:v4.36.1-1cd0a56", ...}]' \
67+
--query 'taskDefinition.taskDefinitionArn' --output text) \
68+
--force-new-deployment
69+
```
70+
71+
In practice this is done by editing the task definition in the AWS
72+
console to point at the desired `v<version>-<sha7>` tag and then
73+
forcing a new deployment.
74+
75+
If you only need to roll the current release back to its previous build
76+
on `main`, the plain `v<version>` tag is the simplest shorthand, but
77+
remember it tracks the **latest** build of that version — once a newer
78+
build is published it moves forward.
79+
80+
## Where the tagging is configured
81+
82+
`.github/workflows/ci.yml`, in the `deploy` job. The `Extract project
83+
version` step reads `pyproject.toml` via `tomllib`; subsequent build
84+
and push steps consume `steps.project_version.outputs.version`
85+
(`v<version>`) and `.build` (`v<version>-<sha7>`).

0 commit comments

Comments
 (0)