repomatic.github.dev_release module

Sync a rolling dev pre-release on GitHub.

Maintains a single draft pre-release that mirrors the unreleased changelog section and always carries the latest successful dev binaries and Python package. The dev tag (e.g. v6.1.1.dev0) is force-updated to point to the latest main commit — no tag proliferation.

When the current version’s dev release already exists, it is edited (not deleted and recreated) so that previously uploaded assets — especially compiled binaries — survive pushes that skip binary compilation (e.g. documentation-only changes). The upload_release_assets() function deletes all existing assets before uploading new ones, preventing stale files from accumulating when the naming scheme changes. Stale dev releases from previous versions are always deleted.

Note

Dev releases are created as drafts so they remain mutable even when GitHub’s immutable releases setting is enabled. Immutability only blocks asset uploads on published releases — deletion still works. But because the workflow needs to upload binaries after creation, the release must stay as a draft throughout its lifetime to allow asset uploads. See CLAUDE.md § Immutable releases.

repomatic.github.dev_release.DEV_ASSET_PATTERNS = ('*.bin', '*.exe', '*.tar.gz', '*.whl')

Glob patterns for dev release assets.

Both halves are spelled as globs from the sets that define them: BINARY_ASSET_SUFFIXES for the compiled binaries, so a dev pre-release carries exactly the artifacts the release workflow downloads and scan-virustotal submits, and PYTHON_DIST_SUFFIXES for what a dev pre-release adds on top. Derived rather than re-listed, because a dev release advertising a different set of assets than the real one is the bug this pairing exists to prevent.

Note

Bare extensions (no repomatic- prefix) keep patterns generic so downstream repositories can reuse the same logic regardless of their package name.

repomatic.github.dev_release.sync_dev_release(changelog_path, version, repository, dry_run=True, asset_dir=None)[source]

Create or update the dev pre-release on GitHub.

Reads the changelog, renders the release body for the given version via build_expected_body(), then either edits the existing dev release or creates a new one. Stale dev releases from previous versions are always cleaned up.

Existing releases are edited (not deleted and recreated) to preserve assets like compiled binaries from previous successful builds. When asset_dir is provided, existing assets are deleted and new ones uploaded via upload_release_assets().

Parameters:
  • changelog_path (Path) – Path to changelog.md.

  • version (str) – Current version string (e.g. 6.1.1.dev0).

  • repository (str) – GitHub repository in owner/name form.

  • dry_run (bool) – If True, report without making changes.

  • asset_dir (Path | None) – Directory containing assets to upload. If None, no asset upload is performed.

Return type:

bool

Returns:

True if the release was synced (or would be in dry-run), False if the changelog section is empty.

repomatic.github.dev_release.upload_release_assets(tag, repository, asset_dir)[source]

Upload assets to a GitHub release.

Scans asset_dir for files matching DEV_ASSET_PATTERNS. If no matching files are found, returns immediately without modifying the release — this preserves existing assets for documentation-only pushes. When files are found, all existing assets are deleted first to prevent stale files from accumulating when the naming scheme changes.

Parameters:
  • tag (str) – Git tag name (e.g. v6.1.1.dev0).

  • repository (str) – GitHub repository in owner/name form.

  • asset_dir (Path) – Directory containing assets to upload.

Return type:

list[Path]

Returns:

List of uploaded file paths.

repomatic.github.dev_release.cleanup_dev_releases(repository, *, keep_tag=None)[source]

Delete stale dev pre-releases from GitHub.

Lists all releases and deletes any whose tag ends with .dev0, except keep_tag which is preserved so its assets (e.g. compiled binaries) survive. This handles stale dev releases left behind after version bumps. Silently succeeds if no dev releases exist or if individual deletions fail.

Parameters:
  • repository (str) – GitHub repository in owner/name form.

  • keep_tag (str | None) – Tag to preserve (e.g. v6.2.0.dev0). If None, all dev releases are deleted.

Return type:

None

repomatic.github.dev_release.delete_release_by_tag(tag, repository)[source]

Delete a release and its tag from GitHub.

Silently succeeds if the release does not exist or cannot be deleted. The outcome is returned rather than announced: this helper deletes any release, so only the caller knows what kind of release it just removed and can name it accurately.

Parameters:
  • tag (str) – Git tag name (e.g. v6.1.1.dev0).

  • repository (str) – GitHub repository in owner/name form.

Return type:

bool

Returns:

True when the release was deleted, False when it did not exist or could not be removed.