mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
feat(install): add --preview and --nightly flags to install.sh
- Once Coolify is healthy, install.sh switches the instance to that update channel with php artisan app:update-channel - The command and the update page share the rule that keeps the updates of the old channel after a switch to a more stable one
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UpdateChannel extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:update-channel {channel : stable, preview or nightly}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Switch the update channel of the instance, as the update page does';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$channel = $this->argument('channel');
|
||||
if (! in_array($channel, ['stable', 'preview', 'nightly'], true)) {
|
||||
$this->error("{$channel} is not an update channel, expected stable, preview or nightly");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$settings = instanceSettings();
|
||||
$settings->switchUpdateChannel($channel);
|
||||
$settings->save();
|
||||
invalidate_versions_cache();
|
||||
$this->info("The instance follows the {$channel} channel");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -73,13 +73,10 @@ class Updates extends Component
|
||||
$this->settings->update_check_frequency = $this->update_check_frequency;
|
||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
||||
$this->settings->docker_registry_url = $validated['docker_registry_url'];
|
||||
$this->settings->update_channel = $validated['update_channel'];
|
||||
$this->settings->switchUpdateChannel($validated['update_channel']);
|
||||
$this->settings->update_custom_tag = $validated['update_custom_tag'];
|
||||
$this->syncRegistryUrlToEnv($validated['docker_registry_url']);
|
||||
$channelChanged = $this->settings->isDirty(['update_channel', 'update_custom_tag']);
|
||||
if ($this->settings->isDirty('update_channel')) {
|
||||
$this->settings->previous_update_channel = $this->channelFollowedUntilRelease($this->settings->getOriginal('update_channel'), $validated['update_channel']);
|
||||
}
|
||||
$changedFields = auditChangedFields($this->settings);
|
||||
$this->settings->save();
|
||||
if ($channelChanged) {
|
||||
@@ -103,22 +100,6 @@ class Updates extends Component
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After a switch to a more stable channel, the instance keeps the updates of the channel it left until the stable release of its version is out.
|
||||
*/
|
||||
private function channelFollowedUntilRelease(?string $from, string $to): ?string
|
||||
{
|
||||
$channels = ['stable', 'preview', 'nightly'];
|
||||
$fromRank = array_search($from, $channels, true);
|
||||
$toRank = array_search($to, $channels, true);
|
||||
if ($fromRank === false || $toRank === false || $toRank >= $fromRank) {
|
||||
return null;
|
||||
}
|
||||
$followed = $this->settings->getOriginal('previous_update_channel');
|
||||
|
||||
return array_search($followed, $channels, true) > $fromRank ? $followed : $from;
|
||||
}
|
||||
|
||||
protected function syncRegistryUrlToEnv(string $registryUrl): void
|
||||
{
|
||||
if (! $this->server) {
|
||||
|
||||
@@ -146,6 +146,28 @@ class InstanceSettings extends Model
|
||||
return random_int(0, 59).' 0 * * *';
|
||||
}
|
||||
|
||||
/**
|
||||
* After a switch to a more stable channel, the instance keeps the updates of the channel it left until the stable release of its version is out.
|
||||
*/
|
||||
public function switchUpdateChannel(string $channel): void
|
||||
{
|
||||
$from = $this->getOriginal('update_channel');
|
||||
$this->update_channel = $channel;
|
||||
if ($channel === $from) {
|
||||
return;
|
||||
}
|
||||
|
||||
$channels = ['stable', 'preview', 'nightly'];
|
||||
$fromRank = array_search($from, $channels, true);
|
||||
$toRank = array_search($channel, $channels, true);
|
||||
$followed = $this->getOriginal('previous_update_channel');
|
||||
$this->previous_update_channel = match (true) {
|
||||
$fromRank === false || $toRank === false || $toRank >= $fromRank => null,
|
||||
array_search($followed, $channels, true) > $fromRank => $followed,
|
||||
default => $from,
|
||||
};
|
||||
}
|
||||
|
||||
public function isPasswordRegistrationAllowed(): bool
|
||||
{
|
||||
if (! $this->is_registration_enabled) {
|
||||
|
||||
@@ -11,12 +11,22 @@
|
||||
## AUTOUPDATE - Set to "false" to disable auto-updates
|
||||
## REGISTRY_URL - Custom registry URL for Docker images (default: docker.io)
|
||||
|
||||
## Arguments that can be passed:
|
||||
## --preview or --nightly - Install the newest version of the preview or nightly channel and switch the instance to that update channel
|
||||
|
||||
set -e # Exit immediately if a command exits with a non-zero status
|
||||
## $1 could be empty, so we need to disable this check
|
||||
#set -u # Treat unset variables as an error and exit
|
||||
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
|
||||
CDN="https://cdn.coollabs.io/coolify"
|
||||
CHANNEL="stable"
|
||||
case "$1" in
|
||||
--preview | --nightly)
|
||||
CHANNEL="${1#--}"
|
||||
CDN="https://cdn.coollabs.io/coolify/$CHANNEL"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
DATE=$(date +"%Y%m%d-%H%M%S")
|
||||
|
||||
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
@@ -1131,6 +1141,11 @@ if [ "$HEALTH" != "healthy" ]; then
|
||||
fi
|
||||
step_done
|
||||
|
||||
if [ "$CHANNEL" != "stable" ]; then
|
||||
docker exec coolify php artisan app:update-channel "$CHANNEL" || warn "Could not switch the instance to the $CHANNEL update channel, switch it on the update page"
|
||||
ui_flush_warnings
|
||||
fi
|
||||
|
||||
if [ "$UI_TTY" = true ]; then
|
||||
printf '%s\n' "$(ui_bar 100 "done")" >&3
|
||||
fi
|
||||
|
||||
@@ -19,6 +19,17 @@ it('reads the Coolify and helper versions of its channel from versions.json in i
|
||||
'nightly' => ['nightly', '4.6.0-beta.1.20261027.1'],
|
||||
]);
|
||||
|
||||
it('selects the channel from the --preview or --nightly flag in install.sh', function (array $arguments, string $selection) {
|
||||
preg_match('/^CDN=.*?^esac$/ms', file_get_contents(dirname(__DIR__, 2).'/scripts/install.sh'), $channelSelection);
|
||||
$output = shell_exec('bash -c '.escapeshellarg($channelSelection[0]."\necho \"\$CHANNEL \$CDN \$1\"").' install.sh '.implode(' ', array_map('escapeshellarg', $arguments)));
|
||||
|
||||
expect(trim($output))->toBe($selection);
|
||||
})->with([
|
||||
'stable' => [[], 'stable https://cdn.coollabs.io/coolify'],
|
||||
'preview' => [['--preview'], 'preview https://cdn.coollabs.io/coolify/preview'],
|
||||
'nightly with a version' => [['--nightly', '4.6.0-beta.1.20261027.1'], 'nightly https://cdn.coollabs.io/coolify/nightly 4.6.0-beta.1.20261027.1'],
|
||||
]);
|
||||
|
||||
it('builds the development helper image from the coolify-helper repository', function (string $file) {
|
||||
expect(file_get_contents(dirname(__DIR__, 2)."/{$file}"))->toContain('https://github.com/coollabsio/coolify-helper.git#main:src');
|
||||
})->with(['scripts/dev-helper', 'docker-compose.dev.yml', 'docker-compose.dev-multi.yml']);
|
||||
|
||||
@@ -202,3 +202,23 @@ it('follows the channel it left after a switch to a more stable channel', functi
|
||||
'stable to nightly' => ['stable', 'nightly', null],
|
||||
'nightly to a custom image' => ['nightly', 'custom', null],
|
||||
]);
|
||||
|
||||
it('switches the update channel with app:update-channel like the update page does', function (string $from, string $to, ?string $followed) {
|
||||
InstanceSettings::findOrFail(0)->update(['update_channel' => $from]);
|
||||
Once::flush();
|
||||
|
||||
$this->artisan('app:update-channel', ['channel' => $to])->assertSuccessful();
|
||||
|
||||
expect(InstanceSettings::findOrFail(0))
|
||||
->update_channel->toBe($to)
|
||||
->previous_update_channel->toBe($followed);
|
||||
})->with([
|
||||
'stable to nightly' => ['stable', 'nightly', null],
|
||||
'nightly to preview' => ['nightly', 'preview', 'nightly'],
|
||||
]);
|
||||
|
||||
it('refuses an update channel that does not exist', function () {
|
||||
$this->artisan('app:update-channel', ['channel' => 'custom'])->assertFailed();
|
||||
|
||||
expect(InstanceSettings::findOrFail(0)->update_channel)->toBe('stable');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user