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
66 changes: 66 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copilot Instructions for Decree

## Project Overview

Decree is a Python 3.11+ reimplementation of `adr-tools` - a CLI for managing Architecture Decision Records (ADRs). The core architecture centers around the `AdrLog` class that manages ADR files in `doc/adr/` directories.

### Key Components

- **`AdrLog`** (`src/decree/core.py`): Main API class managing ADR operations
- **`AdrRecord`/`AdrRef`** (`src/decree/models.py`): Typed data models using dataclasses and `StrEnum`
- **CLI** (`src/decree/cli.py`): Typer-based command interface with beartype validation
- **Templates** (`src/decree/templates.py`): ADR markdown templates with format strings

### Data Flow

ADRs are numbered markdown files (e.g., `0001-record-architecture-decisions.md`) with structured metadata headers. The `AdrLog` class handles file I/O, numbering, and linking between records.

## Development Conventions

### Type Safety Strategy (ADR-0009)
- **mypy strict mode**: All code must pass mypy strict checks
- **beartype on public APIs only**: Runtime type validation using `@beartype` decorator on `AdrLog` methods
- **Frozen dataclasses**: Use `@dataclass(frozen=True, slots=True)` for immutable models

### Testing Approach (ADR-0008)
- **Real filesystem**: Use `tmp_path` fixtures, avoid mocking file operations
- **Golden tests**: Test actual file outputs against expected content
- **Minimal dependencies**: Keep test dependencies light

### Build & Development Workflow
- **uv-based builds**: Use `uv` for dependency management and `uv_build` for packaging
- **nox sessions**: Run `nox -s tests` (multi-version), `nox -s lint`, `nox -s typecheck`
- **File structure**: ADRs live in `doc/adr/`, tests use real filesystem via `tmp_path`

## Code Patterns

### Error Handling
```python
# Use helper for control flow in expressions
def _raise(exc: Exception) -> NoReturn:
raise exc

# Usage in core logic
self.dir.exists() or (_raise(FileNotFoundError(f"{self.dir} does not exist")))
```

### Environment Variable Handling
Environment variables follow the pattern: `ADR_DATE` (override), `DECREE_TZ` (timezone), `ADR_TEMPLATE` (custom templates). Use `os.environ` directly with fallbacks.

### File Naming Convention
ADR files: `NNNN-slug.md` where NNNN is zero-padded number and slug is from `slugify()` function.

## Key Files to Understand

- **`doc/adr/`**: Live ADR documents showing the actual format and conventions
- **`src/decree/core.py`**: Core business logic and file operations
- **`tests/`**: Real filesystem testing patterns using pytest `tmp_path`
- **`noxfile.py`**: Development workflow automation
- **Third-party code**: Goes in `third_party/` with CC-BY-4.0 licensing

## Common Tasks

- **Add new CLI command**: Extend `cli.py` with Typer decorators, add corresponding `AdrLog` method
- **Modify ADR format**: Update `templates.py` and corresponding parsing in `core.py`
- **Add validation**: Use beartype decorators on public methods, mypy handles static checks
- **Testing**: Use `tmp_path`, create real ADR files, assert against file contents
37 changes: 37 additions & 0 deletions .github/workflows/apt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
name: apt
'on':
workflow_dispatch:
push:
tags: ["v*.*.*"]
permissions:
contents: write
pages: write
id-token: write

jobs:
build-deb:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install tools
run: |
sudo apt-get update
sudo apt-get install -y ruby ruby-dev rubygems build-essential rpm
sudo gem install --no-document fpm
pip install uv uv-build
- name: Build wheel
run: python -m uv_build bdist_wheel
- name: Build deb
run: bash packaging/apt/scripts/build_deb.sh
- name: Publish repo to gh-pages
env:
GPG_PRIVATE_KEY: ${{ secrets.APT_GPG_PRIVATE_KEY }}
GPG_KEY_PASSPHRASE: ${{ secrets.APT_GPG_PASSPHRASE }}
run: bash packaging/apt/scripts/publish_repo.sh
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: ci
'on':
push:
branches: [main]
pull_request:
permissions:
contents: read

jobs:
build-test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest] # , windows-latest]
python-version: ["3.11", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install uv
run: pip install uv
- name: Sync deps
run: uv sync --all-extras --dev
- name: Lint
run: uv run ruff check .
- name: Typecheck
run: uv run mypy src tests
- name: Tests
run: uv run pytest -q
33 changes: 33 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: release
'on':
push:
tags: ["v*.*.*"]
permissions:
id-token: write
contents: write

jobs:
build-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv and tools
run: pip install uv uv-build
- name: Set version from tag
env:
GIT_TAG: "${{ github.ref_name }}"
run: python scripts/set_version_from_tag.py
- name: Build dist
run: python -m uv_build sdist bdist_wheel
- name: Publish to PyPI (trusted publishing)
uses: pypa/gh-action-pypi-publish@release/v1
with:
print-hash: true
skip-existing: false
verify-metadata: true
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args: ["-L", "crate,upto,fo"]

- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.18.1
hooks:
- id: markdownlint-cli2

- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
hooks:
- id: yamllint
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.analysis.typeCheckingMode": "strict"
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

# changelog

## 0.1.0

* Initial CLI and API with `init`, `new`, `link`, `list`, `generate toc`,
`generate graph` stub, `upgrade-repository` no-op.
* CI, nox, ruff, mypy strict, pytest harness.
4 changes: 4 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# code of conduct

We follow the Contributor Covenant v2.1.
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

# contributing

* setup: `pip install uv && uv sync --all-extras --dev`
* checks: `uv run nox -s lint typecheck tests`
* style: ruff, mypy strict; no private telemetry
* tests: use `tmp_path`, avoid heavy mocking
* release: tag `vX.Y.Z` on `main`; CI builds and publishes to PyPI
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2025, Steven Cutting
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5 changes: 5 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This project includes small verbatim snippets derived from npryce/adr-tools
for testing and template parity. Those snippets are provided under CC-BY-4.0
and are isolated under third_party/adr-tools-snippets with attribution.

Our original source code is licensed under BSD-3-Clause.
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,40 @@
# decree
# decree

Python 3.11+ reimplementation of `adr-tools` with a typed API and a Typer CLI.

## install

```bash
pipx install decree
# or after release: brew install steven-cutting/decree/decree
````

## quickstart

```bash
decree init
decree new Use beartype on public API
decree list
decree generate toc > doc/adr/README.md
decree generate graph # exits non-zero, not implemented
```

## cli

* `decree init [DIR]`
* `decree new [--status STATUS] [--template PATH] [--dir DIR] TITLE...`
* `decree link SRC REL TGT [--reverse / --no-reverse]`
* `decree list`
* `decree generate toc`
* `decree generate graph` (not implemented)
* `decree upgrade-repository`

## configuration

* `ADR_DATE`: if set, used verbatim as the ADR date
* `DECREE_TZ`: IANA timezone for date formatting (default: `UTC`)
* `ADR_TEMPLATE`: path to a custom template file

## license

BSD-3-Clause for our code. CC-BY-4.0 notices for any third-party snippets.
5 changes: 5 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# security policy

Report vulnerabilities via GitHub Security Advisories.
No telemetry or network calls at runtime.
24 changes: 24 additions & 0 deletions doc/adr/0001-record-architecture-decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

# 0001: Record architecture decisions

Date: 2025-10-06
Status: Accepted

## Context

We need durable, text-based documentation for key technical decisions.

## Decision

Use Architecture Decision Records (ADRs) stored in version control.

## Consequences

* Decisions are discoverable alongside code.
* Changes are reviewed via pull requests.
* Lightweight process encourages adoption.

## Alternatives considered

* Wiki pages.
* Issue tracker comments.
22 changes: 22 additions & 0 deletions doc/adr/0002-language-and-runtime-python-311-plus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

# 0002: Language and runtime: Python 3.11+

Date: 2025-10-06
Status: Accepted

## Context

We want modern features and tight type checking with broad availability.

## Decision

Target Python 3.11 and newer for decree.

## Consequences

* Can use `StrEnum`, `zoneinfo`, and improved typing.
* Smaller compatibility surface.

## Alternatives considered

* Supporting 3.10 would increase maintenance for limited benefit.
22 changes: 22 additions & 0 deletions doc/adr/0003-status-enum-with-strenum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

# 0003: Status enum with StrEnum

Date: 2025-10-06
Status: Accepted

## Context

ADRs use canonical status labels that appear in files and CLI output.

## Decision

Model statuses with `AdrStatus(StrEnum)` and store values as strings.

## Consequences

* Stable text in files.
* Easy CLI flags and parsing.

## Alternatives considered

* Plain strings with ad-hoc validation.
Loading