System Overview¶
High-level architecture of releasio.
Component Architecture¶
%%{init: {'theme': 'neutral'}}%%
graph TB
subgraph User["User Interface"]
CLI[CLI Commands]
GHA[GitHub Action]
end
subgraph Core["Core Engine"]
VP[Version Parser]
VC[Version Calculator]
CP[Commit Parser]
CG[Changelog Generator]
end
subgraph VCS["Version Control"]
GIT[Git Repository]
TAG[Tag Manager]
end
subgraph Remote["Remote Services"]
GH[GitHub API]
PYPI[PyPI Registry]
end
subgraph Config["Configuration"]
CL[Config Loader]
PM[Pydantic Models]
end
CLI --> CL
GHA --> CL
CL --> PM
CLI --> VP
CLI --> VC
CLI --> CP
CLI --> CG
VP --> GIT
VC --> CP
CP --> GIT
CG --> GIT
CG --> TAG
CLI --> GH
CLI --> PYPI
GH --> TAG
Directory Structure¶
src/releasio/
├── cli/ # Command-line interface
│ ├── app.py # Typer app setup
│ ├── check.py # check command
│ ├── update.py # update command
│ ├── release.py # release command
│ ├── release_pr.py # release-pr command
│ └── do_release.py # do-release command
│
├── core/ # Business logic
│ ├── version.py # Version parsing and bumping
│ ├── commits.py # Commit parsing
│ ├── changelog.py # Changelog generation
│ └── version_files.py # Version file management
│
├── config/ # Configuration handling
│ ├── loader.py # Config file discovery
│ └── models.py # Pydantic config models
│
├── vcs/ # Version control
│ └── git.py # Git operations
│
├── forge/ # Forge integrations
│ └── github.py # GitHub API client
│
├── publish/ # Publishing
│ └── pypi.py # PyPI publishing
│
└── exceptions.py # Custom exceptions
Core Components¶
CLI Layer (cli/)¶
The CLI layer handles user interaction:
%%{init: {'theme': 'neutral'}}%%
graph LR
A[User] --> B[Typer App]
B --> C[check]
B --> D[update]
B --> E[release]
B --> F[release-pr]
B --> G[do-release]
Responsibilities:
- Parse command-line arguments
- Validate inputs
- Display formatted output (Rich)
- Orchestrate core operations
Core Layer (core/)¶
The core layer contains all business logic:
%%{init: {'theme': 'neutral'}}%%
graph TB
subgraph Version["version.py"]
V1[Version class]
V2[BumpType enum]
V3[parse_version]
V4[calculate_bump]
end
subgraph Commits["commits.py"]
C1[ParsedCommit]
C2[ConventionalParser]
C3[parse_commits]
end
subgraph Changelog["changelog.py"]
CL1[generate_changelog]
CL2[format_entries]
end
Commits --> Version
Version --> Changelog
Responsibilities:
- Parse commit messages
- Calculate version bumps
- Generate changelogs
- Manage version files
Config Layer (config/)¶
Configuration loading and validation:
%%{init: {'theme': 'neutral'}}%%
graph LR
A[.releasio.toml] --> B[Config Loader]
C[releasio.toml] --> B
D[pyproject.toml] --> B
B --> E[Pydantic Models]
E --> F[Validated Config]
Priority order:
.releasio.toml(highest)releasio.tomlpyproject.tomlunder[tool.releasio]
VCS Layer (vcs/)¶
Git operations abstraction:
class GitRepository:
def get_commits_since_tag(self, tag: str) -> list[Commit]
def get_latest_tag(self) -> str | None
def create_tag(self, tag: str, message: str) -> None
def push(self, ref: str) -> None
Forge Layer (forge/)¶
GitHub integration:
class GitHubClient:
def create_release(self, tag: str, body: str) -> Release
def create_pull_request(self, title: str, body: str) -> PullRequest
def upload_assets(self, release_id: int, files: list[Path]) -> None
Publish Layer (publish/)¶
PyPI publishing:
class PyPIPublisher:
def build(self) -> None
def publish(self, trusted: bool = True) -> None
def validate(self) -> bool
Key Classes¶
Version¶
@dataclass(frozen=True)
class Version:
major: int
minor: int
patch: int
pre_release: str | None = None
pre_release_num: int | None = None
def bump(self, bump_type: BumpType) -> Version:
...
def __str__(self) -> str:
...
ParsedCommit¶
@dataclass(frozen=True, slots=True)
class ParsedCommit:
commit: Commit
commit_type: str | None
scope: str | None
description: str
body: str | None
is_breaking: bool
is_conventional: bool
ReleasePyConfig¶
class ReleasePyConfig(BaseModel):
default_branch: str = "main"
version: VersionConfig = VersionConfig()
changelog: ChangelogConfig = ChangelogConfig()
commits: CommitsConfig = CommitsConfig()
github: GitHubConfig = GitHubConfig()
publish: PublishConfig = PublishConfig()
hooks: HooksConfig = HooksConfig()
Design Patterns¶
Strategy Pattern¶
Different changelog generators:
class ChangelogGenerator(Protocol):
def generate(self, commits: list[ParsedCommit]) -> str:
...
class NativeGenerator:
def generate(self, commits: list[ParsedCommit]) -> str:
...
class GitCliffGenerator:
def generate(self, commits: list[ParsedCommit]) -> str:
...
Factory Pattern¶
Build tool selection:
def get_publisher(tool: str) -> Publisher:
match tool:
case "uv":
return UvPublisher()
case "poetry":
return PoetryPublisher()
case "pdm":
return PdmPublisher()
Repository Pattern¶
Git operations:
class GitRepository:
def __init__(self, path: Path):
self.path = path
def get_commits(self) -> list[Commit]:
...
def get_tags(self) -> list[str]:
...
Error Handling¶
Custom exception hierarchy:
class ReleaseError(Exception):
"""Base exception for releasio."""
class ConfigError(ReleaseError):
"""Configuration-related errors."""
class GitError(ReleaseError):
"""Git operation errors."""
class PublishError(ReleaseError):
"""Publishing errors."""
class GitHubError(ReleaseError):
"""GitHub API errors."""
Testing Strategy¶
tests/
├── unit/ # Fast, isolated tests
│ ├── test_version.py
│ ├── test_commits.py
│ └── test_changelog.py
├── integration/ # Tests with real Git
│ ├── test_cli.py
│ └── test_workflow.py
└── conftest.py # Shared fixtures
Test Fixtures¶
@pytest.fixture
def git_repo(tmp_path: Path) -> GitRepository:
"""Create a temporary Git repository."""
subprocess.run(["git", "init"], cwd=tmp_path)
return GitRepository(tmp_path)
@pytest.fixture
def sample_config() -> ReleasePyConfig:
"""Create a sample configuration."""
return ReleasePyConfig()
See Also¶
- Data Flow - How data moves through the system
- Contributing - Development guide
- API Reference - Code documentation