mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-21 04:24:23 +00:00
feat(release): rebuild stable images from reviewed draft releases
Add manual fix-release validation and stable image rebuilds, inject traceable development versions into SHA builds, and suppress invalid release links for development versions.
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
name: Release Coolify
|
||||
name: Release Coolify Fix
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Existing draft release tag (for example, v4.3.1)
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: coolify-fix-release
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
@@ -14,14 +20,116 @@ env:
|
||||
IMAGE_NAME: coollabsio/coolify
|
||||
|
||||
jobs:
|
||||
promote-image:
|
||||
validate:
|
||||
runs-on: ubuntu-24.04
|
||||
environment: production-release
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
release_id: ${{ steps.draft.outputs.release_id }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Reject releases outside v4.x
|
||||
if: ${{ github.ref_name != 'v4.x' }}
|
||||
run: |
|
||||
echo "Fix releases must run from v4.x, not ${{ github.ref_name }}."
|
||||
exit 1
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Validate version
|
||||
id: version
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tag }}
|
||||
run: |
|
||||
if [[ ! "${TAG_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Unsupported fix release tag: ${TAG_NAME}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="${TAG_NAME#v}"
|
||||
CONFIG_VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)
|
||||
|
||||
if [[ "${CONFIG_VERSION}" != "${VERSION}" ]]; then
|
||||
echo "Release tag ${VERSION} does not match config version ${CONFIG_VERSION}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Validate and pin draft release
|
||||
id: draft
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tag }}
|
||||
with:
|
||||
script: |
|
||||
const releases = await github.paginate(github.rest.repos.listReleases, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
});
|
||||
const release = releases.find((candidate) => candidate.tag_name === process.env.TAG_NAME);
|
||||
|
||||
if (!release) {
|
||||
core.setFailed(`Create a draft release for ${process.env.TAG_NAME} before running this workflow.`);
|
||||
return;
|
||||
}
|
||||
if (!release.draft) {
|
||||
core.setFailed(`Release ${process.env.TAG_NAME} must still be a draft.`);
|
||||
return;
|
||||
}
|
||||
if (release.prerelease) {
|
||||
core.setFailed(`Fix release ${process.env.TAG_NAME} cannot be marked as a prerelease.`);
|
||||
return;
|
||||
}
|
||||
if (!release.body?.trim()) {
|
||||
core.setFailed(`Draft release ${process.env.TAG_NAME} must contain reviewed release notes.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.git.getRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `tags/${process.env.TAG_NAME}`,
|
||||
});
|
||||
core.setFailed(`Git tag ${process.env.TAG_NAME} already exists.`);
|
||||
return;
|
||||
} catch (error) {
|
||||
if (error.status !== 404) throw error;
|
||||
}
|
||||
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: release.id,
|
||||
target_commitish: context.sha,
|
||||
});
|
||||
core.setOutput('release_id', release.id);
|
||||
|
||||
build:
|
||||
needs: validate
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
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
|
||||
|
||||
@@ -39,69 +147,110 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Resolve release image
|
||||
id: release
|
||||
env:
|
||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
if [[ ! "${TAG_NAME}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
|
||||
echo "Unsupported release tag: ${TAG_NAME}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="${TAG_NAME#v}"
|
||||
RELEASE_SHA=$(git rev-list -n 1 "${TAG_NAME}")
|
||||
CONFIG_VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)
|
||||
|
||||
if [[ "${CONFIG_VERSION}" != "${VERSION}" ]]; then
|
||||
echo "Release tag ${VERSION} does not match config version ${CONFIG_VERSION}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "sha=${RELEASE_SHA}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Promote version on ${{ env.GITHUB_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.GITHUB_REGISTRY }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE_TAG="sha-${RELEASE_SHA}"
|
||||
docker buildx imagetools create "${IMAGE}:${SOURCE_TAG}" --tag "${IMAGE}:${VERSION}"
|
||||
|
||||
- name: Promote version on ${{ env.DOCKER_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.DOCKER_REGISTRY }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE_TAG="sha-${RELEASE_SHA}"
|
||||
docker buildx imagetools create "${IMAGE}:${SOURCE_TAG}" --tag "${IMAGE}:${VERSION}"
|
||||
|
||||
- name: Promote latest on ${{ env.GITHUB_REGISTRY }}
|
||||
if: ${{ ! github.event.release.prerelease }}
|
||||
env:
|
||||
REGISTRY: ${{ env.GITHUB_REGISTRY }}
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE_TAG="sha-${RELEASE_SHA}"
|
||||
docker buildx imagetools create "${IMAGE}:${SOURCE_TAG}" --tag "${IMAGE}:latest"
|
||||
|
||||
- name: Promote latest on ${{ env.DOCKER_REGISTRY }}
|
||||
if: ${{ ! github.event.release.prerelease }}
|
||||
env:
|
||||
REGISTRY: ${{ env.DOCKER_REGISTRY }}
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE_TAG="sha-${RELEASE_SHA}"
|
||||
docker buildx imagetools create "${IMAGE}:${SOURCE_TAG}" --tag "${IMAGE}:latest"
|
||||
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
- name: Build and push release image (${{ matrix.arch }})
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}
|
||||
context: .
|
||||
file: docker/production/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
build-args: |
|
||||
COOLIFY_VERSION=${{ needs.validate.outputs.version }}
|
||||
tags: |
|
||||
${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:release-${{ needs.validate.outputs.version }}-${{ github.sha }}-${{ matrix.arch }}
|
||||
${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:release-${{ needs.validate.outputs.version }}-${{ github.sha }}-${{ matrix.arch }}
|
||||
|
||||
publish:
|
||||
needs: [validate, build]
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
steps:
|
||||
- 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: Publish version and latest on ${{ env.GITHUB_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.GITHUB_REGISTRY }}
|
||||
VERSION: ${{ needs.validate.outputs.version }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE="release-${VERSION}-${GITHUB_SHA}"
|
||||
docker buildx imagetools create \
|
||||
"${IMAGE}:${SOURCE}-amd64" \
|
||||
"${IMAGE}:${SOURCE}-aarch64" \
|
||||
--tag "${IMAGE}:${VERSION}" \
|
||||
--tag "${IMAGE}:latest"
|
||||
|
||||
- name: Publish version and latest on ${{ env.DOCKER_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.DOCKER_REGISTRY }}
|
||||
VERSION: ${{ needs.validate.outputs.version }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
SOURCE="release-${VERSION}-${GITHUB_SHA}"
|
||||
docker buildx imagetools create \
|
||||
"${IMAGE}:${SOURCE}-amd64" \
|
||||
"${IMAGE}:${SOURCE}-aarch64" \
|
||||
--tag "${IMAGE}:${VERSION}" \
|
||||
--tag "${IMAGE}:latest"
|
||||
|
||||
- name: Publish reviewed draft release
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
RELEASE_ID: ${{ needs.validate.outputs.release_id }}
|
||||
TAG_NAME: ${{ inputs.tag }}
|
||||
with:
|
||||
script: |
|
||||
const releaseId = Number(process.env.RELEASE_ID);
|
||||
const { data: release } = await github.rest.repos.getRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: releaseId,
|
||||
});
|
||||
|
||||
if (release.tag_name !== process.env.TAG_NAME || !release.draft || release.prerelease) {
|
||||
core.setFailed(`Draft release ${process.env.TAG_NAME} changed while the images were building.`);
|
||||
return;
|
||||
}
|
||||
if (!release.body?.trim()) {
|
||||
core.setFailed(`Draft release ${process.env.TAG_NAME} no longer contains release notes.`);
|
||||
return;
|
||||
}
|
||||
if (release.target_commitish !== context.sha) {
|
||||
core.setFailed(`Draft release ${process.env.TAG_NAME} no longer targets ${context.sha}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.git.getRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `tags/${process.env.TAG_NAME}`,
|
||||
});
|
||||
core.setFailed(`Git tag ${process.env.TAG_NAME} was created while the images were building.`);
|
||||
return;
|
||||
} catch (error) {
|
||||
if (error.status !== 404) throw error;
|
||||
}
|
||||
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: Number(process.env.RELEASE_ID),
|
||||
draft: false,
|
||||
});
|
||||
|
||||
@@ -30,6 +30,12 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Resolve internal version
|
||||
id: version
|
||||
run: |
|
||||
BASE_VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)
|
||||
echo "version=${BASE_VERSION}-dev.${GITHUB_SHA::9}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login to ${{ env.GITHUB_REGISTRY }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -51,6 +57,8 @@ jobs:
|
||||
file: docker/production/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
build-args: |
|
||||
COOLIFY_VERSION=${{ steps.version.outputs.version }}
|
||||
tags: |
|
||||
${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}-${{ matrix.arch }}
|
||||
${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}-${{ matrix.arch }}
|
||||
@@ -78,33 +86,21 @@ jobs:
|
||||
- name: Create & publish manifest on ${{ env.GITHUB_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.GITHUB_REGISTRY }}
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
TAG_ARGS=(--tag "${IMAGE}:sha-${SHA}")
|
||||
# Moving tag for the latest production-line SHA image (v4.x only).
|
||||
if [ "${BRANCH}" = "v4.x" ]; then
|
||||
TAG_ARGS+=(--tag "${IMAGE}:edge")
|
||||
fi
|
||||
docker buildx imagetools create \
|
||||
"${IMAGE}:sha-${SHA}-amd64" \
|
||||
"${IMAGE}:sha-${SHA}-aarch64" \
|
||||
"${TAG_ARGS[@]}"
|
||||
--tag "${IMAGE}:sha-${SHA}"
|
||||
|
||||
- name: Create & publish manifest on ${{ env.DOCKER_REGISTRY }}
|
||||
env:
|
||||
REGISTRY: ${{ env.DOCKER_REGISTRY }}
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
TAG_ARGS=(--tag "${IMAGE}:sha-${SHA}")
|
||||
# Moving tag for the latest production-line SHA image (v4.x only).
|
||||
if [ "${BRANCH}" = "v4.x" ]; then
|
||||
TAG_ARGS+=(--tag "${IMAGE}:edge")
|
||||
fi
|
||||
docker buildx imagetools create \
|
||||
"${IMAGE}:sha-${SHA}-amd64" \
|
||||
"${IMAGE}:sha-${SHA}-aarch64" \
|
||||
"${TAG_ARGS[@]}"
|
||||
--tag "${IMAGE}:sha-${SHA}"
|
||||
|
||||
+15
-14
@@ -20,7 +20,7 @@ Coolify uses two long-lived branches so production fixes can ship without waitin
|
||||
|
||||
| Branch | Role | Docker image tags | How it ships |
|
||||
| --- | --- | --- | --- |
|
||||
| **`v4.x`** | Production / releasable line | `sha-<commit>` and moving `edge` via **Build Coolify (SHA)** | GitHub release promotes the SHA image to a semantic version (and `latest` for stable releases) |
|
||||
| **`v4.x`** | Production / releasable line | Immutable `sha-<commit>` images via **Build Coolify (SHA)** | The manual fix-release workflow rebuilds the selected commit with the stable version and updates `latest` |
|
||||
| **`next`** | Development line for features and larger changes | Branch tag (for example `next`) via **Staging Build** | Becomes production only after merge into `v4.x` |
|
||||
|
||||
### Where to merge
|
||||
@@ -51,7 +51,7 @@ Only commits on **`v4.x`** produce production SHA images and can be tagged for a
|
||||
|
||||
1. **Prepare the Release**
|
||||
- Land the work on **`v4.x`**: merge a fix PR into `v4.x`, or merge ready work from `next` into `v4.x` for a feature release.
|
||||
- Set the release version in `config/constants.php` and `versions.json` on the commit you will tag. Both values must match the planned Git tag without the `v` prefix (for example, `4.2.0` for tag `v4.2.0`).
|
||||
- Set the upcoming release version in `config/constants.php`. It must match the planned Git tag without the `v` prefix (for example, `4.3.1` for tag `v4.3.1`). Keep `coolify.v4.version` in `versions.json` on the currently published stable version until the CDN update.
|
||||
- Verify the changelog and required tests before merging.
|
||||
- After the release (or after the fix merges), merge `v4.x` back into `next` if those branches have diverged.
|
||||
|
||||
@@ -59,23 +59,24 @@ Only commits on **`v4.x`** produce production SHA images and can be tagged for a
|
||||
- Merge the release commit into `v4.x` through a pull request.
|
||||
- The `Build Coolify (SHA)` workflow builds AMD64 and ARM64 images and publishes them to Docker Hub and GHCR using immutable architecture tags.
|
||||
- After both builds complete, the workflow creates the multi-architecture `sha-<commit-sha>` manifest in both registries.
|
||||
- For pushes to **`v4.x`**, the same multi-architecture manifest is also tagged as `edge`, so `coollabsio/coolify:edge` always points at the latest production-line SHA image. Builds from `main` publish only the immutable `sha-<commit-sha>` tags.
|
||||
- The image reports a traceable development version such as `4.3.1-dev.d64cbda3e`.
|
||||
- This workflow does not update a semantic version tag or `latest`.
|
||||
|
||||
3. **Wait for the SHA Image**
|
||||
3. **Prepare the Draft Release**
|
||||
- Confirm the complete `Build Coolify (SHA)` workflow, including its `merge-manifest` job, succeeded.
|
||||
- Do not publish the release before the multi-architecture SHA image exists in both registries.
|
||||
- Create a reviewed draft GitHub release with the planned tag, such as `v4.3.1`, targeting `v4.x`.
|
||||
- Add the final release notes, leave the release as a draft, and do not mark a fix release as a prerelease.
|
||||
|
||||
4. **Create and Publish the GitHub Release**
|
||||
- Create a GitHub release with a semantic version tag such as `v4.2.0`, targeting the exact commit that produced the SHA image.
|
||||
- Mark beta or other test releases as prereleases. Publish production versions as stable releases.
|
||||
- Publishing the release starts the `Release Coolify` workflow. It verifies that the Git tag matches `config/constants.php`, then promotes the existing SHA image without rebuilding it.
|
||||
- The workflow assigns the semantic version tag in Docker Hub and GHCR. Stable releases also update `latest`; prereleases do not.
|
||||
4. **Run the Fix-Release Workflow**
|
||||
- Run `Release Coolify Fix` manually from `v4.x` and enter the existing draft tag.
|
||||
- The workflow validates the draft, its release notes, the version in `config/constants.php`, and the absence of an existing Git tag.
|
||||
- It pins the draft to the selected commit and rebuilds AMD64 and ARM64 images with the exact stable version.
|
||||
- After both builds succeed, it publishes the semantic version and `latest` manifests to Docker Hub and GHCR, then publishes the existing draft release.
|
||||
|
||||
5. **Verify the Promotion**
|
||||
- Confirm the `Release Coolify` workflow succeeded.
|
||||
- Verify the semantic version image has the same manifest digest as `sha-<commit-sha>` in Docker Hub and GHCR.
|
||||
- For stable releases, also verify `latest` points to the promoted release manifest.
|
||||
5. **Verify the Release**
|
||||
- Confirm the `Release Coolify Fix` workflow succeeded and the reviewed draft is now published.
|
||||
- Verify the semantic version image and `latest` contain AMD64 and ARM64 manifests in Docker Hub and GHCR.
|
||||
- Verify Coolify reports the exact stable version without a development SHA suffix.
|
||||
|
||||
6. **Update the CDN**
|
||||
- To make a new version available to self-hosted instances, update the version information on the CDN manually.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.3.0',
|
||||
'version' => env('COOLIFY_VERSION') ?: '4.3.0',
|
||||
'helper_version' => '1.0.14',
|
||||
'realtime_version' => '1.0.17',
|
||||
'railpack_version' => '0.23.0',
|
||||
|
||||
@@ -76,8 +76,11 @@ ARG TARGETPLATFORM
|
||||
ARG POSTGRES_VERSION
|
||||
ARG CLOUDFLARED_VERSION
|
||||
ARG NGINX_VERSION
|
||||
ARG COOLIFY_VERSION
|
||||
ARG CI=true
|
||||
|
||||
ENV COOLIFY_VERSION=${COOLIFY_VERSION}
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
USER root
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-90 hover:opacity-100 dark:hover:text-white hover:text-black']) }}
|
||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}" target="_blank">
|
||||
v{{ config('constants.coolify.version') }}
|
||||
</a>
|
||||
@php($version = config('constants.coolify.version'))
|
||||
|
||||
@if (str_contains($version, '-dev.'))
|
||||
<span {{ $attributes->merge(['class' => 'text-xs opacity-90']) }}>v{{ $version }}</span>
|
||||
@else
|
||||
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-90 hover:opacity-100 dark:hover:text-white hover:text-black']) }}
|
||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}" target="_blank">
|
||||
v{{ $version }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
|
||||
/**
|
||||
* Header version badge should open the matching GitHub release page.
|
||||
*/
|
||||
@@ -15,3 +17,14 @@ test('desktop header version links to the coolify github release for the install
|
||||
->toContain("https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}")
|
||||
->toContain('target="_blank"');
|
||||
});
|
||||
|
||||
test('development versions are not linked to nonexistent github releases', function () {
|
||||
config(['constants.coolify.version' => '4.3.1-dev.d64cbda3e']);
|
||||
|
||||
$version = Blade::render('<x-version />');
|
||||
|
||||
expect($version)
|
||||
->toContain('v4.3.1-dev.d64cbda3e')
|
||||
->not->toContain('href=')
|
||||
->not->toContain('target="_blank"');
|
||||
});
|
||||
|
||||
@@ -1,37 +1,62 @@
|
||||
<?php
|
||||
|
||||
it('publishes v4 branch builds only under the commit sha', function () {
|
||||
it('publishes v4 branch builds under the commit sha with a traceable internal version', function () {
|
||||
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-sha-build.yml');
|
||||
$dockerfile = file_get_contents(dirname(__DIR__, 2).'/docker/production/Dockerfile');
|
||||
$constants = file_get_contents(dirname(__DIR__, 2).'/config/constants.php');
|
||||
|
||||
expect($workflow)
|
||||
->toContain('name: Build Coolify (SHA)')
|
||||
->toContain('sha-${{ github.sha }}-${{ matrix.arch }}')
|
||||
->toContain('sha-${{ github.sha }}')
|
||||
->not->toContain('bootstrap/getVersion.php')
|
||||
->not->toContain('steps.version.outputs.VERSION')
|
||||
->not->toContain('IMAGE_NAME }}:latest');
|
||||
->toContain('php bootstrap/getVersion.php')
|
||||
->toContain('version=${BASE_VERSION}-dev.${GITHUB_SHA::9}')
|
||||
->toContain('COOLIFY_VERSION=${{ steps.version.outputs.version }}')
|
||||
->not->toContain('IMAGE_NAME }}:latest')
|
||||
->and($dockerfile)
|
||||
->toContain('ARG COOLIFY_VERSION')
|
||||
->toContain('ENV COOLIFY_VERSION=${COOLIFY_VERSION}')
|
||||
->and($constants)
|
||||
->toContain("'version' => env('COOLIFY_VERSION') ?: '4.3.0'");
|
||||
});
|
||||
|
||||
it('promotes the released commit image without rebuilding it', function () {
|
||||
it('orders a maintenance development build before its stable release', function () {
|
||||
expect(version_compare('4.3.0-dev.d64cbda3e', '4.3.0', '<'))->toBeTrue()
|
||||
->and(version_compare('4.3.0', '4.3.0-dev.d64cbda3e', '>'))->toBeTrue();
|
||||
});
|
||||
|
||||
it('requires a reviewed draft release before building a stable version', function () {
|
||||
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-release.yml');
|
||||
|
||||
expect($workflow)
|
||||
->toContain('release:')
|
||||
->toContain('types: [published]')
|
||||
->toContain('TAG_NAME: ${{ github.event.release.tag_name }}')
|
||||
->toContain('git rev-list -n 1 "${TAG_NAME}"')
|
||||
->toContain('SOURCE_TAG="sha-${RELEASE_SHA}"')
|
||||
->toContain('workflow_dispatch:')
|
||||
->toContain('tag:')
|
||||
->toContain('github.ref_name != \'v4.x\'')
|
||||
->toContain('github.paginate(github.rest.repos.listReleases')
|
||||
->toContain('release.draft')
|
||||
->toContain('release.prerelease')
|
||||
->toContain('release.body?.trim()')
|
||||
->toContain('bootstrap/getVersion.php')
|
||||
->toContain('--tag "${IMAGE}:${VERSION}"')
|
||||
->not->toContain('docker/build-push-action');
|
||||
->toContain('target_commitish: context.sha')
|
||||
->not->toContain('generate-notes');
|
||||
});
|
||||
|
||||
it('only promotes stable releases to latest', function () {
|
||||
it('rebuilds stable images and publishes the reviewed draft after both architectures succeed', function () {
|
||||
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-release.yml');
|
||||
|
||||
expect($workflow)
|
||||
->toContain('if: ${{ ! github.event.release.prerelease }}')
|
||||
->toContain('--tag "${IMAGE}:latest"');
|
||||
->toContain('docker/build-push-action@v6')
|
||||
->toContain('COOLIFY_VERSION=${{ needs.validate.outputs.version }}')
|
||||
->toContain('release-${{ needs.validate.outputs.version }}-${{ github.sha }}-${{ matrix.arch }}')
|
||||
->toContain('--tag "${IMAGE}:${VERSION}"')
|
||||
->toContain('--tag "${IMAGE}:latest"')
|
||||
->toContain('github.rest.repos.getRelease')
|
||||
->toContain('release.target_commitish !== context.sha')
|
||||
->toContain('release_id: Number(process.env.RELEASE_ID)')
|
||||
->toContain('draft: false')
|
||||
->toContain('permissions: {}')
|
||||
->not->toContain('sarisia/actions-status-discord@v1')
|
||||
->not->toContain('SOURCE_TAG="sha-${RELEASE_SHA}"');
|
||||
});
|
||||
|
||||
it('documents the sha image release process', function () {
|
||||
@@ -46,10 +71,13 @@ it('documents the sha image release process', function () {
|
||||
->toContain('Merge the release commit into `v4.x`')
|
||||
->toContain('`Build Coolify (SHA)`')
|
||||
->toContain('`sha-<commit-sha>`')
|
||||
->toContain('targeting the exact commit that produced the SHA image')
|
||||
->toContain('promotes the existing SHA image without rebuilding it')
|
||||
->toContain('Create a reviewed draft GitHub release')
|
||||
->toContain('rebuilds AMD64 and ARM64 images with the exact stable version')
|
||||
->toContain('publishes the existing draft release')
|
||||
->toContain('Update the CDN')
|
||||
->toContain('Only commits on **`v4.x`** produce production SHA images')
|
||||
->not->toContain('`edge`')
|
||||
->not->toContain('promotes the existing SHA image')
|
||||
->not->toContain('Merging to `main`')
|
||||
->not->toContain('Production Build (v4)');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user