diff --git a/.github/workflows/coolify-release.yml b/.github/workflows/coolify-release.yml index 9cd3b90e5..5f6cb1cd4 100644 --- a/.github/workflows/coolify-release.yml +++ b/.github/workflows/coolify-release.yml @@ -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, + }); diff --git a/.github/workflows/coolify-sha-build.yml b/.github/workflows/coolify-sha-build.yml index 8c653e1d1..5b4e43002 100644 --- a/.github/workflows/coolify-sha-build.yml +++ b/.github/workflows/coolify-sha-build.yml @@ -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}" diff --git a/RELEASE.md b/RELEASE.md index e83609ad3..15a5623ab 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -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-` 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-` 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-` 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-` 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-` 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. diff --git a/config/constants.php b/config/constants.php index fc3179059..2b6ea06f8 100644 --- a/config/constants.php +++ b/config/constants.php @@ -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', diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 263788e4d..cce3764c4 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -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 diff --git a/resources/views/components/version.blade.php b/resources/views/components/version.blade.php index 5aa07d28b..cebd8a7ca 100644 --- a/resources/views/components/version.blade.php +++ b/resources/views/components/version.blade.php @@ -1,4 +1,10 @@ -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') }} - +@php($version = config('constants.coolify.version')) + +@if (str_contains($version, '-dev.')) + merge(['class' => 'text-xs opacity-90']) }}>v{{ $version }} +@else + 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 }} + +@endif diff --git a/tests/Feature/HeaderVersionReleaseLinkTest.php b/tests/Feature/HeaderVersionReleaseLinkTest.php index 5565fdc72..94aba0df9 100644 --- a/tests/Feature/HeaderVersionReleaseLinkTest.php +++ b/tests/Feature/HeaderVersionReleaseLinkTest.php @@ -1,5 +1,7 @@ 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(''); + + expect($version) + ->toContain('v4.3.1-dev.d64cbda3e') + ->not->toContain('href=') + ->not->toContain('target="_blank"'); +}); diff --git a/tests/Unit/ProductionImageWorkflowTest.php b/tests/Unit/ProductionImageWorkflowTest.php index 895ad0693..d45c54247 100644 --- a/tests/Unit/ProductionImageWorkflowTest.php +++ b/tests/Unit/ProductionImageWorkflowTest.php @@ -1,37 +1,62 @@ 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-`') - ->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)'); });