Compare commits

...

3 Commits

Author SHA1 Message Date
Admin
fc73756308 fix: run pocketbase as root + add healthcheck start_period
Some checks failed
Release / Test backend (push) Successful in 56s
Release / Check ui (push) Successful in 1m59s
Release / Docker (push) Successful in 7m49s
Release / Deploy to prod (push) Failing after 33s
Release / Gitea Release (push) Successful in 29s
- Remove non-root user from pocketbase Docker image; the existing pb_data
  volume was created by the previous root-running image so files are owned
  by root — running as a non-root appuser caused an immediate permission
  error and container exit
- Increase healthcheck retries to 10 and add start_period=30s so migrations
  have time to run on first boot before liveness checks begin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:00:21 +05:00
Admin
3f436877ee fix: pocketbase healthcheck (add wget) + saveIfAbsent infinite recursion
Some checks failed
Release / Test backend (push) Successful in 1m6s
Release / Check ui (push) Successful in 1m56s
Release / Docker (push) Successful in 8m19s
Release / Deploy to prod (push) Failing after 36s
Release / Gitea Release (push) Successful in 22s
- Add wget to pocketbase Alpine image so the docker-compose healthcheck
  (wget http://localhost:8090/api/health) can actually run
- Fix saveIfAbsent calling itself instead of app.Save(c) — was an
  infinite recursion that would stack-overflow on a fresh install

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 18:05:02 +05:00
Admin
812028e50d ci: add pocketbase image build + automated prod deploy step
Some checks failed
Release / Test backend (push) Successful in 5m39s
Release / Check ui (push) Successful in 2m1s
Release / Docker (push) Successful in 7m46s
Release / Deploy to prod (push) Failing after 1m53s
Release / Gitea Release (push) Successful in 35s
release.yaml:
  - Build and push kalekber/libnovel-pocketbase image on every release tag
  - Add deploy job (runs after docker): copies docker-compose.yml from the
    tagged commit to /opt/libnovel on prod, pulls new images, restarts
    changed services with --remove-orphans (cleans up removed pb-init)

ci.yaml:
  - Validate cmd/pocketbase builds on every branch push

Required new Gitea secrets: PROD_HOST, PROD_USER, PROD_SSH_KEY,
PROD_SSH_KNOWN_HOSTS (see deploy job comments for instructions).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 11:06:27 +05:00
5 changed files with 71 additions and 6 deletions

View File

@@ -41,6 +41,9 @@ jobs:
- name: Build healthcheck
run: go build -o /dev/null ./cmd/healthcheck
- name: Build pocketbase
run: go build -o /dev/null ./cmd/pocketbase
- name: Run tests
run: go test -short -race -count=1 -timeout=60s ./...

View File

@@ -276,6 +276,69 @@ jobs:
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-caddy:latest
cache-to: type=inline
# ── pocketbase ───────────────────────────────────────────────────────────
- name: Docker meta / pocketbase
id: meta-pocketbase
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKER_USER }}/libnovel-pocketbase
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push / pocketbase
uses: docker/build-push-action@v6
with:
context: backend
target: pocketbase
push: true
tags: ${{ steps.meta-pocketbase.outputs.tags }}
labels: ${{ steps.meta-pocketbase.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_USER }}/libnovel-pocketbase:latest
cache-to: type=inline
# ── deploy: sync docker-compose.yml + restart prod ───────────────────────────
# Runs after all images are pushed to Docker Hub.
# Copies the compose file from the tagged commit to the server, pulls the new
# images, and restarts only the services whose image or config changed.
# --remove-orphans cleans up containers no longer defined in the compose file
# (e.g. the now-removed pb-init container).
#
# Required Gitea secrets:
# PROD_HOST — prod server IP or hostname
# PROD_USER — SSH login user (typically root)
# PROD_SSH_KEY — private key whose public half is in authorized_keys
# PROD_SSH_KNOWN_HOSTS — output of: ssh-keyscan -H <PROD_HOST>
deploy:
name: Deploy to prod
runs-on: ubuntu-latest
needs: [docker]
steps:
- uses: actions/checkout@v4
- name: Install SSH key
run: |
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.PROD_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
printf '%s\n' "${{ secrets.PROD_SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
- name: Copy docker-compose.yml to prod
run: |
scp -i ~/.ssh/deploy_key \
docker-compose.yml \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}:/opt/libnovel/docker-compose.yml"
- name: Pull new images and restart changed services
run: |
ssh -i ~/.ssh/deploy_key \
"${{ secrets.PROD_USER }}@${{ secrets.PROD_HOST }}" \
'set -euo pipefail
cd /opt/libnovel
doppler run -- docker compose pull
doppler run -- docker compose up -d --remove-orphans'
# ── Gitea release ─────────────────────────────────────────────────────────────
release:
name: Gitea Release

View File

@@ -48,13 +48,11 @@ ENTRYPOINT ["/backend"]
# On every `serve` startup it applies any pending migrations automatically.
# Data is stored in /pb_data (mounted as a Docker volume in production).
FROM alpine:3.21 AS pocketbase
RUN apk add --no-cache ca-certificates && \
addgroup -S appgroup && adduser -S appuser -G appgroup
RUN apk add --no-cache ca-certificates wget
COPY --from=builder /out/pocketbase /pocketbase
RUN mkdir -p /pb_data && chown appuser:appgroup /pb_data
RUN mkdir -p /pb_data
VOLUME /pb_data
EXPOSE 8090
USER appuser
CMD ["/pocketbase", "serve", "--dir", "/pb_data", "--http", "0.0.0.0:8090"]
# ── runner service ───────────────────────────────────────────────────────────

View File

@@ -81,7 +81,7 @@ func saveIfAbsent(app core.App, c *core.Collection) error {
if _, err := app.FindCollectionByNameOrId(c.Name); err == nil {
return nil // already exists — skip
}
return saveIfAbsent(app, c)
return app.Save(c)
}
// ── Collection creators ───────────────────────────────────────────────────────

View File

@@ -91,7 +91,8 @@ services:
test: ["CMD", "wget", "-qO-", "http://localhost:8090/api/health"]
interval: 10s
timeout: 5s
retries: 5
retries: 10
start_period: 30s
# ─── Meilisearch (full-text search) ──────────────────────────────────────────
meilisearch: