feat(release): add release.json and upstream.json

- 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
This commit is contained in:
peaklabs-dev committed 2026-11-01 15:15:33 +01:00
1 parent 7560d0745d
commit 911e68ab27
13 files changed
+67 -55

No files matched your search

+3
View File
@@ -17,6 +17,9 @@ indent_size = 2
[docker-compose.yml]
indent_size = 4
[{release.json,upstream.json}]
indent_style = tab
[scripts/{clean-manifests,sync-cdn}.sh]
indent_style = tab
binary_next_line = true
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
- name: Resolve next version
id: version
run: |
RC_VERSION=$(jq -r '.coolify.nightly.version' versions.json)
RC_VERSION=$(jq -r '.coolify.nightly.version' other/nightly/versions.json)
if [[ ! "${RC_VERSION}" =~ ^[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then
echo "Invalid next RC version: ${RC_VERSION}"
exit 1
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
fi
VERSION="${TAG_NAME#v}"
CONFIG_VERSION=$(jq -r '.coolify.nightly.version' versions.json)
CONFIG_VERSION=$(jq -r '.coolify.nightly.version' other/nightly/versions.json)
if [[ "${CONFIG_VERSION}" != "${VERSION}" ]]; then
echo "RC tag ${VERSION} does not match nightly version ${CONFIG_VERSION}."
exit 1
+5 -2
View File
@@ -1,12 +1,15 @@
<?php
$release = json_decode(file_get_contents(dirname(__DIR__).'/release.json'), true);
$upstream = json_decode(file_get_contents(dirname(__DIR__).'/upstream.json'), true);
// 0 or an invalid value would remove the time limit of every remote command.
$sshCommandTimeout = (int) env('SSH_COMMAND_TIMEOUT', 3600);
return [
'coolify' => [
'version' => env('COOLIFY_VERSION') ?: '4.5.4',
'helper_version' => '1.0.19',
'version' => env('COOLIFY_VERSION') ?: $release['stable']['coolify'],
'helper_version' => $upstream['helper']['version'],
'railpack_version' => '0.43.0',
'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'),
+3 -2
View File
@@ -197,8 +197,9 @@ COPY --chown=www-data:www-data resources/views ./resources/views
COPY --chown=www-data:www-data artisan artisan
COPY --chown=www-data:www-data openapi.yaml ./openapi.yaml
COPY --chown=www-data:www-data changelogs/ ./changelogs/
# Version data for Traefik, Sentinel, and helper checks until CheckForUpdatesJob refreshes it
COPY --chown=www-data:www-data versions.json ./versions.json
COPY --chown=www-data:www-data release.json upstream.json ./
RUN jq --arg version "$COOLIFY_VERSION" '{coolify: ({v4: {$version}} + del(.traefik)), traefik}' upstream.json > versions.json \
&& chown www-data:www-data versions.json
RUN composer dump-autoload
+8
View File
@@ -0,0 +1,8 @@
{
"stable": {
"coolify": "4.5.3"
},
"preview": {
"coolify": "4.5.3"
}
}
+2 -2
View File
@@ -519,8 +519,8 @@ fi
# Fetch versions.json once and parse all values from it
VERSIONS_JSON=$(curl -L --silent $CDN/versions.json)
LATEST_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $2}' | tr -d ',')
LATEST_HELPER_VERSION=$(echo "$VERSIONS_JSON" | grep -i version | xargs | awk '{print $6}' | tr -d ',')
LATEST_VERSION=$(echo "$VERSIONS_JSON" | sed -n '/"v4"/,/}/s/.*"version": *"\([^"]*\)".*/\1/p' | head -n 1)
LATEST_HELPER_VERSION=$(echo "$VERSIONS_JSON" | sed -n '/"helper"/,/}/s/.*"version": *"\([^"]*\)".*/\1/p' | head -n 1)
if [ -z "$LATEST_HELPER_VERSION" ]; then
LATEST_HELPER_VERSION=latest
+2 -2
View File
@@ -51,8 +51,8 @@ select_channel() {
}
write_versions() {
jq --indent 4 --arg version "$VERSION" '.coolify.v4.version = $version' versions.json > "$WORK_DIR/versions.json"
ok "versions.json names Coolify $VERSION and the helper, Sentinel and Traefik versions of this commit"
jq --indent 4 --arg version "$VERSION" '{coolify: ({v4: {$version}} + del(.traefik)), traefik}' upstream.json > "$WORK_DIR/versions.json"
ok "versions.json names Coolify $VERSION and the helper, Sentinel and Traefik versions of upstream.json"
}
fetch_releases() {
@@ -55,8 +55,8 @@ it('notification settings have traefik_outdated fields', function () {
expect($team->webhookNotificationSettings)->toHaveKey('traefik_outdated_webhook_notifications');
});
it('versions.json contains traefik branches with patch versions', function () {
$versionsPath = base_path('versions.json');
it('upstream.json contains traefik branches with patch versions', function () {
$versionsPath = base_path('upstream.json');
expect(File::exists($versionsPath))->toBeTrue();
$versions = json_decode(File::get($versionsPath), true);
@@ -257,7 +257,7 @@ it('stops publishing or preserving the obsolete realtime image', function () {
->not->toContain('realtime_image')
->and(file_get_contents(app_path('Actions/Server/CleanupDocker.php')))
->not->toContain('realtimeImage')
->and(file_get_contents(base_path('versions.json')))
->and(file_get_contents(base_path('upstream.json')))
->not->toContain('"realtime"')
->and(is_dir(base_path('docker/coolify-realtime')))->toBeFalse()
->and(file_exists(base_path('.github/workflows/coolify-realtime.yml')))->toBeFalse()
+16 -11
View File
@@ -1,11 +1,18 @@
<?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');
$versions = json_decode(file_get_contents(dirname(__DIR__, 2).'/versions.json'), true, flags: JSON_THROW_ON_ERROR);
$nightlyVersions = json_decode(file_get_contents(dirname(__DIR__, 2).'/other/nightly/versions.json'), true, flags: JSON_THROW_ON_ERROR);
expect($workflow)
->toContain('name: Build Coolify Main')
@@ -30,10 +37,7 @@ it('publishes main builds under rolling and commit sha tags with a traceable int
->toContain('ARG COOLIFY_VERSION')
->toContain('ENV COOLIFY_VERSION=${COOLIFY_VERSION}')
->and($constants)
->toContain("'version' => env('COOLIFY_VERSION') ?: '{$versions['coolify']['v4']['version']}'")
->and($versions['coolify']['nightly']['version'])->toMatch('/^\d+\.\d+-rc\.\d+$/')
->and(version_compare($versions['coolify']['v4']['version'], $versions['coolify']['nightly']['version'], '<'))->toBeTrue()
->and($nightlyVersions)->toBe($versions);
->toContain("'version' => env('COOLIFY_VERSION') ?: \$release['stable']['coolify']");
});
it('orders a maintenance development build before its stable release', function () {
@@ -119,7 +123,7 @@ it('publishes traceable rolling builds from next without creating an exact rc ta
->toContain('name: Build Coolify Next')
->toContain('branches: [next]')
->toContain('group: coolify-next-build')
->toContain("jq -r '.coolify.nightly.version' versions.json")
->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}"')
@@ -140,7 +144,7 @@ it('requires a reviewed draft prerelease before publishing an exact rc', functio
->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' versions.json")
->toContain("jq -r '.coolify.nightly.version' other/nightly/versions.json")
->toContain('release.draft')
->toContain('!release.prerelease')
->toContain('release.body?.trim()')
@@ -215,12 +219,13 @@ it('guides issue authors to the correct contribution branch', function () {
->toContain('Small fixes should target `main`; larger changes should target `next`');
});
it('ships versions.json in the production image so version checks work right after an upgrade', function () {
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 versions.json ./versions.json')
->toContain('COPY --chown=www-data:www-data release.json upstream.json ./')
->toContain('upstream.json > versions.json')
->and($dockerignore)
->not->toMatch('/^\/?versions\.json$/m');
->not->toMatch('/^\/?(release|upstream)\.json$/m');
});
+23
View File
@@ -0,0 +1,23 @@
{
"helper": {
"version": "1.0.19"
},
"sentinel": {
"version": "1.1.3"
},
"sentinel-host": {
"version": "1.1.3",
"digest": "sha256:bca77d22355c9005185b438dad406747b4b776018215a0a4a75c88618d681ff7"
},
"traefik": {
"v3.7": "3.7.16",
"v3.6": "3.6.25",
"v3.5": "3.5.6",
"v3.4": "3.4.5",
"v3.3": "3.3.7",
"v3.2": "3.2.5",
"v3.1": "3.1.7",
"v3.0": "3.0.4",
"v2.11": "2.11.60"
}
}
-31
View File
@@ -1,31 +0,0 @@
{
"coolify": {
"v4": {
"version": "4.5.4"
},
"nightly": {
"version": "4.6-rc.1"
},
"helper": {
"version": "1.0.19"
},
"sentinel": {
"version": "1.1.3"
},
"sentinel-host": {
"version": "1.1.3",
"digest": "sha256:bca77d22355c9005185b438dad406747b4b776018215a0a4a75c88618d681ff7"
}
},
"traefik": {
"v3.7": "3.7.16",
"v3.6": "3.6.25",
"v3.5": "3.5.6",
"v3.4": "3.4.5",
"v3.3": "3.3.7",
"v3.2": "3.2.5",
"v3.1": "3.1.7",
"v3.0": "3.0.4",
"v2.11": "2.11.60"
}
}