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
deploy hub / deploy (push) Successful in 24s
CI / Build og unit tests (push) Successful in 56s
deploy community / deploy (push) Successful in 1s
CI / MSI-installer (push) Successful in 33s
- deploy-community.yml: bygger og starter webstedet ved push (som hubben), bundet til DMZ-adressen. Springes over, indtil COMMUNITY_PORT er sat. Feedet læses skrivebeskyttet fra hubbens feed-mappe; download-links går til hub.sqlinsight.seosoft.dk/updates/ (ny indstilling DownloadUrl). - deploy-forum.yml (manuel): kører discourse_docker-launcheren i en helper-container med /var/discourse og docker.sock fra værten. app.yml er en skabelon, som render.py udfylder fra secrets. - Discourse uden egen TLS: lytter på DMZ-adressen, tvinger https-links og tager klient-IP fra X-Forwarded-For fra proxyen (ellers rammer rate limiting alle brugere samlet). - Caddy-opsætningen er fjernet; docs/community.md beskriver secrets og proxy-opsætning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
141 lines
5.3 KiB
YAML
141 lines
5.3 KiB
YAML
# =============================================================================
|
||
# Deploy SQLInsight Pro-webstedet (sqlinsight.seosoft.dk) til appserveren ved push til main.
|
||
#
|
||
# Samme mønster som deploy-hub.yml: runneren er host-mode, så .env og compose-filen skrives til INSTALL_DIR
|
||
# via en kortlivet helper-container, og 'docker compose up' køres i docker:cli.
|
||
#
|
||
# Påkrævede secrets: HOST_BIND COMMUNITY_PORT COMMUNITY_CONTACT
|
||
# Valgfri: TRUSTED_KEY_ID + TRUSTED_KEY (offentlig nøgle, som feedet er signeret med, hvis den ikke er indbygget)
|
||
# =============================================================================
|
||
name: deploy community
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- "src/**"
|
||
- "docs/**"
|
||
- "rules/**"
|
||
- "deploy/community/**"
|
||
- "Directory.*.props"
|
||
- "global.json"
|
||
- ".forgejo/workflows/deploy-community.yml"
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: deploy-sqlinsight-community
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
INSTALL_DIR: /opt/sqlinsight-community
|
||
IMAGE: sqlinsight-community
|
||
CONTAINER: sqlinsight-community
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: [self-hosted, appserver]
|
||
timeout-minutes: 30
|
||
|
||
steps:
|
||
- name: Configured?
|
||
id: check
|
||
env:
|
||
COMMUNITY_PORT: ${{ secrets.COMMUNITY_PORT }}
|
||
run: |
|
||
# Indtil secrets er sat, springes deploy over i stedet for at fejle hvert push.
|
||
if [ -n "${COMMUNITY_PORT:-}" ]; then
|
||
echo "configured=true" >> "$GITHUB_OUTPUT"
|
||
else
|
||
echo "::warning::COMMUNITY_PORT er ikke sat; webstedet deployes ikke (se docs/community.md)"
|
||
echo "configured=false" >> "$GITHUB_OUTPUT"
|
||
fi
|
||
|
||
- name: Checkout
|
||
if: steps.check.outputs.configured == 'true'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Build image
|
||
if: steps.check.outputs.configured == 'true'
|
||
run: |
|
||
set -e
|
||
docker build -f deploy/community/Dockerfile -t "${IMAGE}:latest" -t "${IMAGE}:${{ github.sha }}" .
|
||
|
||
- name: Write .env + docker-compose.yml to INSTALL_DIR
|
||
if: steps.check.outputs.configured == 'true'
|
||
env:
|
||
HOST_BIND: ${{ secrets.HOST_BIND }}
|
||
COMMUNITY_PORT: ${{ secrets.COMMUNITY_PORT }}
|
||
COMMUNITY_CONTACT: ${{ secrets.COMMUNITY_CONTACT }}
|
||
TRUSTED_KEY_ID: ${{ secrets.TRUSTED_KEY_ID }}
|
||
TRUSTED_KEY: ${{ secrets.TRUSTED_KEY }}
|
||
run: |
|
||
set -eu
|
||
required="HOST_BIND COMMUNITY_PORT COMMUNITY_CONTACT"
|
||
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
|
||
|
||
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 "COMMUNITY_PORT='%s'\n" "$COMMUNITY_PORT"
|
||
printf "SQLInsight__Community__ContactEmail='%s'\n" "$COMMUNITY_CONTACT"
|
||
if [ -n "${TRUSTED_KEY_ID:-}" ] && [ -n "${TRUSTED_KEY:-}" ]; then
|
||
printf "SQLInsight__Community__TrustedKeys__%s='%s'\n" "$TRUSTED_KEY_ID" "$TRUSTED_KEY"
|
||
fi
|
||
} > "$tmp/.env"
|
||
cp deploy/community/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)
|
||
if: steps.check.outputs.configured == 'true'
|
||
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-community up -d --force-recreate --remove-orphans
|
||
|
||
- name: Wait for /health
|
||
if: steps.check.outputs.configured == 'true'
|
||
env:
|
||
HOST_BIND: ${{ secrets.HOST_BIND }}
|
||
COMMUNITY_PORT: ${{ secrets.COMMUNITY_PORT }}
|
||
run: |
|
||
set -eu
|
||
for i in $(seq 1 30); do
|
||
if docker run --rm alpine:3 wget -qO- -T 3 "http://${HOST_BIND}:${COMMUNITY_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() && steps.check.outputs.configured == 'true'
|
||
run: |
|
||
docker image prune -f || true
|
||
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
|