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
72 changes: 72 additions & 0 deletions .github/workflows/pr-fragment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: PR Fragment

on:
pull_request:
types: [opened, edited, synchronize, reopened, ready_for_review]

jobs:
fragment-required:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: dorny/paths-filter@v3
id: paths
with:
filters: |
user_facing:
- 'crates/atomic-rollback/src/**'
fragment:
- 'crates/changelog-core/src/lib.rs'

- name: Enforce fragment requirement
if: ${{ steps.paths.outputs.user_facing == 'true' && steps.paths.outputs.fragment == 'false' }}
run: |
cat <<'MSG'
::error::This PR modifies crates/atomic-rollback/src/** but does not modify crates/changelog-core/src/lib.rs. Every change to user-facing source requires a Fragment.

To fix:

1. Add a variant to the fragments! macro invocation in crates/changelog-core/src/lib.rs
2. Add a match arm in Fragment::status() returning one of:

- Status::Unreleased { section: ..., text: "..." } for changes that will appear in CHANGELOG.md
- Status::InternalOnly { description: "..." } for changes with no user-perceivable effect (pure private refactor, no perf change, no behavior change). Use sparingly and only after checking there is truly no user impact.

Example (user-facing change):

fragments! { /* existing variants, */ DescribeYourChangeHere }

impl Fragment {
pub const fn status(self) -> Status {
match self {
/* existing arms, */
Self::DescribeYourChangeHere => Status::Unreleased {
section: Section::Added,
text: "What users experience, in one sentence.",
},
}
}
}

Example (internal-only change):

Self::RenamedPrivateHelperFoo => Status::InternalOnly {
description: "Renamed private helper Foo to Bar for consistency.",
},

After editing crates/changelog-core/src/lib.rs, regenerate CHANGELOG.md:

cargo run -p changelog > CHANGELOG.md

Then commit both files together. If you do not, build.rs panics on the next cargo build.

If you are stuck, comment on this PR and a maintainer will add the Fragment for you.

See docs/standards/changelog-fragments.md for the full contract, including when InternalOnly is appropriate.
MSG
exit 1
19 changes: 10 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

All notable changes to atomic-rollback are documented here.

## [Unreleased]

## [0.4.0] - 2026-04-14

### Added
Expand Down Expand Up @@ -74,6 +76,10 @@ All notable changes to atomic-rollback are documented here.

## [0.3.2] - 2026-04-01

### Changed

- Internal architecture: all external tool output parsed through grammar-derived types at the boundary. Filesystem type comparisons use an enum instead of string matching.

### Fixed

- Subvolume names with spaces now parse correctly. The btrfs output parser used whitespace splitting which truncated paths containing spaces.
Expand All @@ -82,22 +88,18 @@ All notable changes to atomic-rollback are documented here.
- BLS root= parameter check accepts all kernel device formats (PARTUUID=, PARTLABEL=, /dev/). Previously only root=UUID= and root=/dev/ were accepted.
- ESP grub.cfg migration renders from the generator template instead of line surgery, eliminating the double-prefix bug class by construction.

### Changed
## [0.3.1] - 2026-03-31

- Internal architecture: all external tool output parsed through grammar-derived types at the boundary. Filesystem type comparisons use an enum instead of string matching.
### Changed

## [0.3.1] - 2026-03-31
- Installation via COPR is the only supported method. The crate was removed from crates.io (binary alone is insufficient without the hook and plugin).

### Fixed

- Kernel-install hook uses full binary path (/usr/bin/atomic-rollback). The bare command was not in RPM's scriptlet PATH, causing exit 127 on kernel upgrades.
- RPM spec rewritten for COPR vendored builds. The previous spec used %cargo_build which expects Fedora-packaged crates.
- COPR Makefile builds from cloned source with correct outdir contract.

### Changed

- Installation via COPR is the only supported method. The crate was removed from crates.io (binary alone is insufficient without the hook and plugin).

## [0.3.0] - 2026-03-30

### Added
Expand Down Expand Up @@ -141,8 +143,6 @@ All notable changes to atomic-rollback are documented here.

## [0.1.1] - 2026-03-29

Initial release.

### Added

- `check`, `migrate`, `rollback`, `snapshot` commands.
Expand All @@ -159,3 +159,4 @@ Initial release.
- All system-specific values (device ref, compression, subvol name) derived from fstab.
- Bootability predicate derived from the actual Fedora boot chain.
- RPM spec with kernel-install hook and dnf plugin.

65 changes: 65 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Contributing to atomic-rollback

Thanks for considering a contribution. This guide covers the essentials.

## Prerequisites

- Rust toolchain (`cargo`, `rustc`)
- Git
- For VM integration testing: [lima](https://lima-vm.io/) with a Fedora 43 template

## Build

```sh
cargo build --release
```

The workspace builds all member crates. The `atomic-rollback` binary lands at `target/release/atomic-rollback`.

## Test

```sh
cargo test --release
```

Tests run on Linux and macOS in CI. Production code uses Unix APIs only, so tests are skipped on Windows.

For VM integration testing of `atomic-rollback check` on a real Fedora environment, see `.github/workflows/ci.yml` (x86_64 lima job) for the canonical recipe.

## Commit format

[Conventional Commits](https://www.conventionalcommits.org/):

```
type(scope): lowercase imperative description
```

Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `style`, `perf`, `ci`

Examples:

```
feat(snapshot): auto-named rolling snapshots
fix(hook): transfer kernel-install hook ownership to migrate
ci: add cargo test job on ubuntu + macos matrix
```

## Pull requests

- All changes go through PRs; no exceptions
- Branch protection requires all CI checks to pass before merge
- Rebase merge only (linear history)

PR test plans are mandatory. Unchecked task-list items block merge. Complete tests before merging; removing items is not a valid escape.

## CHANGELOG fragments

Every PR that modifies `crates/atomic-rollback/src/**` requires a Fragment in `crates/changelog-core/src/lib.rs`. Structural, not commit-type-based — the gate reads the diff directly so the requirement cannot be bypassed by relabeling commits.

See [docs/standards/changelog-fragments.md](docs/standards/changelog-fragments.md) for the full contract, including when `Status::InternalOnly` is appropriate.

## Project standards

Detailed standards:

- [CHANGELOG fragments](docs/standards/changelog-fragments.md) — how to add fragments, when they're required, when InternalOnly is appropriate, how releases consume them
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 7 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
[package]
name = "atomic-rollback"
version = "0.4.0"
edition = "2024"
description = "Atomic system rollback for Fedora via Btrfs RENAME_EXCHANGE subvolume swap"
license = "GPL-3.0-only"
repository = "https://github.com/rocketman-code/atomic-rollback"
keywords = ["btrfs", "rollback", "fedora", "snapshot", "atomic"]
categories = ["command-line-utilities", "filesystem"]

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)', 'cfg(verus_only)', 'cfg(verus_keep_ghost)'] }

[dependencies]
libc = "0.2"
vstd = "=0.0.0-2026-03-29-0113"

[package.metadata.verus]
verify = true
[workspace]
resolver = "3"
members = [
"crates/atomic-rollback",
"crates/changelog-core",
"crates/changelog",
]
19 changes: 19 additions & 0 deletions crates/atomic-rollback/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "atomic-rollback"
version = "0.4.0"
edition = "2024"
description = "Atomic system rollback for Fedora via Btrfs RENAME_EXCHANGE subvolume swap"
license = "GPL-3.0-only"
repository = "https://github.com/rocketman-code/atomic-rollback"
keywords = ["btrfs", "rollback", "fedora", "snapshot", "atomic"]
categories = ["command-line-utilities", "filesystem"]

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)', 'cfg(verus_only)', 'cfg(verus_keep_ghost)'] }

[dependencies]
libc = "0.2"
vstd = "=0.0.0-2026-03-29-0113"

[package.metadata.verus]
verify = true
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions crates/changelog-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "changelog-core"
version = "0.1.0"
edition = "2024"
publish = false
description = "Source-of-truth and generator for atomic-rollback's CHANGELOG.md"
Loading
Loading