Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: gofmt
Expand Down Expand Up @@ -114,7 +114,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: ['1.25.x', '1.26.x']
go: ['1.26.x', '1.27.x']
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -131,19 +131,19 @@ jobs:
# on Linux also measures the coverage of the whole suite and holds it
# to the gate of make test (COVERAGE_MIN in the Makefile).
- name: go test -race, the coverage and its gate
if: runner.os == 'Linux' && matrix.go == '1.26.x'
if: runner.os == 'Linux' && matrix.go == '1.27.x'
run: make test

- name: go test -race
if: runner.os == 'Linux' && matrix.go != '1.26.x'
if: runner.os == 'Linux' && matrix.go != '1.27.x'
run: go test -race -timeout 45m ./...

- name: go test
if: runner.os != 'Linux'
run: go test -timeout 45m ./...

- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
if: runner.os == 'Linux' && matrix.go == '1.26.x'
if: runner.os == 'Linux' && matrix.go == '1.27.x'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
Expand All @@ -168,7 +168,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: GitHub's schemas and REST descriptions
Expand Down Expand Up @@ -214,7 +214,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

# Run outside the module's dependencies: go run with a version never
Expand Down Expand Up @@ -262,7 +262,7 @@ jobs:
- if: steps.template.outputs.public == 'true'
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: scripts/validate.sh of the template
Expand Down Expand Up @@ -316,7 +316,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: The Action's step and the release scripts
Expand Down Expand Up @@ -466,7 +466,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

# Every Fuzz* target in every package runs for 30 seconds. Go's fuzzer
Expand Down Expand Up @@ -530,7 +530,7 @@ jobs:
# hand. docs/project/e2e.md explains how to run them locally.
name: e2e (${{ matrix.image }})
runs-on: ubuntu-latest
# The job pulls golang:1.26 (1.3 GB) and the forge, boots the forge (at
# The job pulls golang:1.27 (1.3 GB) and the forge, boots the forge (at
# most 3 minutes), seeds it and builds the tests with the race detector
# from a cold cache before go test starts with its own limit of
# TOUCHMARK_E2E_TEST_TIMEOUT. The job's limit leaves that much room, so
Expand All @@ -549,7 +549,7 @@ jobs:
persist-credentials: false

# The script starts the forge, seeds it and runs go test in
# golang:1.26 next to it; it removes everything it created on exit.
# golang:1.27 next to it; it removes everything it created on exit.
- name: scripts/e2e/gitea.sh
env:
IMAGE: ${{ matrix.image }}
Expand All @@ -575,7 +575,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: go test -run TestPropertyDistribute
Expand Down Expand Up @@ -605,7 +605,7 @@ jobs:

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: true

- name: go test -run 'TestScale|TestPlanScopeScale'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
if: matrix.language == 'go'
with:
go-version: '1.26.x'
go-version: '1.27.x'
cache: false

- uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/e2e-gitlab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
with:
persist-credentials: false

# The GitLab image takes about 6 GB on disk, the runner and golang:1.26
# The GitLab image takes about 6 GB on disk, the runner and golang:1.27
# 3 GB more: drop the preinstalled SDKs the job never uses.
- name: Free disk space
run: |
Expand All @@ -72,7 +72,7 @@ jobs:
df -h /

# The script starts GitLab and a runner, seeds them and runs go test
# in golang:1.26 next to them; it removes everything it created on
# in golang:1.27 next to them; it removes everything it created on
# exit.
- name: scripts/e2e/gitlab.sh
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ concurrency:
env:
GOTOOLCHAIN: local
# The Go of the release builds: the newest the test matrix covers.
GO_VERSION: '1.26.x'
GO_VERSION: '1.27.x'
IMAGE: ghcr.io/bedrock-python/touchmark
# Who signs and attests a release: this workflow, run on master.
SIGNER_WORKFLOW: bedrock-python/touchmark/.github/workflows/publish.yml
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ it adds. If you are unsure whether an idea fits, open an issue before the pull r

## Development setup

You need Go 1.25 or newer (the `go` line of `go.mod`), git, [uv](https://docs.astral.sh/uv/)
You need Go 1.26 or newer (the `go` line of `go.mod`), git, [uv](https://docs.astral.sh/uv/)
for the documentation site, and Docker for the image and the live tests.

```bash
Expand Down Expand Up @@ -47,7 +47,7 @@ on Windows or macOS. With git older than 2.45, the tests of `distribute` skip, a
comes out below the gate. To run the whole suite with a recent git from any OS:

```bash
docker run --rm -v "$PWD:/src" -w /src golang:1.26 \
docker run --rm -v "$PWD:/src" -w /src golang:1.27 \
sh -c 'git config --global --add safe.directory /src && go test -race -timeout 45m ./...'
```

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ ARG BINARY=source
# source: compile touchmark for the target platform on the build platform,
# with the flags .goreleaser.yaml uses (and -buildvcs=false: the build
# context has no .git).
FROM --platform=$BUILDPLATFORM golang:1.26.8-alpine3.24@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c AS source
FROM --platform=$BUILDPLATFORM golang:1.27.1-alpine3.24@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS source
ARG TARGETOS
ARG TARGETARCH
ARG VERSION=dev
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ SHELL := bash
GOLANGCI_LINT_VERSION := v2.14.0
GOVULNCHECK_VERSION := v1.8.0
# The goreleaser of publish.yml, in its image as docs/project/release.md runs
# it. The image brings its own Go (1.27 in v2.18.2), where publish.yml builds
# with 1.26: a snapshot checks the configuration and the files, not the bytes
# of a release.
# it. The image brings its own Go (1.27 in v2.18.2), the same minor as
# publish.yml's; a snapshot checks the configuration and the files, not the
# bytes of a release.
GORELEASER_IMAGE := goreleaser/goreleaser:v2.18.2@sha256:7077423cf5ef643ff56a34b58f93c1364e927e5c3dfa470eeabc44cab1a9c72b

# A golangci-lint on PATH runs when it is the pinned version; otherwise go run
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Three ways in, one version number:
|---|---|---|
| **GitHub Action** | `uses: bedrock-python/touchmark@<commit> # v0.1.0` | The hub's workflows on GitHub. Runs the release's image by digest, after checking its build provenance. [The Action](https://bedrock-python.github.io/touchmark/reference/action/) |
| **Container image** | `ghcr.io/bedrock-python/touchmark:0.1.0` | GitLab CI, Gitea and Forgejo Actions, `docker run`: touchmark and git on Alpine, as a non-root user. [Run it in CI](https://bedrock-python.github.io/touchmark/guide/ci/) |
| **Binary** | [Releases](https://github.com/bedrock-python/touchmark/releases) | Linux, macOS and Windows on amd64 and arm64, one static binary; or `go install github.com/bedrock-python/touchmark/cmd/touchmark@latest` with Go 1.25 or newer. [Installation](https://bedrock-python.github.io/touchmark/getting-started/installation/) |
| **Binary** | [Releases](https://github.com/bedrock-python/touchmark/releases) | Linux, macOS and Windows on amd64 and arm64, one static binary; or `go install github.com/bedrock-python/touchmark/cmd/touchmark@latest` with Go 1.26 or newer. [Installation](https://bedrock-python.github.io/touchmark/getting-started/installation/) |

<!-- x-release-please-end -->

Expand Down Expand Up @@ -88,7 +88,7 @@ docker pull ghcr.io/bedrock-python/touchmark:0.1.0
<!-- x-release-please-end -->

**Requirements:** git 2.31+ for `check`, `status`, `apply` and `manifest`, git 2.45+ for
`plan` and `distribute`, which read the targets (the image brings its own); Go 1.25+ to
`plan` and `distribute`, which read the targets (the image brings its own); Go 1.26+ to
build from source. To check an archive or the image before you
run it, see [Verifying what you run](SECURITY.md#verifying-what-you-run).

Expand Down
2 changes: 1 addition & 1 deletion docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
|---|---|
| Program | `touchmark`, one static Go binary; the same binary in the image `ghcr.io/bedrock-python/touchmark` and the GitHub Action `bedrock-python/touchmark` |
| Requires | git 2.31+ for `check`, `status`, `apply`, `manifest`; git 2.45+ for `plan` and `distribute`, which read the targets (the image brings its own) |
| Install | archives on [GitHub releases](https://github.com/bedrock-python/touchmark/releases) · `go install github.com/bedrock-python/touchmark/cmd/touchmark@latest` (Go 1.25+) · `docker run ghcr.io/bedrock-python/touchmark:<version>` |
| Install | archives on [GitHub releases](https://github.com/bedrock-python/touchmark/releases) · `go install github.com/bedrock-python/touchmark/cmd/touchmark@latest` (Go 1.26+) · `docker run ghcr.io/bedrock-python/touchmark:<version>` |
| Platforms | GitHub (github.com, GHE.com, GHES 3.19+), GitLab 17.0+, Gitea 1.26+, Forgejo 15+ |
| Hub template | <https://github.com/bedrock-python/engineering-assets-template> |
| Source | <https://github.com/bedrock-python/touchmark> |
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ On Windows, take `touchmark_X.Y.Z_windows_amd64.zip` and put `touchmark.exe` on
go install github.com/bedrock-python/touchmark/cmd/touchmark@latest
```

Go 1.25 or newer. A binary built this way reports the module version in
Go 1.26 or newer. A binary built this way reports the module version in
`touchmark version`.

## The image
Expand Down
16 changes: 8 additions & 8 deletions docs/project/e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Run

You need Docker and bash (Git Bash on Windows). Nothing else: the tests run in `golang:1.26`.
You need Docker and bash (Git Bash on Windows). Nothing else: the tests run in `golang:1.27`.

```sh
bash scripts/e2e/gitea.sh docker.gitea.com/gitea:1.27.3
Expand Down Expand Up @@ -37,7 +37,7 @@ The flavor comes from the image name. One image takes 8 to 10 minutes, the build
| `jdoe` | person | `write:repository,write:issue,read:user` |

4. Creates the private organisation `acme`: the team `readers` gives the reader read access to code, issues and pull requests, the team `writers` gives the writer write access, and `jdoe` is an owner. The script checks all of this through the API, and that the reader and the writer cannot create repositories.
5. Runs `go test -tags e2e -count=1 -race -v ./internal/e2e ./internal/e2e/gitlab` in `golang:1.26`, in the server container's network namespace (`internal/e2e/github` needs no server: the unit suite runs it).
5. Runs `go test -tags e2e -count=1 -race -v ./internal/e2e ./internal/e2e/gitlab` in `golang:1.27`, in the server container's network namespace (`internal/e2e/github` needs no server: the unit suite runs it).

touchmark sends a credential over plain http only to loopback ([Tokens and git](threat-model.md#tokens-and-git)). So the tests reach the server at `http://localhost:3000`, and that is also its `ROOT_URL`, so clone URLs in API answers work.

Expand Down Expand Up @@ -150,7 +150,7 @@ A cold GitLab boots in 3 to 6 minutes on a Linux host with 4 CPUs (expected; not
### What the script does

1. Creates the network `touchmark-e2e-<rand>` and starts GitLab as `touchmark-e2e-gitlab`: `external_url http://localhost`, Puma in single mode, 10 Sidekiq threads, no Prometheus and exporters, registry, Pages or KAS, `--shm-size 256m`, `--memory 4608m`. `GITLAB_OMNIBUS_CONFIG` holds no secret: it stays in the container's configuration (`docker inspect`) for the container's life. Omnibus generates root's password, which nothing uses (root's token comes from a `gitlab-rails runner` script), and the script deletes `/etc/gitlab/initial_root_password` once GitLab is ready.
2. Builds a linux `touchmark` from the working tree in `golang:1.26` into the volume `touchmark-e2e-<rand>-bin` while GitLab boots.
2. Builds a linux `touchmark` from the working tree in `golang:1.27` into the volume `touchmark-e2e-<rand>-bin` while GitLab boots.
3. Waits for `/-/readiness?all=1` and for the API (at most 15 minutes, printing progress and memory every 30 seconds).
4. Seeds, and checks through the API:

Expand All @@ -163,7 +163,7 @@ A cold GitLab boots in 3 to 6 minutes on a Linux host with 4 CPUs (expected; not

Which path an image takes, checked on 2026-09-27: CE 18.11.12 answers 404 to `GET /service_accounts` (there the API is EE code, `ee/lib/api/service_accounts.rb`, which the `gitlab-ce` image lacks), so its reader and writer are group access token bots (`group_<id>_bot_<hex>`); CE 19.4.1 has instance service accounts (`touchmark-reader`, `touchmark-writer`). The script logs the path, and the tests get it in `TOUCHMARK_E2E_GITLAB_ACCOUNTS`. The checks: every token acts as its account, only root is an administrator, the scopes by `GET /personal_access_tokens/self`, the roles by the reader's `GET /groups/:id/members/all/:user_id` in `acme` and `acme/sub`, and the reader cannot create a project.
5. Creates an instance runner with `POST /user/runners` (a `glrt-` token), starts `gitlab/gitlab-runner` of the same minor in GitLab's network namespace with the binary at `/opt/touchmark/touchmark`, registers it with the shell executor (the token reaches `gitlab-runner register` through the environment) and waits until GitLab reports it `online`.
6. Runs `go test -tags e2e -count=1 -race -v ./internal/e2e/gitlab/...` in `golang:1.26`, in GitLab's network namespace.
6. Runs `go test -tags e2e -count=1 -race -v ./internal/e2e/gitlab/...` in `golang:1.27`, in GitLab's network namespace.

touchmark sends a credential over plain http only to loopback ([Tokens and git](threat-model.md#tokens-and-git)). So GitLab's `external_url` is `http://localhost`, and the tests and the runner share its network namespace: clone URLs in API answers and `CI_SERVER_URL` in jobs are the URL touchmark uses. GitLab's nginx listens on IPv4 only; Go, git and curl fall back from `::1` to `127.0.0.1`, busybox `wget` does not.

Expand Down Expand Up @@ -270,7 +270,7 @@ bash scripts/e2e/gitea.sh --template ../engineering-assets-template --run TestTe
bash scripts/e2e/gitlab.sh --template ../engineering-assets-template --touchmark-image ghcr.io/bedrock-python/touchmark@sha256:<digest> --run TestTemplate gitlab/gitlab-ce:18.11.12-ce.0
# the GitHub workflow: a dry run against the fake, Linux only (the Action needs a Linux runner)
docker run --rm --name touchmark-e2e-template-github -v "$PWD:/src:ro" -v "$PWD/../engineering-assets-template:/template:ro" -w /src \
-e TOUCHMARK_E2E_TEMPLATE=/template golang:1.26 \
-e TOUCHMARK_E2E_TEMPLATE=/template golang:1.27 \
sh -c 'git config --global --add safe.directory /src && go test -count=1 -run TestTemplateWorkflow ./internal/e2e/github/'
```

Expand All @@ -279,7 +279,7 @@ Without `--run`, `--template` adds `TestTemplate` to the whole suite. Without `-
### What `--template` adds (`scripts/e2e/template-lib.sh`)

1. The touchmark image: built from the working tree's `Dockerfile` (`BINARY=source`), or `--touchmark-image`, pulled. It is pushed to a registry (`registry:3.1.2`, by digest) that listens on `127.0.0.1:<random port>` in the Docker host's network namespace, as `localhost:<port>/touchmark:0.0.0-e2e@sha256:<digest>`: the `NAME:TAG@sha256:<digest>` form the template pins the release image in. The Docker daemon pulls from a registry on localhost without TLS.
2. A forwarder (`scripts/e2e/loopback.go`, built and run in `golang:1.26` with `--network host`) on `127.0.0.1` and `[::1]` of the Docker host's network namespace, at the forge's port (80, 3000), to the forge's address on the run's network. The runners start the job containers in that namespace (network mode `host`), so the jobs reach the forge at its external URL, `http://localhost` or `http://localhost:3000`, the only plain-http host touchmark sends a credential to. A job container cannot join the forge's network namespace instead: GitLab's Docker executor gives every container a hostname, and Docker refuses `--hostname` with `--network container:<name>` ("conflicting options: hostname and the network mode", checked on Docker 29.3). On Docker Desktop, that namespace is the Linux VM's, not the desktop's, and nothing is published on the desktop.
2. A forwarder (`scripts/e2e/loopback.go`, built and run in `golang:1.27` with `--network host`) on `127.0.0.1` and `[::1]` of the Docker host's network namespace, at the forge's port (80, 3000), to the forge's address on the run's network. The runners start the job containers in that namespace (network mode `host`), so the jobs reach the forge at its external URL, `http://localhost` or `http://localhost:3000`, the only plain-http host touchmark sends a credential to. A job container cannot join the forge's network namespace instead: GitLab's Docker executor gives every container a hostname, and Docker refuses `--hostname` with `--network container:<name>` ("conflicting options: hostname and the network mode", checked on Docker 29.3). On Docker Desktop, that namespace is the Linux VM's, not the desktop's, and nothing is published on the desktop.
3. The runner:
- GitLab: `touchmark-e2e-gitlab-image-runner`, the `gitlab/gitlab-runner` image of the GitLab minor with the Docker socket, registered as a group runner of the group `hubs` (the person is its Owner): Docker executor, network mode `host`, `disable_cache` (no volumes left behind), the image under test as its default image. The template's hub turns the instance runners off, so only this runner runs its jobs. The runner pulls its helper image from `registry.gitlab.com` on the first job.
- Gitea: `touchmark-e2e-<rand>-actions`, Gitea's runner (`docker.gitea.com/runner:4.1.0`, by digest, formerly `act_runner`) with the Docker socket, registered for the instance with a token from `gitea actions generate-runner-token`: `container.network: host`, `docker_host: "-"` (jobs get no Docker socket), the cache server off. The organisation `hubs`, with the person as an owner, holds the hub.
Expand Down Expand Up @@ -322,10 +322,10 @@ go test ./internal/e2e/github/ # conformance as githu
TOUCHMARK_HEAVY_TESTS=1 go test ./internal/e2e/github/ # also the conformance as GHES outside Linux
```

`TestDistribute` and `TestDistributeUnsigned` run `distribute`, which needs git 2.45 or later: on an older git they skip, and the Docker run of the unit suite (`golang:1.26`, git 2.47) covers them:
`TestDistribute` and `TestDistributeUnsigned` run `distribute`, which needs git 2.45 or later: on an older git they skip, and the Docker run of the unit suite (`golang:1.27`, git 2.47) covers them:

```sh
docker run --rm --name touchmark-e2e-unit-suite -v "$PWD:/src" -w /src golang:1.26 \
docker run --rm --name touchmark-e2e-unit-suite -v "$PWD:/src" -w /src golang:1.27 \
sh -c 'git config --global --add safe.directory /src; go test -race -count=1 -timeout 45m ./...'
```

Expand Down
Loading
Loading