Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f848c04
docs(spec): establish v0.9.1 readiness package
bcherrington Jul 18, 2026
0c9b291
docs(spec): reconcile release readiness findings
bcherrington Jul 18, 2026
2abdeb6
test(ci): isolate live MinIO profile
bcherrington Jul 18, 2026
5ec8409
test(ci): provision MinIO integration profile
bcherrington Jul 18, 2026
3137a7c
test(telemetry): enable backend test explicitly
bcherrington Jul 18, 2026
8a7e1c1
fix(ci): retain coverage data artifact
bcherrington Jul 19, 2026
9183aba
docs(spec): complete release readiness phase 1
bcherrington Jul 19, 2026
6572f65
perf(selection): stabilize stress threshold
bcherrington Jul 19, 2026
9348c58
docs(spec-007): start artifact preparation
bcherrington Jul 19, 2026
e854905
build(release): validate 0.9.1 artifacts
bcherrington Jul 19, 2026
0c597e6
ci(release): run artifact smoke on pull requests
bcherrington Jul 19, 2026
58ccec9
test(release): report artifact smoke failures
bcherrington Jul 19, 2026
4a2d998
fix(cli): keep help portable on Windows
bcherrington Jul 19, 2026
1dcf910
fix(release): complete phase 3 validation
bcherrington Jul 19, 2026
a754462
feat(release): add safe release rehearsal
bcherrington Jul 19, 2026
433c0aa
feat(backup): complete machine acceptance hardening
bcherrington Jul 19, 2026
2c93709
feat(backup): add NPBackup migration parity
bcherrington Jul 19, 2026
6896c8d
fix(restore): preserve selective recovery paths
bcherrington Jul 19, 2026
3a4572c
feat(backup): preserve NPBackup exclusion parity
bcherrington Jul 19, 2026
2eb9928
fix(monitoring): skip tray initialization when headless
bcherrington Jul 19, 2026
310548d
docs(spec): record failed timer observation
bcherrington Jul 19, 2026
daaad53
fix(cli): preserve native restic repository URIs
bcherrington Jul 19, 2026
aacc00c
fix(schedule): avoid immediate systemd service start
bcherrington Jul 20, 2026
d394a30
docs(spec): record NPBackup cutover
bcherrington Jul 20, 2026
5830194
docs(spec): strengthen cutover evidence
bcherrington Jul 20, 2026
1bfea08
docs(spec): close NPBackup migration
bcherrington Jul 20, 2026
2487a35
docs(spec): resolve NPBackup closure record
bcherrington Jul 20, 2026
cacb227
docs(spec): define system operations UX requirements
bcherrington Jul 20, 2026
7fd11f9
docs(spec): prepare release readiness closure
bcherrington Jul 20, 2026
6334af0
docs(spec): close release readiness stabilization
bcherrington Jul 20, 2026
6bee87c
docs(spec): resolve release readiness closure record
bcherrington Jul 20, 2026
ab80b89
docs: reconcile spec 009 requirements
bcherrington Jul 20, 2026
a601d03
docs: clarify retention trigger semantics
bcherrington Jul 24, 2026
1e29d91
feat: complete spec 009 phase 1 foundation
bcherrington Jul 26, 2026
b268b4c
feat: complete spec 009 phase 2 system cli
bcherrington Jul 26, 2026
7b77d62
feat: complete spec 009 phase 3 automation
bcherrington Jul 26, 2026
1ec3897
feat: complete spec 009 release readiness
bcherrington Jul 26, 2026
234f5c7
test: verify spec 009 release integration
bcherrington Jul 26, 2026
52a9c1d
fix(system): make installed launcher startup portable
bcherrington Jul 26, 2026
d4d5e88
fix(system): allow operator socket traversal
bcherrington Jul 26, 2026
8630b24
fix(system): preserve control socket across restarts
bcherrington Jul 26, 2026
6fe7848
feat(system): activate protected retention adapter
bcherrington Jul 26, 2026
f1243f3
fix(system): gate automatic retention activation
bcherrington Jul 26, 2026
2388e1d
feat(system): coordinate protected backup runs
bcherrington Jul 26, 2026
32ab1fe
fix(tray): prioritize current operation state
bcherrington Jul 26, 2026
84581d2
docs(spec-009): complete live acceptance
bcherrington Jul 26, 2026
3f009a8
docs: promote spec 009 system operations
bcherrington Jul 26, 2026
d4ce71d
feat: complete spec 009 system operations
bcherrington Jul 26, 2026
aba9587
docs: close spec 009
bcherrington Jul 26, 2026
d5c6b97
docs: record spec 009 cleanup commit
bcherrington Jul 26, 2026
04a9ef9
feat(tray): show branded backup status
bcherrington Jul 26, 2026
123ab89
feat: add event-driven tray status
bcherrington Jul 27, 2026
d540b45
fix: reconnect tray promptly after backend restart
bcherrington Jul 27, 2026
3df80cd
fix: harden tray event acceptance
bcherrington Jul 27, 2026
8e8ebad
fix: harden T011 deployment transaction
bcherrington Jul 27, 2026
a67c83a
fix(deploy): preserve staged wheel filename
bcherrington Jul 27, 2026
ae329a5
docs(spec): define protected system deployment
bcherrington Jul 27, 2026
2e1b565
feat: make tray status health-aware
bcherrington Jul 28, 2026
a122b80
fix(deploy): bind backend probe to release manifest
bcherrington Jul 28, 2026
5166242
fix(deploy): defer cross-version system probe
bcherrington Jul 28, 2026
18990e1
fix(deploy): upgrade stable launcher transactionally
bcherrington Jul 28, 2026
6544a8a
docs(spec): record protocol 2 deployment
bcherrington Jul 28, 2026
8820e65
docs(spec): close rejected resident tray design
bcherrington Aug 12, 2026
4122746
docs(spec): remove closed spec 010 package
bcherrington Aug 12, 2026
a9f6252
docs(spec): resolve spec 010 closure metadata
bcherrington Aug 12, 2026
b91c0ff
feat(system-control): replace resident daemon with one-shot service
bcherrington Aug 12, 2026
b43acc5
docs(specs): close protected system deployment
bcherrington Aug 12, 2026
0abf006
docs(specs): resolve protected deployment closure metadata
bcherrington Aug 12, 2026
20cc4b2
fix(deploy): allow upgrade from stopped legacy service
bcherrington Aug 13, 2026
fed8e16
fix(deploy): seed offline upgrade dependencies
bcherrington Aug 13, 2026
1f110bf
fix(deploy): expose read-only deployment status
bcherrington Aug 13, 2026
68d8a6f
fix(release): reconcile v0.9.1 publication gates
bcherrington Aug 13, 2026
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
3 changes: 1 addition & 2 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.9.0
current_version = 0.9.1
commit = True
tag = True
tag_name = v{new_version}
Expand All @@ -14,4 +14,3 @@ replace = version = "{new_version}"
[bumpversion:file:src/TimeLocker/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

71 changes: 71 additions & 0 deletions .github/workflows/artifact-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Release Artifact Smoke

on:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Build once and inspect artifacts
run: |
python -m pip install --upgrade pip build
python -m build
python scripts/validate_release_artifacts.py --expected-version 0.9.1
- name: Upload immutable artifact set
uses: actions/upload-artifact@v4
with:
name: timelocker-0.9.1-distributions
path: dist/
if-no-files-found: error

smoke:
needs: build
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.12", "3.13"]
artifact: [wheel, sdist]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout smoke tooling
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: timelocker-0.9.1-distributions
path: dist
- name: Select artifact
id: artifact
shell: python
run: |
from pathlib import Path
import os

suffix = ".whl" if "${{ matrix.artifact }}" == "wheel" else ".tar.gz"
matches = list(Path("dist").glob(f"*{suffix}"))
if len(matches) != 1:
raise SystemExit(f"Expected one {suffix} artifact, found {matches}")
with open(os.environ["GITHUB_OUTPUT"], "a") as output:
output.write(f"path={matches[0]}\n")
- name: Install and smoke ${{ matrix.artifact }}
run: >-
python scripts/smoke_release_artifact.py
"${{ steps.artifact.outputs.path }}"
--expected-version 0.9.1
97 changes: 97 additions & 0 deletions .github/workflows/release-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: TimeLocker Release Validation

on:
workflow_call:
inputs:
version_ref:
description: Semantic release reference to validate
required: true
type: string
workflow_dispatch:
inputs:
version_ref:
description: Semantic release reference to rehearse without publishing
required: true
default: v0.9.1
type: string

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
env:
VERSION_REF: ${{ inputs.version_ref }}

steps:
- name: Checkout source
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

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

- name: Install Restic 0.18.0
shell: bash
run: |
set -euo pipefail
restic_version="0.18.0"
wget --quiet "https://github.com/restic/restic/releases/download/v${restic_version}/restic_${restic_version}_linux_amd64.bz2"
wget --quiet "https://github.com/restic/restic/releases/download/v${restic_version}/SHA256SUMS"
grep " restic_${restic_version}_linux_amd64.bz2$" SHA256SUMS | sha256sum --check --strict
bunzip2 "restic_${restic_version}_linux_amd64.bz2"
sudo install -m 0755 "restic_${restic_version}_linux_amd64" /usr/local/bin/restic
restic version

- name: Install build and test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build
python -m pip install -e '.[dev]'

- name: Verify release intent and workflow boundary
run: |
python scripts/validate_release_intent.py --version-ref "$VERSION_REF"
python scripts/validate_release_workflows.py

- name: Run required test suite
env:
PYTHONPATH: src
run: python -m pytest -m "not performance and not stress and not minio"

- name: Build and validate distributions
run: |
python -m build
python scripts/validate_release_artifacts.py --expected-version "${VERSION_REF#v}"

- name: Smoke wheel and source distribution
shell: bash
run: |
set -euo pipefail
python scripts/smoke_release_artifact.py dist/*.whl --expected-version "${VERSION_REF#v}"
python scripts/smoke_release_artifact.py dist/*.tar.gz --expected-version "${VERSION_REF#v}"

- name: Derive release notes from changelog
run: >-
python scripts/extract_release_notes.py
--version "${VERSION_REF#v}"
--output release-notes.md

- name: Upload validated distributions
uses: actions/upload-artifact@v4
with:
name: timelocker-distributions-${{ inputs.version_ref }}
path: dist/*
if-no-files-found: error

- name: Upload release-note preview
uses: actions/upload-artifact@v4
with:
name: timelocker-release-notes-${{ inputs.version_ref }}
path: release-notes.md
if-no-files-found: error
100 changes: 18 additions & 82 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,101 +6,37 @@ on:
- "v*.*.*"

permissions:
contents: write
contents: read

jobs:
release:
validate:
uses: ./.github/workflows/release-validation.yml
with:
version_ref: ${{ github.ref_name }}

publish:
needs: validate
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout tagged source
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- name: Set up Python 3.12
uses: actions/setup-python@v5
- name: Download validated distributions
uses: actions/download-artifact@v4
with:
python-version: "3.12"
cache: pip

- name: Install Restic 0.18.0
shell: bash
run: |
set -euo pipefail
restic_version="0.18.0"
wget --quiet "https://github.com/restic/restic/releases/download/v${restic_version}/restic_${restic_version}_linux_amd64.bz2"
wget --quiet "https://github.com/restic/restic/releases/download/v${restic_version}/SHA256SUMS"
grep " restic_${restic_version}_linux_amd64.bz2$" SHA256SUMS | sha256sum --check --strict
bunzip2 "restic_${restic_version}_linux_amd64.bz2"
sudo install -m 0755 "restic_${restic_version}_linux_amd64" /usr/local/bin/restic
restic version

- name: Install build and test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build
python -m pip install -e '.[dev]'

- name: Verify tag and package versions
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import os
import tomllib

from TimeLocker import __version__

tag = os.environ["GITHUB_REF_NAME"]
if not tag.startswith("v"):
raise SystemExit(f"Release tag must start with v: {tag}")

tag_version = tag[1:]
with open("pyproject.toml", "rb") as pyproject_file:
package_version = tomllib.load(pyproject_file)["project"]["version"]

if tag_version != package_version or package_version != __version__:
raise SystemExit(
"Version mismatch: "
f"tag={tag_version}, pyproject={package_version}, package={__version__}"
)

print(f"Version verified: {package_version}")
PY

- name: Run required test suite
env:
PYTHONPATH: src
run: python -m pytest -m "not performance and not stress"

- name: Build source and wheel distributions
run: |
python -m build
sha256sum dist/* > dist/SHA256SUMS

- name: Smoke test the built wheel
shell: bash
run: |
set -euo pipefail
python -m venv /tmp/timelocker-release-smoke
/tmp/timelocker-release-smoke/bin/python -m pip install --upgrade pip
/tmp/timelocker-release-smoke/bin/python -m pip install dist/*.whl
installed_version=$(/tmp/timelocker-release-smoke/bin/tl version --short)
test "$installed_version" = "${GITHUB_REF_NAME#v}"
name: timelocker-distributions-${{ github.ref_name }}
path: dist

- name: Upload distribution artifacts
uses: actions/upload-artifact@v4
- name: Download derived release notes
uses: actions/download-artifact@v4
with:
name: timelocker-${{ github.ref_name }}
path: dist/*
if-no-files-found: error
name: timelocker-release-notes-${{ github.ref_name }}

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" dist/* \
--verify-tag \
--generate-notes \
--notes-file release-notes.md \
--title "TimeLocker $GITHUB_REF_NAME"
Loading
Loading