Skip to content

Commit a11a180

Browse files
authored
docs: Standardize via CONTRIBUTING.md + WRITING.md (#99)
2 parents e9f2146 + 57da9fb commit a11a180

10 files changed

Lines changed: 1156 additions & 1055 deletions

File tree

‎.github/CONTRIBUTING.md‎

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Contributing
2+
3+
Thanks for looking. gp-libs is pre-1.0 (`0.0.x`); a minor version bump may
4+
still carry a breaking change. Bug reports with a reproduction, and notes on
5+
where the documentation misled you, are the most useful contributions right
6+
now.
7+
8+
How this project writes prose — README, `CHANGES`, release notes, commit
9+
messages, docstrings, source comments, and log messages — is set out
10+
separately in [WRITING.md](WRITING.md). Read that before changing any of it.
11+
The constraints every change is held to, and the map of what is where, are in
12+
[AGENTS.md](../AGENTS.md).
13+
14+
## Getting set up
15+
16+
Install [git], [uv], and [just].
17+
18+
```console
19+
$ git clone https://github.com/git-pull/gp-libs.git
20+
```
21+
22+
```console
23+
$ cd gp-libs
24+
```
25+
26+
```console
27+
$ uv sync --all-extras --dev
28+
```
29+
30+
[git]: https://git-scm.com/
31+
[uv]: https://github.com/astral-sh/uv
32+
[just]: https://just.systems/
33+
34+
## The gates
35+
36+
Format:
37+
38+
```console
39+
$ uv run ruff format .
40+
```
41+
42+
Lint:
43+
44+
```console
45+
$ uv run ruff check . --fix --show-fixes
46+
```
47+
48+
Type-check — strict mypy across `src/` and `tests/`:
49+
50+
```console
51+
$ uv run mypy .
52+
```
53+
54+
Test:
55+
56+
```console
57+
$ uv run pytest
58+
```
59+
60+
**Imports.** Namespace stdlib imports — `import enum`, not
61+
`from enum import Enum` — so a call site reads `enum.Enum`. Third-party
62+
packages may use `from X import Y`. For typing, `import typing as t` and
63+
access via the namespace: `t.NamedTuple`, `t.TYPE_CHECKING`. Every file
64+
starts with `from __future__ import annotations`; ruff's isort
65+
`required-imports` (`pyproject.toml`) enforces that one, the rest is
66+
convention ruff does not check.
67+
68+
Documentation is a gate, not a courtesy. Examples in `src/*.py` docstrings
69+
and under `docs/` are executed by `uv run pytest`, because `testpaths` in
70+
`pyproject.toml` lists `tests`, `docs`, and `src`; `README.md` is not in
71+
`testpaths`, so its three `>>> ` examples do not currently run under CI.
72+
There is no separate doctest step for the paths that are collected — a
73+
green `pytest` is the proof. Which blocks qualify, and the one mistake that
74+
silently removes a test, are in
75+
[WRITING.md](WRITING.md#documented-examples-that-run).
76+
77+
Before claiming a test or a gate works, show it failing. A gate that has
78+
never been red is an assumption.
79+
80+
CI (`.github/workflows/tests.yml`) runs the equivalent of the four commands
81+
above (`ruff check .`, `ruff format . --check`, `mypy .`,
82+
`py.test --cov=./ --cov-report=xml`) across a matrix of Python 3.10-3.14,
83+
docutils 0.20 and 0.22.4, and pytest 8 and 9 — gp-libs supports a wider
84+
version span than most consumers of it, because everything downstream
85+
depends on this collector staying compatible.
86+
87+
## Tests
88+
89+
Write tests as standalone functions, not classes — no `class TestFoo:`
90+
groupings. Use descriptive function names and file organization instead.
91+
92+
Prefer fixtures from `tests/conftest.py` over `monkeypatch` and
93+
`unittest.mock` when one exists; document in the test docstring why a
94+
standard fixture was not used for an exceptional case. Use `tmp_path`
95+
(`pathlib.Path`) over `tempfile`, and `monkeypatch` over `unittest.mock`
96+
when you do need one.
97+
98+
`tests/conftest.py` also provides the Sphinx `app_params`/`make_app_params`
99+
fixtures (via `pytest_plugins = ["sphinx.testing.fixtures", "pytester"]`)
100+
for tests that build a throwaway Sphinx app — used by
101+
`tests/test_linkify_issues.py` and the plugin-suppression tests.
102+
`tests/regressions/` holds one file per historical bug, named for its
103+
issue.
104+
105+
Run continuously with [pytest-watcher]:
106+
107+
```console
108+
$ just start
109+
```
110+
111+
Or with [entr(1)] when you want a shell-only watcher:
112+
113+
```console
114+
$ just watch-test
115+
```
116+
117+
[pytest-watcher]: https://github.com/olzhasar/pytest-watcher
118+
[entr(1)]: http://eradman.com/entrproject/
119+
120+
## Documentation
121+
122+
Build the docs:
123+
124+
```console
125+
$ just build-docs
126+
```
127+
128+
Start the default preview server, which watches for file changes:
129+
130+
```console
131+
$ just start-docs
132+
```
133+
134+
From inside `docs/`, the local `docs/justfile` has finer-grained recipes:
135+
`just html` builds once, `just serve` serves the built output, `just watch`
136+
rebuilds on change, `just dev` watches and serves together, and
137+
`just design` disables incremental builds while you edit static assets.
138+
139+
`docs/conf.py` also enables `sphinx.ext.doctest` with a
140+
`doctest_global_setup` that imports `is_allowed_version` and
141+
`pytest_ignore_collect`, so the `{doctest}` blocks under `docs/` are valid
142+
input to Sphinx's own doctest builder as well as to pytest. Run it with
143+
`just -f docs/justfile doctest`. Neither `tests.yml` nor `docs.yml` invokes
144+
that recipe — it is a manual, local-only check, not a CI gate. `just
145+
build-docs` is what CI runs, and it is also the only thing that catches a
146+
broken `{ref}`/`{doc}` cross-reference; the doctests do not.
147+
148+
## Releasing
149+
150+
gp-libs is pre-1.0: minor version bumps may include breaking changes. [uv]
151+
handles virtualenv creation, package requirements, versioning, building,
152+
and publishing — there is no `setup.py` or requirements file.
153+
154+
1. Update `CHANGES` with release notes.
155+
2. Bump the version in `src/gp_libs.py` and `pyproject.toml`.
156+
3. Create the release commit:
157+
158+
```console
159+
$ git commit -m 'Tag v0.1.1'
160+
```
161+
162+
4. Push the branch for review:
163+
164+
```console
165+
$ git push
166+
```
167+
168+
5. After review, the release owner creates and pushes the tag. Never
169+
create tags. Never push tags. The owner handles tagging and tag pushes,
170+
because a tag triggers the publish workflow. See
171+
[Release commits](WRITING.md#release-commits).
172+
173+
## Pull requests
174+
175+
One subject per pull request. Unrelated cleanup found along the way
176+
belongs in its own commit, and usually in its own pull request.
177+
178+
Discuss a substantial change via an issue before making it.
179+
180+
Commit format is in [WRITING.md](WRITING.md#commits).
181+
182+
## Decorum
183+
184+
- Participants will be tolerant of opposing views.
185+
- Participants must ensure that their language and actions are free of
186+
personal attacks and disparaging personal remarks.
187+
- When interpreting the words and actions of others, participants should
188+
always assume good intentions.
189+
- Behaviour which can be reasonably considered harassment will not be
190+
tolerated.
191+
192+
Based on [Ruby's Community Conduct Guideline](https://www.ruby-lang.org/en/conduct/).
193+
194+
## Security
195+
196+
Please do not open a public issue for a vulnerability. Report it privately
197+
through GitHub:
198+
<https://github.com/git-pull/gp-libs/security/advisories/new>.

0 commit comments

Comments
 (0)