Skip to content
rlsbl.targets.protocol
On this page

Release target protocol defining the formal interface that all target implementations must satisfy for detection, versioning, and scaffolding.

#rlsbl.targets.protocol

#rlsbl.targets.protocol

Release target protocol defining the formal interface that all target implementations must satisfy for detection, versioning, and scaffolding.

#ReleaseTarget

Protocol defining a release target.

Targets handle version management, scaffolding templates, and optionally build/publish steps for a specific ecosystem (npm, pypi, go, codehome, docs, etc.)

#name

python
def name(self) -> str

Unique identifier for this target (e.g. 'npm', 'pypi', 'codehome').

#supports_publication_probe

python
def supports_publication_probe(self) -> bool

Whether publication_probe gives a real answer for this target.

#supports_cached_registry_probe

python
def supports_cached_registry_probe(self) -> bool

Whether cached_registry_probe gives a real answer for this target.

#release_materialization_policy

python
def release_materialization_policy(self) -> str

Whether a reconcile may recreate this target's missing release refs.

"materialize" -- recreating a released version's absent ref is a pure repair. "refuse-identity-transition" -- the target's tags ARE its published artifact, so a version whose published identity has since changed must not have its refs recreated under the new one.

#supports_read_name

python
def supports_read_name(self) -> bool

Whether read_name reads a real name for this target.

#supports_read_metadata

python
def supports_read_metadata(self) -> bool

Whether read_metadata reads real metadata for this target.

#supports_dev_install

python
def supports_dev_install(self) -> bool

Whether dev_install_command yields a spec for any mode.

#provides_ci_templates

python
def provides_ci_templates(self) -> bool

Whether this target ships a CI workflow template to scaffold.

#supports_import_analysis

python
def supports_import_analysis(self) -> bool

Whether rlsbl can read this target's sources to follow imports.

#supports_circular_dep_analysis

python
def supports_circular_dep_analysis(self) -> bool

Whether cycle detection is meaningful for this target's ecosystem.

#has_builtin_test_runner

python
def has_builtin_test_runner(self) -> bool

Whether this target ships a built-in test runner.

#supports_version_query

python
def supports_version_query(self) -> bool

Whether this target's registry answers a latest-version query.

#supports_name_claim

python
def supports_name_claim(self) -> bool

Whether a name can be claimed on this target's registry.

#supports_yank

python
def supports_yank(self) -> bool

Whether this target's registry offers a removal action.

#detect

python
def detect(self, dir_path: str) -> bool

Check if this target is present/applicable in the given directory.

#read_version

python
def read_version(self, dir_path: str) -> str

Read the current version from the target's manifest file.

#read_name

python
def read_name(self, dir_path: str, ctx) -> str | None

Read the project's package name from the manifest, or None.

#read_metadata

python
def read_metadata(self, dir_path: str) -> dict[str, str]

Read optional metadata (license, description) from the manifest.

#write_version

python
def write_version(self, dir_path: str, version: str, ctx) -> None

Write a new version to the target's manifest file (atomic).

#version_file

python
def version_file(self, dir_path: str | None=None) -> str | None

Filename that holds the version (e.g. 'package.json'), or None if inherited.

When dir_path is provided, implementations may resolve the filename dynamically (e.g. Deno choosing between deno.json and deno.jsonc).

#tag_format

python
def tag_format(self, version: str) -> str

Format the git tag for a release. Returns f'v{version}' by default.

#monorepo_tag_format

python
def monorepo_tag_format(self, name: str, version: str, path: str | None=None) -> str

Format the git tag for a monorepo release. Default: f'{name}@v{version}'.

#monorepo_tag_glob

python
def monorepo_tag_glob(self, name: str, path: str | None=None) -> str

Return a glob pattern matching all monorepo version tags. Default: f'{name}@v*'.

#companion_tags

python
def companion_tags(self, name: str, version: str, path: str | None=None) -> list[str]

Return additional tags to create alongside the primary release tag.

Ecosystems that require extra tags (e.g. Go module proxy tags) override this. The default returns no companion tags.

#expected_refs

python
def expected_refs(self, version: str, context)

Every git ref version owns: the primary tag, companions, aliases.

THE single authority for a released version's ref set. context is a :class:~rlsbl.targets.refs.RefContext; the return is an :class:~rlsbl.targets.refs.ExpectedRefs. The release flow creates and pushes exactly this set and the unpublished-refs check renders exactly this set against reality, so the two can never diverge.

Composed from the per-target facts above rather than overridden: no target implements this itself.

#format_version

python
def format_version(self, version: str) -> str

Translate a semver version into this ecosystem's version format.

The default is the identity, which is correct for npm, Go, Deno and most others. PyPI overrides it for PEP 440.

#normalize_package_name

python
def normalize_package_name(self, raw_name: str) -> str

Reduce a package name to the form this registry compares by.

PyPI folds runs of -_. to a single hyphen, npm removes them, Go compares the last path segment. The default lowercases.

#query_latest_version

python
def query_latest_version(self, name: str) -> dict

Ask this target's registry for the latest published version.

Returns a dict with status "found" (plus version), "not_found", or "error" (plus message). The default answers error naming the target, so "this ecosystem has no version API" is never mistaken for "the package is unpublished".

#claim_placeholder

python
def claim_placeholder(self, name: str, tmpdir: str) -> str

Publish a minimal placeholder package to reserve name.

Targets whose registry accepts a publish override this; the default raises, and claimable_targets() derives the accepted set from exactly this method.

#registry_display_name

python
def registry_display_name(self) -> str

How to spell this target's registry in user-facing output.

#template_dir

python
def template_dir(self) -> str | None

Absolute path to target-specific template directory, or None.

#shared_template_dir

python
def shared_template_dir(self) -> str | None

Absolute path to shared template directory, or None.

#template_vars

python
def template_vars(self, dir_path: str, ctx) -> dict[str, str]

Extract template placeholder values from the project.

#template_mappings

python
def template_mappings(self, ctx) -> list[dict[str, str]]

Target-specific template-to-output-path mappings.

#shared_template_mappings

python
def shared_template_mappings(self, ctx) -> list[dict[str, str]]

Shared template-to-output-path mappings.

#check_project_exists

python
def check_project_exists(self, dir_path: str) -> bool

Check if the target's project file exists (alias for detect).

#get_project_init_hint

python
def get_project_init_hint(self) -> str

Human-readable hint for initializing a project for this target.

#publication_probe

python
def publication_probe(self, dir_path: str, version: str, ctx=None)

Probe the registry to determine if a specific version is published.

Returns a PublicationProbeResult (PUBLISHED, UNPUBLISHED, or UNPROBEABLE). Default: UNPROBEABLE.

#cached_registry_probe

python
def cached_registry_probe(self, dir_path: str, version: str, ctx=None)

Ask the REGISTRY ITSELF whether a version is out in the world.

The second probe, for a target whose primary one answers from somewhere other than the registry. Two-valued: PUBLISHED or UNPROBEABLE, never UNPUBLISHED -- a lazily-indexed registry's silence is not evidence. Default: UNPROBEABLE. The fact is supports_cached_registry_probe.

#build

python
def build(self, dir_path: str, version: str, *, config: dict | None=None) -> None

Pre-publish build step (e.g. generate docs). No-op by default.

#dev_install_command

python
def dev_install_command(self, project_dir: str) -> dict[str, dict | None]

Return the local-install specs for this target, keyed by mode.

Used by rlsbl dev install to install/uninstall the project for local development. Returns a dict with two keys:

"global": spec for the global-install mode, where the project is installed as a globally-available tool or symlink (e.g. uv tool install -e ., npm link, go install). None if the target has no global-install concept.

"venv": spec for the local/venv-install mode, where dependencies are fetched into the project's own environment without exposing a global CLI (e.g. uv sync, npm install). None for targets that have no separate local-environment concept (e.g. Go, Zig, Swift).

Each spec dict has the shape: { "tool": "uv", "args": ["tool", "install", "-e", "."], "uninstall_args_template": ["tool", "uninstall", "{name}"], "purpose": "for editable Python install", }

Fields: tool: CLI tool that must be on PATH. args: argv passed to the tool to install. uninstall_args_template: argv list of templates passed to the tool to uninstall. Each entry may contain {name} (replaced with the project's package name) or {dir} (replaced with the project directory basename). None means uninstall is not supported for this mode of this target. purpose: human-readable string for the require_tool error message.

The returned dict may also carry an optional top-level "reason" key: a human-readable explanation for why the modes are None (e.g. "Go library: nothing to install"). rlsbl dev install surfaces it in the skip message instead of the generic "not yet supported" line.

#find_dead_modules

python
def find_dead_modules(self, root: str, *, exclude_dirs=None, suppress=frozenset()) -> list[tuple[str, str]]

Find source files or packages nothing else references.

Returns (path, reason) pairs, where reason is the ecosystem-specific explanation shown to the user. The default returns nothing: a target with no import scanner has no opinion, and supports_import_analysis is derived from this override.

#find_circular_dependencies

python
def find_circular_dependencies(self, root: str, *, exclude_dirs=None) -> list[list[str]]

Find import cycles within this target's sources.

Returns a list of cycles, each a list of module identifiers. The default returns nothing, and supports_circular_dep_analysis is derived from this override.

#run_tests

python
def run_tests(self, *, project_dir: str | None=None, workspace_root: str | None=None, skip_sync: bool=False, config: dict | None=None, check_timeout: int | None=None)

Run this target's built-in test suite.

Returns a SuiteRunOutcome. The default answers SKIPPED naming the target, so a project whose target ships no runner records a visible skip rather than a passing step for a suite that never ran.

#rewrite_mirror_identity

python
def rewrite_mirror_identity(self, clone_dir: str, mirror_remote: str) -> list

Rewrite mirror_identity_files onto the mirror's own identity.

Returns the repository-relative paths rewritten. Raises when the mirror's identity cannot be derived: a manifest still naming the monorepo is one that does not resolve from the mirror.

#yank

python
def yank(self, project_dir: str, version: str, tag: str, *, reason: str | None=None, dry_run: bool=False)

Remove a published version from this target's registry.

Returns a YankOutcome. The default answers UNSUPPORTED naming the target, so rlsbl release yank reports a target it cannot act on instead of passing over it.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search