repomatic.github.releases module¶
GitHub Releases API client.
The single home for reading GitHub Releases: raw cached API access (tags,
versions), tag-to-version extraction, tag-to-SHA resolution, and the
range-to-release-notes fetch every dependency report shares
(fetch_github_release_notes()). The repomatic.deps.pins adapters
and the repomatic.deps.report release-notes helper build on top of these
reads, and so do the review steps of the prepare-release checklist
(release_review_steps()).
The write helpers live here too, so every release writer carries the same
arguments and failure contract: edit_release_notes(), the shared
gh release edit path behind the dev pre-release sync
(repomatic.github.dev_release) and the changelog-to-release-notes sync
(repomatic.github.release_sync), create_release() behind the
release draft, and upload_release_files() behind every asset upload.
- repomatic.github.releases.GITHUB_API_RELEASES_URL = 'https://api.github.com/repos/{owner}/{repo}/releases'¶
GitHub API URL for fetching all releases for a repository.
- repomatic.github.releases.GITHUB_API_TAG_REF_URL = 'https://api.github.com/repos/{owner}/{repo}/git/ref/tags/{tag}'¶
GitHub API URL for resolving a tag name to its git object.
- repomatic.github.releases.GITHUB_API_TAG_OBJECT_URL = 'https://api.github.com/repos/{owner}/{repo}/git/tags/{sha}'¶
GitHub API URL for dereferencing an annotated tag object to its commit.
- repomatic.github.releases.RELEASES_PAGE_SIZE = 100¶
Releases per page of a listing, the most the REST API serves.
- repomatic.github.releases.RELEASE_TAGS_CACHE = 'github-release-tags'¶
HTTP-cache namespace of the release map
get_release_tags()reads.
- repomatic.github.releases.RELEASE_NOT_FOUND_MARKER = 'release not found'¶
What
gh release editwrites to its error output for a tag with no release.
Bases:
RuntimeErrorRaised when the GitHub Releases API call could not complete cleanly.
Signals a transient failure (network error, timeout, JSON parse error, or pagination breaking mid-stream) where the result cannot safely be treated as “no releases.”
Callers that drive destructive operations (rewriting the changelog, deleting tags, etc.) must catch this and refuse to act, rather than silently rewriting state with a corrupted or empty view of release history.
- class repomatic.github.releases.GitHubRelease(date, body)[source]¶
Bases:
NamedTupleRelease metadata for a single version from GitHub.
Create new instance of GitHubRelease(date, body)
- class repomatic.github.releases.ReleaseAsset(name, size, sha256, download_url)[source]¶
Bases:
NamedTupleA single downloadable asset attached to a GitHub release.
Create new instance of ReleaseAsset(name, size, sha256, download_url)
- class repomatic.github.releases.ReleaseWithAssets(tag, date, draft, prerelease, assets, html_url='')[source]¶
Bases:
NamedTupleFull release metadata including its assets and visibility flags.
Create new instance of ReleaseWithAssets(tag, date, draft, prerelease, assets, html_url)
- assets: tuple[ReleaseAsset, ...]¶
Assets attached to the release, in API order.
- html_url: str¶
Browser URL of the release page.
For a draft release this is the only resolvable link: drafts have no public
releases/tag/<tag>URL, so GitHub serves them at an unguessablereleases/tag/untagged-<hash>path exposed only in this field. The prepare-release PR body links the rolling dev pre-release through it (seedev_release_url_and_previous_version()).
- repomatic.github.releases.get_release_tags(repo_url, *, force_refresh=False)[source]¶
Get every published release, keyed by its raw, unstripped tag name.
Fetches every release page on a cache miss, and caches a non-empty map under
RELEASE_TAGS_CACHE. Every tag scheme is kept, so a caller extracts the version with its own pattern: lychee tagslychee-v…, biome tags@biomejs/biome@…. Usespublished_at(falling back tocreated_at) for the date.A draft is left out. It is visible to a maintainer’s token alone, so a map holding it would answer differently per token, and a draft has no published notes to compare or link to.
- Parameters:
- Return type:
- Returns:
Dict mapping raw tag names to
GitHubReleasetuples. Empty only when the repository has no published release or repo_url does not parse to anowner/repopair.- Raises:
GitHubReleasesUnavailable – When any page fetch fails or returns unparsable JSON. An empty return value means “the repo has no releases”; a raised exception means “the answer is unknown.”
- repomatic.github.releases.get_github_releases(repo_url, *, force_refresh=False)[source]¶
Get the published releases tagged
vX.Y.Z, keyed by bare version.The
get_release_tags()map, narrowed to thev-prefixed tags with the prefix stripped.- Parameters:
repo_url (
str) – Repository URL (likehttps://github.com/user/repo).force_refresh (
bool) – Ignore any cached map and re-fetch: seeget_release_tags().
- Return type:
- Returns:
Dict mapping version strings to
GitHubReleasetuples.- Raises:
GitHubReleasesUnavailable – When any page fetch fails or returns unparsable JSON.
- repomatic.github.releases.get_releases_with_assets(repo_url)[source]¶
Get every release with its assets, visibility flags, and digests.
Deliberately uncached, unlike
get_github_releases(): the main consumer issync-binaries, which runs minutes after a release is published and must see the assets that were just uploaded. A cached view would regenerate the binaries page from a pre-release snapshot.- Parameters:
repo_url (
str) – Repository URL (e.g.https://github.com/user/repo).- Return type:
- Returns:
One
ReleaseWithAssetsper release (drafts and pre-releases included, for the caller to filter), in API order (newest first). Empty when the repository has no releases or repo_url does not parse to anowner/repopair.- Raises:
GitHubReleasesUnavailable – When any page fetch fails or returns unparsable JSON.
- repomatic.github.releases.dev_release_url_and_previous_version(repo_url, version)[source]¶
Look up the two release references the prepare-release PR body links to.
A single
get_releases_with_assets()fetch yields both:Dev pre-release URL: the
html_urlof the draft pre-release whose version shares version’s release segment (the rollingv{version}.dev0draft). Drafts are visible only to authenticated maintainers, so an unauthenticated or token-less caller getsNonehere even when the draft exists.Previous version: the highest final release (draft, pre-release, and
.devtags excluded) already published. At prepare-release time the tag for version does not exist yet, so this is the release the new one supersedes, used for thev{previous}...maincomparison link.
- Parameters:
- Return type:
- Returns:
An
(dev_release_url, previous_version)pair. Either element isNonewhen its release cannot be found or the API is unavailable, so the caller degrades each list item independently.
- repomatic.github.releases.release_review_steps(repo_url, version)[source]¶
Build the two optional review steps of the prepare-release checklist.
The
How-to releaselist opens with two review steps that render only when their GitHub data is reachable, so the checklist degrades to the bare merge instructions offline (or when the dev pre-release is disabled):Dev pre-release review: links the rolling
v{version}.dev0draft through itshtml_url(drafts have no public tag URL). Omitted when no such draft is visible.Full-changes review: links the
v{previous}...maincomparison. Omitted when no prior release exists to compare against.
Both come from a single
dev_release_url_and_previous_version()lookup. Each returned string is a complete ordered-list line ending in a newline (or empty), written with a1.marker so the surrounding lazily numbered list renumbers correctly however many steps survive.
- repomatic.github.releases.resolve_tag_to_sha(repo_url, tag)[source]¶
Resolve a release tag to its 40-character commit SHA.
Reads the tag’s git reference. An annotated tag points at an intermediate tag object, dereferenced one hop to the commit it targets; a lightweight tag points straight at the commit.
- repomatic.github.releases.extract_version(tag, tag_pattern)[source]¶
Extract a version from a GitHub release tag.
- repomatic.github.releases.edit_release_notes(tag, repository, body, *, title='')[source]¶
Edit a release’s notes (and optionally its title) in place.
The one
gh release editpath shared by every release writer, so the dev pre-release sync and the changelog-to-release-notes sync carry the same arguments and failure contract. Assets are never touched.- Parameters:
- Return type:
- Returns:
Truewhen the edit landed,Falsewhen no release carries tag.- Raises:
GhCommandError – When the edit failed for any other reason. A transient failure is not an absence: a caller reading it as one would create a second release for the same tag.
- repomatic.github.releases.release_exists(tag, repository)[source]¶
Whether repository has a release for tag, draft or published.
- Parameters:
- Raises:
GhCommandError – When the lookup fails for another reason than an absent release. A transient failure is not an absence: a caller reading it as one would create a second release for the same tag.
- Return type:
- repomatic.github.releases.create_release(tag, repository, *, target, notes, files=())[source]¶
Create the release draft of tag, unless a release already carries it.
The draft is titled after its tag, targets target, and starts with files attached. It stays a draft: assets can still be added to it, and publishing it is the last step of the release, after which an immutable release locks them.
Idempotent: a release that already exists, from an earlier attempt of the same run, is left as it is.
- Parameters:
- Return type:
- Returns:
Truewhen the draft was created,Falsewhen a release already carried tag.- Raises:
GhCommandError – When the lookup or the creation fails.
- repomatic.github.releases.upload_release_files(tag, repository, files)[source]¶
Attach files to the release of tag, replacing same-named assets.
The one upload path of every release writer: the release draft of the release workflow and the rolling dev pre-release alike. An upload to a published immutable release fails, so the release workflow attaches every asset while its release is still a draft.
- class repomatic.github.releases.ReleaseNotesRequest(name, repo_url, old, new, tag_pattern=None, skip=frozenset({}))[source]¶
Bases:
NamedTupleOne version bump whose GitHub release notes a report shows.
Create new instance of ReleaseNotesRequest(name, repo_url, old, new, tag_pattern, skip)
- old: str¶
The bare version the bump leaves. Empty for a new dependency, whose notes are those of
newalone.
- repomatic.github.releases.in_bump_range(version, old, new)[source]¶
Whether the notes of a bump from old to new cover version.
The half-open range
(old, new]. A pre-release inside it is left out, the way the sweep that picks new leaves it out. new itself is kept, even as a pre-release. With no old, the range is new alone.
- repomatic.github.releases.fetch_github_release_notes(requests)[source]¶
Fetch the GitHub release notes of a batch of version bumps.
For each request, lists the releases of the repository once (a cached call, often warm from a prior candidate sweep) and keeps those
in_bump_range()covers, oldest first. A repository costs one listing per page ofRELEASES_PAGE_SIZEreleases, however many versions the bump spans. Non-GitHub datasources (npm, PyPI workflow literals) make no request here and render no notes.- Parameters:
requests (
Iterable[ReleaseNotesRequest]) – One request per bump.- Return type:
- Returns:
A dict mapping names to
(repo_url, versions)tuples, whereversionsholds a(tag, body)pair per release. Only names with at least one non-empty release body are included.
