mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
- config/constants.php reads the Coolify and helper versions from them - The image and sync-cdn.sh write versions.json from upstream.json, install.sh reads it by key
232 lines
11 KiB
PHP
232 lines
11 KiB
PHP
<?php
|
|
|
|
it('reads the Coolify and helper versions by key from the versions.json of the CDN in install.sh', function () {
|
|
$versions = shell_exec("jq --indent 4 --arg version 4.6.0-beta.1 '{coolify: ({v4: {\$version}} + del(.traefik)), traefik}' ".escapeshellarg(dirname(__DIR__, 2).'/upstream.json'));
|
|
$parse = implode("\n", preg_grep('/^LATEST_(HELPER_)?VERSION=\$\(echo "\$VERSIONS_JSON"/', file(dirname(__DIR__, 2).'/scripts/install.sh')));
|
|
$output = shell_exec('VERSIONS_JSON='.escapeshellarg($versions).' bash -c '.escapeshellarg($parse."\necho \"\$LATEST_VERSION \$LATEST_HELPER_VERSION\""));
|
|
$helper = json_decode(file_get_contents(dirname(__DIR__, 2).'/upstream.json'), true)['helper']['version'];
|
|
|
|
expect(trim($output))->toBe("4.6.0-beta.1 {$helper}");
|
|
});
|
|
|
|
it('publishes main builds under rolling and commit sha tags with a traceable internal version', function () {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-main-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 Main')
|
|
->toContain('branches: ["main"]')
|
|
->not->toContain('v4.x')
|
|
->toContain('group: coolify-main-build')
|
|
->toContain('cancel-in-progress: false')
|
|
->toContain('IMAGE_NAME }}:main-${{ matrix.arch }}')
|
|
->toContain('--tag "${IMAGE}:main"')
|
|
->toContain('short_sha=${GITHUB_SHA::7}')
|
|
->toContain('SHA: ${{ needs.build-push.outputs.short_sha }}')
|
|
->toContain('--tag "${IMAGE}:sha-${SHA}"')
|
|
->not->toContain('sha-${{ steps.version.outputs.short_sha }}-${{ matrix.arch }}')
|
|
->toContain('org.opencontainers.image.revision=${{ github.sha }}')
|
|
->toContain('php bootstrap/getVersion.php')
|
|
->toContain('version=${BASE_VERSION}-dev.${GITHUB_SHA::9}')
|
|
->toContain('COOLIFY_VERSION=${{ steps.version.outputs.version }}')
|
|
->toContain('sarisia/actions-status-discord@v1')
|
|
->toContain('webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}')
|
|
->not->toContain('IMAGE_NAME }}:latest')
|
|
->and($dockerfile)
|
|
->toContain('ARG COOLIFY_VERSION')
|
|
->toContain('ENV COOLIFY_VERSION=${COOLIFY_VERSION}')
|
|
->and($constants)
|
|
->toContain("'version' => env('COOLIFY_VERSION') ?: \$release['stable']['coolify']");
|
|
});
|
|
|
|
it('orders a maintenance development build before its stable release', function () {
|
|
expect(version_compare('4.3.2-dev.d64cbda3e', '4.3.2', '<'))->toBeTrue()
|
|
->and(version_compare('4.3.2', '4.3.2-dev.d64cbda3e', '>'))->toBeTrue();
|
|
});
|
|
|
|
it('orders rolling and exact release candidates before the stable release', function () {
|
|
expect(version_compare('4.4-rc.1.d64cbda', '4.4-rc.1', '<'))->toBeTrue()
|
|
->and(version_compare('4.4-rc.1', '4.4-rc.2', '<'))->toBeTrue()
|
|
->and(version_compare('4.4-rc.2', '4.4.0', '<'))->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('name: Release Coolify Stable')
|
|
->toContain('run-name: ${{ inputs.tag }}')
|
|
->toContain('workflow_dispatch:')
|
|
->toContain('tag:')
|
|
->toContain("github.ref_name != 'main'")
|
|
->toContain('github.paginate(github.rest.repos.listReleases')
|
|
->toContain('release.draft')
|
|
->toContain('release.prerelease')
|
|
->toContain('release.body?.trim()')
|
|
->toContain('bootstrap/getVersion.php')
|
|
->toContain('target_commitish: context.sha')
|
|
->toContain('tag_name: process.env.TAG_NAME')
|
|
->toContain('actions/github-script@v8')
|
|
->not->toContain('actions/github-script@v7')
|
|
->not->toContain('environment: production-release')
|
|
->not->toContain('generate-notes');
|
|
});
|
|
|
|
it('runs support image workflows from main', function (string $workflowFile) {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2)."/.github/workflows/{$workflowFile}");
|
|
|
|
expect($workflow)
|
|
->toContain('branches: [ "main" ]')
|
|
->not->toContain('v4.x');
|
|
})->with([
|
|
'helper' => 'coolify-helper.yml',
|
|
]);
|
|
|
|
it('prevents the stable helper workflow from publishing an existing version', function () {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-helper.yml');
|
|
|
|
expect($workflow)
|
|
->toContain('workflow_dispatch:')
|
|
->toContain('check-version:')
|
|
->toContain('needs: check-version')
|
|
->toContain('VERSION="${BASE_VERSION}"')
|
|
->toContain('docker buildx imagetools inspect "$IMAGE"')
|
|
->toContain('Version $VERSION already exists in $registry')
|
|
->toContain('Version $VERSION is available in both registries')
|
|
->toContain('Could not verify $IMAGE')
|
|
->toContain('cancel-in-progress: false');
|
|
});
|
|
|
|
it('publishes the testing host only to Docker Hub', function () {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-testing-host.yml');
|
|
$cleanupWorkflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/cleanup-ghcr-untagged.yml');
|
|
$windowsCompose = file_get_contents(dirname(__DIR__, 2).'/docker-compose.windows.yml');
|
|
$developmentCompose = file_get_contents(dirname(__DIR__, 2).'/docker-compose.dev.yml');
|
|
|
|
expect($workflow)
|
|
->toContain('DOCKER_REGISTRY: docker.io')
|
|
->toContain('IMAGE_NAME: "coollabsio/coolify-testing-host"')
|
|
->toContain('docker/testing-host/Dockerfile')
|
|
->not->toContain('ghcr.io')
|
|
->not->toContain('GITHUB_REGISTRY')
|
|
->and($cleanupWorkflow)->not->toContain('coolify-testing-host')
|
|
->and($windowsCompose)->toContain('docker.io/coollabsio/coolify-testing-host:latest')
|
|
->and($developmentCompose)->toContain('image: coolify-testing-host:dev')
|
|
->toContain('dockerfile: ./docker/testing-host/Dockerfile');
|
|
});
|
|
|
|
it('publishes traceable rolling builds from next without creating an exact rc tag', function () {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-next-build.yml');
|
|
|
|
expect($workflow)
|
|
->toContain('name: Build Coolify Next')
|
|
->toContain('branches: [next]')
|
|
->toContain('group: coolify-next-build')
|
|
->toContain("jq -r '.coolify.nightly.version' other/nightly/versions.json")
|
|
->toContain('VERSION="${RC_VERSION}.${SHORT_SHA}"')
|
|
->toContain('COOLIFY_VERSION=${{ needs.prepare.outputs.version }}')
|
|
->toContain('--tag "${IMAGE}:sha-${SHA}"')
|
|
->toContain('--tag "${IMAGE}:${VERSION}"')
|
|
->toContain('--tag "${IMAGE}:next"')
|
|
->toContain('sarisia/actions-status-discord@v1')
|
|
->toContain('webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}')
|
|
->not->toContain('--tag "${IMAGE}:${RC_VERSION}"')
|
|
->not->toContain('--tag "${IMAGE}:latest"');
|
|
});
|
|
|
|
it('requires a reviewed draft prerelease before publishing an exact rc', function () {
|
|
$workflow = file_get_contents(dirname(__DIR__, 2).'/.github/workflows/coolify-rc-release.yml');
|
|
|
|
expect($workflow)
|
|
->toContain('name: Release Coolify RC')
|
|
->toContain('run-name: ${{ inputs.tag }}')
|
|
->toContain("github.ref != 'refs/heads/next'")
|
|
->toContain('group: coolify-rc-release')
|
|
->toContain('^v[0-9]+\\.[0-9]+-rc\\.[0-9]+$')
|
|
->toContain("jq -r '.coolify.nightly.version' other/nightly/versions.json")
|
|
->toContain('release.draft')
|
|
->toContain('!release.prerelease')
|
|
->toContain('release.body?.trim()')
|
|
->toContain('revalidate:')
|
|
->toMatch('/revalidate:.*?permissions:\s+contents: write/s')
|
|
->toContain('needs: [validate, build, revalidate]')
|
|
->toContain('COOLIFY_VERSION=${{ needs.validate.outputs.version }}')
|
|
->toContain('--tag "${IMAGE}:${VERSION}"')
|
|
->toContain('--tag "${IMAGE}:next"')
|
|
->toContain('prerelease: true')
|
|
->toContain('actions/github-script@v8')
|
|
->not->toContain('--tag "${IMAGE}:latest"')
|
|
->not->toContain('environment:');
|
|
});
|
|
|
|
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('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 production, rc, and hotfix release flows', function () {
|
|
$releaseGuide = file_get_contents(dirname(__DIR__, 2).'/RELEASE.md');
|
|
|
|
expect($releaseGuide)
|
|
->toContain('| `main` | Latest production source |')
|
|
->toContain('| `next` | Feature integration and RC releases |')
|
|
->toContain('| `hotfix/X.Y.Z` | Production fixes based on `main` |')
|
|
->toContain('feature/* → next → RC')
|
|
->toContain('next → main → stable release')
|
|
->toContain('main → hotfix/X.Y.Z → main → next')
|
|
->toContain('reviewed draft GitHub Release')
|
|
->toContain('workflows never edit or commit versions')
|
|
->toContain('Update the CDN only after the release is approved')
|
|
->not->toContain('`edge`')
|
|
->not->toContain('promotes the existing SHA image');
|
|
});
|
|
|
|
it('documents pull request targets for fixes and features', function () {
|
|
$contributingGuide = file_get_contents(dirname(__DIR__, 2).'/CONTRIBUTING.md');
|
|
|
|
expect($contributingGuide)
|
|
->toContain('Fixes and small improvements')
|
|
->toContain('target `main`')
|
|
->toContain('New features and larger changes')
|
|
->toContain('target `next`')
|
|
->toContain('branch from `main`')
|
|
->toContain('branch from `next`')
|
|
->not->toContain('All pull requests must target the `next` branch');
|
|
});
|
|
|
|
it('guides issue authors to the correct contribution branch', function () {
|
|
$bugReport = file_get_contents(dirname(__DIR__, 2).'/.github/ISSUE_TEMPLATE/01_BUG_REPORT.yml');
|
|
$issueConfig = file_get_contents(dirname(__DIR__, 2).'/.github/ISSUE_TEMPLATE/config.yml');
|
|
|
|
expect($bugReport)
|
|
->toContain('branch from `main` and target `main`')
|
|
->and($issueConfig)
|
|
->toContain('Feature code should branch from `next` and target `next`')
|
|
->toContain('Small fixes should target `main`; larger changes should target `next`');
|
|
});
|
|
|
|
it('writes versions.json in the production image from upstream.json so version checks work right after an upgrade', function () {
|
|
$dockerfile = file_get_contents(dirname(__DIR__, 2).'/docker/production/Dockerfile');
|
|
$dockerignore = file_get_contents(dirname(__DIR__, 2).'/.dockerignore');
|
|
|
|
expect($dockerfile)
|
|
->toContain('COPY --chown=www-data:www-data release.json upstream.json ./')
|
|
->toContain('upstream.json > versions.json')
|
|
->and($dockerignore)
|
|
->not->toMatch('/^\/?(release|upstream)\.json$/m');
|
|
});
|