Skip to content

Publishing

python_project_foundry.publishing

Explicit GitHub publishing for generated projects.

PublishError

Bases: RuntimeError

Raised when a project cannot be safely published.

Source code in src/python_project_foundry/publishing.py
14
15
class PublishError(RuntimeError):
    """Raised when a project cannot be safely published."""

ProjectPublishingConfig dataclass

Metadata required to publish a generated project.

Source code in src/python_project_foundry/publishing.py
18
19
20
21
22
23
24
25
@dataclass(frozen=True)
class ProjectPublishingConfig:
    """Metadata required to publish a generated project."""

    root: Path
    repository: str
    description: str
    documentation_url: str

load_project_publishing_config

load_project_publishing_config(
    project_path: Path,
) -> ProjectPublishingConfig

Load publishing metadata from a generated project's pyproject.toml.

Source code in src/python_project_foundry/publishing.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def load_project_publishing_config(project_path: Path) -> ProjectPublishingConfig:
    """Load publishing metadata from a generated project's pyproject.toml."""
    root = project_path.expanduser().resolve()
    pyproject_path = root / "pyproject.toml"
    if not pyproject_path.is_file():
        raise PublishError(f"Generated project metadata not found: {pyproject_path}")

    try:
        pyproject = tomllib.loads(pyproject_path.read_text())
        project = pyproject["project"]
        urls = project["urls"]
        description = str(project["description"])
        source_url = str(urls["Source"])
        documentation_url = str(urls["Documentation"])
    except (KeyError, TypeError, tomllib.TOMLDecodeError) as error:
        raise PublishError(f"Invalid generated project metadata in {pyproject_path}") from error

    return ProjectPublishingConfig(
        root=root,
        repository=_repository_from_url(source_url),
        description=description,
        documentation_url=documentation_url,
    )

publish_project

publish_project(
    project_path: Path,
    *,
    visibility: str,
    remote: str = "origin",
    configure_pages: bool = True,
    dry_run: bool = False,
) -> None

Create and push a GitHub repository, then optionally enable Pages.

Source code in src/python_project_foundry/publishing.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def publish_project(
    project_path: Path,
    *,
    visibility: str,
    remote: str = "origin",
    configure_pages: bool = True,
    dry_run: bool = False,
) -> None:
    """Create and push a GitHub repository, then optionally enable Pages."""
    config = load_project_publishing_config(project_path)
    commands = _publish_commands(
        config,
        visibility=visibility,
        remote=remote,
        configure_pages=configure_pages,
    )
    _preflight(config, remote=remote, require_gh=not dry_run)

    if dry_run:
        print(f"Would publish {config.root} to {config.repository}:")
        for command in commands:
            print(f"  {shlex.join(command)}")
        return

    _run_command(commands[0], cwd=config.root)
    if configure_pages:
        try:
            _run_command(commands[1], cwd=config.root)
        except PublishError as error:
            repository_url = f"https://github.com/{config.repository}"
            raise PublishError(
                f"Repository created and pushed to {repository_url}, but GitHub Pages "
                f"configuration failed. Retry the Pages API command shown by --dry-run.\n{error}"
            ) from error

    print("Published successfully.")
    print(f"  repository: https://github.com/{config.repository}")
    if configure_pages:
        print(f"  documentation: {config.documentation_url}")
        print("  GitHub Actions will build and deploy the documentation from main.")
    else:
        print("  documentation: Pages configuration skipped")