ci(deploy): add gitea LXC deploy + validate workflows
Mirrors the buildfor_life_budget workflow pair: Gitea runs both deploy and validate, GitHub mirrors validate only. Differences from the sibling: pnpm + fnm instead of npm, Node pinned via .node-version, and the repo is cloned over public HTTPS so no separate deploy key is needed for git.b4l.co.th. Document required Gitea secrets in DEPLOYMENT.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
name: Deploy to LXC
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Deploy via SSH
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
username: ${{ secrets.DEPLOY_USER }}
|
||||||
|
key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||||
|
script: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
APP_DIR="${{ secrets.DEPLOY_PATH || '/opt/buildfor_life_ops/app' }}"
|
||||||
|
REPO_URL="https://git.b4l.co.th/B4L/buildfor_life_ops.git"
|
||||||
|
|
||||||
|
# Clone if first deploy, otherwise pull. Public HTTPS — no deploy key needed.
|
||||||
|
if [ ! -d "$APP_DIR" ]; then
|
||||||
|
echo "==> First deploy, cloning..."
|
||||||
|
git clone "$REPO_URL" "$APP_DIR"
|
||||||
|
cd "$APP_DIR"
|
||||||
|
else
|
||||||
|
cd "$APP_DIR"
|
||||||
|
echo "==> Resetting local changes..."
|
||||||
|
git checkout -- .
|
||||||
|
echo "==> Pulling latest code..."
|
||||||
|
git pull origin main
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate the pinned Node via fnm and make pnpm available via Corepack.
|
||||||
|
# Both are expected to be installed per DEPLOYMENT.md.
|
||||||
|
export PATH="$HOME/.local/share/fnm:$PATH"
|
||||||
|
eval "$(fnm env --shell bash)"
|
||||||
|
fnm use --install-if-missing
|
||||||
|
corepack enable >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "==> Installing dependencies..."
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
echo "==> Building..."
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
|
echo "==> Running migrations..."
|
||||||
|
pnpm run db:migrate
|
||||||
|
|
||||||
|
echo "==> Restarting service..."
|
||||||
|
sudo systemctl restart buildfor_life_ops
|
||||||
|
|
||||||
|
echo "==> Waiting for startup..."
|
||||||
|
sleep 2
|
||||||
|
systemctl is-active --quiet buildfor_life_ops && echo "Deploy successful!" || (echo "Service failed to start!" && exit 1)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
name: Validate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9.15.0
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
cache: pnpm
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run validation (svelte-check + build)
|
||||||
|
run: pnpm run validate
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
name: Validate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9.15.0
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
cache: pnpm
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run validation (svelte-check + build)
|
||||||
|
run: pnpm run validate
|
||||||
+18
-2
@@ -236,6 +236,22 @@ Environment=HOST_HEADER=x-forwarded-host
|
|||||||
|
|
||||||
## 12. Upgrades
|
## 12. Upgrades
|
||||||
|
|
||||||
|
### Automated (CI-driven)
|
||||||
|
|
||||||
|
`.gitea/workflows/deploy.yml` runs on every push to `main`. It SSHes into the LXC host, pulls, installs, builds, migrates, and restarts the service. Required Gitea secrets:
|
||||||
|
|
||||||
|
| Secret | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `DEPLOY_HOST` | SSH host of the LXC container |
|
||||||
|
| `DEPLOY_USER` | SSH user (must own `$DEPLOY_PATH` and have a sudoers entry for `systemctl restart buildfor_life_ops`) |
|
||||||
|
| `DEPLOY_KEY` | Private SSH key matching an authorized key on the deploy user |
|
||||||
|
| `DEPLOY_PORT` | *(optional, default `22`)* |
|
||||||
|
| `DEPLOY_PATH` | *(optional, default `/opt/buildfor_life_ops/app`)* |
|
||||||
|
|
||||||
|
The repo itself is cloned from `https://git.b4l.co.th/B4L/buildfor_life_ops.git` (public HTTPS) — no repo deploy key needed, unlike the budget sibling.
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/buildfor_life_ops/app
|
cd /opt/buildfor_life_ops/app
|
||||||
git fetch --tags
|
git fetch --tags
|
||||||
@@ -245,15 +261,15 @@ git checkout <tag-or-sha>
|
|||||||
fnm use --install-if-missing
|
fnm use --install-if-missing
|
||||||
|
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
pnpm run db:migrate
|
|
||||||
pnpm run build
|
pnpm run build
|
||||||
|
pnpm run db:migrate
|
||||||
pnpm install --prod --frozen-lockfile
|
pnpm install --prod --frozen-lockfile
|
||||||
|
|
||||||
systemctl restart buildfor_life_ops
|
systemctl restart buildfor_life_ops
|
||||||
journalctl -u buildfor_life_ops -n 100 --no-pager
|
journalctl -u buildfor_life_ops -n 100 --no-pager
|
||||||
```
|
```
|
||||||
|
|
||||||
A migration that cannot be rolled back forward-only (rare — see `drizzle/README.md`) needs a maintenance window and a DB snapshot first.
|
A migration that cannot be rolled forward-only (rare — see `drizzle/README.md`) needs a maintenance window and a DB snapshot first.
|
||||||
|
|
||||||
## 13. Rollback
|
## 13. Rollback
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user