docs: rewrite RELEASE.md for the new release process

This commit is contained in:
peaklabs-dev
2026-11-01 19:37:36 +01:00
parent 02c370d34b
commit 49590cf959
+134 -65
View File
@@ -1,78 +1,147 @@
# Coolify Release Guide
# Releases
## Branches
Every release is tagged `v<version>`, such as `v4.6.0-beta.1`, and publishes the image with the tag `4.6.0-beta.1` on ghcr.io and Docker Hub.
| Branch | Purpose |
| --- | --- |
| `main` | Latest production source |
| `next` | Feature integration and RC releases for the next version |
| `feature/*` | New features based on and merged into `next` |
| `hotfix/X.Y.Z` | Production fixes based on `main` |
> [!IMPORTANT]
> A push that changes `release.json` releases the version it sets, unless that version is released already. Run `scripts/version.sh` first to see what a push releases.
`next` is the only long-lived development branch. Do not create `release/v*` branches.
## Rules
Release workflows never edit or commit versions. Stable versions come from `config/constants.php`; RC versions come from `coolify.nightly.version` in `versions.json` and `other/nightly/versions.json`.
| Branch | Takes | Releases |
| -------------- | ---------------------------------------------------------- | -------------------------------------- |
| `main` | fixes, and `next` or a release branch for a new version | stable versions |
| `next` | features | prereleases of the next minor or major |
| `release/v4.6` | the minor 4.6, split off `next` so `next` can go on to 4.7 | prereleases of `4.6.0` |
| `release/v5.0` | the next major, cut from `next` | prereleases of `5.0.0` |
| `v4.x` | backports, once `main` is on v5 | stable `4.x` versions |
| `v4.4.x` | backports and hotfixes, cut from the newest tag of v4.4 | stable `4.4.x` versions |
## Where changes go
- `main` takes fixes only. A new minor or major reaches it as a merge of `next` or a release branch
- `next` and the release branches publish prereleases only, their stable versions are released on `main`
- Name a branch after what it changes: `fix/<topic>`, `feat/<topic>`, and `fix/<topic>-v4.4.8` for a backport, after the version that releases it
- Squash merge pull requests from contributors and pull requests with a single change, such as dependency updates
- Merge everything else with a merge commit: team pull requests with several commits, backports, `next`, the release branches and `v4.5.x` into `main`
- The merge forward workflow merges `main` into `release/v4.6` and `next`, and `release/v4.6` into `next`
- While `main` is on 4.5, `v4.3.x` and `v4.4.x` are never merged into another branch
- Delete `release/v4.6` once `main` has released `4.6.0`
- Delete `v4.5.x` once it is merged into `main`
- Delete `v4.3.x` and `v4.4.x` once their release is published, the next backport cuts them again from the newest tag
- Fixes, security updates, and small improvements target `main`.
- New features and larger changes target `next`.
- **Sync main to next** merges `main` into `next` every 10 minutes, so every production fix is included in the next release. If the merge conflicts, it opens a `chore: merge main into next` pull request to resolve.
- Do not merge `next` into `main` until an RC is approved for a stable release.
> [!CAUTION]
> Never squash merge `next` or a release branch. Their commits stay unmerged and come back as conflicts in every later merge.
## Feature and RC flow
## Create a release
```text
feature/* → next → RC
> [!NOTE]
> `next` publishes prereleases only. For its stable version, merge `next` into `main` with a merge commit and create the release on `main`.
1. Describe the changes under `## [Unreleased]` in `CHANGELOG.md`, they become the release notes
2. Check what the branch releases:
```sh
scripts/version.sh
```
3. Select the stage and the version, the script commits them and asks to push:
```sh
scripts/release.sh
```
4. The pushed commit changes `release.json`, so it runs the release workflow, which builds and publishes the release and moves its tags
5. After a stable release that moves `latest`, merge the pull request of the stable channel on `coollabsio/coollabs-cdn`, see [CDN](#cdn)
> [!TIP]
> To take the release commit back before the push:
>
> ```sh
> scripts/undo-release.sh
> ```
## Backport a fix
`main` is on 4.5, and 4.3 and 4.4 still get fixes. A fix reaches each of them through a patch branch and a pull request of its own:
1. Cut the patch branch from the newest tag of the minor, unless it exists:
```sh
git switch -c v4.4.x v4.4.7 && git push -u origin v4.4.x
```
2. Put the fix on a branch named after the version that releases it, cherry-picked from `main` when it is there already:
```sh
git switch -c fix/<topic>-v4.4.8 origin/v4.4.x && git cherry-pick -x <commit>
```
3. Merge a pull request from `fix/<topic>-v4.4.8` into `v4.4.x` with a merge commit
4. Create the release on your checkout of `v4.4.x`:
```sh
git switch v4.4.x && git pull && scripts/release.sh
```
5. Repeat with `v4.3.x` and `fix/<topic>-v4.3.25`. 4.2 gets no backports
> [!IMPORTANT]
> A hotfix for 4.5 while `main` holds unreleased changes goes through `v4.5.x` the same way. Merge `v4.5.x` into `main` with a merge commit afterwards and delete it. `v4.3.x` and `v4.4.x` are never merged, their fixes come from `main`.
## Release a major version
1. Cut `release/v5.0` from `next` and create its prereleases there with `scripts/release.sh`:
```sh
git switch -c release/v5.0 origin/next && git push -u origin release/v5.0
```
2. Merge `main` into `release/v5.0`, so v5 has every fix of v4 and the pull request into `main` has no conflicts:
```sh
git switch release/v5.0 && git pull && git merge origin/main && git push
```
3. Keep v4 on a branch of its own:
```sh
git switch -c v4.x origin/main && git push -u origin v4.x
```
4. Merge a pull request from `release/v5.0` into `main` with a merge commit
5. Create the release on `main` and select `5.0.0`:
```sh
git switch main && git pull && scripts/release.sh
```
6. Delete `release/v5.0`
> [!IMPORTANT]
> Cut `v4.x` from `main`, or bring an existing one up to date with `main`, before `release/v5.0` is merged into `main`. Afterwards `main` is v5 and v4 lives on `v4.x` alone.
## CDN
`scripts/sync-cdn.sh` syncs a channel of `coollabsio/coollabs-cdn` from the commit of the version it syncs: the install and upgrade scripts, the compose files, the environment file, the service templates, the GitHub releases and `versions.json`.
| Channel | Path | Synced by |
| --------- | ------------------- | --------------------------------------------------------- |
| `stable` | `/coolify/` | the release workflow when `latest` moves, a pull request |
| `preview` | `/coolify/preview/` | the release workflow when `preview` moves, pushed to main |
- The preview channel takes betas and release candidates, no alphas
- `CDN_TOKEN` in the environment `release` is a fine-grained token for `coollabsio/coollabs-cdn` alone, with read and write access to contents and pull requests
- `versions.json` of a channel names its Coolify version and the helper, Sentinel and Traefik versions of `upstream.json`
- `/coolify/release.json` lists the Coolify version of every channel, each sync sets the version of its channel
Cloudflare keeps a file of the CDN for up to 10 minutes. To clear the cache sooner, for example once the pull request of the stable channel is merged and deployed, set `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ZONE_ID` in `.env` or the environment, with a token that can purge the cache of the zone:
```sh
scripts/sync-cdn.sh --purge
```
1. Merge feature branches into `next`.
2. Set `coolify.nightly.version` in both version files to the intended RC, such as `4.5-rc.1`.
3. Every push to `next` runs **Build Coolify Next**. It publishes `sha-<short-sha>`, `4.5-rc.1.<short-sha>`, and the moving `next` tag. It never publishes the exact `4.5-rc.1` tag.
4. Create a reviewed draft GitHub Release named `v4.5-rc.1` and mark it as a prerelease.
5. Run **Release Coolify RC** manually from `next` and enter `v4.5-rc.1`.
6. The workflow validates the draft and configured nightly version, builds the exact RC, publishes `4.5-rc.1`, updates the `next` image tag, and publishes the draft prerelease.
7. Advance `coolify.nightly.version` to the next intended RC version.
## Update the helper, Sentinel or Traefik
## Stable release flow
`upstream.json` pins them. A change of it releases nothing, every Coolify instance reads the versions from the CDN. To ship it before the next release, sync the stable channel on `main`:
```text
next → main → stable release
```sh
scripts/sync-cdn.sh --stable <latest version>
```
1. Temporarily stop merging features into `next`.
2. Change the version on `next` from the approved RC to the stable version, such as `4.5.0`.
3. Merge `next` into `main`.
4. Create a reviewed draft GitHub Release named `v4.5.0`.
5. Run the stable release workflow from `main`.
6. The workflow rebuilds the exact stable version, publishes `4.5.0` and `latest`, then publishes the draft.
7. Update the CDN only after the release is approved.
8. Advance `next` to the next development version, such as `4.6-rc.1`.
## Hotfix flow
```text
main → hotfix/X.Y.Z → main → next
```
1. Create `hotfix/X.Y.Z` from `main` when a patch needs an integration branch. A single fix may use a normal branch from `main` instead.
2. Set the intended patch version.
3. Implement and test the fix. SHA images report `X.Y.Z-dev.<short-sha>`.
4. Merge the fix into `main`.
5. Create a reviewed draft GitHub Release named `vX.Y.Z`.
6. Run the stable release workflow from `main`.
7. **Sync main to next** merges `main` into `next`. If the version conflicts, resolve it in favor of the next intended RC in the sync pull request. Delete the hotfix branch if one was used.
8. Update the CDN only after the release is approved.
## Image tags
| Tag | Meaning |
| --- | --- |
| `latest` | Latest stable release |
| `next` | Latest successful `next` build or RC release |
| `X.Y-rc.N.<short-sha>` | Exact `next` commit build |
| `X.Y.Z` | Exact stable release |
| `X.Y-rc.N` | Exact RC release |
| `sha-<commit>` | Exact commit build |
Git tags use the `v` prefix, such as `v4.5.0`. Docker image tags do not.