Deploy tagged releases via versioned dir + symlink swap
All checks were successful
Build and Test / build-and-test (push) Successful in 36s
All checks were successful
Build and Test / build-and-test (push) Successful in 36s
On tag push, scp the binary to ~/hsa-app/releases/hsa-app-V<tag>/hsa, repoint the ~/hsa-app/hsa symlink, and restart the user systemd service. Add deploy/hsa_app.service unit and deploy/INSTALL.md with host layout, one-time install, deploy, and rollback steps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2f817d16d0
commit
f47e611e2c
3 changed files with 107 additions and 3 deletions
|
|
@ -21,7 +21,11 @@ jobs:
|
|||
run: |
|
||||
echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
DEST=hsa-app-V${{ github.ref_name }}
|
||||
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no ${{ vars.HSA_APP_USER }}@${{ vars.HSA_APP_HOST }} "mkdir -p $DEST"
|
||||
scp -i /tmp/deploy_key -o StrictHostKeyChecking=no hsa ${{ vars.HSA_APP_USER }}@${{ vars.HSA_APP_HOST }}:$DEST/hsa
|
||||
TAG=${{ github.ref_name }}
|
||||
REL=hsa-app/releases/hsa-app-V$TAG
|
||||
TARGET=${{ vars.HSA_APP_USER }}@${{ vars.HSA_APP_HOST }}
|
||||
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no $TARGET "mkdir -p $REL"
|
||||
scp -i /tmp/deploy_key -o StrictHostKeyChecking=no hsa $TARGET:$REL/hsa
|
||||
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no $TARGET \
|
||||
"cd hsa-app && chmod +x releases/hsa-app-V$TAG/hsa && ln -sfn releases/hsa-app-V$TAG/hsa hsa && systemctl --user restart hsa_app"
|
||||
rm /tmp/deploy_key
|
||||
|
|
|
|||
86
deploy/INSTALL.md
Normal file
86
deploy/INSTALL.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Deploying hsa-app
|
||||
|
||||
The app runs as a **user systemd service** (no sudo). Tagged releases are
|
||||
pushed by CI into `~/hsa-app/releases/hsa-app-V<tag>/hsa`, and a `current`
|
||||
symlink (`~/hsa-app/hsa`) points at the live release. Restarting the service
|
||||
picks up whatever the symlink resolves to.
|
||||
|
||||
## Layout on the host
|
||||
|
||||
Mutable state (DB, env, config) lives at the top of `~/hsa-app/` and survives
|
||||
every deploy. Only the binaries live under `releases/`.
|
||||
|
||||
```
|
||||
~/hsa-app/
|
||||
hsa_app.sh # launcher: sources env file, exec's the binary
|
||||
hsa_app.env # environment (KEY=VALUE)
|
||||
config.json
|
||||
hsa.db, hsa.db-shm, hsa.db-wal # SQLite state
|
||||
releases/
|
||||
hsa-app-V0.0.0a0/hsa
|
||||
hsa-app-V0.0.1/hsa
|
||||
hsa -> releases/hsa-app-V0.0.1/hsa # current symlink, swapped on deploy
|
||||
```
|
||||
|
||||
## The current symlink
|
||||
|
||||
Create/repoint it **from inside `~/hsa-app`** so the relative target resolves
|
||||
against the link's own directory (running `ln -s` from `~` produces a broken
|
||||
link that points at `~/hsa-app/hsa-app/...`):
|
||||
|
||||
```bash
|
||||
cd ~/hsa-app
|
||||
chmod +x releases/hsa-app-V<tag>/hsa
|
||||
ln -sfn releases/hsa-app-V<tag>/hsa hsa # -f replace, -n don't follow existing link
|
||||
ls -l hsa # target must resolve (not broken)
|
||||
```
|
||||
|
||||
This same `ln -sfn` + `chmod +x` is what the CI deploy step runs on each tag.
|
||||
|
||||
## Install the service (once)
|
||||
|
||||
Save [hsa_app.service](hsa_app.service) to `~/.config/systemd/user/hsa_app.service`,
|
||||
then:
|
||||
|
||||
```bash
|
||||
loginctl enable-linger "$USER" # run the service without an active login session
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now hsa_app
|
||||
systemctl --user status hsa_app
|
||||
journalctl --user -u hsa_app -f # follow logs
|
||||
```
|
||||
|
||||
### Why these choices
|
||||
|
||||
- **User service** (`systemctl --user`) — no sudo, matches the home-dir deploy.
|
||||
`enable-linger` lets it start at boot / stay up without an interactive login.
|
||||
- **`Type=exec`** — `hsa_app.sh` ends in `exec "$BIN"`, so the binary becomes the
|
||||
unit's main process; signals and exit codes propagate correctly.
|
||||
- **`WorkingDirectory=%h/hsa-app`** — relative paths in the env file resolve here.
|
||||
The launcher's comment recommends absolute paths for `DB_PATH` / `STORAGE_DIR` /
|
||||
`BACKUP_DIR` / `CONFIG_PATH`; either works.
|
||||
- **ExecStart passes the env file as `$1`** — matches `hsa_app.sh`'s first-arg
|
||||
precedence, so no `/etc/hsa-app/...` file is needed. `%h` expands to the home dir.
|
||||
- The launcher defaults its binary to `./hsa` next to itself (`~/hsa-app/hsa`, the
|
||||
symlink), so a restart after a symlink swap runs the new release automatically.
|
||||
|
||||
## Deploy a tagged release
|
||||
|
||||
CI pushes the binary on tag and runs the swap + restart. To do it manually:
|
||||
|
||||
```bash
|
||||
cd ~/hsa-app
|
||||
chmod +x releases/hsa-app-V<tag>/hsa
|
||||
ln -sfn releases/hsa-app-V<tag>/hsa hsa
|
||||
systemctl --user restart hsa_app
|
||||
```
|
||||
|
||||
## Roll back
|
||||
|
||||
Every release stays under `releases/`, so rollback is a symlink repoint:
|
||||
|
||||
```bash
|
||||
cd ~/hsa-app
|
||||
ln -sfn releases/hsa-app-V<old-tag>/hsa hsa
|
||||
systemctl --user restart hsa_app
|
||||
```
|
||||
14
deploy/hsa_app.service
Normal file
14
deploy/hsa_app.service
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=HSA app
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
WorkingDirectory=%h/hsa-app
|
||||
ExecStart=%h/hsa-app/hsa_app.sh %h/hsa-app/hsa_app.env
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Loading…
Reference in a new issue