From c9aa00d80f9f006f4ba48b1e78cb2f3be7219aa4 Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 17:55:18 -0400 Subject: [PATCH 01/10] add build YML --- .gitea/workflows/deploy.yml | 134 ++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..f92dfc5 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,134 @@ +name: Deploy (stellaamor) + +on: + push: + branches: [ "main" ] # change if you use main + +jobs: + deploy: + runs-on: [ self-hosted, mainhost, docker ] + concurrency: + group: deploy-stellaamor + cancel-in-progress: false + + env: + # ---- required (set these as repo/org SECRETS) ---- + SSH_HOST: ${{ secrets.SSH_HOST }} # e.g. 192.168.122.50 (the stellaamor VM) + SSH_USER: ${{ secrets.SSH_USER }} # e.g. deploy + SSH_KEY: ${{ secrets.SSH_KEY }} # private key (ed25519), one line + # optional but recommended: known_hosts entry for your VM + SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} + + # ---- repo-scoped settings (safe to commit) ---- + APP_ROOT: /var/www/stellaamor # base dir on the VM + UPLOADS_DIR: uploads # relative to shared/ + KEEP_N: "5" # how many releases to keep + HEALTH_URL: https://stellaamor.com/ # simple GET should return 200 + SERVICE_RELOAD: "systemctl reload apache2 || true" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # only build assets if you actually have a package.json with build script + - name: Maybe build frontend (Vue/etc) + if: hashFiles('package.json') != '' + uses: actions/setup-node@v4 + with: + node-version: "20" + - name: npm ci + if: hashFiles('package.json') != '' + run: npm ci + - name: npm build + if: hashFiles('package.json') != '' + run: 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 + # include everything except VCS, node dev dirs, and your uploads (they live in shared/) + tar \ + --exclude-vcs \ + --exclude='./node_modules' \ + --exclude='./${{ env.UPLOADS_DIR }}' \ + -czf "release/${REL}.tar.gz" \ + . + + - name: Write SSH key + run: | + umask 077 + printf "%s" "${SSH_KEY}" > ~/.ssh/id_ed25519 + if [ -n "${SSH_KNOWN_HOSTS}" ]; then + printf "%s\n" "${SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts + else + # fall back only if you must (less secure) + echo "StrictHostKeyChecking no" >> ~/.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" + + # 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} + sudo install -d -o ${SSH_USER} -g ${SSH_USER} -m 755 ${SHARED}/${{ env.UPLOADS_DIR }} + " + + # upload tar + scp -i ~/.ssh/id_ed25519 ${TAR} ${SSH_USER}@${SSH_HOST}:/tmp/${REL}.tar.gz + + # unpack to new release, link shared, write metadata, flip symlink, reload, health check + ssh -i ~/.ssh/id_ed25519 ${SSH_USER}@${SSH_HOST} ' + set -euo pipefail + REL="'${REL}'"; APP="'${APP}'"; SHARED="'${SHARED}'"; RELEASES="'${RELEASES}'"; CUR="'${CUR}'"; + + NEW="${RELEASES}/${REL}" + mkdir -p "${NEW}" + tar -xzf "/tmp/${REL}.tar.gz" -C "${NEW}" + rm -f "/tmp/${REL}.tar.gz" + + # link shared paths (uploads, env/config if you keep one there) + rm -rf "${NEW}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" + ln -s "${SHARED}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" "${NEW}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" + + # optional: link a shared .env if you use one + if [ -f "${SHARED}/.env" ]; then + ln -sf "${SHARED}/.env" "${NEW}/.env" + fi + + # metadata + printf "sha=%s\nbuilt_at=%s\n" "'${{ github.sha }}'" "$(date -u +%FT%TZ)" > "${NEW}/RELEASE" + + # keep previous target for rollback + PREV="$(readlink -f "${CUR}" || true)" + ln -sfn "${NEW}" "${CUR}" + + # reload services + '"${{ env.SERVICE_RELOAD }}"' >/dev/null 2>&1 || true + + # health check (simple GET) + 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 + + # prune old releases + cd "${RELEASES}" + ls -1tr | head -n -'${{ env.KEEP_N }}' | xargs -r -I{} rm -rf "{}" + ' + From e63b3cf5254a2c4b84367457830b02b2fb80717a Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:07:53 -0400 Subject: [PATCH 02/10] Add .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .gitea/workflows/smoke.yml diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml new file mode 100644 index 0000000..f7a7e5f --- /dev/null +++ b/.gitea/workflows/smoke.yml @@ -0,0 +1,42 @@ +name: Smoke (SSH + layout) + +on: + workflow_dispatch: + push: + branches: [ "ci/smoke" ] + +jobs: + smoke: + runs-on: [ self-hosted, mainhost, docker ] + + env: + APP_ROOT: /var/www/stellaamor + UPLOADS_DIR: uploads + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Write SSH key + run: | + umask 077 + printf "%s" "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 + if [ -n "${{ secrets.SSH_KNOWN_HOSTS }}" ]; then + printf "%s\n" "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts + else + echo "StrictHostKeyChecking no" >> ~/.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/${UPLO + +ADS_DIR} + echo smoke-$(date -u +%FT%TZ) > ${APP_ROOT}/shared/ci-smoke.txt + ls -la ${APP_ROOT} ${APP_ROOT}/shared + " From 9be1311a37c1f88b2e5ee2c516e71338c4079639 Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:08:26 -0400 Subject: [PATCH 03/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index f7a7e5f..704baf3 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -34,9 +34,7 @@ jobs: 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/${UPLO - -ADS_DIR} + 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 " From 361e0cfb376280c3886e445c1e6e3304bbd3f06f Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:11:30 -0400 Subject: [PATCH 04/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index 704baf3..3fca759 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -7,7 +7,7 @@ on: jobs: smoke: - runs-on: [ self-hosted, mainhost, docker ] + runs-on: [ mainhost, docker ] env: APP_ROOT: /var/www/stellaamor From c7e7013fce97f73f1f26d0e2ec0ca8fd00912f7e Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:14:22 -0400 Subject: [PATCH 05/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index 3fca759..3ab42d0 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -15,6 +15,12 @@ jobs: steps: - name: Checkout + run: | + git init + git remote add origin $GITHUB_SERVER_URL/$GITHUB_REPOSITORY + git fetch origin $GITHUB_REF + git checkout FETCH_HEAD + uses: actions/checkout@v4 - name: Write SSH key From 33f3552dd87dba2a115d22a4a0253c79e2d350d9 Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:15:42 -0400 Subject: [PATCH 06/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index 3ab42d0..5f621f9 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -14,14 +14,12 @@ jobs: UPLOADS_DIR: uploads steps: - - name: Checkout + - name: Checkout (pure git) run: | git init - git remote add origin $GITHUB_SERVER_URL/$GITHUB_REPOSITORY - git fetch origin $GITHUB_REF - git checkout FETCH_HEAD - - uses: actions/checkout@v4 + 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: | From c312697a6e588586d7fb0b615bdbb6210968c16e Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:16:56 -0400 Subject: [PATCH 07/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index 5f621f9..69f9788 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -23,14 +23,19 @@ jobs: - name: Write SSH key run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh umask 077 printf "%s" "${{ secrets.SSH_KEY }}" > ~/.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 echo "StrictHostKeyChecking no" >> ~/.ssh/config fi + - name: Ping server run: ssh -i ~/.ssh/id_ed25519 ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'echo ok' From 57968fe74d1fd44cfb779a303f58b3938dd9a022 Mon Sep 17 00:00:00 2001 From: eddie Date: Tue, 7 Oct 2025 18:18:38 -0400 Subject: [PATCH 08/10] Update .gitea/workflows/smoke.yml --- .gitea/workflows/smoke.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/smoke.yml b/.gitea/workflows/smoke.yml index 69f9788..e0cc878 100644 --- a/.gitea/workflows/smoke.yml +++ b/.gitea/workflows/smoke.yml @@ -23,19 +23,21 @@ jobs: - name: Write SSH key run: | - mkdir -p ~/.ssh - chmod 700 ~/.ssh - umask 077 - printf "%s" "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 + 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 + printf '%s\n' "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts else - echo "StrictHostKeyChecking no" >> ~/.ssh/config + printf 'StrictHostKeyChecking no\n' >> ~/.ssh/config fi + - name: Ping server run: ssh -i ~/.ssh/id_ed25519 ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'echo ok' From 2eeac785fb6fadd2178611179120b9db0fea0845 Mon Sep 17 00:00:00 2001 From: eddie Date: Wed, 8 Oct 2025 03:42:58 -0400 Subject: [PATCH 09/10] Update robots.txt --- robots.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/robots.txt b/robots.txt index ac4509a..31deac3 100644 --- a/robots.txt +++ b/robots.txt @@ -1,3 +1,4 @@ User-agent: * Disallow: /404 +Disallow: /app Sitemap: https://stellaamor.com/sitemap.xml \ No newline at end of file From 295793ad7f15bf2b8f6a9a21a12d931cef308d3c Mon Sep 17 00:00:00 2001 From: eddie Date: Wed, 8 Oct 2025 04:04:51 -0400 Subject: [PATCH 10/10] Update .gitea/workflows/deploy.yml --- .gitea/workflows/deploy.yml | 95 ++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index f92dfc5..455db36 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -2,71 +2,66 @@ name: Deploy (stellaamor) on: push: - branches: [ "main" ] # change if you use main + branches: [ "main" ] jobs: deploy: - runs-on: [ self-hosted, mainhost, docker ] + runs-on: [ mainhost, docker ] concurrency: group: deploy-stellaamor cancel-in-progress: false env: - # ---- required (set these as repo/org SECRETS) ---- - SSH_HOST: ${{ secrets.SSH_HOST }} # e.g. 192.168.122.50 (the stellaamor VM) - SSH_USER: ${{ secrets.SSH_USER }} # e.g. deploy - SSH_KEY: ${{ secrets.SSH_KEY }} # private key (ed25519), one line - # optional but recommended: known_hosts entry for your VM + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_KEY: ${{ secrets.SSH_KEY }} SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} - # ---- repo-scoped settings (safe to commit) ---- - APP_ROOT: /var/www/stellaamor # base dir on the VM - UPLOADS_DIR: uploads # relative to shared/ - KEEP_N: "5" # how many releases to keep - HEALTH_URL: https://stellaamor.com/ # simple GET should return 200 + 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 - uses: actions/checkout@v4 + - 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" - # only build assets if you actually have a package.json with build script - - name: Maybe build frontend (Vue/etc) + # Build only if package.json exists — run Node inside a throwaway container + - name: Build frontend (if present) if: hashFiles('package.json') != '' - uses: actions/setup-node@v4 - with: - node-version: "20" - - name: npm ci - if: hashFiles('package.json') != '' - run: npm ci - - name: npm build - if: hashFiles('package.json') != '' - run: npm run build + 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 - # include everything except VCS, node dev dirs, and your uploads (they live in shared/) - tar \ - --exclude-vcs \ - --exclude='./node_modules' \ - --exclude='./${{ env.UPLOADS_DIR }}' \ - -czf "release/${REL}.tar.gz" \ - . + UPLOADS="${{ env.UPLOADS_DIR }}" + tar --exclude-vcs --exclude='./node_modules' --exclude="./${UPLOADS}" \ + -czf "release/${REL}.tar.gz" . - name: Write SSH key run: | - umask 077 - printf "%s" "${SSH_KEY}" > ~/.ssh/id_ed25519 + 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 + printf '%s\n' "${SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts else - # fall back only if you must (less secure) - echo "StrictHostKeyChecking no" >> ~/.ssh/config + printf 'StrictHostKeyChecking no\n' >> ~/.ssh/config fi - name: Upload & activate atomically @@ -78,47 +73,39 @@ jobs: 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} - sudo install -d -o ${SSH_USER} -g ${SSH_USER} -m 755 ${SHARED}/${{ env.UPLOADS_DIR }} + 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 to new release, link shared, write metadata, flip symlink, reload, health check + # 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}'"; + 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" - # link shared paths (uploads, env/config if you keep one there) - rm -rf "${NEW}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" - ln -s "${SHARED}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" "${NEW}/${UPLOADS_DIR:-'${{ env.UPLOADS_DIR }}'}" + rm -rf "${NEW}/${UPLOADS}" + ln -s "${SHARED}/${UPLOADS}" "${NEW}/${UPLOADS}" - # optional: link a shared .env if you use one - if [ -f "${SHARED}/.env" ]; then - ln -sf "${SHARED}/.env" "${NEW}/.env" - fi + if [ -f "${SHARED}/.env" ]; then ln -sf "${SHARED}/.env" "${NEW}/.env"; fi - # metadata printf "sha=%s\nbuilt_at=%s\n" "'${{ github.sha }}'" "$(date -u +%FT%TZ)" > "${NEW}/RELEASE" - # keep previous target for rollback PREV="$(readlink -f "${CUR}" || true)" ln -sfn "${NEW}" "${CUR}" - # reload services '"${{ env.SERVICE_RELOAD }}"' >/dev/null 2>&1 || true - # health check (simple GET) 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..." @@ -127,8 +114,6 @@ jobs: } fi - # prune old releases cd "${RELEASES}" ls -1tr | head -n -'${{ env.KEEP_N }}' | xargs -r -I{} rm -rf "{}" ' -