mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 09:34:36 +08:00
chore(release): use next as the development branch
Sync main into next automatically and update release workflows and contributor docs for the long-lived next branch.
This commit is contained in:
1 parent
87dade4517
commit
408ee23936
6 files changed
+89
-251
No files matched your search
@@ -1,144 +0,0 @@
|
||||
name: Build Coolify Release Branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["release/v*"]
|
||||
paths-ignore:
|
||||
- .github/workflows/coolify-helper.yml
|
||||
- .github/workflows/coolify-helper-next.yml
|
||||
- .github/workflows/pr-quality.yaml
|
||||
- docker/coolify-helper/Dockerfile
|
||||
- docker/testing-host/Dockerfile
|
||||
- templates/**
|
||||
- CHANGELOG.md
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: coolify-release-branch-build-${{ github.ref_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
DOCKER_REGISTRY: docker.io
|
||||
IMAGE_NAME: coollabsio/coolify
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
dev_version: ${{ steps.version.outputs.dev_version }}
|
||||
short_sha: ${{ steps.version.outputs.short_sha }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Resolve version from branch name
|
||||
id: version
|
||||
env:
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ ! "${BRANCH}" =~ ^release/v([0-9]+)\.([0-9]+)$ ]]; then
|
||||
echo "Invalid release branch: ${BRANCH}. Expected release/vX.Y, such as release/v4.5."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEV_VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}-dev"
|
||||
SHORT_SHA="${GITHUB_SHA::7}"
|
||||
echo "dev_version=${DEV_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
echo "version=${DEV_VERSION}.${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-24.04
|
||||
- arch: aarch64
|
||||
platform: linux/aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to ${{ env.GITHUB_REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GITHUB_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to ${{ env.DOCKER_REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.DOCKER_REGISTRY }}
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push release branch image (${{ matrix.arch }})
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/production/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
build-args: |
|
||||
COOLIFY_VERSION=${{ needs.prepare.outputs.version }}
|
||||
tags: |
|
||||
${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.version }}-${{ matrix.arch }}
|
||||
${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.version }}-${{ matrix.arch }}
|
||||
|
||||
publish:
|
||||
needs: [prepare, build]
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
registry: [ghcr.io, docker.io]
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to ${{ env.GITHUB_REGISTRY }}
|
||||
if: matrix.registry == 'ghcr.io'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GITHUB_REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to ${{ env.DOCKER_REGISTRY }}
|
||||
if: matrix.registry == 'docker.io'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.DOCKER_REGISTRY }}
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Publish release branch manifest on ${{ matrix.registry }}
|
||||
env:
|
||||
REGISTRY: ${{ matrix.registry }}
|
||||
DEV_VERSION: ${{ needs.prepare.outputs.dev_version }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
docker buildx imagetools create \
|
||||
"${IMAGE}:${VERSION}-amd64" \
|
||||
"${IMAGE}:${VERSION}-aarch64" \
|
||||
--tag "${IMAGE}:${VERSION}" \
|
||||
--tag "${IMAGE}:${DEV_VERSION}"
|
||||
|
||||
notify:
|
||||
needs: [prepare, build, publish]
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||
status: ${{ contains(needs.*.result, 'failure') && 'failure' || (contains(needs.*.result, 'cancelled') && 'cancelled' || 'success') }}
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Sync main to next
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/10 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: sync-main-to-next
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Merge main into next
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Merge main into next
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git fetch origin main next
|
||||
git checkout --quiet -B next origin/next
|
||||
|
||||
if git merge --no-edit origin/main; then
|
||||
git push origin HEAD:refs/heads/next
|
||||
exit 0
|
||||
fi
|
||||
|
||||
conflicts=$(git diff --name-only --diff-filter=U)
|
||||
git merge --abort
|
||||
|
||||
if [ -z "$conflicts" ]; then
|
||||
echo '::error title=main to next sync failed::The merge failed without conflicts, so no pull request was created.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sync_branch='automation/sync-main-to-next'
|
||||
existing_pr=$(gh pr list --base next --head "$sync_branch" --state open --json url --jq '.[0].url')
|
||||
if [ -n "$existing_pr" ]; then
|
||||
# The conflict was already reported when this pull request was opened.
|
||||
# Stay green until it is merged instead of failing every scheduled run.
|
||||
echo "::notice title=main to next sync blocked::main still conflicts with next; resolve the open pull request: $existing_pr"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git push --force origin origin/main:"refs/heads/$sync_branch"
|
||||
gh pr create \
|
||||
--base next \
|
||||
--head "$sync_branch" \
|
||||
--title 'chore: merge main into next' \
|
||||
--body 'This pull request was created automatically because main could not be merged into next without conflicts. Resolve conflicts on this temporary branch; never update main with next.'
|
||||
|
||||
echo '::error title=main to next sync blocked::main could not be merged into next without conflicts; a pull request was opened to resolve them.'
|
||||
exit 1
|
||||
@@ -1,81 +0,0 @@
|
||||
name: Sync main to release branches
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/10 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: sync-main-to-release
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Merge main into release branches
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Merge main into every release/v* branch
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git fetch origin main '+refs/heads/release/v*:refs/remotes/origin/release/v*'
|
||||
|
||||
branches=$(git for-each-ref --format='%(refname:lstrip=3)' 'refs/remotes/origin/release/v*')
|
||||
if [ -z "$branches" ]; then
|
||||
echo 'No release/v* branch exists, so there is nothing to sync.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
failed=0
|
||||
for branch in $branches; do
|
||||
echo "::group::Sync main into $branch"
|
||||
git checkout --quiet -B "$branch" "origin/$branch"
|
||||
|
||||
if git merge --no-edit origin/main; then
|
||||
git push origin "HEAD:refs/heads/$branch"
|
||||
echo '::endgroup::'
|
||||
continue
|
||||
fi
|
||||
|
||||
conflicts=$(git diff --name-only --diff-filter=U)
|
||||
git merge --abort
|
||||
echo '::endgroup::'
|
||||
|
||||
if [ -z "$conflicts" ]; then
|
||||
echo "::error title=main to $branch sync failed::The merge failed without conflicts, so no pull request was created."
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
|
||||
sync_branch="automation/sync-main-to-${branch//\//-}"
|
||||
existing_pr=$(gh pr list --base "$branch" --head "$sync_branch" --state open --json url --jq '.[0].url')
|
||||
if [ -n "$existing_pr" ]; then
|
||||
# The conflict was already reported when this pull request was opened.
|
||||
# Stay green until it is merged instead of failing every scheduled run.
|
||||
echo "::notice title=main to $branch sync blocked::main still conflicts with $branch; resolve the open pull request: $existing_pr"
|
||||
continue
|
||||
fi
|
||||
|
||||
git push --force origin origin/main:"refs/heads/$sync_branch"
|
||||
gh pr create \
|
||||
--base "$branch" \
|
||||
--head "$sync_branch" \
|
||||
--title "chore: merge main into $branch" \
|
||||
--body "This pull request was created automatically because main could not be merged into $branch without conflicts. Resolve conflicts on this temporary branch; never update main with $branch."
|
||||
|
||||
echo "::error title=main to $branch sync blocked::main could not be merged into $branch without conflicts; a pull request was opened to resolve them."
|
||||
failed=1
|
||||
done
|
||||
|
||||
exit "$failed"
|
||||
@@ -251,8 +251,8 @@ The persistent-storage implementation is the reference pattern: `Project\Service
|
||||
## Git Workflow
|
||||
|
||||
- Production branch: `main`
|
||||
- Development branch: `release/v4.x`, where `x` is the next minor release (for example, `release/v4.5` while production is `4.4.x`). Do not use `next`.
|
||||
- Fix PRs should target the current production branch; feature PRs should target the current `release/v4.x` branch
|
||||
- Development branch: `next`. Do not create `release/v*` branches.
|
||||
- Fix PRs should target the current production branch; feature PRs should target `next`
|
||||
|
||||
<laravel-boost-guidelines>
|
||||
=== foundation rules ===
|
||||
|
||||
+3
-4
@@ -193,12 +193,11 @@ Choose the branch based on the type of change:
|
||||
| --- | --- | --- |
|
||||
| Fixes and small improvements | `main` | `main` |
|
||||
| Security fixes | `main` | `main` |
|
||||
| New features and larger changes | `release/v4.x` | `release/v4.x` |
|
||||
| New features and larger changes | `next` | `next` |
|
||||
|
||||
- For a fix, branch from `main` and target `main`.
|
||||
- For a feature, branch from `release/v4.x` and target `release/v4.x`.
|
||||
- `x` is the number of the next minor release. For example, when the production version is `4.4.x`, use `release/v4.5`. There is no `next` branch.
|
||||
- If a fix is discovered while developing a feature, submit it separately to `main`. Maintainers will merge `main` into `release/v4.x` so the fix is included there too.
|
||||
- For a feature, branch from `next` and target `next`.
|
||||
- If a fix is discovered while developing a feature, submit it separately to `main`. `main` is merged into `next` automatically, so the fix is included there too.
|
||||
- Pull requests targeting the wrong branch may be closed or asked to retarget.
|
||||
|
||||
|
||||
|
||||
+19
-20
@@ -5,54 +5,54 @@
|
||||
| Branch | Purpose |
|
||||
| --- | --- |
|
||||
| `main` | Latest production source |
|
||||
| `release/v4.x` | Feature integration and RC releases for the next minor release, such as `release/v4.5` |
|
||||
| `feature/*` | New features based on and merged into the current `release/v4.x` branch |
|
||||
| `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` |
|
||||
|
||||
`x` is the number of the next minor release. When `4.4.x` is the production version, the release branch is `release/v4.5`. Create a new `release/v4.x` branch from `main` after each stable minor release. Do not use a long-lived `next` branch.
|
||||
`next` is the only long-lived development branch. Do not create `release/v*` branches.
|
||||
|
||||
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`.
|
||||
|
||||
## Where changes go
|
||||
|
||||
- Fixes, security updates, and small improvements target `main`.
|
||||
- New features and larger changes target the current `release/v4.x` branch.
|
||||
- **Sync main to release branches** merges `main` into every `release/v*` branch every 10 minutes, so every production fix is included in the next release. If a merge conflicts, it opens a `chore: merge main into release/vX.Y` pull request to resolve.
|
||||
- Do not merge `release/v4.x` into `main` until an RC is approved for a stable release.
|
||||
- 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.
|
||||
|
||||
## Feature and RC flow
|
||||
|
||||
```text
|
||||
feature/* → release/v4.x → RC
|
||||
feature/* → next → RC
|
||||
```
|
||||
|
||||
1. Merge feature branches into `release/v4.x`.
|
||||
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 `release/v4.x` runs **Build Coolify Release Branch**. It calculates the version from the branch name and publishes `4.5-dev.<short-sha>` and the moving `4.5-dev` tag. It never publishes an RC or stable tag.
|
||||
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 `release/v4.x` and enter `v4.5-rc.1`.
|
||||
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.
|
||||
|
||||
## Stable release flow
|
||||
|
||||
```text
|
||||
release/v4.x → main → stable release
|
||||
next → main → stable release
|
||||
```
|
||||
|
||||
1. Temporarily stop merging features into `release/v4.x`.
|
||||
2. Change the version on `release/v4.x` from the approved RC to the stable version, such as `4.5.0`.
|
||||
3. Merge `release/v4.x` into `main`.
|
||||
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. Create the next release branch from `main`, such as `release/v4.6`, and set its development version. Delete the old release branch.
|
||||
8. Advance `next` to the next development version, such as `4.6-rc.1`.
|
||||
|
||||
## Hotfix flow
|
||||
|
||||
```text
|
||||
main → hotfix/X.Y.Z → main → release/v4.x
|
||||
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.
|
||||
@@ -61,7 +61,7 @@ main → hotfix/X.Y.Z → main → release/v4.x
|
||||
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. Merge `main` into `release/v4.x`, resolve the version in favor of the next intended RC, and delete the hotfix branch if one was used.
|
||||
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
|
||||
@@ -69,9 +69,8 @@ main → hotfix/X.Y.Z → main → release/v4.x
|
||||
| Tag | Meaning |
|
||||
| --- | --- |
|
||||
| `latest` | Latest stable release |
|
||||
| `next` | Latest successful `next` build (legacy branch) |
|
||||
| `X.Y-dev` | Latest successful `release/vX.Y` build |
|
||||
| `X.Y-dev.<short-sha>` | Exact `release/vX.Y` commit build |
|
||||
| `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 |
|
||||
|
||||
Reference in new issue
Block a user