diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..455db36 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,119 @@ +name: Deploy (stellaamor) + +on: + push: + branches: [ "main" ] + +jobs: + deploy: + runs-on: [ mainhost, docker ] + concurrency: + group: deploy-stellaamor + cancel-in-progress: false + + env: + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_KEY: ${{ secrets.SSH_KEY }} + SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} + + APP_ROOT: /var/www/stellaamor + UPLOADS_DIR: uploads + KEEP_N: "5" + HEALTH_URL: https://stellaamor.com/ + SERVICE_RELOAD: "systemctl reload apache2 || true" + + steps: + - name: Checkout (pure git) + run: | + git init + git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" + git fetch --depth=1 origin "$GITHUB_SHA" + git checkout -q "$GITHUB_SHA" + + # Build only if package.json exists — run Node inside a throwaway container + - name: Build frontend (if present) + if: hashFiles('package.json') != '' + run: | + docker run --rm -v "$PWD:/app" -w /app node:20 bash -lc " + npm ci + npm run build + " + + - name: Prepare release tarball + run: | + set -euo pipefail + REL="$(date -u +%Y%m%d-%H%M%SZ)-${{ github.sha }}" + echo "REL=$REL" >> $GITHUB_ENV + mkdir -p release + UPLOADS="${{ env.UPLOADS_DIR }}" + tar --exclude-vcs --exclude='./node_modules' --exclude="./${UPLOADS}" \ + -czf "release/${REL}.tar.gz" . + + - name: Write SSH key + run: | + set -eu + install -d -m 700 ~/.ssh + printf '%s\n' "${SSH_KEY}" > ~/.ssh/id_ed25519 + sed -i 's/\r$//' ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + if [ -n "${SSH_KNOWN_HOSTS}" ]; then + printf '%s\n' "${SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + else + printf 'StrictHostKeyChecking no\n' >> ~/.ssh/config + fi + + - name: Upload & activate atomically + run: | + set -euo pipefail + REL="${{ env.REL }}" + TAR="release/${REL}.tar.gz" + APP="${{ env.APP_ROOT }}" + SHARED="${APP}/shared" + RELEASES="${APP}/releases" + CUR="${APP}/current" + UPLOADS="${{ env.UPLOADS_DIR }}" + + # ensure layout exists + ssh -i ~/.ssh/id_ed25519 ${SSH_USER}@${SSH_HOST} " + set -e + sudo install -d -o ${SSH_USER} -g ${SSH_USER} -m 755 ${RELEASES} ${SHARED} ${SHARED}/${UPLOADS} + " + + # upload tar + scp -i ~/.ssh/id_ed25519 ${TAR} ${SSH_USER}@${SSH_HOST}:/tmp/${REL}.tar.gz + + # unpack, link shared, flip symlink, reload, health check, prune + ssh -i ~/.ssh/id_ed25519 ${SSH_USER}@${SSH_HOST} ' + set -euo pipefail + REL="'${REL}'"; APP="'${APP}'"; SHARED="'${SHARED}'"; RELEASES="'${RELEASES}'"; CUR="'${CUR}'"; UPLOADS="'${UPLOADS}'"; + + NEW="${RELEASES}/${REL}" + mkdir -p "${NEW}" + tar -xzf "/tmp/${REL}.tar.gz" -C "${NEW}" + rm -f "/tmp/${REL}.tar.gz" + + rm -rf "${NEW}/${UPLOADS}" + ln -s "${SHARED}/${UPLOADS}" "${NEW}/${UPLOADS}" + + if [ -f "${SHARED}/.env" ]; then ln -sf "${SHARED}/.env" "${NEW}/.env"; fi + + printf "sha=%s\nbuilt_at=%s\n" "'${{ github.sha }}'" "$(date -u +%FT%TZ)" > "${NEW}/RELEASE" + + PREV="$(readlink -f "${CUR}" || true)" + ln -sfn "${NEW}" "${CUR}" + + '"${{ env.SERVICE_RELOAD }}"' >/dev/null 2>&1 || true + + if command -v curl >/dev/null 2>&1; then + curl -fsS --max-time 5 "'"${{ env.HEALTH_URL }}"'" >/dev/null || { + echo "Health check failed, rolling back..." + [ -n "${PREV}" ] && ln -sfn "${PREV}" "${CUR}" && '"${{ env.SERVICE_RELOAD }}"' >/dev/null 2>&1 || true + exit 1 + } + fi + + cd "${RELEASES}" + ls -1tr | head -n -'${{ env.KEEP_N }}' | xargs -r -I{} rm -rf "{}" + ' diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml new file mode 100644 index 0000000..e0cc878 --- /dev/null +++ b/.gitea/workflows/smoke.yml @@ -0,0 +1,51 @@ +name: Smoke (SSH + layout) + +on: + workflow_dispatch: + push: + branches: [ "ci/smoke" ] + +jobs: + smoke: + runs-on: [ mainhost, docker ] + + env: + APP_ROOT: /var/www/stellaamor + UPLOADS_DIR: uploads + + steps: + - name: Checkout (pure git) + run: | + git init + git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" + git fetch --depth=1 origin "$GITHUB_SHA" + git checkout -q "$GITHUB_SHA" + + - name: Write SSH key + run: | + set -eu + install -d -m 700 ~/.ssh + printf '%s\n' "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 + # normalize (fix CRLF) + sed -i 's/\r$//' ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + if [ -n "${{ secrets.SSH_KNOWN_HOSTS }}" ]; then + printf '%s\n' "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + else + printf 'StrictHostKeyChecking no\n' >> ~/.ssh/config + fi + + + + - name: Ping server + run: ssh -i ~/.ssh/id_ed25519 ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'echo ok' + + - name: Ensure layout (no deploy yet) + run: | + ssh -i ~/.ssh/id_ed25519 ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} " + set -e + install -d -m 755 ${APP_ROOT}/releases ${APP_ROOT}/shared ${APP_ROOT}/shared/${UPLOADS_DIR} + echo smoke-$(date -u +%FT%TZ) > ${APP_ROOT}/shared/ci-smoke.txt + ls -la ${APP_ROOT} ${APP_ROOT}/shared + "