feat(selfhost): ship public GHCR deployment flow

Publish stable GHCR self-host images, switch self-host deploys to official image pulls with a source-build fallback, and move self-host signup / Google OAuth config onto runtime /api/config.
This commit is contained in:
devv-eve
2026-04-22 16:58:42 +08:00
committed by GitHub
parent 936df59fa1
commit fbf41bde73
21 changed files with 478 additions and 72 deletions

View File

@@ -39,6 +39,55 @@ function Get-LatestVersion {
}
}
function Get-SelfHostRef {
if ($env:MULTICA_SELFHOST_REF) {
return $env:MULTICA_SELFHOST_REF
}
$latest = Get-LatestVersion
if ($latest) {
return $latest
}
return "main"
}
function Checkout-ServerRef {
param([string]$Ref)
if ($Ref -eq "main") {
git fetch origin main --depth 1 2>$null
git checkout --force main 2>$null
git reset --hard origin/main 2>$null
return
}
git fetch origin --tags --force 2>$null
$tagRef = "refs/tags/$Ref"
git show-ref --verify --quiet $tagRef 2>$null
if ($LASTEXITCODE -eq 0) {
git checkout --force $Ref 2>$null
return
}
git fetch origin $Ref --depth 1 2>$null
git checkout --force $Ref 2>$null
}
function Pull-OfficialSelfHostImages {
docker compose -f docker-compose.selfhost.yml pull
if ($LASTEXITCODE -eq 0) {
return
}
Write-Host ""
Write-Warn "Official images for the selected self-host channel are not published yet."
Write-Host "This can happen before the first GHCR release is available."
Write-Host "From $InstallDir, build from source instead:"
Write-Host " docker compose -f docker-compose.selfhost.yml -f docker-compose.selfhost.build.yml up -d --build"
exit 1
}
# ---------------------------------------------------------------------------
# CLI Installation
# ---------------------------------------------------------------------------
@@ -202,14 +251,12 @@ After installing Docker, re-run this script with `$env:MULTICA_MODE="local"`.
# ---------------------------------------------------------------------------
function Install-Server {
Write-Info "Setting up Multica server..."
$serverRef = Get-SelfHostRef
Write-Info "Using self-host assets from $serverRef..."
if (Test-Path (Join-Path $InstallDir ".git")) {
Write-Info "Updating existing installation at $InstallDir..."
Write-Warn "Any local changes in $InstallDir will be overwritten."
Push-Location $InstallDir
git fetch origin main --depth 1 2>$null
git reset --hard origin/main 2>$null
Pop-Location
} else {
Write-Info "Cloning Multica repository..."
if (-not (Test-CommandExists "git")) {
@@ -226,9 +273,9 @@ function Install-Server {
git clone --depth 1 $RepoUrl $InstallDir
}
Write-Ok "Repository ready at $InstallDir"
Push-Location $InstallDir
Checkout-ServerRef $serverRef
Write-Ok "Repository ready at $InstallDir ($serverRef)"
if (-not (Test-Path ".env")) {
Write-Info "Creating .env with random JWT_SECRET..."
@@ -240,8 +287,10 @@ function Install-Server {
Write-Ok "Using existing .env"
}
Write-Info "Pulling official Multica images..."
Pull-OfficialSelfHostImages
Write-Info "Starting Multica services (this may take a few minutes on first run)..."
docker compose -f docker-compose.selfhost.yml up -d --build
docker compose -f docker-compose.selfhost.yml up -d
Write-Info "Waiting for backend to be ready..."
$ready = $false

View File

@@ -140,6 +140,55 @@ get_latest_version() {
curl -sI "$REPO_WEB_URL/releases/latest" 2>/dev/null | grep -i '^location:' | sed 's/.*tag\///' | tr -d '\r\n' || true
}
get_selfhost_ref() {
if [ -n "${MULTICA_SELFHOST_REF:-}" ]; then
printf '%s' "$MULTICA_SELFHOST_REF"
return
fi
local latest
latest=$(get_latest_version)
if [ -n "$latest" ]; then
printf '%s' "$latest"
return
fi
printf '%s' "main"
}
checkout_server_ref() {
local ref="$1"
if [ "$ref" = "main" ]; then
git fetch origin main --depth 1 2>/dev/null || true
git checkout --force main 2>/dev/null || true
git reset --hard origin/main 2>/dev/null || true
return
fi
git fetch origin --tags --force 2>/dev/null || true
if git rev-parse --verify --quiet "refs/tags/$ref" >/dev/null; then
git checkout --force "$ref" 2>/dev/null || git checkout --force "tags/$ref" 2>/dev/null || true
return
fi
git fetch origin "$ref" --depth 1 2>/dev/null || true
git checkout --force "$ref" 2>/dev/null || true
}
pull_official_selfhost_images() {
if docker compose -f docker-compose.selfhost.yml pull; then
return
fi
echo ""
warn "Official images for the selected self-host channel are not published yet."
echo "This can happen before the first GHCR release is available."
echo "From $INSTALL_DIR, build from source instead:"
echo " docker compose -f docker-compose.selfhost.yml -f docker-compose.selfhost.build.yml up -d --build"
exit 1
}
upgrade_cli_brew() {
info "Upgrading Multica CLI via Homebrew..."
brew update 2>/dev/null || true
@@ -221,12 +270,13 @@ After installing Docker, re-run this script with --with-server."
# ---------------------------------------------------------------------------
setup_server() {
info "Setting up Multica server..."
local server_ref
server_ref=$(get_selfhost_ref)
info "Using self-host assets from ${server_ref}..."
if [ -d "$INSTALL_DIR/.git" ]; then
info "Updating existing installation at $INSTALL_DIR..."
cd "$INSTALL_DIR"
git fetch origin main --depth 1 2>/dev/null || true
git reset --hard origin/main 2>/dev/null || true
else
info "Cloning Multica repository..."
if ! command_exists git; then
@@ -242,7 +292,9 @@ setup_server() {
cd "$INSTALL_DIR"
fi
ok "Repository ready at $INSTALL_DIR"
checkout_server_ref "$server_ref"
ok "Repository ready at $INSTALL_DIR ($server_ref)"
# Generate .env if needed
if [ ! -f .env ]; then
@@ -261,8 +313,10 @@ setup_server() {
fi
# Start Docker Compose
info "Pulling official Multica images..."
pull_official_selfhost_images
info "Starting Multica services (this may take a few minutes on first run)..."
docker compose -f docker-compose.selfhost.yml up -d --build
docker compose -f docker-compose.selfhost.yml up -d
# Wait for health check
info "Waiting for backend to be ready..."