Skip to content

Commit bd9f4cf

Browse files
authored
Merge pull request #72 from aviator5/gts-spec-tests-docker-release
ci(release): publish gts-spec-tests image to ghcr.io on tags
2 parents 3d4d3eb + 7cf9be8 commit bd9f4cf

6 files changed

Lines changed: 234 additions & 5 deletions

File tree

.github/dependabot.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
version: 2
2+
3+
updates:
4+
# Keep SHA-pinned GitHub Actions fresh. Dependabot understands SHA pins with
5+
# trailing version comments (e.g. `@<sha> # v4`) and updates both the SHA
6+
# and the comment together — same UX as tag-based references, without the
7+
# supply-chain risk of mutable tags.
8+
- package-ecosystem: "github-actions"
9+
directory: "/"
10+
schedule:
11+
interval: "weekly"
12+
day: "monday"
13+
commit-message:
14+
prefix: "ci(deps)"
15+
# Roll all action bumps into a single PR per week to avoid PR spam across
16+
# our small workflow set.
17+
groups:
18+
github-actions:
19+
patterns:
20+
- "*"
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
name: Release Tests Image
2+
3+
# Publishes the gts-spec test runner image to GHCR on every semver tag.
4+
# Tag format: vMAJOR.MINOR.PATCH (e.g. v0.11.3).
5+
# MAJOR.MINOR must match the spec version declared in README.md.
6+
on:
7+
push:
8+
tags:
9+
- 'v*.*.*'
10+
11+
permissions:
12+
contents: write # create GitHub Release
13+
packages: write # push to ghcr.io
14+
15+
jobs:
16+
release:
17+
runs-on: ubuntu-latest
18+
# Only publish from the canonical repository, never from forks.
19+
if: github.repository == 'GlobalTypeSystem/gts-spec'
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
23+
with:
24+
# Defense in depth: don't leave the GITHUB_TOKEN in .git/config.
25+
# The workflow doesn't currently upload artifacts or cache .git,
26+
# but this guards against future modifications that might.
27+
persist-credentials: false
28+
29+
- name: Verify spec version matches tag
30+
run: |
31+
set -euo pipefail
32+
TAG="${GITHUB_REF#refs/tags/}" # v0.11.3 or v0.11.3-rc.1
33+
VERSION="${TAG#v}" # 0.11.3 or 0.11.3-rc.1
34+
# Extract leading "MAJOR.MINOR" — anchored at the start so any patch
35+
# and/or prerelease suffix is ignored (e.g. 0.11.3-rc.1 -> 0.11).
36+
MAJOR_MINOR=$(printf '%s' "$VERSION" | grep -oE '^[0-9]+\.[0-9]+' || true)
37+
if [ -z "${MAJOR_MINOR:-}" ]; then
38+
echo "::error::Tag $TAG does not start with MAJOR.MINOR"
39+
exit 1
40+
fi
41+
42+
# The canonical spec version is the machine-readable marker in README.md:
43+
# <!-- gts-spec-version: X.Y -->
44+
# The human-readable "**VERSION**" line below it is for readers only and
45+
# may be reworded freely.
46+
SPEC_VERSION=$(grep -oE '<!-- gts-spec-version: [0-9]+\.[0-9]+ -->' README.md \
47+
| grep -oE '[0-9]+\.[0-9]+')
48+
49+
if [ -z "${SPEC_VERSION:-}" ]; then
50+
echo "::error file=README.md::Missing or malformed '<!-- gts-spec-version: X.Y -->' marker in README.md"
51+
exit 1
52+
fi
53+
54+
MARKER_COUNT=$(grep -cE '<!-- gts-spec-version: [0-9]+\.[0-9]+ -->' README.md)
55+
if [ "$MARKER_COUNT" -ne 1 ]; then
56+
echo "::error file=README.md::Expected exactly one gts-spec-version marker in README.md, found $MARKER_COUNT"
57+
exit 1
58+
fi
59+
60+
echo "Tag: $TAG"
61+
echo "Tag major.minor: $MAJOR_MINOR"
62+
echo "README spec version: $SPEC_VERSION"
63+
64+
if [ "$MAJOR_MINOR" != "$SPEC_VERSION" ]; then
65+
echo "::error::Tag $TAG (major.minor=$MAJOR_MINOR) does not match README spec version $SPEC_VERSION"
66+
exit 1
67+
fi
68+
69+
- name: Set up QEMU
70+
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
71+
72+
- name: Set up Docker Buildx
73+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
74+
75+
- name: Log in to GHCR
76+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
77+
with:
78+
registry: ghcr.io
79+
username: ${{ github.actor }}
80+
password: ${{ secrets.GITHUB_TOKEN }}
81+
82+
- name: Compute image tags
83+
id: meta
84+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
85+
with:
86+
images: ghcr.io/globaltypesystem/gts-spec-tests
87+
# `latest` is intentionally NOT set automatically: backport patches to
88+
# older spec lines (e.g. v0.9.4 cut from release/v0.9 while main is at
89+
# 0.11) would otherwise silently move `latest` backwards. Consumers
90+
# should pin to `vX.Y.Z` for reproducibility or use the rolling
91+
# `vX.Y` tag for the freshest patch of a given spec version.
92+
tags: |
93+
type=semver,pattern=v{{version}}
94+
type=semver,pattern=v{{major}}.{{minor}}
95+
96+
- name: Build and push image
97+
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
98+
with:
99+
context: tests
100+
file: tests/Dockerfile
101+
platforms: linux/amd64,linux/arm64
102+
push: true
103+
tags: ${{ steps.meta.outputs.tags }}
104+
labels: ${{ steps.meta.outputs.labels }}
105+
106+
- name: Create GitHub Release
107+
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
108+
with:
109+
name: ${{ github.ref_name }}
110+
generate_release_notes: true
111+
body: |
112+
Docker image for the GTS specification e2e test runner.
113+
114+
```
115+
docker pull ghcr.io/globaltypesystem/gts-spec-tests:${{ github.ref_name }}
116+
```
117+
118+
Run against a server reachable from the container (Mac/Docker Desktop):
119+
```
120+
docker run --rm ghcr.io/globaltypesystem/gts-spec-tests:${{ github.ref_name }} \
121+
--gts-base-url http://host.docker.internal:8000
122+
```
123+
124+
Linux hosts:
125+
```
126+
docker run --rm --add-host=host.docker.internal:host-gateway \
127+
ghcr.io/globaltypesystem/gts-spec-tests:${{ github.ref_name }} \
128+
--gts-base-url http://host.docker.internal:8000
129+
```

CONTRIBUTING.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,36 @@ Specification development guidelines:
116116
- Validate schemas against JSON Schema Draft 7 or later
117117
- Include both GTS Type Schemas (the canonical JSON definitions of types) and GTS Instance examples
118118
- Document any deviations or implementation-specific choices
119+
120+
## Releases
121+
122+
The specification version is declared in `README.md` via a machine-readable marker on the first line:
123+
124+
```html
125+
<!-- gts-spec-version: X.Y -->
126+
```
127+
128+
This marker is the canonical source of truth and is parsed by CI. The visible `> **VERSION**: ...` line below it is for human readers only and may be reworded freely, but its `X.Y` value MUST match the marker. When bumping the spec version, update both lines in the same change.
129+
130+
A versioned Docker image of the conformance test suite is published to GHCR on every git tag matching `vX.Y.Z`, where:
131+
132+
- `X.Y` MUST match the spec version declared in `README.md`. The release workflow enforces this and will fail the build on mismatch.
133+
- `Z` increments independently for changes to the test suite itself (additions, fixes, refactors) within the same spec version.
134+
135+
When the specification moves to the next minor version (e.g. `0.11``0.12`), the README version line is updated in the same change, and the next release tag starts at `vX.Y.0`.
136+
137+
### Cutting a release (maintainers only)
138+
139+
Releases are produced from `github.com/GlobalTypeSystem/gts-spec`. The workflow is restricted to that repository; pushing a tag from a fork has no effect.
140+
141+
```bash
142+
git tag v0.11.3
143+
git push origin v0.11.3
144+
```
145+
146+
The [`Release Tests Image`](.github/workflows/release-tests-image.yml) workflow:
147+
148+
1. Verifies the tag's `major.minor` matches the spec version in `README.md`.
149+
2. Builds the test runner image for `linux/amd64` and `linux/arm64`.
150+
3. Pushes it to `ghcr.io/globaltypesystem/gts-spec-tests` with two tags: the exact release `vX.Y.Z` and the rolling per-spec-version `vX.Y`. No floating `latest` tag is published — see `tests/README.md` for the rationale and consumer-side tag selection guidance.
151+
4. Creates a GitHub Release with auto-generated notes.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- gts-spec-version: 0.11 -->
12
> **VERSION**: GTS specification draft, version 0.11
23
34
# Global Type System (GTS) Specification

tests/Dockerfile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,20 @@
55
# to be reachable from the container (e.g. via host.docker.internal when the
66
# server runs on the Docker host).
77
#
8+
# A pre-built image is published to GHCR for every release; rebuild locally
9+
# only when iterating on the tests themselves. See tests/README.md for the
10+
# tag selection guide (no `latest` tag is published — pin to vX.Y or vX.Y.Z):
11+
# docker pull ghcr.io/globaltypesystem/gts-spec-tests:v0.11
12+
#
813
# Build (from the gts-spec repo root):
9-
# docker build -t gts-spec-e2e -f tests/Dockerfile tests
14+
# docker build -t gts-spec-tests -f tests/Dockerfile tests
1015
#
1116
# Run against a server on the host (Mac/Docker Desktop):
12-
# docker run --rm gts-spec-e2e --gts-base-url http://host.docker.internal:8000
17+
# docker run --rm gts-spec-tests --gts-base-url http://host.docker.internal:8000
1318
#
1419
# Run against a server on the host (Linux — host.docker.internal not built-in):
1520
# docker run --rm --add-host=host.docker.internal:host-gateway \
16-
# gts-spec-e2e --gts-base-url http://host.docker.internal:8000
21+
# gts-spec-tests --gts-base-url http://host.docker.internal:8000
1722

1823
FROM python:3.12-slim
1924

tests/README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,49 @@ This approach provides:
5151

5252
## Running the tests
5353

54+
### With Docker (recommended)
55+
56+
A pre-built image is published to the GitHub Container Registry on every release. The image tag tracks the specification version: `vMAJOR.MINOR` matches the spec, and the `PATCH` segment increments on test-suite changes (e.g. `v0.11.3` runs against spec `0.11`).
57+
58+
Pick the tag that fits your use case:
59+
60+
- `vX.Y.Z` — exact release (e.g. `v0.11.3`). Recommended for CI / reproducible runs.
61+
- `vX.Y` — rolling tag for the freshest patch of a given spec version (e.g. `v0.11`). Recommended for interactive use when you want to validate against a specific spec version.
62+
63+
> There is no `latest` tag. Multiple spec versions can be maintained in parallel (e.g. a `v0.9.x` backport patch while main is at `0.11`), and a single floating `latest` would not have an unambiguous meaning. Always pin to either `vX.Y.Z` or `vX.Y`.
64+
65+
```bash
66+
# Start your server on the host (port 8000 in this example)
67+
<your-server-start-command>
68+
69+
# Run the test suite from the published image (Mac/Docker Desktop)
70+
docker run --rm ghcr.io/globaltypesystem/gts-spec-tests:v0.11 \
71+
--gts-base-url http://host.docker.internal:8000
72+
73+
# Linux hosts (host.docker.internal is not built-in)
74+
docker run --rm --add-host=host.docker.internal:host-gateway \
75+
ghcr.io/globaltypesystem/gts-spec-tests:v0.11 \
76+
--gts-base-url http://host.docker.internal:8000
77+
78+
# Pin to an exact release for reproducible CI runs
79+
docker run --rm ghcr.io/globaltypesystem/gts-spec-tests:v0.11.3 \
80+
--gts-base-url http://host.docker.internal:8000
81+
82+
# Run a specific test file or pytest selector
83+
docker run --rm ghcr.io/globaltypesystem/gts-spec-tests:v0.11 \
84+
--gts-base-url http://host.docker.internal:8000 \
85+
test_op1_id_validation.py
86+
```
87+
88+
To rebuild the image locally while iterating on tests:
89+
90+
```bash
91+
docker build -t gts-spec-tests -f tests/Dockerfile tests
92+
docker run --rm gts-spec-tests --gts-base-url http://host.docker.internal:8000
93+
```
94+
95+
### With Python
96+
5497
```bash
5598
# Start your server on 8000 port
5699
<your-server-start-command>
@@ -72,8 +115,6 @@ export GTS_BASE_URL=http://127.0.0.1:8001
72115
pytest
73116
```
74117

75-
```
76-
77118
## Implemented test cases
78119

79120
- [x] **OP#1 - ID Validation**: Verify identifier syntax using regex patterns

0 commit comments

Comments
 (0)