Skip to content
rlsbl.targets.refs
On this page

The value types expected_refs speaks: a version's primary tag, the companion tags its ecosystem requires, and the aliases its own records attribute to it.

#rlsbl.targets.refs

#rlsbl.targets.refs

The value types expected_refs speaks: what a version's ref set is, and everything needed to derive it.

expected_refs is the single authority for the git refs one released version owns -- its primary tag, the companion tags its ecosystem requires, and the alias tags this repository's own records attribute to it. Before it existed the answer was assembled independently at four sites (the release tag step, the dry-run preview, release undo, and the tag/Release checks), each of which knew about a different subset.

The three sources, and why each is where it is:

  • The primary tag is a naming decision. A releasable owns its own

tag_format, a monorepo package gets the target's monorepo_tag_format, and a standalone repository gets the target's tag_format. The context states which of the three applies; the target renders it.

  • Companion tags are an ecosystem requirement. Go's module proxy resolves

{path}/v{version}, so a releasable whose primary tag is not in that form needs one companion per publishing Go member. Both rules the collector carried -- skip when the primary tag is already Go-compatible, skip publish-suppressed members -- live in :meth:BaseTarget._companion_refs.

  • Recorded aliases are a repository FACT, read rather than recomputed, from

TWO sources that say the same kind of thing. A rename creates [email protected] beside the existing [email protected]; a conversion does the same at its boundary. Both write a boundary-alias event, and both tags address the same version, so both belong to that version's ref set. A version whose archive records shipped_as = "[email protected]" says the same thing from the other side -- that spelling is the one it actually shipped under -- and it is the only source for a version tagged before any alias event was written.

The version's expected PRIMARY ref stays the CURRENT scheme's spelling in both cases. That is what makes a renamed releasable's past versions repairable: rlsbl release reconcile sees the current spelling missing and mints it at the archive's release commit through its ordinary materialize path, while the old spelling is an explained ref that stands where it is.

When both sources cover one version and name DIFFERENT spellings, neither outranks the other and :class:ExpectedRefsError names both. A precedence rule here would pick one of two contradictory statements about which ref a published version owns.

#ExpectedRefsError

A version's ref set cannot be derived, because its sources disagree.

#RefContext

Everything expected_refs needs that is not the version itself.

Built by :func:ref_context rather than by hand, so the transition records to consult are derived in one place instead of at every call site.

Attributes:

  • repo_root: absolute path of the git repository (the workspace root in a

monorepo). Every relative path here is relative to it.

  • project_path: repo-relative directory of the project being released,

or None for a standalone repository whose project IS the root.

  • monorepo_name: the workspace project's name, when the release is a

monorepo package release. None for a standalone repository.

  • primary_tag_format: the releasable's tag_format

("{name}@v{version}") when a releasable owns the naming. None means the target's own tag format decides.

  • releasable_name: the releasable's name, substituted into

primary_tag_format.

  • member_package_paths: repo-relative member directories of the

releasable being released. None -- not an empty tuple -- means this is not a releasable release, which is exactly the condition under which companion tags were never collected.

  • releasable_config_dir: the releasable's state directory, for the

member-config inheritance the publish-mode rule reads.

  • transition_record_paths: the transition records that may carry aliases for this

project's versions, in read order.

  • releases_dirs: the release-archive directories whose shipped_as

fields may name a version's historical tag spelling, in read order. Derived from the same fork as transition_record_paths, so the two alias sources are always read for the same project.

  • tag_explanation_record_paths: the transition records consulted for facts

about the repository's TAG NAMESPACE -- "is this tag explained?" -- in read order. This project's own record PLUS the repository-scoped one, because a tag name is repository-unique and rlsbl transition record writes its non-version-tag declarations repository-wide. Wider than transition_record_paths on purpose: see :func:ref_context.

#ExpectedRefs

The full git ref set one released version owns.

The three groups are kept apart because they fail differently: a missing primary tag means the release never tagged, a missing companion means an ecosystem cannot resolve the module, and a missing alias means a recorded fact has no ref behind it. :attr:tags is the flat, deduplicated, primary-first order anything that creates or pushes them uses.

:attr:shipped_as_aliases is the SUBSET of :attr:aliases that came from an archive's shipped_as field rather than from a boundary-alias event -- the spelling a version ACTUALLY shipped under, from before a rename or a repository boundary moved. It is stated here so a caller that treats the ref set as "what this release created" can tell the two apart: rlsbl release undo deletes the refs the release it is undoing created, and a historical spelling predates that release. It stands where it is, neither moved nor deleted.

#tags

python
def tags(self) -> tuple[str, ...]

Every ref, primary first, in declaration order, deduplicated.

#ref_context

python
def ref_context(*, repo_root, project_path=None, monorepo_name=None, primary_tag_format=None, releasable_name=None, member_package_paths=None, releasable_config_dir=None) -> RefContext

Build a :class:RefContext, deriving which records to consult.

TWO different questions read transition records here, and they are scoped differently on purpose:

  • Version-keyed alias derivation (transition_record_paths, and the

releases_dirs whose shipped_as fields say the same kind of thing) consults exactly ONE record and ONE releases directory, following the project's release identity: a releasable's own state directory when a releasable owns the versioning, otherwise the project's standalone .rlsbl/. The two are derived from the same fork, so an alias event and a shipped_as field can only ever be read for the same project. The repository-scoped record is deliberately NOT consulted for this: an alias recorded there for a DIFFERENT releasable could carry the same version number as this one, and versions collide across releasables.

  • The tag-namespace question (tag_explanation_record_paths, read by

:mod:rlsbl.tag_explanation) consults that record AND the repository-scoped one. It is keyed by TAG NAME, which is unique across a repository, so nothing can collide -- and the operator's door for the declaration, rlsbl transition record --non-version-tag, writes the repository-scoped record for exactly that reason. Consulting only the releasable's record would leave the declaration unread and keep rlsbl release reconcile firing its tripwire on the tag it silences.

#_event_aliases

python
def _event_aliases(context: RefContext, version: str) -> list[tuple[str, str]]

(tag, record_path) for every boundary-alias tag carrying version.

A boundary-alias event names two tags -- the alias created and the tag it duplicates -- and BOTH address the version they carry, so both join that version's ref set. Which version a tag carries is :func:~rlsbl.tag_glob.parse_version_tag's answer, not a substring test: the three schemes it recognizes (v1.2.3, [email protected], path/v1.2.3) are exactly the ones rlsbl writes, and a tag under none of them carries no version rather than a guessed one.

A missing record yields nothing; a MALFORMED one raises, because :func:~rlsbl.transition_record.read_events is the read-for-use site and a record that cannot be read in full cannot be trusted in part.

#_shipped_as_aliases

python
def _shipped_as_aliases(context: RefContext, version: str) -> list[tuple[str, str]]

(tag, archive_path) for version's recorded historical spelling.

At most one entry: a version has one archive, and an archive records one shipped_as. The field is read tolerantly by :func:~rlsbl.tag_explanation.shipped_as_index -- an archive written before the strictspec gate is exactly the kind this field appears on, and refusing to look at it would defeat the purpose.

#recorded_alias_groups

python
def recorded_alias_groups(context: RefContext, version: str)

(every recorded alias, the shipped_as-derived ones) for version.

The full answer :func:recorded_aliases returns, plus which of those tags came from an archive's shipped_as rather than from a boundary-alias event. Both groups are aliases and both belong to the version's ref set; the split exists because a caller asking "which of these did the release I am undoing CREATE?" must not count a spelling that predates it.

#recorded_aliases

python
def recorded_aliases(context: RefContext, version: str) -> tuple[str, ...]

Alias tags this repository's own records attribute to version.

Two sources, read together: the boundary-alias events in the project's transition record, and the shipped_as field of the version's own release archive. Both state which spelling a version is addressable under besides the current scheme's, so both contribute to the same group.

They may agree, and either may be the only one present -- an archive predating alias events carries only shipped_as, a boundary alias created for a version whose archive says nothing carries only the event. When both cover this version and name DIFFERENT spellings, :class:ExpectedRefsError names both sources with both spellings: the two are contradictory statements about which ref a published version owns, and a precedence rule would silently pick one of them.

Search