Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 1m10s
deploy hub / deploy (push) Successful in 19s
CI / MSI-installer (push) Successful in 50s
deploy-hub.yml builds deploy/hub/Dockerfile on the appserver runner, writes .env from repo secrets and starts docker-compose.prod.yml (external PostgreSQL, /data bind-mounted), then polls /health. ci.yml used runs-on: docker, which no runner has, so jobs never started. Build/tests and the MSI now run inside the SDK image via deploy/ci/Dockerfile, because the host-mode runner ignores container: and has no apt-get. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
137 lines
5.2 KiB
YAML
137 lines
5.2 KiB
YAML
# =============================================================================
|
||
# Deploy SQLInsight Hub til appserveren ved push til main.
|
||
#
|
||
# Kører på den self-hostede appserver-runner (host-mode, docker.sock).
|
||
# Runner-containeren har ikke INSTALL_DIR mountet, så .env, compose-filen og
|
||
# 'docker compose up' håndteres via kortlivede helper-containere med
|
||
# INSTALL_DIR bind-mountet fra værten.
|
||
#
|
||
# Alle miljøværdier (også IP'er) kommer fra Forgejo repo-secrets.
|
||
#
|
||
# Påkrævede secrets: HOST_BIND HOST_PORT DB_SERVER DB_PORT DB_NAME DB_USER DB_PASSWORD KNOWN_PROXY
|
||
# Valgfri: TRUSTED_KEY_ID + TRUSTED_KEY (offentlig licensnøgle fra keygen)
|
||
# =============================================================================
|
||
name: deploy hub
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- "src/**"
|
||
- "deploy/hub/**"
|
||
- "Directory.*.props"
|
||
- "global.json"
|
||
- ".forgejo/workflows/deploy-hub.yml"
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: deploy-sqlinsight-hub
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
INSTALL_DIR: /opt/sqlinsight-hub
|
||
IMAGE: sqlinsight-hub
|
||
CONTAINER: sqlinsight-hub
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: [self-hosted, appserver]
|
||
timeout-minutes: 30
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Build image
|
||
run: |
|
||
set -e
|
||
docker build -f deploy/hub/Dockerfile -t "${IMAGE}:latest" -t "${IMAGE}:${{ github.sha }}" .
|
||
|
||
- name: Write .env + docker-compose.yml to INSTALL_DIR
|
||
env:
|
||
HOST_BIND: ${{ secrets.HOST_BIND }}
|
||
HOST_PORT: ${{ secrets.HOST_PORT }}
|
||
DB_SERVER: ${{ secrets.DB_SERVER }}
|
||
DB_PORT: ${{ secrets.DB_PORT }}
|
||
DB_NAME: ${{ secrets.DB_NAME }}
|
||
DB_USER: ${{ secrets.DB_USER }}
|
||
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
||
KNOWN_PROXY: ${{ secrets.KNOWN_PROXY }}
|
||
TRUSTED_KEY_ID: ${{ secrets.TRUSTED_KEY_ID }}
|
||
TRUSTED_KEY: ${{ secrets.TRUSTED_KEY }}
|
||
run: |
|
||
set -eu
|
||
required="HOST_BIND HOST_PORT DB_SERVER DB_PORT DB_NAME DB_USER DB_PASSWORD KNOWN_PROXY"
|
||
missing=0
|
||
for v in $required; do
|
||
if [ -z "$(printenv "$v" || true)" ]; then
|
||
echo "ERROR: required secret $v is not set in repo settings"
|
||
missing=1
|
||
fi
|
||
done
|
||
[ "$missing" -eq 0 ] || exit 1
|
||
|
||
# Værdier skrives single-quoted, så compose ikke udvider '$' i passwords
|
||
for v in $required TRUSTED_KEY_ID TRUSTED_KEY; do
|
||
case "$(printenv "$v" || true)" in
|
||
*"'"*|*";"*) echo "ERROR: secret $v indeholder ' eller ; – ikke understøttet"; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
tmp="$(mktemp -d)"
|
||
{
|
||
printf "HOST_BIND='%s'\n" "$HOST_BIND"
|
||
printf "HOST_PORT='%s'\n" "$HOST_PORT"
|
||
printf "ConnectionStrings__Hub='Host=%s;Port=%s;Database=%s;Username=%s;Password=%s;GSS Encryption Mode=Disable'\n" \
|
||
"$DB_SERVER" "$DB_PORT" "$DB_NAME" "$DB_USER" "$DB_PASSWORD"
|
||
printf "SQLInsight__Hub__KnownProxies__0='%s'\n" "$KNOWN_PROXY"
|
||
if [ -n "${TRUSTED_KEY_ID:-}" ] && [ -n "${TRUSTED_KEY:-}" ]; then
|
||
printf "SQLInsight__Hub__TrustedKeys__%s='%s'\n" "$TRUSTED_KEY_ID" "$TRUSTED_KEY"
|
||
else
|
||
echo "WARNING: TRUSTED_KEY_ID/TRUSTED_KEY ikke sat – hubben kan ikke verificere licenser" >&2
|
||
fi
|
||
} > "$tmp/.env"
|
||
cp deploy/hub/docker-compose.prod.yml "$tmp/docker-compose.yml"
|
||
|
||
tar -C "$tmp" -cf - .env docker-compose.yml | docker run --rm -i \
|
||
-v "${INSTALL_DIR}:/out" alpine:3 \
|
||
sh -c 'tar -C /out -xf - && chown root:root /out/.env /out/docker-compose.yml && chmod 600 /out/.env && chmod 644 /out/docker-compose.yml'
|
||
rm -rf "$tmp"
|
||
|
||
- name: Deploy (compose up)
|
||
run: |
|
||
set -eu
|
||
docker run --rm \
|
||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||
-v "${INSTALL_DIR}:${INSTALL_DIR}" \
|
||
-w "${INSTALL_DIR}" \
|
||
docker:cli \
|
||
docker compose --project-name sqlinsight-hub up -d --force-recreate --remove-orphans
|
||
|
||
- name: Wait for /health
|
||
env:
|
||
HOST_BIND: ${{ secrets.HOST_BIND }}
|
||
HOST_PORT: ${{ secrets.HOST_PORT }}
|
||
run: |
|
||
set -eu
|
||
# aspnet-imaget har hverken curl eller wget – poll udefra
|
||
for i in $(seq 1 30); do
|
||
if docker run --rm alpine:3 wget -qO- -T 3 "http://${HOST_BIND}:${HOST_PORT}/health" 2>/dev/null | grep -q '"ok"'; then
|
||
echo "${CONTAINER} is healthy"
|
||
exit 0
|
||
fi
|
||
echo "attempt $i: not ready"
|
||
sleep 5
|
||
done
|
||
echo "${CONTAINER} did not answer /health within 150s — last 100 log lines:"
|
||
docker logs --tail 100 "${CONTAINER}" || true
|
||
exit 1
|
||
|
||
- name: Prune old images
|
||
if: always()
|
||
run: |
|
||
docker image prune -f || true
|
||
# Behold latest + de 3 nyeste sha-tags
|
||
docker images "${IMAGE}" --format '{{.Tag}} {{.CreatedAt}}' \
|
||
| grep -v '^latest ' | sort -k2 -r | awk 'NR>3 {print $1}' \
|
||
| xargs -r -I{} docker image rm "${IMAGE}:{}" || true
|