diff --git a/.circleci/download_artifacts.py b/.circleci/download_artifacts.py index 809d9c3..ef4da20 100644 --- a/.circleci/download_artifacts.py +++ b/.circleci/download_artifacts.py @@ -7,13 +7,21 @@ import requests import time import os +import yaml import argparse from pathlib import Path circle_token = os.environ.get('CIRCLE_TOKEN') if not circle_token: - raise ValueError('CIRCLE_TOKEN environment variable not set') + cli_yml = Path.home() / ".circleci" / "cli.yml" + if cli_yml.exists(): + with open(cli_yml) as f: + data = yaml.safe_load(f) + if data: + circle_token = data.get("token") +if not circle_token: + raise ValueError('CIRCLE_TOKEN not set and no token found in ~/.circleci/cli.yml') parser = argparse.ArgumentParser(description='Download artifacts from CircleCI') parser.add_argument('build_num', type=str, help='Build number') diff --git a/.claude/commands/draft-release.md b/.claude/commands/draft-release.md new file mode 100644 index 0000000..3b9a68e --- /dev/null +++ b/.claude/commands/draft-release.md @@ -0,0 +1,95 @@ +--- +description: Generate GitHub release notes from docs/changelog.rst and create a draft release with artifacts +argument-hint: +model: sonnet +allowed-tools: Read, Write +--- + +# Task + +Create a draft GitHub release for zrepl version $ARGUMENTS and upload release artifacts. + +## Inputs + +Here is the current changelog RST source: + +!`cat docs/changelog.rst` + +## Instructions + +### Phase 1: Pre-flight Sanity Checks + +Before creating the release, verify that the previous build steps have been completed. +Run these checks using bash commands: + +```bash +test -d artifacts/release && echo "✓ artifacts/release directory exists" || echo "✗ Missing artifacts/release directory - run: make download-circleci-release JOB_NUM=" +test -f artifacts/release/sha512sum.txt && echo "✓ sha512sum.txt exists" || echo "✗ Missing sha512sum.txt - run: make download-circleci-release JOB_NUM=" +test -f artifacts/release/sha512sum.txt.asc && echo "✓ sha512sum.txt.asc exists (signed)" || echo "✗ Missing signature - run: make verify-and-sign" +ls artifacts/release/zrepl-* >/dev/null 2>&1 && echo "✓ Release artifacts present" || echo "✗ No release artifacts found" +``` + +If ANY check fails (shows ✗), **STOP** and print the error messages. Do not proceed with release creation. + +### Phase 2: Generate Release Notes + +2. Extract the changelog section for version $ARGUMENTS from the RST source above. +3. Extract all GitHub usernames mentioned in that changelog section (look for @username patterns or other contributor attributions). +4. Write a short "Highlights" section (2-4 bullets) summarizing the most impactful user-facing changes in plain language. Do NOT include commit/issue links. +5. Write a "Contributors" thank you line for all GitHub users found in step 3. Format as: "Thanks to @user1, @user2, and @user3 for their contributions to this release!" +6. Write a "Breaking Changes" section. Convert RST formatting to plain Markdown text (no commit or issue links): + - `:commit:\`abc123\`` → just remove it or describe what it changed + - `:issue:\`123\`` → just remove it or describe the issue in plain text + - `:ref:\`display text \`` → just use `display text` as plain text + - `:repomasterlink:\`path\`` → just mention the path without a link + - RST inline code ` ``code`` ` → markdown `` `code` `` + - RST links `` `text `_ `` → just use `text` without the link + If there are no breaking changes, write "No breaking changes. vX.Y.Z-1 is interoperable with vX.Y.Z." (fill in actual versions). +7. Assemble the full release notes using the template below. **IMPORTANT**: Do NOT include a detailed changelog list. Only include what is specified in the template. +8. Write the result to `artifacts/release-notes.md`. +9. Validate all links in the release notes by checking HTTP status codes with curl: + - Extract all URLs from the markdown file + - Check each URL with `curl -sI -w "%{http_code}" -o /dev/null ` + - Report any broken links (non-200 status codes) + - If any links are broken, stop and notify the user before creating the release + +### Phase 3: Create Draft Release and Upload Artifacts + +10. Run: `gh release create $ARGUMENTS --title "$ARGUMENTS" --notes-file artifacts/release-notes.md --draft` +11. Upload all artifacts: `gh release upload $ARGUMENTS artifacts/release/*` +12. Verify upload succeeded: `gh release view $ARGUMENTS --json assets --jq '.assets | length'` +13. Print the release URL and confirm that artifacts were uploaded successfully. + +## Template + +``` +The full changelog is [on the docs site](https://zrepl.github.io/changelog.html). + +## Highlights + +{2-4 bullet plain-language summary of the most impactful changes} + +{Thank all GitHub users mentioned in the changelog, e.g., "Thanks to @user1, @user2, and @user3 for their contributions to this release!"} + +## Breaking Changes + +{breaking changes, or "No breaking changes."} + +## New Users + +We provide [quick-start guides](https://zrepl.github.io/quickstart.html) for different usage scenarios. +We also recommend studying the [overview section of the configuration chapter](https://zrepl.github.io/configuration/overview.html). + +## Testing & Upgrading + +* Read the [Changelog](https://zrepl.github.io/changelog.html) +* [Run the platform tests](https://zrepl.github.io/usage.html#platform-tests) on a test system. +* Download & deploy the `zrepl` binary / distro package. + +## Donations + +zrepl is a spare-time project primarily developed by [Christian Schwarz](https://cschwarz.com). +Express your support through a donation to keep maintenance and feature development going. + +[![Support me on Patreon](https://img.shields.io/badge/dynamic/json?color=yellow&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F3095079)](https://patreon.com/zrepl) [![Donate via GitHub Sponsors](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=yellow)](https://github.com/sponsors/problame) [![Donate via Liberapay](https://img.shields.io/liberapay/patrons/zrepl.svg?logo=liberapay)](https://liberapay.com/zrepl/donate) [![Donate via PayPal](https://img.shields.io/badge/donate-paypal-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R5QSXJVYHGX96) +``` diff --git a/.claude/commands/update-supporters.md b/.claude/commands/update-supporters.md new file mode 100644 index 0000000..7632f56 --- /dev/null +++ b/.claude/commands/update-supporters.md @@ -0,0 +1,149 @@ +--- +description: Update supporters list from git history and changelog contributors +argument-hint: +model: sonnet +--- + +# Task + +Automatically update `docs/supporters.rst` by extracting GitHub contributors from: +1. Git commit history (code contributors) +2. `docs/changelog.rst` (all mentioned GitHub users) + +## Arguments + +Specify a time range or release range to filter contributors: + +**Time range examples:** +- `--since="2024-01-01"` - Contributors since a specific date +- `--since="1 year ago"` - Contributors in the last year +- `--since="6 months ago"` - Contributors in the last 6 months + +**Release range examples:** +- `v0.6.0..HEAD` - Contributors between v0.6.0 and current HEAD +- `v0.6.0..v0.7.0` - Contributors between two releases +- `--all` - All contributors (default if no argument provided) + +## Instructions + +### Step 1: Extract Contributors from Git History + +Parse the arguments to determine the git log range: +- If `--since="..."` is provided, use: `git log --since="..." --format='%aN <%aE>' | sort -u` +- If a commit range like `v0.6.0..HEAD` is provided, use: `git log v0.6.0..HEAD --format='%aN <%aE>' | sort -u` +- If `--all` or no argument, use: `git log --format='%aN <%aE>' | sort -u` + +Parse the output to identify: +- GitHub users (look for @users.noreply.github.com emails or known GitHub patterns) +- Real names of code contributors +- Filter out the main maintainer (Christian Schwarz / problame) as they're already credited + +### Step 2: Extract GitHub Users from Changelog + +Read `docs/changelog.rst` and extract all GitHub usernames mentioned: +- Pattern: `` `@username `_ `` +- Pattern: `@username` in text +- Collect all unique usernames + +### Step 3: Categorize Contributors + +For each contributor, determine their tier: + +**Code contributors** (`|supporter-code|`): +- Anyone who appears in git history as a commit author +- Prioritize those with substantial contributions (multiple commits) + +**Other contributors** (keep existing `|supporter-gold|` and `|supporter-std|`): +- Keep all existing gold/std supporters as-is (monetary supporters) +- Only update code contributors + +### Step 4: Read Current Supporters File + +Read `docs/supporters.rst` and parse existing entries to: +- Preserve all `|supporter-gold|` entries (monetary supporters - don't touch these) +- Preserve all `|supporter-std|` entries (monetary supporters - don't touch these) +- Preserve ALL existing `|supporter-code|` entries in their current order +- Extract list of existing code contributor names/GitHub usernames to avoid duplicates + +### Step 5: Identify Contributors to Promote/Add + +**IMPORTANT: Find contributors in the range, and either ADD them (if new) or MOVE them up (if recurring).** + +For each GitHub user found in changelog or git history for the specified range: +1. Check if they're already listed as `|supporter-code|` in the existing file +2. Categorize them: + - **NEW**: Not currently in the file → will be added + - **RECURRING**: Already in the file with new contributions in this range → will be moved to top + - **UNCHANGED**: Not in this range → stays in original position +3. For both NEW and RECURRING contributors: + - Get their latest contribution date from the range: `git log --author="email" --format="%ai" -1` + - Format as: `* |supporter-code| `Name `_` + - Use their real name if available from git history, otherwise use GitHub username +4. Sort NEW + RECURRING contributors together by their contribution date (newest first) +5. Keep list of UNCHANGED contributors in their original order + +**The final order will be: [NEW + RECURRING from range, sorted newest first] + [UNCHANGED in original order]** + +### Step 6: Generate Updated File + +Create the updated supporters list: +- Keep all `|supporter-gold|` entries unchanged and in their exact positions +- Keep all `|supporter-std|` entries unchanged and in their exact positions +- For `|supporter-code|` entries, reorganize as follows: + 1. **Top section**: Contributors from the analyzed range (NEW + RECURRING), sorted newest first + 2. **Bottom section**: UNCHANGED contributors (not in this range), in their original order +- Update the spacer comment showing the range that was just processed: + ```rst + .. + ↓ claude --permission-mode default /update-supporters + ``` + Replace `` with the actual argument passed (e.g., `v0.6.1..v0.7.0`) + This comment should appear immediately before the first code contributor entry + If there's an existing comment in this format, replace it; otherwise add it +- Maintain all other file structure and RST formatting, including the "Before /update-supporters" marker comment + +### Step 7: Apply Changes + +- Use Edit tool to update the `docs/supporters.rst` file +- Display summary of changes made: + - Time/release range analyzed + - Number of contributors found + - New code contributors added + - Final counts by tier + +## Implementation Notes + +- **DO NOT modify, remove, or re-sort** any `|supporter-gold|` or `|supporter-std|` entries +- **For code contributors**: ADD new ones and MOVE UP recurring ones (those with contributions in the analyzed range) +- When extracting from git history, focus on substantial contributors (filter out single-commit or trivial changes if needed) +- **CRITICAL:** Contributors from the analyzed range go to the top + - Use `git log --author="" --format="%ai" -1` to get latest contribution date in the range + - Both NEW and RECURRING contributors from this range are sorted together (newest first) and placed at the TOP + - UNCHANGED contributors (not in this range) stay in their original relative order below + - This creates a chronological history: most recent release's contributors appear first +- Add/update the spacer comment showing the processed range +- Handle edge cases: + - Contributors mentioned in changelog but not in git history (external contributors, reporters) + - Contributors in git history but not mentioned in changelog (decide whether to include) + +## Example Output + +``` +Analyzing contributors for range: v0.6.0..v0.7.0 +- Found 8 code contributors in git history +- Found 12 GitHub users mentioned in changelog.rst (v0.7.0 section) +- Current supporters.rst has 8 gold, 12 std, 10 code contributors + +New contributors: ++ Jane Doe (@janedoe) ++ John Smith (@jsmith) + +Promoted (recurring) contributors: +↑ Alice Developer (@alice) - new contributions in v0.6.0..v0.7.0 + +Updated docs/supporters.rst: +- Gold supporters: 8 (unchanged) +- Std supporters: 12 (unchanged) +- Code contributors: 10 → 12 +- Promoted to top: 3 contributors (2 new + 1 recurring) +``` diff --git a/Makefile b/Makefile index 2bf3506..29e93b9 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ release-docker-mkcachemount: endif ##################### PRODUCING A RELEASE ############# -.PHONY: release wrapup-and-checksum check-git-clean sign clean ensure-release-toolchain +.PHONY: release wrapup-and-checksum check-git-clean verify-and-sign clean ensure-release-toolchain ensure-release-toolchain: # ensure the toolchain is actually the one we expect @@ -211,7 +211,8 @@ tag-release: test -n "$(ZREPL_TAG_VERSION)" || exit 1 git tag -u '328A6627FA98061D!' -m "$(ZREPL_TAG_VERSION)" "$(ZREPL_TAG_VERSION)" -sign: +verify-and-sign: + cd "$(ARTIFACTDIR)/release" && sha512sum -c sha512sum.txt gpg -u '328A6627FA98061D!' \ --armor \ --detach-sign $(ARTIFACTDIR)/release/sha512sum.txt @@ -222,7 +223,7 @@ clean: docs-clean download-circleci-release: rm -rf "$(ARTIFACTDIR)" mkdir -p "$(ARTIFACTDIR)/release" - python3 .circleci/download_artifacts.py --prefix 'artifacts/release/' "$(BUILD_NUM)" "$(ARTIFACTDIR)/release" + python3 .circleci/download_artifacts.py --prefix 'artifacts/release/' "$(JOB_NUM)" "$(ARTIFACTDIR)/release" ##################### MULTI-ARCH HELPERS ##################### diff --git a/README.md b/README.md index 0352fcf..4386b4d 100644 --- a/README.md +++ b/README.md @@ -93,19 +93,40 @@ The procedure to issue a release is as follows: * Prepare the release (as a PR to `master`): * Finalize `docs/changelog.rst` for the release. + * Update the supporters list based on contributors for this release: + ```bash + claude --permission-mode default '/update-supporters v0.6.1..v0.7.0' + ``` + Replace version tags with the appropriate range for your release. * Merge the PR. Docs are auto-published to zrepl.github.io on merge. * Tag the release: * Git tag the release on the `master` branch (e.g., `vMAJOR.MINOR.0`). + ``` + make tag-release ZREPL_TAG_VERSION=v0.7.0 + ``` * Push the tag. * Build and publish: - * Run the `release` pipeline (trigger via CircleCI UI). - * Download artifacts: `make download-circleci-release BUILD_NUM=` - * Create GitHub release and upload artifacts: - ```bash - gh release create vX.Y.Z --title "vX.Y.Z" --notes "See changelog" --draft - gh release upload vX.Y.Z artifacts/release/* + * Run the `release` pipeline against the `master` branch (trigger via CircleCI UI). + This URL: https://app.circleci.com/pipelines/github/zrepl/zrepl?branch=master. + Example pipeline: https://app.circleci.com/pipelines/github/zrepl/zrepl/8547 + * Download artifacts using this handy makefile target. + Note: `JOB_NUM` must be the **job number** from the `release-upload` job, **not the pipeline number**. + Find it via: pipeline → `release` workflow → `release-upload` job number. + Example URL: https://app.circleci.com/pipelines/github/zrepl/zrepl/8547/workflows/65feb2c9-15d7-46ab-a551-46d62a5769b0/jobs/66079/steps + ``` - * Review the draft release, edit the changelog, then publish. + make download-circleci-release JOB_NUM=66079 + ``` + * Verify checksums and sign the checksums file: + ``` + make verify-and-sign + ``` + * Create GitHub draft release and upload artifacts: + ```bash + claude --permission-mode default '/draft-release v0.7.0' + ``` + This command will verify that artifacts are ready, create the draft release, and upload all artifacts. + * Review the draft release on GitHub, then publish. * Add the .rpm and .deb files to the official zrepl repos. * Code for management of these repos: https://github.com/zrepl/package-repo-ops (private repo at this time) * Update docs version list: diff --git a/docs/changelog.rst b/docs/changelog.rst index 6a7ef72..b66fdde 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -26,10 +26,15 @@ Changelog * |bugfix| Detect duplicate and internal job names at config parse time. zrepl will now refuse to load a config with duplicate or internally reserved job names, instead of failing later at daemon startup. (:commit:`860a9be`). +* |bugfix| Fix ``last_n`` keep rule behavior (:commit:`ebc46cf`, thanks, `@dsh2dsh `_). +* |bugfix| Allow different jobs to use the same ``root_fs`` for sinks (:commit:`27012e5`, thanks, `@dsh2dsh `_). * |docs| Add installation instructions for openSUSE (:commit:`40c4827`, thanks, `@findesgh `_). * |docs| Improve documentation on ``send_properties`` configuration and NFS/SMB considerations (:commit:`b5d8538`, thanks, `@Malvineous `_). * |docs| Warn more prominently about the risks of the ``not_replicated`` keep rule (:commit:`affe00a`, thanks, `@wxiaoguang `_). * |docs| Improve TLS/EasyRSA setup instructions (:commit:`e524b60`, thanks, `@alorimer `_). +* |docs| Remove dead Bountysource link (:commit:`440b074`, thanks, `@lpulley `_). +* |docs| Fix missing newline in compile-from-source documentation (:commit:`e2fcf9f`, thanks, `@Raupinger `_). +* |docs| Fix apt repository installation snippet (:commit:`8305367`, thanks, `@fermino `_). * |docs| ``zrepl.github.io``: auto-publish from ``master`` branch, retire ``stable`` branch (:commit:`4f950bb`). * |maint| ``zrepl status``: switch from the unmaintained ``cview`` fork back to the actively maintained ``tview`` library (:commit:`d7ede3f`). * |maint| Update to Go 1.25 toolchain with Go 1.24 language level & Go dependencies diff --git a/docs/supporters.rst b/docs/supporters.rst index 39781b2..9bbc352 100644 --- a/docs/supporters.rst +++ b/docs/supporters.rst @@ -32,6 +32,25 @@ We would like to thank the following people and organizations for supporting zre
+.. + The list below is (roughly) sorted by date of latest contribution, newest first. + + .. + ↓ claude --permission-mode default /update-supporters v0.6.1..v0.7.0 + +* |supporter-code| `Bakhtiyar Neyman `_ +* |supporter-code| `Zeyad Tamimi `_ +* |supporter-code| `Orsiris de Jong `_ +* |supporter-code| `Martin Glatzle `_ +* |supporter-code| `Andrew Lorimer `_ +* |supporter-code| `Adam Nielsen `_ +* |supporter-code| `wxiaoguang `_ +* |supporter-code| `Logan Pulley `_ +* |supporter-code| `Florian `_ +* |supporter-code| `Fermín Olaiz `_ +* |supporter-code| `Denis Shaposhnikov `_ + .. + ↓ Before /update-supporters * |supporter-gold| `Hostsharing eG – die Hosting-Genossenschaft `_ * |supporter-std| `Max Christian Pohle `_ * |supporter-gold| Prominic.NET, Inc.