Skip to content
Open
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
16 changes: 8 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ These hold across the whole codebase; the skills explain the mechanisms.
image tag, so `just version::check` (a CI stage) fails where these disagree.
The frontend reads `VERSION` at build time (`vite.config.ts` → `$lib/version`
→ the footer) and displays it.
- **`main` is the only long-lived branch, and merging to it reaches the running
app.** One focused pull request per change, each carrying its `CHANGELOG.md`
entry. Every push to `main` moves `temporary/*:latest`, and a deployment at
running https://app.hackagon.dev.renku.ch/ that tag with
`imagePullPolicy: Always` picks it up on _any_ container restart. This
deployment is just temporary. The real deployment happens via Chart where an
`appVersion` is pinned. Charts have there own decoupled Chart version. See
`RELEASING.md`.
- **`main` is the only long-lived branch, and merging to it deploys to dev.**
One focused pull request per change, each carrying its manually added
`CHANGELOG.md` entry. Both clusters install the same chart from the GitOps
setup in `sdsc-ordes/cloud-infra` and differ only in image overrides: **dev**
runs `temporary/*:latest` with `pullPolicy: Always`, so every push to `main`
reaches it on the next container restart; **prod** takes the chart defaults,
`release/*` at `appVersion`, so nothing merged can reach it until someone
bumps `appVersion` and installs the new chart version. See `RELEASING.md`.
- **The chart versions itself; `VERSION` never touches it.**
`helm-chart/Chart.yaml` holds two hand-edited numbers: `version` is the
chart's own release, `appVersion` is the app release it deploys.
Expand Down
34 changes: 22 additions & 12 deletions RELEASING.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment: IMO the deployment-specific documentation belongs in that deployment's repo. The app repo should stick to deployment-agnostic documentation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cmdoret What is the deployment's repo in this case?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ordes cloud-infra

Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ chart lint, build, tests, and an image build. That is the only gate in front of

## Deployment

There are two deployments. Only one of them exists today.
Two clusters, both installed from the Helm chart by the GitOps setup in
`sdsc-ordes/cloud-infra` — a base `values.yaml` plus per-cluster overrides. The
only thing that differs between them is which images they pull.

### The temporary one — <https://app.hackagon.dev.renku.ch>
| Cluster | Images | Moves when |
| ------- | -------------------------------------------- | -------------------------------- |
| dev | `temporary/*:latest`, `pullPolicy: Always` | anything is merged to `main` |
| prod | chart defaults — `release/*` at `appVersion` | a new chart version is installed |

It runs `temporary/*:latest` with `imagePullPolicy: Always`. Every push to
`main` rebuilds that tag, so this site serves whatever was merged last — from
the next container restart onwards.
### dev follows `main`

```
merge to main ──► CI rebuilds temporary/*:latest ──► next container restart
Expand All @@ -46,23 +49,30 @@ kubectl rollout restart deploy/hackagon-frontend

To see which build it is serving, read the version at the bottom of the page.

`pullPolicy: Always` is what makes this work. The chart defaults to
`IfNotPresent`, which never re-pulls a moving tag — the node would keep serving
the image it first fetched.

Every push also publishes an immutable tag beside `latest` — the version plus
the first 12 characters of the commit, e.g. `0.8.0-efc7c9ace429`. `latest` gets
the first 12 characters of the commit, e.g. `0.9.1-848209cd2c52`. `latest` gets
overwritten; those never do, so a specific build can be pinned:

```bash
--set frontend.image.tag=0.8.0-efc7c9ace429
--set frontend.image.tag=0.9.1-848209cd2c52
--set frontend.image.pullPolicy=IfNotPresent
```

`skopeo list-tags docker://<repository>` shows which tags exist.

### The real one — not set up yet
### prod follows releases

No image overrides at all. With `tag: ""` the chart falls back to `appVersion`,
and release builds tag their images with the bare version, so
`appVersion: "0.9.1"` resolves to `release/backend-service:0.9.1` — an image
that exists because a `v*` tag built it.

dev and prod will be installed from the Helm chart, which names the app release
it deploys in `appVersion`. Such a deployment ignores `latest` completely and
changes only when someone installs a new chart version. How it will be set up is
not decided yet, so this file does not describe it.
Nothing merged to `main` can reach prod. It changes only when someone bumps
`appVersion`, publishes the chart, and installs that chart version.

## Three version numbers

Expand Down
2 changes: 1 addition & 1 deletion helm-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type: application

# The chart's own version. Bumped by hand when the chart changes, and
# independent of the app: the CI publishes whatever it finds here.
version: 0.3.0
version: 0.3.1
# The app release this chart deploys. A new app release does
# not become deployable until someone points the chart at it.
appVersion: "0.9.1"
Expand Down
20 changes: 12 additions & 8 deletions helm-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@ baseDomain: "example.com"
# Images
# ============================================================
# An image address is `repository:tag`. Two repositories exist:
# release/ one image per `v*` tag — what you deploy
# temporary/ one image per push to main — for testing, moves constantly
# release/ one image per `v*` tag — what prod runs
# temporary/ one image per push to main — what dev runs, moves constantly
Comment on lines +11 to +12

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment: this works, but it is non-standard, and we could drop the {release,temporary}/ prefix. Normally one would just tag release images v<core-version> and temporary image v<core-version>+<build>. For example v0.9.0+7bb2580. This is semver-compliant syntax.

#
# If there is no `tag` specified: then the chart then uses its the
# `appVersion` from Chart.yaml,
# With no `tag` set, the chart falls back to `appVersion` from Chart.yaml.
# Release builds tag their images with the bare version, so that resolves to an
# image that exists. This is the prod arrangement: no image overrides at all.
Comment on lines +14 to +16

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment: I am not sure I understand this sentence. And I don't think we need 3 lines to explain the tag.
suggestion: plain human english. like:

# Image tags default to the Chart's appVersion.

#
# But for test deploys you might want to override this, with a
# tagged image from the `temporary/` repository.
#
# To test against the head of `main`, override both keys, e.g.:
# A cluster that should follow `main` instead overrides three keys per
# component:
# --set frontend.image.repository=<registry>/temporary/frontend-service
# --set frontend.image.tag=latest
# --set frontend.image.pullPolicy=Always
#
# The pull policy is not optional there. The default `IfNotPresent` below never
# re-pulls a moving tag, so the node would keep serving whichever image it
# fetched first.
Comment on lines +22 to +26

@cmdoret cmdoret Sep 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# --set frontend.image.pullPolicy=Always
#
# The pull policy is not optional there. The default `IfNotPresent` below never
# re-pulls a moving tag, so the node would keep serving whichever image it
# fetched first.

comment: I just learnt that kubernetes sets pullPolicy: Always when the tag is latest and the policy is not specified (source).
suggestion: set pullPolicy: "" in values.yaml. It will simply default to ifNotPresent on version tags, and Always on moving tags.

#
# ============================================================
# Frontend
Expand Down
Loading