Release 1.0.0 #1

Merged
nev merged 21 commits from dev into master 2026-08-13 23:39:11 +02:00
Owner

Merges the production fixes and dependency modernisation into master.

  • restic 0.15.1 to 0.19.1, docker SDK 6.x to 7.x. The 6.x SDK is broken
    against requests >= 2.32, so any image built recently was dead on arrival.
  • Fixes backups that exited 0 on failure, RESTIC_KEEP_* retention that was
    silently ignored, and a world-readable /env.sh holding the repository
    password.
  • Adds MariaDB 11 support, Forgejo Actions CI, and a release workflow that
    publishes the image to the container registry on a version tag.

Behaviour changes to be aware of: a failing backup now exits non-zero, so
problems that previously passed silently will start being reported, and the
container refuses to start if no repository password is configured.

Verified with a full end-to-end backup against real mariadb and postgres
containers, including restic check --read-data. Repository format is
untouched: a v1 repo stays v1 and is still readable by restic 0.17.3.

Merges the production fixes and dependency modernisation into master. * restic 0.15.1 to 0.19.1, docker SDK 6.x to 7.x. The 6.x SDK is broken against requests >= 2.32, so any image built recently was dead on arrival. * Fixes backups that exited 0 on failure, `RESTIC_KEEP_*` retention that was silently ignored, and a world-readable `/env.sh` holding the repository password. * Adds MariaDB 11 support, Forgejo Actions CI, and a release workflow that publishes the image to the container registry on a version tag. Behaviour changes to be aware of: a failing backup now exits non-zero, so problems that previously passed silently will start being reported, and the container refuses to start if no repository password is configured. Verified with a full end-to-end backup against real mariadb and postgres containers, including `restic check --read-data`. Repository format is untouched: a v1 repo stays v1 and is still readable by restic 0.17.3.
nev added 21 commits 2026-08-13 23:37:04 +02:00
Merge master into dev; fix silent backup failures and modernise deps
All checks were successful
tests / test (3.13) (push) Successful in 29s
tests / test (3.11) (push) Successful in 32s
tests / test (3.9) (push) Successful in 17s
tests / lint (push) Successful in 14s
c2ebcae614
dev had been sitting on upstream's unfinished March 2023 command refactor
and never received the production fixes made on master. This merges those
9 commits in and brings the whole stack up to date.

Conflicts were resolved in favour of master's runtime behaviour: database
dumps are executed inside the target container over the docker API, keeping
--no-tablespaces and the MYSQL_PWD handling. master's commands.py was folded
into utils.py, since dev had already turned commands/ into a package.

Unbreaks the build and the backup
---------------------------------
* docker SDK 6.x is incompatible with requests >= 2.32 and fails at startup
  with "Not supported URL scheme http+docker". Any image built today was
  dead on arrival. Pinned to docker>=7.1.0,<8 and declared requests, which
  alerts/discord.py imported without depending on.
* `rcb backup` returned exit code 0 even when the backup process failed.
  With no alert backend configured a failing backup was indistinguishable
  from a successful one. It now exits 1.
* cleanup returned `forget_result and prune_result`, which evaluates to 0
  whenever forget succeeded, masking every prune failure. Now uses `or`.
* start-backup-process read `result` before assignment after running
  cleanup, raising NameError on a successful backup with no database
  containers, and never checking the cleanup exit code.
* containers_db called commands.ping_*, which no longer existed after the
  package split, so every database ping raised AttributeError. status.py
  had worked around this by hardcoding the ping result to 0.
* MariaDB 11 dropped the mysqldump/mysqladmin compatibility symlinks in
  favour of mariadb-dump/mariadb-admin. utils.resolve_binary() now probes
  the container, so both old and new images work.
* The mariadb image prefers MARIADB_USER/MARIADB_PASSWORD. Those are now
  read as a fallback, and a missing user raises a message naming the
  service instead of "TypeError: sequence item 3: expected str instance".
* status catches per-service failures, so one misconfigured database no
  longer aborts the whole run. It runs first inside start-backup-process.

Configuration
-------------
* RESTIC_KEEP_DAILY/WEEKLY/MONTHLY/YEARLY were documented in the README,
  the docs and restic_compose_backup.env, but the code read the unprefixed
  KEEP_* names, so retention policy was silently ignored and the built-in
  7/4/12/3 defaults always applied. The documented names now work; the
  unprefixed ones remain as a fallback.
* The mandatory-value check tested RESTIC_REPOSITORY twice, so a missing
  password was never caught. It now accepts RESTIC_PASSWORD, its _FILE or
  its _COMMAND variant, and fails at startup rather than at backup time.
* Commands are keyed on Command.name rather than the module filename,
  restoring the hyphenated CLI names that entrypoint.sh and docs/guide
  already referenced, and adding dump-env as a real command.

Security
--------
* /env.sh holds RESTIC_PASSWORD and any cloud or SMTP credentials and was
  written world-readable. entrypoint.sh now sets umask 077 and chmod 600.
* dump-env quotes values with shlex.quote. The previous export K='V' broke
  on any value containing a single quote, and skips names that are not
  valid shell identifiers, which would otherwise make `source /env.sh`
  fail and silently skip the backup that follows it in cron.
* Base image restic 0.15.1 -> 0.19.1, picking up the 0.17.3 SSH DoS fix
  and the 0.18.0 content-defined chunking mitigation (Sec #5291). Verified
  that this does not migrate the repository format: a v1 repo stays v1 and
  is still readable by 0.17.3 afterwards.
* get_label ignored its default argument, so a container without compose
  labels produced None and crashed with "unsupported operand type(s)
  for /: 'PosixPath' and 'NoneType'" when building the backup destination.

Image
-----
* Installs into a virtualenv instead of overwriting Alpine's managed python
  packages via PIP_BREAK_SYSTEM_PACKAGES.
* Drops mariadb-client, postgresql-client and mariadb-connector-c-dev.
  Every dump and ping now runs inside the target container, so no database
  client binaries are needed here.
* apk --no-cache, COPY instead of ADD.

Tooling
-------
* Travis (long dead) replaced with a Forgejo Actions test and lint workflow.
* tox targets py39-py313; pytest 4.3.1 (2019) -> 8.3.
* Dev compose stack off EOL mysql:5, mariadb:10 and postgres:11; dropped
  the obsolete version key.
* Removed the unused backup/ stub modules, including an off-topic ASCII-art
  comment, and restic's deprecated --last in favour of --latest 1.
* Regression tests for retention resolution, dump-env quoting and the
  missing-label crash. 13 tests, flake8 clean.

Behaviour changes for operators
-------------------------------
* A failing backup now exits non-zero. Combined with restic 0.19 returning
  exit code 3 from forget, problems that previously passed silently will
  start being reported.
* The container refuses to start if no repository password is configured.
* RESTIC_KEEP_* values now take effect. Setting them to values lower than
  7/4/12/3 will forget and prune more than before, irreversibly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alerts.send() already prefixes the subject with the alert type, and
SMTPAlert.send() prefixed it again using its own default, so every error
mail arrived as '[INFO] [ERROR] ...' and every info mail as '[INFO]
[INFO] ...'. Subject-based mail filters sorted outage alerts as
informational. Discord was unaffected: it ignores the parameter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add CLAUDE.md, refresh README and project metadata
All checks were successful
tests / test (3.13) (push) Successful in 17s
tests / test (3.11) (push) Successful in 19s
tests / lint (push) Successful in 13s
tests / test (3.9) (push) Successful in 17s
6d7bb673bc
Add release workflow publishing to the Forgejo registry
All checks were successful
tests / test (3.11) (push) Successful in 16s
tests / test (3.13) (push) Successful in 19s
tests / lint (push) Successful in 14s
tests / test (3.9) (push) Successful in 17s
a7ac6c9b4b
Pushing a v* tag builds the image with Kaniko and publishes it to
forgejo.nevy.xyz/nev/restic-compose-backup under three tags: the git tag
name, latest, and a 12 character commit SHA for pinning a deployment once
latest has moved. The full SHA stays on the OCI revision label.

A verify job gates the publish. It checks the tag matches the packaged
version and runs the test suite, because tag pushes do not trigger
test.yml and a release would otherwise ship untested.

The Dockerfile expects src/ as its build context, matching `build: ./src`
in docker-compose.yaml, so the build narrows the context with
--context-sub-path instead of pointing --dockerfile at src/Dockerfile.
Both forms were tried against a local Kaniko run; the latter fails with
"does not appear to be a Python project".

Requires two repo secrets: REGISTRY_USER holding the PAT owner's handle,
and REGISTRY_TOKEN holding a PAT with write:package and read:repository.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Release on bare version tags and document the published image
All checks were successful
tests / lint (push) Successful in 14s
tests / test (3.13) (pull_request) Successful in 16s
tests / test (3.11) (pull_request) Successful in 19s
tests / test (3.11) (push) Successful in 16s
tests / test (3.13) (push) Successful in 20s
tests / test (3.9) (push) Successful in 17s
tests / lint (pull_request) Successful in 14s
tests / test (3.9) (pull_request) Successful in 17s
8e23259441
nev merged commit ae1b9bb4ca into master 2026-08-13 23:39:11 +02:00
nev referenced this pull request from a commit 2026-08-13 23:39:12 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nev/restic-compose-backup!1
No description provided.