Compare commits
7 commits
42fd0a2af4
...
3bb0e5f678
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bb0e5f678 | ||
|
|
fafd763d99 | ||
|
|
f47e611e2c | ||
|
|
2f817d16d0 | ||
|
|
cddc42b32d | ||
|
|
5bf03feb94 | ||
|
|
15335a9f4c |
4 changed files with 163 additions and 0 deletions
30
.forgejo/workflows/build.yml
Normal file
30
.forgejo/workflows/build.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: Build and Test
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: shell
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone --branch ${{ github.ref_name }} ${{ github.server_url }}/${{ github.repository }}.git .
|
||||||
|
- name: Test
|
||||||
|
run: docker run --rm -w /src -v $PWD:/src golang:1.26 go test ./...
|
||||||
|
- name: Build
|
||||||
|
run: docker run --rm -w /src -v $PWD:/src -e CGO_ENABLED=0 golang:1.26 go build -buildvcs=false -ldflags "-s -w" -o hsa ./cmd/hsa
|
||||||
|
- name: Upload binary
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: hsa
|
||||||
|
path: hsa
|
||||||
|
- name: Stage release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
||||||
|
chmod 600 /tmp/deploy_key
|
||||||
|
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 "chmod +x $REL/hsa"
|
||||||
|
rm /tmp/deploy_key
|
||||||
24
.forgejo/workflows/deploy.yml
Normal file
24
.forgejo/workflows/deploy.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: Deploy
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release version to activate, e.g. 0.0.0a1 (must already be staged in ~/hsa-app/releases/hsa-app-V<tag>/hsa)'
|
||||||
|
required: true
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: shell
|
||||||
|
steps:
|
||||||
|
- name: Activate release
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.FORGEJO_SSH }}" > /tmp/deploy_key
|
||||||
|
chmod 600 /tmp/deploy_key
|
||||||
|
TAG=${{ github.event.inputs.tag }}
|
||||||
|
TARGET=${{ vars.HSA_APP_USER }}@${{ vars.HSA_APP_HOST }}
|
||||||
|
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no $TARGET \
|
||||||
|
"cd hsa-app \
|
||||||
|
&& test -f releases/hsa-app-V$TAG/hsa \
|
||||||
|
&& 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
|
||||||
95
deploy/INSTALL.md
Normal file
95
deploy/INSTALL.md
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Deploying hsa-app
|
||||||
|
|
||||||
|
The app runs as a **user systemd service** (no sudo). Deployment is two steps:
|
||||||
|
|
||||||
|
1. **Stage (automatic on tag)** — pushing a git tag builds + tests, then CI
|
||||||
|
copies the binary into `~/hsa-app/releases/hsa-app-V<tag>/hsa`. Nothing goes
|
||||||
|
live yet.
|
||||||
|
2. **Activate (manual button)** — run the **Deploy** workflow from the Forgejo
|
||||||
|
Actions tab ("Run workflow"), entering the tag to activate. It points the
|
||||||
|
`~/hsa-app/hsa` symlink at that release and restarts the service.
|
||||||
|
|
||||||
|
The symlink decouples "what's on disk" from "what's running," so activation and
|
||||||
|
rollback are just a symlink repoint + restart.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
Pushing a tag stages the binary automatically. To activate it, open the repo's
|
||||||
|
**Actions → Deploy** workflow, click **Run workflow**, and enter the tag (e.g.
|
||||||
|
`0.0.0a1`). To activate from the host instead:
|
||||||
|
|
||||||
|
```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