diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..111e754 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,86 @@ +name: Deploy (stellaamor) + +on: + push: + branches: [ "main" ] + +jobs: + deploy: + runs-on: [ mainhost ] # must match your runner label (e.g. mainhost:host) + env: + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_OPTS: >- + -F /dev/null + -o IdentitiesOnly=yes + -o IdentityAgent=none + -o PreferredAuthentications=publickey + -o PubkeyAuthentication=yes + -o PasswordAuthentication=no + -o NumberOfPasswordPrompts=0 + -o BatchMode=yes + -o ServerAliveInterval=15 + -o ServerAliveCountMax=3 + -o ConnectTimeout=20 + -o StrictHostKeyChecking=no + + APP_ROOT: /var/www/api.stellaamor + UPLOADS_DIR: uploads + KEEP_N: "5" + HEALTH_URL: https://api.stellaamor.com/ + SERVICE_RELOAD: "systemctl reload apache2 || true" + SSH_KEY_PATH: /home/gitea-runner/.ssh/id_ed25519 + + + 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: SSH smoke test + run: ssh $SSH_OPTS -i "$SSH_KEY_PATH" ${SSH_USER}@${SSH_HOST} true + + - name: Upload & activate atomically + run: | + set -euo pipefail + REL="$(date -u +%Y%m%d-%H%M%SZ)-${{ github.sha }}" + echo "REL=$REL" >> $GITHUB_ENV + TAR="/tmp/${REL}.tar.gz" + APP="${{ env.APP_ROOT }}" + SHARED="${APP}/shared" + RELEASES="${APP}/releases" + CUR="${APP}/current" + UPLOADS="${{ env.UPLOADS_DIR }}" + + tar -czf "$TAR" --exclude-vcs --exclude='./node_modules' --exclude="./${UPLOADS}" --exclude='./release' . + mkdir -p release && mv "$TAR" "release/${REL}.tar.gz" + + ssh $SSH_OPTS -i "$SSH_KEY_PATH" ${SSH_USER}@${SSH_HOST} \ + "set -e; install -d -m 755 ${RELEASES} ${SHARED} ${SHARED}/${UPLOADS}" + + scp -O $SSH_OPTS -vvv -i "$SSH_KEY_PATH" "release/${REL}.tar.gz" ${SSH_USER}@${SSH_HOST}:/tmp/${REL}.tar.gz + + ssh $SSH_OPTS -i "$SSH_KEY_PATH" ${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}" + [ -f "${SHARED}/.env" ] && ln -sf "${SHARED}/.env" "${NEW}/.env" || true + 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/rollback.yml b/.gitea/workflows/rollback.yml new file mode 100644 index 0000000..202f3af --- /dev/null +++ b/.gitea/workflows/rollback.yml @@ -0,0 +1,47 @@ +# manual rollback +on: + workflow_dispatch: + inputs: + steps: + description: "How many releases to roll back (1 = previous)" + default: "1" + +jobs: + rollback: + runs-on: [ mainhost ] + env: + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_KEY_PATH: /home/gitea-runner/.ssh/id_ed25519 + SSH_OPTS: >- + -F /dev/null -o IdentitiesOnly=yes -o IdentityAgent=none + -o PreferredAuthentications=publickey -o PubkeyAuthentication=yes + -o PasswordAuthentication=no -o NumberOfPasswordPrompts=0 -o BatchMode=yes + -o ServerAliveInterval=15 -o ServerAliveCountMax=3 -o ConnectTimeout=20 + -o StrictHostKeyChecking=no + APP_ROOT: /var/www/stellaamor + SERVICE_RELOAD: "systemctl reload apache2 || true" + + steps: + - name: Roll back symlink to an older release + run: | + set -euo pipefail + APP="${{ env.APP_ROOT }}" + RELEASES="${APP}/releases" + CUR="${APP}/current" + N="${{ github.event.inputs.steps || '1' }}" + + # pick target release (1=previous) + TARGET="$(ls -1tr "${RELEASES}" | tail -n +"$((N+1))" | tail -n 1)" + if [ -z "${TARGET}" ]; then + echo "No release found to roll back to."; exit 1 + fi + echo "Rolling back to: ${TARGET}" + + ssh $SSH_OPTS -i "$SSH_KEY_PATH" ${SSH_USER}@${SSH_HOST} " + set -euo pipefail + APP='${APP}'; RELEASES='${RELEASES}'; CUR='${CUR}'; TARGET='${TARGET}'; + [ -d \"\${RELEASES}/\${TARGET}\" ] || { echo 'Target release missing'; exit 1; } + ln -sfn \"\${RELEASES}/\${TARGET}\" \"\${CUR}\" + ${SERVICE_RELOAD} >/dev/null 2>&1 || true + " 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 + "