Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env sh
set -eu

msg_file="$1"

first_line=$(sed -n '1p' "$msg_file")

case "$first_line" in
Merge\ *|Revert\ *)
exit 0
;;
esac

if ! printf '%s\n' "$first_line" | grep -Eq '^(feat|fix|docs|refactor|perf|test|chore|build|ci|style)(\([A-Za-z0-9_.-]+\))?!?: .+'; then
cat >&2 <<'EOF'
commit-msg: expected a conventional commit subject.
Example: fix: handle missing Docker socket gracefully
Allowed types: feat, fix, docs, refactor, perf, test, chore, build, ci, style
EOF
exit 1
fi
7 changes: 7 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env sh
set -eu

uv run --locked python scripts/check_version_sync.py
uv run --locked python scripts/check_publishing_ready.py
uv run --locked python scripts/generate_cli_skills.py --check
git diff --check
2 changes: 1 addition & 1 deletion .github/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ This Code of Conduct is adapted from the [Contributor Covenant](https://www.cont

<p align="center">
Slavic Kozyuk<br>
&copy; 2026 <a href="https://www.cruxexperts.com/">Crux Experts LLC</a> &mdash; <a href="https://github.com/cptnfren/best-backup/blob/main/LICENSE">MIT License</a>
&copy; 2026 <a href="https://www.cruxexperts.com/">Crux Experts LLC</a> &mdash; <a href="https://github.com/CruxExperts/best-backup/blob/main/LICENSE">MIT License</a>
</p>

<!-- project-footer:end -->
24 changes: 15 additions & 9 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ Thanks for taking the time to contribute. This document covers how to set up a d

## Development setup

For contributing you need an editable install so source changes take effect immediately. Clone the repo and use a virtual environment (pipx does not support editable mode):
For contributing you need a UV-managed editable environment so source changes take effect immediately:

```bash
git clone https://github.com/cptnfren/best-backup.git
git clone https://github.com/CruxExperts/best-backup.git
cd best-backup

python3 -m venv .venv
source .venv/bin/activate
pip install -e .
uv sync --locked

# Verify
bbackup --version
bbman --version
uv run bbackup --version
uv run bbman --version
```

You will need Docker running locally to test backup and restore operations. `rsync` is required for volume backups; install it with your system package manager if it is not already present.

Enable the repo-managed Git hooks once per checkout:

```bash
git config core.hooksPath .githooks
```

The hooks validate conventional commit subjects and run release-readiness checks before push. See [docs/VERSIONING.md](../docs/VERSIONING.md) for the full version and release checklist.

---

## Making changes
Expand All @@ -32,7 +38,7 @@ Keep changes focused. A pull request that fixes one bug or adds one feature is e
Run the syntax check before pushing:

```bash
python3 -m py_compile bbackup/*.py bbackup/management/*.py
uv run python -m py_compile bbackup.py bbman.py bbackup/*.py bbackup/data/*.py bbackup/management/*.py scripts/*.py
```

---
Expand Down Expand Up @@ -89,7 +95,7 @@ This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). Treat every

<p align="center">
Slavic Kozyuk<br>
&copy; 2026 <a href="https://www.cruxexperts.com/">Crux Experts LLC</a> &mdash; <a href="https://github.com/cptnfren/best-backup/blob/main/LICENSE">MIT License</a>
&copy; 2026 <a href="https://www.cruxexperts.com/">Crux Experts LLC</a> &mdash; <a href="https://github.com/CruxExperts/best-backup/blob/main/LICENSE">MIT License</a>
</p>

<!-- project-footer:end -->
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ $ bbackup ...
## Environment

- OS and version:
- Python version (`python3 --version`):
- Python version (`uv run python --version` or `python3 --version`):
- Docker version (`docker --version`):
- bbackup version (`bbackup --version`):
- Installation method (pip install / symlink / PATH):
- Installation method (uv tool / uv sync / symlink / PATH):

## Configuration

Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

## Checklist

- [ ] Code runs without syntax errors (`python3 -m py_compile bbackup/*.py`)
- [ ] Code runs without syntax errors (`uv run python -m py_compile bbackup.py bbman.py bbackup/*.py bbackup/data/*.py bbackup/management/*.py scripts/*.py`)
- [ ] Commit messages follow conventional commit format (`feat:`, `fix:`, `docs:`, etc.)
- [ ] Documentation updated if behavior changed
- [ ] No secrets, keys, or personal data included
Expand Down
81 changes: 54 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,66 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
lint-and-check:
name: Lint and syntax check (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.12", "3.13"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
run: python -m pip install uv

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
pip install -e .
run: uv sync --locked

- name: Lint with ruff
run: ruff check bbackup/
run: uv run ruff check bbackup/

- name: Check syntax (py_compile)
run: |
python -m py_compile bbackup/*.py
python -m py_compile bbackup/management/*.py
uv run python -m py_compile bbackup.py bbman.py
uv run python -m py_compile bbackup/*.py
uv run python -m py_compile bbackup/data/*.py
uv run python -m py_compile bbackup/management/*.py
uv run python -m py_compile scripts/*.py

- name: Check imports
run: |
python -c "from bbackup.config import Config; print('config OK')"
python -c "from bbackup.cli import cli; print('cli OK')"
python -c "from bbackup.encryption import EncryptionManager; print('encryption OK')"
python -c "from bbackup.remote import RemoteStorage; print('remote OK')"
python -c "from bbackup.rotation import BackupRotation; print('rotation OK')"
uv run python -c "from bbackup.config import Config; print('config OK')"
uv run python -c "from bbackup.cli import cli; print('cli OK')"
uv run python -c "from bbackup.encryption import EncryptionManager; print('encryption OK')"
uv run python -c "from bbackup.remote import RemoteStorage; print('remote OK')"
uv run python -c "from bbackup.rotation import BackupRotation; print('rotation OK')"

- name: Verify version references are in sync
run: uv run python scripts/check_version_sync.py

- name: Verify publishing checklist is present
run: |
test -f docs/PUBLISHING_CHECKLIST.md
test -f SUPPORT.md

- name: Verify publishing readiness
run: uv run python scripts/check_publishing_ready.py

- name: Verify installed artifact smoke test
run: |
uv build
uv run python scripts/smoke_installed_artifact.py

test:
name: Unit tests (Python ${{ matrix.python-version }})
Expand All @@ -52,47 +74,52 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.12", "3.13"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
run: python -m pip install uv

- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt && pip install -e .
run: uv sync --locked

- name: Run unit tests with coverage
run: pytest tests/ -m "not integration" --cov=bbackup --cov-report=xml --cov-report=term-missing
run: uv run pytest tests/ -m "not integration" --cov=bbackup --cov-report=xml --cov-report=term-missing

- name: Upload coverage artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: coverage-${{ matrix.python-version }}
path: coverage.xml

- name: Verify CLI skills docs are up to date
run: |
python scripts/generate_cli_skills.py --check
run: uv run python scripts/generate_cli_skills.py --check

integration-test:
name: Integration tests
needs: test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install uv
run: python -m pip install uv

- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt && pip install -e .
run: uv sync --locked

- name: Run integration tests
run: pytest tests/integration/ -m integration -v --tb=short
run: uv run pytest tests/integration/ -m integration -v --tb=short
33 changes: 30 additions & 3 deletions .github/workflows/release-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,57 @@ jobs:
contents: write

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install uv
run: python -m pip install uv

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

- name: Verify release version and publishing readiness
run: |
test "${{ steps.version.outputs.version }}" = "$(cat VERSION)"
uv sync --locked
uv run python scripts/check_version_sync.py
uv run python scripts/check_publishing_ready.py
uv run ruff check bbackup/ scripts/check_publishing_ready.py scripts/check_version_sync.py scripts/smoke_installed_artifact.py
uv run python scripts/generate_cli_skills.py --check
uv run python -m py_compile bbackup.py bbman.py bbackup/*.py bbackup/data/*.py bbackup/management/*.py scripts/*.py
uv run pytest

- name: Extract changelog section for this version
id: changelog
run: |
VERSION="${{ steps.version.outputs.version }}"
# Pull the block between the version header and the next version header
NOTES=$(awk "/^## \[$VERSION\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md)
if [ -z "$NOTES" ]; then
NOTES="See CHANGELOG.md for details."
echo "No CHANGELOG.md section found for v$VERSION" >&2
exit 1
fi
# Write to file to preserve newlines
echo "$NOTES" > release_notes.txt

- name: Build release artifacts
run: uv build

- name: Smoke test built wheel
run: uv run python scripts/smoke_installed_artifact.py

- name: Create GitHub release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
name: "v${{ steps.version.outputs.version }}"
body_path: release_notes.txt
files: dist/*
draft: false
prerelease: false
6 changes: 5 additions & 1 deletion .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ on:
- cron: "0 9 * * 1" # Every Monday at 09:00 UTC
workflow_dispatch:

permissions:
issues: write
pull-requests: write

jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ ENV/
# Cursor (IDE-local, not tracked)
.cursor/

# Localsetup framework (tooling, not part of this repo)
# Localsetup and Codex runtime state (tooling, not part of this repo)
_localsetup/
.localsetup/
.codex/skills/
.codex/runs/
.codex/sessions/
.codex/logs/
.codex/tmp/

# Release and maintenance tooling (local only, not published)
maintenance/
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
Loading