On this page
File management for JSONL changelog files: reading, writing, appending entries, version finalization, stale-entry detection, and path resolution.
#rlsbl.changelog.files
#rlsbl.changelog.files
File management layer for JSONL changelog files including reading, writing, appending entries, and path resolution for .rlsbl directories.
#_parse_semver
def _parse_semver(filename: str) -> tuple[int, int, int] | NoneExtract (major, minor, patch) from a versioned filename, or None.
#get_changes_dir
def get_changes_dir(project_path: str) -> strReturn the path to .rlsbl/changes/ inside the project.
#changes_dir_exists
def changes_dir_exists(project_path: str) -> boolCheck if .rlsbl/changes/ exists in the project.
#list_versioned_files
def list_versioned_files(changes_dir: str) -> list[tuple[str, str]]List all x.y.z.jsonl files, sorted by semver (newest first).
Returns (version_string, filepath) pairs.
#read_unreleased
def read_unreleased(changes_dir: str) -> list[ChangelogEntry]Read unreleased.jsonl and return entries. Empty list if file missing.
#append_entry
def append_entry(changes_dir: str, entry: ChangelogEntry) -> NoneAppend one entry to unreleased.jsonl atomically.
Writes the serialized line to a temp file, then appends it to the target. Creates the changes directory and unreleased.jsonl if they don't exist.
#append_entry_to_version
def append_entry_to_version(changes_dir: str, version: str, entry: ChangelogEntry) -> NoneAppend one entry to a versioned JSONL file (e.g., 0.39.0.jsonl).
The caller is responsible for unlocking/re-locking the file if it is read-only.
#_append_entry_to_file
def _append_entry_to_file(target: str, entry: ChangelogEntry) -> NoneAppend one entry to any JSONL file atomically.
Writes the serialized line to a temp file, then appends it to the target. Creates parent directories if they don't exist.
#_warn_stale_entries
def _warn_stale_entries(src: str, tag_glob: str) -> NoneWarn on stderr for entries in unreleased.jsonl referencing out-of-range commits.
In monorepo mode, an entry whose commits all sit before the project's last tag is stale — typically left over from a sibling project's release. We emit a warning per stale entry but do not strip them (warn-only).
#finalize_version
def finalize_version(changes_dir: str, version: str, tag_glob: str | None=None) -> NoneRename unreleased.jsonl to x.y.z.jsonl and create a fresh unreleased.jsonl.
Sets the versioned file read-only (chmod 0o444). Raises FileNotFoundError if unreleased.jsonl doesn't exist.
When tag_glob is provided (monorepo mode), inspects each entry in unreleased.jsonl before the rename and warns on stderr for any whose commits fall outside the current project's unreleased range. Warn-only: the stale entries are not stripped.
#unfinalize_version
def unfinalize_version(changes_dir: str, version: str) -> list[str]Reverse a finalize_version: restore x.y.z.jsonl back to unreleased.jsonl.
- Makes the versioned file writable.
- Renames it to unreleased.jsonl.
- Deletes the per-version .md file if present.
- Returns the list of changed file paths (for committing).
Returns an empty list if the versioned file doesn't exist.
#is_read_only
def is_read_only(path: str) -> boolCheck if a file has no write permissions (for any user class).