From 58313d39dcbda933d9a9e616355fd0ad307b92cb Mon Sep 17 00:00:00 2001 From: AlexeyShalaev <75322386+AlexeyShalaev@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:48:55 +0300 Subject: [PATCH 1/2] test(e2e): run the template's pipelines once it pins a release The Gitea and GitLab template tests replaced only the TODO(release) placeholder, whose digest is all zeros, with the image under test, so after the template pinned v0.1.0 they failed before running a job. They now replace the template's touchmark image whatever its digest, as the release checklist runs them after a release. --- internal/e2e/gitlab/template_test.go | 17 ++++++++++------- internal/e2e/template_test.go | 20 +++++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/internal/e2e/gitlab/template_test.go b/internal/e2e/gitlab/template_test.go index 30bb0e9..53a8330 100644 --- a/internal/e2e/gitlab/template_test.go +++ b/internal/e2e/gitlab/template_test.go @@ -33,7 +33,8 @@ import ( // 1. the hub: the template's files in one commit, as GitLab's import of a // repository makes a project (no pipeline runs), in the group of hubs, // with the instance runners off; the one change is the image line, the -// TODO(release) placeholder replaced with the image under test, in the +// template's pin (a release's digest, or the TODO(release) placeholder +// before the first release) replaced with the image under test, in the // same NAME:TAG@sha256: form; // 2. `touchmark setup gitlab` from a clone of the hub, with the person's // token (README "On GitLab", steps 2 to 4); @@ -257,17 +258,19 @@ func templateFiles(tpl map[string][]byte, replace map[string]string) []conforman return files } -// placeholderImage is the template's image line before a release pins it. -var placeholderImage = regexp.MustCompile(`(?m)^(\s*name: )ghcr\.io/bedrock-python/touchmark:[^@\s]+@sha256:0{64}$`) +// templateImage is the template's touchmark image line: a release's, +// pinned by digest, or before the first release the TODO(release) +// placeholder, whose digest is all zeros. +var templateImage = regexp.MustCompile(`(?m)^(\s*name: )ghcr\.io/bedrock-python/touchmark:[^@\s]+@sha256:[0-9a-f]{64}$`) -// pinImage replaces the one placeholder image of the template's +// pinImage replaces the one touchmark image of the template's // .gitlab-ci.yml with image, as a release's update does. func pinImage(t *testing.T, ci, image string) string { t.Helper() - if n := len(placeholderImage.FindAllString(ci, -1)); n != 1 { - t.Fatalf("the template's .gitlab-ci.yml has %d placeholder image lines (name: ghcr.io/bedrock-python/touchmark:@sha256:0…), want 1", n) + if n := len(templateImage.FindAllString(ci, -1)); n != 1 { + t.Fatalf("the template's .gitlab-ci.yml has %d touchmark image lines (name: ghcr.io/bedrock-python/touchmark:@sha256:), want 1", n) } - return placeholderImage.ReplaceAllString(ci, "${1}"+image) + return templateImage.ReplaceAllString(ci, "${1}"+image) } // describeHub sets id and writer in the template's hub.yml, as README step diff --git a/internal/e2e/template_test.go b/internal/e2e/template_test.go index 537b650..db3a0a0 100644 --- a/internal/e2e/template_test.go +++ b/internal/e2e/template_test.go @@ -26,7 +26,8 @@ import ( // // 1. the hub: the template's files in one commit on main, as a migration // makes it (the commit skips CI), in the organisation of hubs; the one -// change is the image of the jobs, the TODO(release) placeholder +// change is the image of the jobs, the template's pin (a release's +// digest, or the TODO(release) placeholder before the first release) // replaced with the image under test in the same NAME:TAG@sha256 form; // the reader's and the writer's tokens as the Actions secrets // TOUCHMARK_READ_TOKEN and TOUCHMARK_WRITE_TOKEN (README "On Gitea or @@ -304,18 +305,19 @@ func templateFiles(tpl map[string][]byte, replace map[string]string) []conforman return files } -// placeholderImage is a job's image in the template before a release pins -// it. -var placeholderImage = regexp.MustCompile(`(?m)^(\s*image: )ghcr\.io/bedrock-python/touchmark:[^@\s]+@sha256:0{64}$`) +// templateImage is a job's touchmark image in the template: a release's, +// pinned by digest, or before the first release the TODO(release) +// placeholder, whose digest is all zeros. +var templateImage = regexp.MustCompile(`(?m)^(\s*image: )ghcr\.io/bedrock-python/touchmark:[^@\s]+@sha256:[0-9a-f]{64}$`) -// pinImages replaces every placeholder image of a workflow with image, as -// a release's update does; the workflow must have one per job. +// pinImages replaces every touchmark image of a workflow with image, as a +// release's update does; the workflow must have one per job. func pinImages(t *testing.T, name, workflow, image string) string { t.Helper() - if len(placeholderImage.FindAllString(workflow, -1)) == 0 { - t.Fatalf("%s has no placeholder image (image: ghcr.io/bedrock-python/touchmark:@sha256:0…)", name) + if len(templateImage.FindAllString(workflow, -1)) == 0 { + t.Fatalf("%s has no touchmark image (image: ghcr.io/bedrock-python/touchmark:@sha256:)", name) } - return placeholderImage.ReplaceAllString(workflow, "${1}"+image) + return templateImage.ReplaceAllString(workflow, "${1}"+image) } // describeHub sets id and writer in the template's hub.yml, as README step From 68c35ccd406ae814e511535da39739410c5a22f0 Mon Sep 17 00:00:00 2001 From: AlexeyShalaev <75322386+AlexeyShalaev@users.noreply.github.com> Date: Wed, 7 Oct 2026 11:39:34 +0300 Subject: [PATCH 2/2] test(e2e): reference the image by the digest the local registry serves With Docker's containerd image store, pushing a multi-platform image that was pulled for one platform sends that platform's manifest alone, while RepoDigests keeps the index's digest. The template stands referenced the image by that digest, which the local registry lacked: GitLab's runner, which always pulls, failed every job with a published image; Gitea's found the image in the daemon and passed. template-lib.sh now asks the registry for the tag's digest. --- scripts/e2e/template-lib.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/e2e/template-lib.sh b/scripts/e2e/template-lib.sh index 6750549..9bed72d 100644 --- a/scripts/e2e/template-lib.sh +++ b/scripts/e2e/template-lib.sh @@ -97,9 +97,16 @@ tpl_image() { tpl_pushed=localhost:$port/touchmark:0.0.0-e2e docker tag "$image" "$tpl_pushed" docker push -q "$tpl_pushed" >/dev/null || die "cannot push to the registry on localhost:$port" - digest=$(docker image inspect -f '{{range .RepoDigests}}{{println .}}{{end}}' "$tpl_pushed" | - sed -n "s|^localhost:$port/touchmark@\(sha256:[0-9a-f]\{64\}\)\$|\1|p" | head -n 1) - [ -n "$digest" ] || die "the pushed image has no digest of localhost:$port" + # The digest is the one the registry serves for the tag, not the one + # RepoDigests names: with the containerd image store, a multi-platform + # image pulled for one platform is pushed as that platform's manifest + # alone, while RepoDigests keeps the index's digest, which the registry + # then lacks (a runner that always pulls fails on it). + digest=$(docker exec "$tpl_registry" wget -S -q -O /dev/null \ + --header 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json' \ + "http://127.0.0.1:$port/v2/touchmark/manifests/0.0.0-e2e" 2>&1 | + sed -n 's/^ *[Dd]ocker-[Cc]ontent-[Dd]igest: *\(sha256:[0-9a-f]\{64\}\).*$/\1/p' | head -n 1) + [ -n "$digest" ] || die "the registry on localhost:$port serves no digest for $tpl_pushed" tpl_image_ref=$tpl_pushed@$digest log "touchmark image: $image, as $tpl_image_ref ($(docker run --rm --entrypoint touchmark "$tpl_image_ref" version 2>&1 | head -n 1))" }