mirror of
https://github.com/coollabsio/coolify.git
synced 2026-08-24 02:24:11 -05:00
281 lines
8.5 KiB
Bash
Executable File
281 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
REPOSITORY=${HELPER_IMAGE_REPOSITORY:-docker.io/coollabsio/coolify-helper}
|
|
DEFAULT_TAG="dev-$(git -C "$ROOT_DIR" rev-parse --short HEAD)"
|
|
COOLIFY_CONTAINER=${COOLIFY_CONTAINER:-coolify}
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $0 <command> [tag]
|
|
|
|
Commands:
|
|
build [tag] Build the local helper image
|
|
use [tag] Configure local Coolify to use an existing helper image
|
|
verify [tag] Verify tools and run a Docker Compose socket smoke test
|
|
deploy [tag] Deploy seeded Dockerfile, Compose, and Nixpacks applications
|
|
reset Clear the local helper version override
|
|
test [tag] Build, select, verify, and run real seeded deployments
|
|
|
|
Default tag: $DEFAULT_TAG
|
|
EOF
|
|
}
|
|
|
|
require_local_coolify() {
|
|
local is_dev
|
|
|
|
if ! docker inspect "$COOLIFY_CONTAINER" >/dev/null 2>&1; then
|
|
echo "Coolify container '$COOLIFY_CONTAINER' is not running." >&2
|
|
exit 1
|
|
fi
|
|
|
|
is_dev=$(docker exec "$COOLIFY_CONTAINER" php artisan tinker --execute 'echo isDev() ? "yes" : "no";' 2>/dev/null | tail -1)
|
|
if [[ $is_dev != yes ]]; then
|
|
echo "The running Coolify instance is not in development mode." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
validate_tag() {
|
|
local tag=$1
|
|
|
|
if [[ ! $tag =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]]; then
|
|
echo "Invalid Docker tag: $tag" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
image_for() {
|
|
echo "${REPOSITORY}:$1"
|
|
}
|
|
|
|
build_helper() {
|
|
local tag=$1
|
|
local image
|
|
image=$(image_for "$tag")
|
|
|
|
echo "Building $image"
|
|
docker build --progress=plain -f "$ROOT_DIR/docker/coolify-helper/Dockerfile" -t "$image" "$ROOT_DIR"
|
|
}
|
|
|
|
use_helper() {
|
|
local tag=$1
|
|
local image
|
|
image=$(image_for "$tag")
|
|
|
|
require_local_coolify
|
|
docker image inspect "$image" >/dev/null
|
|
docker exec -e DEV_HELPER_VERSION="$tag" "$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
App\Models\InstanceSettings::findOrFail(0)->update([
|
|
"dev_helper_version" => getenv("DEV_HELPER_VERSION"),
|
|
]);
|
|
' >/dev/null
|
|
|
|
local selected
|
|
selected=$(docker exec "$COOLIFY_CONTAINER" php artisan tinker --execute 'echo getHelperVersion();' 2>/dev/null | tail -1)
|
|
if [[ $selected != "$tag" ]]; then
|
|
echo "Coolify selected helper '$selected' instead of '$tag'." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Coolify now uses $image"
|
|
}
|
|
|
|
verify_helper() {
|
|
local tag=$1
|
|
local image project compose
|
|
image=$(image_for "$tag")
|
|
project="coolify-helper-${tag//[^A-Za-z0-9_-]/-}-smoke"
|
|
compose=$'services:\n app:\n image: alpine:3.21\n command: ["sh", "-c", "sleep 30"]\n'
|
|
|
|
docker image inspect "$image" >/dev/null
|
|
|
|
docker run --rm "$image" docker --version
|
|
docker run --rm "$image" docker compose version
|
|
docker run --rm "$image" docker buildx version
|
|
docker run --rm "$image" pack version
|
|
docker run --rm "$image" nixpacks --version
|
|
docker run --rm "$image" railpack --version
|
|
|
|
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
|
docker version --format 'client={{.Client.Version}} server={{.Server.Version}} api={{.Client.APIVersion}}/{{.Server.APIVersion}}'
|
|
|
|
cleanup_compose() {
|
|
printf '%s' "$compose" | docker run --rm -i \
|
|
-v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
|
docker compose -p "$project" -f - down --remove-orphans >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup_compose RETURN
|
|
|
|
printf '%s' "$compose" | docker run --rm -i \
|
|
-v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
|
docker compose -p "$project" -f - up -d --wait
|
|
printf '%s' "$compose" | docker run --rm -i \
|
|
-v /var/run/docker.sock:/var/run/docker.sock "$image" \
|
|
docker compose -p "$project" -f - ps --format json
|
|
cleanup_compose
|
|
trap - RETURN
|
|
|
|
if [[ -n $(docker ps -aq --filter "label=com.docker.compose.project=$project") ]]; then
|
|
echo "Compose smoke-test resources were not removed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Helper verification passed for $image"
|
|
}
|
|
|
|
queue_deployment() {
|
|
local application_uuid=$1
|
|
|
|
docker exec -e APPLICATION_UUID="$application_uuid" "$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
$application = App\Models\Application::query()
|
|
->where("uuid", getenv("APPLICATION_UUID"))
|
|
->firstOrFail();
|
|
$deploymentUuid = (string) Illuminate\Support\Str::uuid();
|
|
queue_application_deployment(
|
|
application: $application,
|
|
deployment_uuid: $deploymentUuid,
|
|
force_rebuild: true,
|
|
no_questions_asked: true,
|
|
);
|
|
echo "DEPLOYMENT_UUID={$deploymentUuid}";
|
|
' 2>/dev/null | sed -n 's/^DEPLOYMENT_UUID=//p' | tail -1
|
|
}
|
|
|
|
wait_for_deployment() {
|
|
local deployment_uuid=$1
|
|
local expected_image=$2
|
|
local status
|
|
|
|
for _ in $(seq 1 150); do
|
|
status=$(docker exec -e DEPLOYMENT_UUID="$deployment_uuid" "$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
$deployment = App\Models\ApplicationDeploymentQueue::query()
|
|
->where("deployment_uuid", getenv("DEPLOYMENT_UUID"))
|
|
->first();
|
|
echo $deployment?->status ?? "missing";
|
|
' 2>/dev/null | tail -1)
|
|
|
|
case "$status" in
|
|
finished)
|
|
break
|
|
;;
|
|
failed|cancelled|missing)
|
|
echo "Deployment $deployment_uuid ended with status: $status" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
sleep 4
|
|
done
|
|
|
|
if [[ $status != finished ]]; then
|
|
echo "Deployment $deployment_uuid timed out with status: $status" >&2
|
|
return 1
|
|
fi
|
|
|
|
local used_expected_helper
|
|
used_expected_helper=$(docker exec \
|
|
-e DEPLOYMENT_UUID="$deployment_uuid" \
|
|
-e EXPECTED_HELPER_IMAGE="$expected_image" \
|
|
"$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
$deployment = App\Models\ApplicationDeploymentQueue::query()
|
|
->where("deployment_uuid", getenv("DEPLOYMENT_UUID"))
|
|
->firstOrFail();
|
|
$logs = collect(json_decode($deployment->logs, true))
|
|
->pluck("output")
|
|
->implode("\n");
|
|
echo str_contains(
|
|
$logs,
|
|
"Preparing container with helper image: ".getenv("EXPECTED_HELPER_IMAGE"),
|
|
) ? "yes" : "no";
|
|
' 2>/dev/null | tail -1)
|
|
|
|
if [[ $used_expected_helper != yes ]]; then
|
|
echo "Deployment $deployment_uuid did not use $expected_image." >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
deploy_examples() {
|
|
local tag=$1
|
|
local image application_uuid deployment_uuid fqdn
|
|
image=$(image_for "$tag")
|
|
|
|
require_local_coolify
|
|
use_helper "$tag"
|
|
|
|
for application_uuid in dockerfile docker-compose nodejs; do
|
|
echo "Deploying seeded application: $application_uuid"
|
|
deployment_uuid=$(queue_deployment "$application_uuid")
|
|
if [[ -z $deployment_uuid ]]; then
|
|
echo "Could not queue $application_uuid." >&2
|
|
exit 1
|
|
fi
|
|
|
|
wait_for_deployment "$deployment_uuid" "$image"
|
|
|
|
fqdn=$(docker exec -e APPLICATION_UUID="$application_uuid" "$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
echo App\Models\Application::query()
|
|
->where("uuid", getenv("APPLICATION_UUID"))
|
|
->value("fqdn") ?? "";
|
|
' 2>/dev/null | tail -1)
|
|
|
|
if [[ -n $fqdn ]]; then
|
|
curl --fail --silent --show-error --output /dev/null "$fqdn"
|
|
fi
|
|
|
|
echo "Deployment passed: $application_uuid ($deployment_uuid)"
|
|
done
|
|
}
|
|
|
|
reset_helper() {
|
|
require_local_coolify
|
|
docker exec "$COOLIFY_CONTAINER" php artisan tinker --execute '
|
|
App\Models\InstanceSettings::findOrFail(0)->update([
|
|
"dev_helper_version" => null,
|
|
]);
|
|
' >/dev/null
|
|
echo "Development helper override cleared."
|
|
}
|
|
|
|
command=${1:-help}
|
|
tag=${2:-$DEFAULT_TAG}
|
|
|
|
case "$command" in
|
|
build)
|
|
validate_tag "$tag"
|
|
build_helper "$tag"
|
|
;;
|
|
use)
|
|
validate_tag "$tag"
|
|
use_helper "$tag"
|
|
;;
|
|
verify)
|
|
validate_tag "$tag"
|
|
verify_helper "$tag"
|
|
;;
|
|
deploy)
|
|
validate_tag "$tag"
|
|
deploy_examples "$tag"
|
|
;;
|
|
reset)
|
|
reset_helper
|
|
;;
|
|
test)
|
|
validate_tag "$tag"
|
|
build_helper "$tag"
|
|
use_helper "$tag"
|
|
verify_helper "$tag"
|
|
deploy_examples "$tag"
|
|
;;
|
|
help|-h|--help)
|
|
usage
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|