Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,23 @@

- **Reference:** Use the markdown files within the `docs/` directory as a
knowledge base and source of truth for project requirements, dependency
choices, and architectural decisions.
choices, and architectural decisions. Start with
[documentation contents](docs/contents.md) and
[repository layout](docs/repository-layout.md) when orienting within the
project.
- **Update:** When new decisions are made, requirements change, libraries are
added/removed, or architectural patterns evolve, **proactively update** the
relevant file(s) in the `docs/` directory to reflect the latest state. Ensure
the documentation remains accurate and current.
- **Design decisions:** Record design decisions in the relevant design
document. When a decision is substantive, capture it in an Architectural
Decision Record (ADR) following the documentation style guide, and reference
that ADR from the design document.
- **User-facing behaviour:** Update [users' guide](docs/users-guide.md) for
behaviour or user-interface changes that users should know about.
- **Internal interfaces:** Document internally facing interfaces in the
relevant component architecture document. Document internally facing
conventions and practices in [developers' guide](docs/developers-guide.md).
- **Style:** All documentation must adhere to the
[documentation style guide](docs/documentation-style-guide.md).

Expand All @@ -36,9 +48,22 @@ When implementing changes, adhere to the following testing procedures:

- **New Functionality:**
- Implement unit tests covering all new code units (functions, components,
classes). Implement tests **before** implementing the unit.
- Implement behavioral tests that verify the end-to-end behavior of the new
feature from a user interaction perspective.
classes) using `pytest`. Implement tests **before** implementing the unit.
- Implement behavioural tests using `pytest-bdd` that verify the
end-to-end behaviour of the new feature from a user interaction
perspective.
- Add snapshot tests using `syrupy` where output format consistency is
relevant to the requirements.
- Add end-to-end tests when the change affects externally observable
workflows, integration contracts, persistence, command-line behaviour,
network boundaries, UI flows, or other system-level behaviour.
- Add property tests using `hypothesis`, or a bounded model checker such as
CrossHair, when the change introduces an invariant over a range of inputs,
states, orderings, or transitions.
- For introduced axioms or contractual business logic, prefer provable code
in a Rust extension when an exhaustive proof, for example using Verus, is
suitable. Any proof must be substantive, rigorous, and well-founded, not
merely a restatement of the assumed property.
- Ensure both unit and behavioral tests pass before considering the
functionality complete.
- Ensure that new functionality is clearly documented in the
Expand Down Expand Up @@ -136,8 +161,6 @@ When implementing changes, adhere to the following testing procedures:
pass before and after, unit tests added for new units).
- Ensure the refactoring commit itself passes all quality gates.



## Markdown Guidance

- Validate Markdown files using `make markdownlint`.
Expand Down
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
# create-labels

Example package generated from this Copier template.
`create-labels` creates and updates GitHub repository labels from a TOML
configuration file or from the built-in df12 label set. It is a small Python
command-line tool for making label setup repeatable across repositories without
shelling out to the GitHub CLI.

## Why use this?

- Apply a consistent default label set to new repositories.
- Keep repository labels in sync from reviewed TOML configuration.
- Support GitHub Enterprise and local API simulators through `--api-url`.
- Avoid deleting labels that are outwith the desired configuration.

## Quick start

Install the package, provide a token, and choose a target repository:

```bash
create-labels --repository owner/repo --token "$GITHUB_TOKEN"
```

To use a TOML configuration file:

```bash
create-labels --config labels.toml --repository owner/repo
```

When no labels are provided in TOML, the built-in default labels are applied.

## Documentation

Read the [users' guide](docs/users-guide.md) for command-line options,
configuration format, GitHub Enterprise usage, and local validation commands.

Read the [developers' guide](docs/developers-guide.md) for package boundaries,
data flow, normalization rules, and testing strategy.

## Compatibility note

This package is no longer the Copier template placeholder project. The old
sample `hello` function has been removed; use the `create-labels` console
script or the public exports from `create_labels` instead.
37 changes: 23 additions & 14 deletions create_labels/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
"""create-labels package."""
"""Public API for create-labels.

from __future__ import annotations

import importlib
import typing as typ
The package root is the stable import boundary for library consumers. It
re-exports typed configuration values from ``create_labels.config``, the
imported default label set from ``create_labels.defaults``, and the pure label
synchronization function and result type from ``create_labels.sync``.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
if typ.TYPE_CHECKING:
import collections.abc as cabc
Use these exports when embedding create-labels in tests, scripts, or other
tools. The submodules keep parsing, default data, GitHub integration, and sync
decisions separate, while this module provides the small public surface that
callers should depend on.
"""

PACKAGE_NAME = "create_labels"
from __future__ import annotations

try: # pragma: no cover - Rust optional
rust = importlib.import_module(f"._{PACKAGE_NAME}_rs", package=__name__)
hello = typ.cast("cabc.Callable[[], str]", rust.hello)
except ModuleNotFoundError: # pragma: no cover - Python fallback
from .pure import hello
from .config import LabelConfig, LabelSpec, RepositorySpec, load_config
from .defaults import DEFAULT_LABELS
from .sync import LabelSyncResult, sync_labels

__all__ = ["hello"]
__all__ = [
"DEFAULT_LABELS",
"LabelConfig",
"LabelSpec",
"LabelSyncResult",
"RepositorySpec",
"load_config",
"sync_labels",
]
105 changes: 105 additions & 0 deletions create_labels/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""Cyclopts command-line interface for create-labels.

This module provides the console entrypoint that wires together argument
parsing, TOML configuration loading, github3.py integration, and result
formatting. ``main`` is the Cyclopts-decorated command used by the
``create-labels`` project script.

Repository resolution prefers ``--repository``, then ``[repository]`` from the
TOML file, then ``GITHUB_REPOSITORY``. Tokens prefer ``--token`` and then
``GITHUB_TOKEN``. API URLs prefer ``--api-url``, then ``github.api_url`` from
TOML, then ``https://api.github.com``.

Examples
--------
Apply the imported default labels::

create-labels --repository owner/repo

Apply labels from TOML::

create-labels --config labels.toml

Target GitHub Enterprise or a simulator::

create-labels --repository owner/repo --api-url https://github.example/api/v3

"""

from __future__ import annotations

import pathlib

from cyclopts import App

from .config import LabelConfig, RepositorySpec, load_config
from .github import sync_repository_labels

app: App = App(help="Create or update GitHub repository labels from TOML.")


@app.default
def main(
*,
config: str | None = None,
repository: str | None = None,
token: str | None = None,
api_url: str | None = None,
) -> None:
"""Create or update labels in a GitHub repository.

Parameters
----------
config
TOML configuration path. When omitted, the default imported labels are
used.
repository
Repository in ``owner/name`` form. Overrides ``[repository]`` in the
TOML file and ``GITHUB_REPOSITORY``.
token
GitHub token. When omitted, ``GITHUB_TOKEN`` is used.
api_url
GitHub API URL. Use this for GitHub Enterprise or local simulators.

"""
label_config = (
load_config(pathlib.Path(config))
if config is not None
else LabelConfig(None, ())
)
results = sync_repository_labels(
config=label_config,
repository=_parse_repository_argument(repository),
token=token,
api_url=api_url,
)

for result in results:
print(f"{result.action}: {result.name}")


def _parse_repository_argument(repository: str | None) -> RepositorySpec | None:
"""Parse an ``owner/name`` repository argument into a RepositorySpec."""
if repository is None:
return None

owner, separator, name = repository.partition("/")
if not all((separator, owner, name)) or "/" in name:
msg = "repository must use the owner/name format"
raise ValueError(msg)
return RepositorySpec(owner=owner, name=name)


def run() -> None:
"""Execute the Cyclopts application.

This thin wrapper delegates to ``app`` so packaging can expose a stable
project-script entrypoint.

Raises
------
SystemExit
Raised by Cyclopts when argument parsing or command execution fails.

"""
app()
Loading
Loading