Setting up your repository
How to wire a GitLab repository to the PEACH build pipeline (deploy_on_peach_v2), with or without a Python package of your own.
This is the detail behind the overview in the
Data Science workflow;
promotion and rollback are covered in
Promotion and rollback. If your repository
still runs the previous (v1) pipeline, start from
Migrating to deploy_on_peach_v2.
Wiring your repository to the build
Any GitLab repository on git.ebu.io can be built by the PEACH pipeline. Add a
.gitlab-ci.yml that includes the shared CI template:
include:
project: pipe/peach-ray-ci
file: deploy_on_peach_v2.yml
That single line is the entire file for most repositories. The runner, image, configuration validation, deploy and image build all come from the template. No job token permissions need to be granted and no tokens need to be declared as CI/CD variables in your repository — the pipeline reads credentials from group-level variables managed by the PEACH core team.
What a push builds
A push to your default branch (main or master) runs in order:
- Validate — checks your
peach.confagainst the PEACH schema. - Deploy —
peach-ray-buildgenerates the Ray Serve config and Dockerfiles and writes them to your group'speach-rayrepo and topeach-k8s. - Build image — runs the Docker build on AWS CodeBuild through the ops-service relay and pushes the image to ECR.
- Promote — registers the new tag with peach-ops-service, which rolls it out to your codops' dev environment.
A push never deploys to production: you promote a tag there deliberately, once you are happy with it in dev. (A codops that has no dev environment is deployed straight to prod.)
Repos without a Python package
The default configuration above covers this case. The only file you need is
.gitlab-ci.yml. Add any override variables you need above the include::
variables:
PROMOTE: "false" # build + publish the image tag but don't auto-deploy
include:
project: pipe/peach-ray-ci
file: deploy_on_peach_v2.yml
Repos that publish a Python package
If your repository contains a Python package (a pyproject.toml using
hatch-vcs) that your notebooks depend on, set HAS_PACKAGE: "true". This
changes the pipeline shape so the image is always built after the package
is published to CodeArtifact:
variables:
HAS_PACKAGE: "true"
include:
project: pipe/peach-ray-ci
file: deploy_on_peach_v2.yml
How it works:
- A push to
masterruns validation and thensemantic-release. Afeat:commit cuts a minor version, afix:commit cuts a patch. The master pipeline stops there — no image build happens yet. - The new tag fires a second pipeline that publishes the package to CodeArtifact
and then builds the image. This guarantees the image's
uv syncalways resolves the freshly published version.
Prerequisites — ask the PEACH core team to:
- Add your group to the
codebuildRelayAllowedGroupsallowlist inpeach-ops-service. - Add a
host/<group>entry to thepeach-ops-codebuild-relay-runner-tokenssecret (a group access token withapi+create_runner, Maintainer role).
You also need to mint a project access token on your own repo (api scope,
Developer role) and set it as a masked CI/CD variable named GITLAB_TOKEN.
This is the credential semantic-release uses to push the version tag and
create the GitLab release. Without it the release job skips loudly and no
tag is ever cut.
Releasing your library with semantic commit messages
There is no version to bump by hand — not in pyproject.toml (the version is
the git tag, via hatch-vcs), not in peach.conf (the pin is the
LATEST_PUBLISHED_VERSION sentinel), not in uv.lock. The commit message
is the release mechanism: semantic-release reads the messages that land on
master and decides whether — and how much — to bump.
| Commit message starts with | Release cut | Example |
|---|---|---|
fix: |
patch (1.2.3 → 1.2.4) |
fix: handle empty user history in ranker |
feat: |
minor (1.2.3 → 1.3.0) |
feat: add language boost to scoring |
feat!: or a BREAKING CHANGE: footer |
major (1.2.3 → 2.0.0) |
feat!: rename score() to rank() |
chore:, docs:, ci:, style:, refactor:, test: |
none | docs: explain the fallback chain |
Day to day this means:
- A library-only change ships by itself. Commit it as
feat:orfix:, merge tomaster, and the release pipeline bumps, publishes and redeploys — you do not need to touchpeach.confor a notebook to trigger a deploy. - The message that counts is the one that lands on
master. If you squash-merge a merge request, it is the squash commit's message thatsemantic-releasereads — set it tofeat: …/fix: …even if the branch commits were messy. - A push with several
feat:/fix:commits cuts one release, with the highest bump among them. - A
chore:/docs:commit deploys nothing. That is the point — but it also means a library change mislabelled aschore:silently never ships. If that happens, follow up with an emptyfix:commit to cut the release.
Migrating an existing package: before switching to this flow, tag master
with the version you currently publish (e.g. git tag v1.4.2 && git push origin
v1.4.2) — semantic-release continues numbering from the latest vX.Y.Z tag
it finds, and without a baseline it would restart at v0.1.0.
Each release is visible in three places: the vX.Y.Z tag and GitLab release on
your repo, and the published my-package==X.Y.Z in CodeArtifact.
Pinning the just-published version with LATEST_PUBLISHED_VERSION
If your peach.conf depends on the package your own repo publishes, you can
pin the dependency to the exact version produced by the same pipeline run using
the LATEST_PUBLISHED_VERSION sentinel:
# peach.conf/endpoints.yaml
py_environments:
base:
dependencies:
- my-package==LATEST_PUBLISHED_VERSION
- other-dep
The deploy job exports LATEST_PUBLISHED_VERSION=<tag> (e.g. 1.1.1) and
peach-ray-lib resolves the sentinel before generating the pyproject.toml
for your uv workspace. The image is therefore built with the exact version that
was just published — not whatever uv would resolve to as "latest".
This sentinel only works in the HAS_PACKAGE=true tag pipeline. If it appears
in a master-push pipeline where the variable is not set, peach-ray-lib raises
a ValueError early rather than producing a broken image.
Package setup — using hatch-vcs
For the version to be derived from the git tag (rather than a hardcoded string
in setup.py) your package should use hatch-vcs:
# pyproject.toml
[project]
name = "my-package"
dynamic = ["version"]
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["my_package"]
[tool.hatch.version]
source = "vcs"
With this in place each tag pipeline publishes my-package==<tag> to
CodeArtifact, matching the version the LATEST_PUBLISHED_VERSION sentinel
resolves to.