fix(groundskeeper): order the sims after the image build
Both groundskeeper sim arms ran before the job that builds the image they run in. This adds a needs: override at each include site so they wait for build:ci-groundskeeper whenever it is in the pipeline.
main is already green. Retrying the two jobs once ci-groundskeeper:latest existed passed both and took pipeline 2774262574 to success, so this MR removes the need for that retry rather than repairing a break that is still open.
What happened
The first main pipeline after !144 (merged) merged failed on sim-groundskeeper and sim-groundskeeper-image, both allow_failure: false. Both died in prepare_executor with runner_configuration_error:
WARNING: Failed to pull image with policy "always": Error response from daemon: manifest for
registry.gitlab.com/gitlab-com/public-sector/pipeline/ci-groundskeeper:latest not found:
manifest unknown: manifest unknownNo script ran and no assertion was evaluated. The two sim jobs started at 21:38:48, and build:ci-groundskeeper started at 21:41:11 and finished at 21:42:00. The registry records ci-groundskeeper:latest as created at 21:41:48, inside that build window. The sims pulled two minutes and twenty-three seconds before their image existed.
Timing makes this deterministic rather than a race: the build takes roughly 50 seconds and the sims pull within 3 seconds of pipeline start.
Why the merge request pipeline was green
The jobs were never created on it. The shared rules anchor at .gitlab/ci/sim/sim-groundskeeper.yml:53 has three clauses: schedule to never, a release tag, and main with changes:. There is no merge_request_event clause, so on a merged-results pipeline no clause matches. Pipeline 2774262574 was the first execution these two jobs ever had.
That omission is the house pattern for sims whose assertions depend on a freshly built image, and it is intact here. Giving these arms merge request coverage would require giving build:ci-groundskeeper the same coverage, because otherwise arm 2 compares against whatever main last built. That is a larger change and is left alone.
The cause, and the part that outlives the retry
Arm 1 inherits needs: [] from the component at templates/groundskeeper/template.yml:77; arm 2 declared it directly. That default is correct for a real consumer, whose scheduled upkeep lane should run even when an unrelated earlier job fails. It does not fit a pipeline that also builds the image.
With needs: [] the sims always pull the previous pipeline's :latest. Arm 2 asserts byte identity between the baked engine and scripts/groundskeep.py, so it fails on any commit that touches the engine, and the next-action it prints points at build:ci-groundskeeper's rules:changes, which already lists that path. A reader would be sent to a correct configuration looking for a fault. These rules also fire on the tag lane, so a release pipeline reaches the same failure.
The image was sound throughout
Nothing that failed evaluated a claim about the component:
build:ci-groundskeepersucceeded, andcontainers/ci-groundskeeper.Containerfile:50self-tests the installed engine during the build, covering install path, executable bit, and startup.validate:groundskeeperran the offline suite in the same pipeline: 217 assertions, 0 failures.ci-groundskeeper:latestand:b49664acshare digestsha256:58effb99d9c9, so the published image was built from currentmain.
A consumer pulling the image at any point got a real, signed artifact.
Why optional: true
Three of the sim's rules:changes paths are absent from the build's: .gitlab/ci/sim/sim-groundskeeper.yml, .gitlab/ci/sim/fixtures/groundskeeper.json, and templates/groundskeeper/template.yml. A hard needs: fails pipeline creation on a change touching only those. This MR is itself such a change, so the first pipeline to exercise the fix takes that path. None of the three can stale the baked engine, since they exclude scripts/groundskeep.py, so :latest is current whenever the build is absent.
The idiom is already house style: pipeline-self-validates uses it six times, and build:ci-groundskeeper uses it for build:ci-tools.
Why artifacts: false
.image-build publishes a dotenv report, and dotenv variables inherited through needs: take precedence over a job's own variables:. Nothing here reads PRIMARY or DIGEST, so this closes the path before something does.
Limitation
GitLab drops absent optional needs and treats an empty resolved list as needs: []. Ordering therefore applies only when the build is present. The sim-only case keeps its immediate start, where the current image is already correct.
Validation
Run locally against this branch:
scripts/lint-templates.py: all 22 templates pass the v2 canonical shape, zero advisories againsttemplates/groundskeeper/template.yml.tests/groundskeeper/run.sh: 217 assertions, 0 failures.tests/groundskeeper/validate-manifests.py: both manifests validate.- Template syntax check: 22 templates valid.
- Strict duplicate-key parse across
.gitlab-ci.yml,.gitlab/ci/**/*.yml,presets/*.gitlab-ci.ymlandtemplates/*/template.yml: 31 files, no duplicate mapping keys. POST /ci/linton the interpolated config:valid: true, with a warning set identical to themainbaseline (15 pre-existingretry:whendeprecations). Resolvedneedsconfirmed on both jobs.- A YAML load confirms
needssits at job level in both places and thatrules: *sim_rulesstill resolves to the anchor.
The sim arms cannot run on this MR's pipeline, for the reasons above. validate:groundskeeper, validate:templates:syntax, validate:templates:shape and pipeline-self-validates are created and were green locally. The fix gets its real exercise on the post-merge main pipeline.
Also included
One vale SubstitutionWarning that !144 (merged) introduced in CONTRIBUTING.md, changing "normally" to "usually". The five other findings in that file predate it and are untouched. sim-vale-advisory is allow_failure: true and has been reporting those five on prior main pipelines, including ones that finished green.