From ab9eba3bf91cf252bb7b36056a70524701b24f45 Mon Sep 17 00:00:00 2001 From: nold Date: Fri, 24 Apr 2026 12:27:36 +0200 Subject: [PATCH 1/2] feat(ci): build unstable arm again --- .woodpecker.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 8fcf82d..32f6b94 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -3,13 +3,7 @@ matrix: include: - BASE: unstable-slim TAGS: '[ "unstable" ]' - PLATFORMS: linux/386,linux/amd64 - - BASE: trixie-slim - TAGS: '[ "trixie", "1.4", "latest" ]' - PLATFORMS: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 - - BASE: bookworm-slim - TAGS: '[ "bookworm", "1.2", "oldstable" ]' - PLATFORMS: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 + PLATFORMS: linux/arm64/v8 variables: - &build-settings From cd40efef6714edda646a0c26c4233d42c0702d80 Mon Sep 17 00:00:00 2001 From: nold Date: Thu, 14 May 2026 20:28:08 +0200 Subject: [PATCH 2/2] feat(ci): add github action - by qwen3.6-30b-a3b --- .github/workflows/build.yml | 164 ++++++++++++++++++++++++++++++++++++ CLAUDE.md | 51 +++++++++++ 2 files changed, 215 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 CLAUDE.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..19b1471 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,164 @@ +name: build + +on: + pull_request: + branches: [master] + push: + branches: [master] + schedule: + - cron: '0 0 * * 0' # weekly + workflow_dispatch: + +env: + REGISTRY_DOCKER: docker.io + REGISTRY_GHCR: ghcr.io + IMAGE_NAME_DOCKER: nold360/borgserver + IMAGE_NAME_GHCR: ghcr.io/nold360/borgserver + +jobs: + build-arm64: + runs-on: ubuntu-24.04-arm + strategy: + fail-fast: false + matrix: + include: + - base: debian:trixie-slim + tags: | + trixie + latest + - base: debian:bookworm-slim + tags: | + bookworm + - base: debian:unstable-slim + tags: | + unstable + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.REGISTRY_DOCKER }}/${{ env.IMAGE_NAME_DOCKER }} + ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME_GHCR }} + tags: ${{ matrix.tags }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Dry-run build + if: github.event_name == 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/arm64 + push: false + tags: ${{ steps.meta.outputs.tags }} + build-args: | + BASE_IMAGE=${{ matrix.base }} + + - name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GH_PUSH_TOKEN }} + + - name: Build and push + if: github.event_name != 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BASE_IMAGE=${{ matrix.base }} + + build-amd64: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - base: debian:trixie-slim + tags: | + trixie + latest + - base: debian:bookworm-slim + tags: | + bookworm + - base: debian:unstable-slim + tags: | + unstable + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.REGISTRY_DOCKER }}/${{ env.IMAGE_NAME_DOCKER }} + ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME_GHCR }} + tags: ${{ matrix.tags }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Dry-run build + if: github.event_name == 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: false + tags: ${{ steps.meta.outputs.tags }} + build-args: | + BASE_IMAGE=${{ matrix.base }} + + - name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GH_PUSH_TOKEN }} + + - name: Build and push + if: github.event_name != 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BASE_IMAGE=${{ matrix.base }} + + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1b82e80 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,51 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a Docker image for running a BorgBackup server. It builds a Debian-based container with openssh-daemon that accepts SSH key auth for borg clients. Each client SSH key in `/sshkeys/clients/` maps to a separate borg repository. + +## Key Files + +- `Dockerfile` - Builds the image from `debian:-slim`, installs `borgbackup` + `openssh-server` +- `data/run.sh` - Entrypoint script: adjusts PUID/PGID, generates SSH host keys, imports client keys into `authorized_keys` with restricted `borg serve` commands, starts sshd +- `data/sshd_config` - Hardened SSH config: pubkey-only, no forwarding, no TTY, SFTP disabled +- `.woodpecker.yml` - CI pipeline using Woodpecker CI with Docker Buildx for multi-platform builds +- `docker-compose.yml` - Example compose file + +## Architecture + +The entrypoint (`run.sh`) works as follows: +1. Adjusts `borg` user/group IDs to match host PUID/PGID +2. Validates `/backup` and `/sshkeys` volumes exist and that at least one client key is present (sshd won't start otherwise) +3. Generates SSH host keys (ed25519, rsa) in `/sshkeys/host/` if missing +4. Iterates over files in `/sshkeys/clients/`, creating an `authorized_keys` entry per client with: + - `restrict` flag + `command=` wrapping a `borg serve --restrict-to-path` command + - If `BORG_ADMIN` is set, that client gets unrestricted access to all repos + - If `BORG_APPEND_ONLY=yes`, non-admin clients get `--append-only` +5. Starts `/usr/sbin/sshd -D -e` + +## Environment Variables + +| Variable | Default | Description | +|---|---|---| +| `BORG_APPEND_ONLY` | `no` | Restrict clients to append-only mode | +| `BORG_ADMIN` | unset | Client key name that gets full access to all repos | +| `BORG_SERVE_ARGS` | unset | Extra args passed to `borg serve` | +| `PUID` | `1000` | User ID for `borg` user | +| `PGID` | `1000` | Group ID for `borg` group | + +## CI/CD + +Built via Woodpecker CI (`.woodpecker.yml`). Matrix builds target: +- `unstable-slim` → `linux/arm64/v8` tag `unstable` + +Builds publish to Docker Hub (`nold360/borgserver`) and GHCR (`ghcr.io/nold360/borgserver`). + +## Building Locally + +```bash +docker build -t borgserver:dev . +docker build --build-arg BASE_IMAGE=debian:trixie-slim -t borgserver:trixie . +```