feat(v5): add coolify CLI version check

This commit is contained in:
Andras Bacsai
2026-06-16 13:57:42 +02:00
parent d92c6a8dab
commit c884aaaeea
15 changed files with 365 additions and 117 deletions
+2
View File
@@ -42,4 +42,6 @@ tests/Browser/Screenshots
tests/v4/Browser/Screenshots
# Local generated Lima configs
.dev/bin/
.dev/lima/*.generated.yaml
.dev/lima/ssh.config
@@ -5,7 +5,9 @@ namespace App\Http\Controllers\V5;
use App\Http\Controllers\Controller;
use App\Models\Team;
use App\Models\User;
use App\Services\Coold\CoolifyCliVersion;
use App\Services\Flux\FluxHealth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
@@ -43,6 +45,11 @@ class HomeController extends Controller
]);
}
public function coolifyCliVersion(CoolifyCliVersion $coolifyCliVersion): JsonResponse
{
return response()->json($coolifyCliVersion->check());
}
/**
* @return array<int, array{id: string, wireguardIp: string|null, capabilities: array<int, string>, builderEnabled: bool, builderCapacity: int}>
*/
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace App\Services\Coold;
use Illuminate\Support\Facades\Process;
use Throwable;
class CoolifyCliVersion
{
/**
* @return array{available: bool, label: string, version: string|null, message: string, binary: string|null}
*/
public function check(): array
{
$binary = config('coold.coolify_cli_bin', '/usr/local/bin/coolify');
if (! is_string($binary) || $binary === '') {
return $this->unavailable(null, 'coolify binary is not configured.');
}
try {
$result = Process::timeout(5)->run(escapeshellarg($binary).' --version');
} catch (Throwable $exception) {
return $this->unavailable($binary, $exception->getMessage());
}
if (! $result->successful()) {
return $this->unavailable($binary, trim($result->errorOutput()) ?: 'coolify version check failed.');
}
$version = trim($result->output());
return [
'available' => true,
'label' => 'Installed',
'version' => $version !== '' ? $version : null,
'message' => $version !== '' ? "Installed version: {$version}." : 'coolify is installed.',
'binary' => $binary,
];
}
/**
* @return array{available: false, label: string, version: null, message: string, binary: string|null}
*/
private function unavailable(?string $binary, string $message): array
{
return [
'available' => false,
'label' => 'Unavailable',
'version' => null,
'message' => $message,
'binary' => $binary,
];
}
}
+5 -4
View File
@@ -1,11 +1,12 @@
<?php
return [
'coolify_cli_bin' => env('COOLIFY_CLI_BIN', '/usr/local/bin/coolify'),
'dev_host_count' => (int) env('COOLIFY_COOLD_VM_COUNT', 2),
'dev_host_id' => env('COOLIFY_COOLD_DEV_HOST_ID', 'coolify-coold-dev'),
'dev_host_id_2' => env('COOLIFY_COOLD_LIMA_INSTANCE_2', env('COOLIFY_COOLD_DEV_HOST_ID', 'coolify-coold-dev').'-2'),
'dev_wireguard_ip_1' => env('COOLIFY_COOLD_VM_WG_IP_1', '100.64.0.10'),
'dev_wireguard_ip_2' => env('COOLIFY_COOLD_VM_WG_IP_2', '100.64.0.11'),
'dev_host_id' => env('COOLIFY_COOLD_DEV_HOST_ID', 'coold-dev'),
'dev_host_id_2' => env('COOLIFY_COOLD_LIMA_INSTANCE_2', env('COOLIFY_COOLD_DEV_HOST_ID', 'coold-dev').'-2'),
'dev_wireguard_ip_1' => env('COOLIFY_COOLD_VM_WG_IP_1', '100.64.0.1'),
'dev_wireguard_ip_2' => env('COOLIFY_COOLD_VM_WG_IP_2', '100.64.0.2'),
'dev_builder_capacity' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2),
'dev_builder_enabled' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2) > 0,
];
+10 -10
View File
@@ -6,7 +6,7 @@ This file documents the current local v5/coold dev setup in Coolify.
- `scripts/dev.sh` owns local developer convenience.
- Lima VMs act like real deployment servers.
- `cooldctl init bootstrap` owns host wiring:
- `coolify init bootstrap` owns host wiring:
- WireGuard
- Podman mesh networks
- Corrosion config/schema/service
@@ -32,16 +32,16 @@ scripts/dev.sh list
containers, WireGuard keys, Corrosion DB, installed binaries, and firewall state.
It does not delete the Coolify repo.
## cooldctl helper
## coolify helper
```bash
scripts/dev.sh cooldctl install
scripts/dev.sh cooldctl path
scripts/dev.sh cooldctl bootstrap-command
scripts/dev.sh cooldctl run <args>
scripts/dev.sh coolify install
scripts/dev.sh coolify path
scripts/dev.sh coolify bootstrap-command
scripts/dev.sh coolify run <args>
```
On macOS, the helper builds `cooldctl` from the local coold repo because the
On macOS, the helper builds `coolify` from the local coold repo because the
nightly release currently publishes Linux binaries. On Linux, it downloads the
nightly release artifact.
@@ -49,7 +49,7 @@ The generated bootstrap command uses Lima's forwarded SSH ports and dev
WireGuard endpoint overrides, for example:
```bash
.dev/bin/cooldctl init bootstrap \
.dev/bin/coolify init bootstrap \
--nodes "127.0.0.1:<ssh1>,127.0.0.1:<ssh2>" \
--ssh-key "$HOME/.lima/_config/user" \
--ssh-user "$USER" \
@@ -61,12 +61,12 @@ WireGuard endpoint overrides, for example:
```
Lima does not allow direct root SSH by default, so dev uses the normal Lima user
with passwordless sudo. `cooldctl` wraps remote commands in `sudo -n bash -lc`
with passwordless sudo. `coolify` wraps remote commands in `sudo -n bash -lc`
when the SSH user is not `root`.
## Default dev topology
After `cooldctl init bootstrap`, defaults are:
After `coolify init bootstrap`, defaults are:
| VM | WireGuard IP | WireGuard endpoint | Podman subnet | Gateway |
| --- | --- | --- | --- | --- |
+2 -2
View File
@@ -1,4 +1,4 @@
# Lima VM for testing Coolify v5 against cooldctl-provisioned hosts.
# Lima VM for testing Coolify v5 against coolify-provisioned hosts.
# Start with: scripts/coold-vm.sh up
#
# This file is copied into .dev/lima/coold.generated.yaml by the wrapper script,
@@ -58,4 +58,4 @@ provision:
mkdir -p /etc/coolify /var/lib/coolify-dev
chmod 755 /etc/coolify
echo "[coold-vm] Minimal provisioning complete. cooldctl bootstrap will install WireGuard, Podman, Corrosion, coold, and builder."
echo "[coold-vm] Minimal provisioning complete. coolify bootstrap will install WireGuard, Podman, Corrosion, coold, and builder."
+4
View File
@@ -8,10 +8,14 @@ services:
args:
- USER_ID=${USERID:-1000}
- GROUP_ID=${GROUPID:-1000}
- COOLIFY_FLUX_VERSION=${COOLIFY_FLUX_VERSION:-nightly}
- COOLIFY_CLI_VERSION=${COOLIFY_CLI_VERSION:-nightly}
ports:
- "${APP_PORT:-8000}:8080"
environment:
AUTORUN_ENABLED: false
COOLIFY_FLUX_VERSION: "${COOLIFY_FLUX_VERSION:-nightly}"
COOLIFY_CLI_VERSION: "${COOLIFY_CLI_VERSION:-nightly}"
PUSHER_HOST: "${PUSHER_HOST:-}"
PUSHER_PORT: "${PUSHER_PORT:-}"
PUSHER_SCHEME: "${PUSHER_SCHEME:-http}"
+2
View File
@@ -9,6 +9,7 @@ services:
- USER_ID=${USERID:-1000}
- GROUP_ID=${GROUPID:-1000}
- COOLIFY_FLUX_VERSION=${COOLIFY_FLUX_VERSION:-nightly}
- COOLIFY_CLI_VERSION=${COOLIFY_CLI_VERSION:-nightly}
ports:
- "${APP_PORT:-8000}:8080"
- "${FORWARD_FLUX_PORT:-6443}:6443"
@@ -17,6 +18,7 @@ services:
COOLIFY_CONTAINER_ROLE: "${COOLIFY_CONTAINER_ROLE:-all}"
COOLIFY_COOLD_VERSION: "${COOLIFY_COOLD_VERSION:-nightly}"
COOLIFY_FLUX_VERSION: "${COOLIFY_FLUX_VERSION:-nightly}"
COOLIFY_CLI_VERSION: "${COOLIFY_CLI_VERSION:-nightly}"
COOLIFY_CORROSION_VERSION: "${COOLIFY_CORROSION_VERSION:-v1.0.0}"
PUSHER_HOST: "${PUSHER_HOST:-}"
PUSHER_PORT: "${PUSHER_PORT:-}"
+23
View File
@@ -7,6 +7,7 @@ ARG MINIO_VERSION=RELEASE.2025-05-21T01-59-54Z
ARG CLOUDFLARED_VERSION=2025.7.0
# https://github.com/coollabsio/coold/releases/tag/nightly
ARG COOLIFY_FLUX_VERSION=nightly
ARG COOLIFY_CLI_VERSION=nightly
# https://www.postgresql.org/support/versioning/
# Note: We are using version 18 of the postgres client (while still using postgres 15 for the postgres server) as version 15 has been removed from Alpine 3.23+ https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.23&repo=&arch=x86_64&origin=&flagged=&maintainer=
ARG POSTGRES_VERSION=18
@@ -30,6 +31,7 @@ ARG TARGETARCH
ARG POSTGRES_VERSION
ARG CLOUDFLARED_VERSION
ARG COOLIFY_FLUX_VERSION
ARG COOLIFY_CLI_VERSION
ARG NGINX_VERSION
WORKDIR /var/www/html
@@ -108,6 +110,27 @@ RUN set -eux; \
install -m 0755 /tmp/flux /usr/local/bin/flux; \
rm -f /tmp/flux /tmp/flux.tar.gz
# Install coolify from coold nightly release based on architecture
RUN set -eux; \
mkdir -p /usr/local/bin; \
case "${TARGETARCH:-}" in \
amd64|arm64) COOLIFY_CLI_ARCH="${TARGETARCH}" ;; \
"") \
case "$(uname -m)" in \
x86_64) COOLIFY_CLI_ARCH="amd64" ;; \
aarch64) COOLIFY_CLI_ARCH="arm64" ;; \
*) echo "unsupported coolify arch: $(uname -m)" >&2; exit 1 ;; \
esac ;; \
*) echo "unsupported coolify TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
curl -fsSL --retry 3 --max-time 120 \
-o /tmp/coolify.tar.gz \
"https://github.com/coollabsio/coold/releases/download/${COOLIFY_CLI_VERSION}/coolify-linux-musl-${COOLIFY_CLI_ARCH}.tar.gz"; \
tar -xzf /tmp/coolify.tar.gz -C /tmp; \
test -f /tmp/coolify; \
install -m 0755 /tmp/coolify /usr/local/bin/coolify; \
rm -f /tmp/coolify /tmp/coolify.tar.gz
# Configure PHP
COPY docker/development/etc/php/conf.d/zzz-custom-php.ini /usr/local/etc/php/conf.d/zzz-custom-php.ini
ENV PHP_OPCACHE_ENABLE=0
+23
View File
@@ -7,6 +7,7 @@ ARG MINIO_VERSION=RELEASE.2025-05-21T01-59-54Z
ARG CLOUDFLARED_VERSION=2025.7.0
# https://github.com/coollabsio/coold/releases/tag/nightly
ARG COOLIFY_FLUX_VERSION=nightly
ARG COOLIFY_CLI_VERSION=nightly
# https://www.postgresql.org/support/versioning/
# Note: We are using version 18 of the postgres client (while still using postgres 15 for the postgres server) as version 15 has been removed from Alpine 3.23+ https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.23&repo=&arch=x86_64&origin=&flagged=&maintainer=
ARG POSTGRES_VERSION=18
@@ -79,6 +80,7 @@ ARG TARGETARCH
ARG POSTGRES_VERSION
ARG CLOUDFLARED_VERSION
ARG COOLIFY_FLUX_VERSION
ARG COOLIFY_CLI_VERSION
ARG NGINX_VERSION
ARG CI=true
@@ -155,6 +157,27 @@ RUN set -eux; \
install -m 0755 /tmp/flux /usr/local/bin/flux; \
rm -f /tmp/flux /tmp/flux.tar.gz
# Install coolify from coold nightly release based on architecture
RUN set -eux; \
mkdir -p /usr/local/bin; \
case "${TARGETARCH:-}" in \
amd64|arm64) COOLIFY_CLI_ARCH="${TARGETARCH}" ;; \
"") \
case "$(uname -m)" in \
x86_64) COOLIFY_CLI_ARCH="amd64" ;; \
aarch64) COOLIFY_CLI_ARCH="arm64" ;; \
*) echo "unsupported coolify arch: $(uname -m)" >&2; exit 1 ;; \
esac ;; \
*) echo "unsupported coolify TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
curl -fsSL --retry 3 --max-time 120 \
-o /tmp/coolify.tar.gz \
"https://github.com/coollabsio/coold/releases/download/${COOLIFY_CLI_VERSION}/coolify-linux-musl-${COOLIFY_CLI_ARCH}.tar.gz"; \
tar -xzf /tmp/coolify.tar.gz -C /tmp; \
test -f /tmp/coolify; \
install -m 0755 /tmp/coolify /usr/local/bin/coolify; \
rm -f /tmp/coolify /tmp/coolify.tar.gz
# Configure PHP
COPY docker/production/etc/php/conf.d/zzz-custom-php.ini /usr/local/etc/php/conf.d/zzz-custom-php.ini
ENV PHP_OPCACHE_ENABLE=1
+47
View File
@@ -1,6 +1,34 @@
import { Head } from '@inertiajs/react';
import { useState } from 'react';
export default function Home({ status, currentTeam, teams, flux, cooldHosts }) {
const [coolify, setCoolify] = useState(null);
const [checkingCoolify, setCheckingCoolify] = useState(false);
async function checkCoolifyCliVersion() {
setCheckingCoolify(true);
try {
const response = await fetch('/v5/coolify/version', {
headers: {
Accept: 'application/json',
},
});
setCoolify(await response.json());
} catch (error) {
setCoolify({
available: false,
label: 'Unavailable',
version: null,
message: 'Could not check the installed coolify version.',
binary: null,
});
} finally {
setCheckingCoolify(false);
}
}
return (
<>
<Head title="V5" />
@@ -45,6 +73,25 @@ export default function Home({ status, currentTeam, teams, flux, cooldHosts }) {
</ul>
</section>
<section aria-labelledby="coolify-heading">
<h2 id="coolify-heading">coolify</h2>
<button type="button" onClick={checkCoolifyCliVersion} disabled={checkingCoolify}>
{checkingCoolify ? 'Checking coolify...' : 'Check coolify version'}
</button>
{coolify ? (
<div>
<p>
<strong>{coolify.label}</strong>
</p>
{coolify.version ? <p>Version: {coolify.version}</p> : null}
<p>{coolify.message}</p>
{coolify.binary ? <p>Binary: {coolify.binary}</p> : null}
</div>
) : null}
</section>
<h2>Current team</h2>
{currentTeam ? (
+1
View File
@@ -5,4 +5,5 @@ use Illuminate\Support\Facades\Route;
Route::middleware('v5.authenticated')->group(function () {
Route::get('/', HomeController::class)->name('home');
Route::get('/coolify/version', [HomeController::class, 'coolifyCliVersion'])->name('coolify.version');
});
+7 -4
View File
@@ -40,7 +40,7 @@ if [ "$BUILDER_CAPACITY" = "0" ]; then
BUILDER_ENABLED="false"
fi
TEMPLATE="$ROOT/dev/lima/coold.yaml"
GENERATED="$ROOT/.dev/lima/coold.generated.yaml"
GENERATED="$ROOT/.dev/lima/${INSTANCE}.generated.yaml"
GUEST_COOLIFY_ROOT="/workspace/coolify"
usage() {
@@ -436,8 +436,11 @@ wait_for_lima_start() {
lima_shell sudo sh -c 'test -f /var/log/cloud-init-output.log && tail -n 12 /var/log/cloud-init-output.log || true' 2>/dev/null | awk '{ print "[guest] " $0; fflush(); }' || true
if ! printf '%s' "$status" | grep -q running; then
kill "$start_pid" >/dev/null 2>&1 || true
break
if kill -0 "$start_pid" 2>/dev/null; then
kill "$start_pid" >/dev/null 2>&1 || true
disown "$start_pid" >/dev/null 2>&1 || true
fi
return
fi
else
message="$(latest_lima_message)"
@@ -503,7 +506,7 @@ up_with_logs() {
wait_for_lima_start
wait_for_guest_provisioning
echo "==> VM is ready. Run cooldctl bootstrap via: scripts/dev.sh up"
echo "==> VM is ready. Run coolify bootstrap via: scripts/dev.sh up"
}
cmd="${1:-}"
+106 -93
View File
@@ -100,30 +100,11 @@ host_os() {
esac
}
cooldctl_bin() {
printf '%s\n' "$ROOT/.dev/bin/cooldctl"
coolify_cli_bin() {
printf '%s\n' "$ROOT/.dev/bin/coolify"
}
build_local_cooldctl() {
local bin
local coold_repo
bin="$(cooldctl_bin)"
coold_repo="$(read_coolify_env COOLIFY_COOLD_REPO /Users/heyandras/devel/coold)"
if [ ! -d "$coold_repo/cooldctl" ]; then
echo "ERROR: macOS cooldctl release artifact is not available yet and local coold repo was not found at ${coold_repo}." >&2
echo "Set COOLIFY_COOLD_REPO=/path/to/coold or build/copy cooldctl to ${bin}." >&2
exit 1
fi
echo "==> Building local macOS cooldctl from ${coold_repo}"
(cd "$coold_repo" && cargo build -p cooldctl)
mkdir -p "$(dirname "$bin")"
install -m 0755 "$coold_repo/target/debug/cooldctl" "$bin"
}
ensure_cooldctl() {
ensure_coolify() {
local bin
local version
local arch
@@ -131,63 +112,84 @@ ensure_cooldctl() {
local url
local tmpdir
bin="$(cooldctl_bin)"
version="$(read_coolify_env COOLIFY_COOLDCTL_VERSION "$(read_coolify_env COOLIFY_COOLD_VERSION nightly)")"
bin="$(coolify_cli_bin)"
version="$(read_coolify_env COOLIFY_CLI_VERSION "$(read_coolify_env COOLIFY_COOLD_VERSION nightly)")"
arch="$(host_arch)"
os="$(host_os)"
if [ "$os" = "darwin" ]; then
build_local_cooldctl
if [ -x "$bin" ] && "$bin" --version >/dev/null 2>&1 && [ "${COOLIFY_CLI_FORCE_DOWNLOAD:-false}" != "true" ]; then
return
fi
if [ -x "$bin" ] && "$bin" --version >/dev/null 2>&1 && [ "${COOLIFY_COOLDCTL_FORCE_DOWNLOAD:-false}" != "true" ]; then
return
fi
url="https://github.com/coollabsio/coold/releases/download/${version}/cooldctl-${os}-${arch}.tar.gz"
echo "==> Installing cooldctl from ${url}"
url="https://github.com/coollabsio/coold/releases/download/${version}/coolify-${os}-${arch}.tar.gz"
echo "==> Installing coolify from ${url}"
mkdir -p "$(dirname "$bin")"
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' RETURN
curl -fsSL --retry 3 --max-time 120 -o "$tmpdir/cooldctl.tar.gz" "$url"
tar -xzf "$tmpdir/cooldctl.tar.gz" -C "$tmpdir"
install -m 0755 "$tmpdir/cooldctl" "$bin"
if ! curl -fsSL --retry 3 --max-time 120 -o "$tmpdir/coolify.tar.gz" "$url"; then
rm -rf "$tmpdir"
return 1
fi
tar -xzf "$tmpdir/coolify.tar.gz" -C "$tmpdir"
install -m 0755 "$tmpdir/coolify" "$bin"
rm -rf "$tmpdir"
}
lima_ssh_target() {
local index="$1"
local instance
local ssh
instance="$(coold_vm_instance "$index")"
ssh="$(limactl list 2>/dev/null | awk -v name="$instance" 'NR > 1 && $1 == name {print $3}')"
if [ -z "$ssh" ] || [ "$ssh" = "-" ]; then
echo "ERROR: could not determine SSH target for Lima VM ${instance}. Start it first with scripts/dev.sh up." >&2
if [ ! -f "$HOME/.lima/${instance}/ssh.config" ]; then
echo "ERROR: Lima SSH config for ${instance} was not found. Start it first with scripts/dev.sh up." >&2
exit 1
fi
printf '%s\n' "$ssh"
printf 'lima-%s\n' "$instance"
}
cooldctl_nodes_arg() {
lima_ssh_config() {
local count
local config="$ROOT/.dev/lima/ssh.config"
local instance
count="$(coold_vm_count)"
mkdir -p "$(dirname "$config")"
: > "$config"
for index in $(seq 1 "$count"); do
instance="$(coold_vm_instance "$index")"
if [ ! -f "$HOME/.lima/${instance}/ssh.config" ]; then
echo "ERROR: Lima SSH config for ${instance} was not found. Start it first with scripts/dev.sh up." >&2
exit 1
fi
cat "$HOME/.lima/${instance}/ssh.config" >> "$config"
printf '\n' >> "$config"
done
printf '%s\n' "$config"
}
coolify_nodes_arg() {
local count
local nodes=""
local node
count="$(coold_vm_count)"
for index in $(seq 1 "$count"); do
node="$(lima_ssh_target "$index")" || return 1
if [ -n "$nodes" ]; then
nodes="${nodes},"
fi
nodes="${nodes}$(lima_ssh_target "$index")"
nodes="${nodes}${node}"
done
printf '%s\n' "$nodes"
}
cooldctl_wg_listen_overrides_arg() {
coolify_wg_listen_overrides_arg() {
local count
local overrides=""
local node
@@ -204,7 +206,7 @@ cooldctl_wg_listen_overrides_arg() {
printf '%s\n' "$overrides"
}
cooldctl_wg_endpoint_overrides_arg() {
coolify_wg_endpoint_overrides_arg() {
local count
local overrides=""
local node
@@ -221,45 +223,63 @@ cooldctl_wg_endpoint_overrides_arg() {
printf '%s\n' "$overrides"
}
cooldctl_ssh_key() {
read_coolify_env COOLIFY_COOLDCTL_SSH_KEY "$HOME/.lima/_config/user"
coolify_ssh_key() {
read_coolify_env COOLIFY_CLI_SSH_KEY "$HOME/.lima/_config/user"
}
cooldctl_ssh_user() {
read_coolify_env COOLIFY_COOLDCTL_SSH_USER "$USER"
coolify_ssh_user() {
read_coolify_env COOLIFY_CLI_SSH_USER "$USER"
}
cooldctl_bootstrap_command() {
ensure_cooldctl
coolify_bootstrap_command() {
local nodes
local ssh_config
local listen_overrides
local endpoint_overrides
ensure_coolify
nodes="$(coolify_nodes_arg)" || return 1
ssh_config="$(lima_ssh_config)" || return 1
listen_overrides="$(coolify_wg_listen_overrides_arg)" || return 1
endpoint_overrides="$(coolify_wg_endpoint_overrides_arg)" || return 1
cat <<CMD
$(cooldctl_bin) init bootstrap \\
--nodes "$(cooldctl_nodes_arg)" \\
--ssh-key "$(cooldctl_ssh_key)" \\
--ssh-user "$(cooldctl_ssh_user)" \\
--wg-listen-port-overrides "$(cooldctl_wg_listen_overrides_arg)" \\
--wg-endpoint-overrides "$(cooldctl_wg_endpoint_overrides_arg)" \\
$(coolify_cli_bin) init bootstrap \\
--nodes "${nodes}" \\
--ssh-config "${ssh_config}" \\
--ssh-user "$(coolify_ssh_user)" \\
--wg-listen-port-overrides "${listen_overrides}" \\
--wg-endpoint-overrides "${endpoint_overrides}" \\
--coold-version "$(read_coolify_env COOLIFY_COOLD_VERSION nightly)" \\
--corrosion-version "$(read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0)" \\
--yes
CMD
}
cooldctl_bootstrap() {
ensure_cooldctl
coolify_bootstrap() {
local nodes
local ssh_config
local listen_overrides
local endpoint_overrides
ensure_coolify
"$(cooldctl_bin)" init bootstrap \
--nodes "$(cooldctl_nodes_arg)" \
--ssh-key "$(cooldctl_ssh_key)" \
--ssh-user "$(cooldctl_ssh_user)" \
--wg-listen-port-overrides "$(cooldctl_wg_listen_overrides_arg)" \
--wg-endpoint-overrides "$(cooldctl_wg_endpoint_overrides_arg)" \
nodes="$(coolify_nodes_arg)" || return 1
ssh_config="$(lima_ssh_config)" || return 1
listen_overrides="$(coolify_wg_listen_overrides_arg)" || return 1
endpoint_overrides="$(coolify_wg_endpoint_overrides_arg)" || return 1
"$(coolify_cli_bin)" init bootstrap \
--nodes "$nodes" \
--ssh-config "$ssh_config" \
--ssh-user "$(coolify_ssh_user)" \
--wg-listen-port-overrides "$listen_overrides" \
--wg-endpoint-overrides "$endpoint_overrides" \
--coold-version "$(read_coolify_env COOLIFY_COOLD_VERSION nightly)" \
--corrosion-version "$(read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0)" \
--yes
}
cooldctl_dev() {
coolify_dev() {
local command="${1:-help}"
if [ $# -gt 0 ]; then
shift
@@ -267,37 +287,37 @@ cooldctl_dev() {
case "$command" in
install)
ensure_cooldctl
"$(cooldctl_bin)" --version
ensure_coolify
"$(coolify_cli_bin)" --version
;;
path)
ensure_cooldctl
cooldctl_bin
ensure_coolify
coolify_cli_bin
;;
bootstrap-command)
cooldctl_bootstrap_command
coolify_bootstrap_command
;;
run)
ensure_cooldctl
exec "$(cooldctl_bin)" "$@"
ensure_coolify
exec "$(coolify_cli_bin)" "$@"
;;
-h|--help|help)
cat <<'USAGE'
Usage: scripts/dev.sh cooldctl <command>
Usage: scripts/dev.sh coolify <command>
Commands:
install Download/install the nightly cooldctl dev binary
path Print the local cooldctl path
install Download/install the nightly coolify dev binary
path Print the local coolify path
bootstrap-command Print the dev Lima bootstrap command without running it
run <args> Run cooldctl with arbitrary args
run <args> Run coolify with arbitrary args
Example:
scripts/dev.sh cooldctl bootstrap-command
scripts/dev.sh coolify bootstrap-command
USAGE
;;
*)
echo "unknown cooldctl command: $command" >&2
echo "Run: scripts/dev.sh cooldctl help" >&2
echo "unknown coolify command: $command" >&2
echo "Run: scripts/dev.sh coolify help" >&2
exit 1
;;
esac
@@ -415,15 +435,8 @@ up() {
if [ "$coold_vm_enabled" != "false" ]; then
echo "==> Starting ${count} Coolify coold VM(s) before Spin..."
vm_pids=""
for index in $(seq 1 "$count"); do
(
coold_vm "$index" up
) &
vm_pids="${vm_pids} $!"
done
for pid in $vm_pids; do
wait "$pid"
coold_vm "$index" up
done
else
echo "==> COOLIFY_COOLD_VM_ENABLED=false; skipping coold VM."
@@ -433,8 +446,8 @@ up() {
spin up -d "$@"
if [ "$coold_vm_enabled" != "false" ]; then
echo "==> Bootstrapping coold VM mesh with cooldctl..."
cooldctl_bootstrap
echo "==> Bootstrapping coold VM mesh with coolify..."
coolify_bootstrap
for index in $(seq 1 "$count"); do
configure_flux_dev_for_vm "$index"
@@ -911,7 +924,7 @@ Commands:
corrosion <command> Inspect Corrosion state, config, logs, and registered containers
firewall <command> Manage dev coold firewall allow rules
example-nginx <command> Start/check example nginx containers with coold DNS
cooldctl <command> Install/run the released cooldctl dev helper
coolify <command> Install/run the released coolify dev helper
USAGE
}
@@ -945,8 +958,8 @@ case "$cmd" in
example-nginx)
example_nginx "$@"
;;
cooldctl)
cooldctl_dev "$@"
coolify)
coolify_dev "$@"
;;
-h|--help|help|"")
usage
+71 -4
View File
@@ -9,6 +9,7 @@ use App\Models\Team;
use App\Models\User;
use App\Services\Flux\FluxHealth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
use Mockery\MockInterface;
@@ -105,10 +106,10 @@ it('serves the v5 inertia shell', function () {
->assertSee('v5-ready', false)
->assertSee('Running')
->assertSee('Flux is running.')
->assertSee('coolify-coold-dev')
->assertSee('coolify-coold-dev-2')
->assertSee('100.64.0.10')
->assertSee('100.64.0.11')
->assertSee('coold-dev')
->assertSee('coold-dev-2')
->assertSee('100.64.0.1')
->assertSee('100.64.0.2')
->assertSee('builder')
->assertSee('builderCapacity')
->assertSee('V5 Shared Team')
@@ -173,6 +174,50 @@ it('shows when flux is unavailable', function () {
->assertSee('Flux socket was not found.');
});
it('checks the installed coolify version', function () {
createSharedUserAndTeamTables();
[$user, $team] = createV5UserWithTeam();
Process::fake([
'*' => Process::result(output: 'coolify nightly-20260616', exitCode: 0),
]);
$this
->actingAs($user)
->withSession(['currentTeam' => $team])
->getJson('/v5/coolify/version')
->assertSuccessful()
->assertJson([
'available' => true,
'label' => 'Installed',
'version' => 'coolify nightly-20260616',
'message' => 'Installed version: coolify nightly-20260616.',
'binary' => '/usr/local/bin/coolify',
]);
});
it('shows when coolify version check fails', function () {
createSharedUserAndTeamTables();
[$user, $team] = createV5UserWithTeam();
Process::fake([
'*' => Process::result(errorOutput: 'not found', exitCode: 127),
]);
$this
->actingAs($user)
->withSession(['currentTeam' => $team])
->getJson('/v5/coolify/version')
->assertSuccessful()
->assertJson([
'available' => false,
'label' => 'Unavailable',
'version' => null,
'message' => 'not found',
'binary' => '/usr/local/bin/coolify',
]);
});
function fakeFluxHealth(bool $available = true, string $message = 'Flux is running.'): void
{
app()->instance(FluxHealth::class, Mockery::mock(FluxHealth::class, function (MockInterface $mock) use ($available, $message) {
@@ -218,3 +263,25 @@ function createSharedUserAndTeamTables(): void
$table->unique(['team_id', 'user_id']);
});
}
/**
* @return array{0: User, 1: Team}
*/
function createV5UserWithTeam(): array
{
$user = User::withoutEvents(fn () => User::query()->create([
'name' => 'Margaret Hamilton',
'email' => 'margaret@example.com',
'email_verified_at' => now(),
'password' => 'password',
]));
$team = Team::withoutEvents(fn () => Team::query()->create([
'name' => 'V5 Tooling Team',
'description' => null,
'personal_team' => false,
'show_boarding' => false,
]));
$user->teams()->attach($team, ['role' => 'owner']);
return [$user, $team];
}