diff --git a/.agents/skills/generate-release-note/SKILL.md b/.agents/skills/generate-release-note/SKILL.md new file mode 100644 index 00000000..8dcd95b3 --- /dev/null +++ b/.agents/skills/generate-release-note/SKILL.md @@ -0,0 +1,86 @@ +--- +name: generate-release-note +description: Generate and insert a user-facing release note entry for the current GitHub pull request in docs/about/release-notes.md. Use when the user asks to create a release note, changelog entry, user-facing PR summary, or to update release notes for a PR. +--- + +# Generate Release Note + +## Overview + +Generate one concise, user-facing release note entry for the current PR and insert it into the latest release section of `docs/about/release-notes.md`. + +## Workflow + +1. Inspect the current release note style: + + ```bash + sed -n '1,120p' docs/about/release-notes.md + ``` + +2. Gather current PR context: + + ```bash + gh pr view --json number,title,body,url,closingIssuesReferences,files,commits + ``` + +3. Draft one release note bullet in the style of the latest section. Use English by default. + +4. Choose the target category: + - `Fixed` for bug fixes and corrected user-visible behavior. + - `Added` for new user-facing capabilities. + - `Changed` for changed behavior, defaults, or compatibility. + - `Maintenance` for dependencies, packaging, CI, release automation, or maintainer-facing work. + - Prefer an existing category in the latest release section when it fits. + +5. Insert the bullet: + + ```bash + python3 .agents/skills/generate-release-note/scripts/insert_release_note.py \ + --category Fixed \ + --note '* Fix anchor link validation so strict builds fail when missing anchors are reported as warnings. #30' + ``` + +6. Re-read the top of `docs/about/release-notes.md` and make sure the entry landed in the correct latest release section. + +## Placement Rules + +Always update `docs/about/release-notes.md`. Insert into the first `## Version ... (...)` section in the file, which is the current next-release section. + +If the top section is an unreleased version such as `## Version 1.7.1 (Unreleased)`, use that section. If the next version number is already known, use that version section. Do not insert into older published versions. + +When a target category already exists, append the entry at the end of that category. When it does not exist, create the category inside the latest release section before the next version heading. + +## Entry Style + +Match the existing release note source style: + +- Use `* ` bullets. +- Write concise, user-facing English. +- Describe behavior or value users can observe. +- Avoid implementation details such as file names, commit hashes, tests, or internal refactors unless directly relevant to users. +- Use backticks for commands, options, configuration keys, package names, and code-like terms. + +Examples: + +```markdown +* Fix `mkdocs serve --livereload` so the option is recognized correctly again. #4 +* Remove the unmaintained `mergedeep` dependency by replacing it with an internal deep-merge helper for inherited YAML configuration. #29 +``` + +## Link Rules + +If `closingIssuesReferences` has user issues, end the entry with the issue number references such as `#30`. Keep issue references short in the source file; `docs/hooks.py` rewrites them to the correct GitHub issue links during docs rendering. + +If there is no user issue, end the entry with the bare PR number reference such as `#123`. + +If multiple user issues are relevant, include all issue references at the end of the same bullet. + +## Verification + +After insertion, check: + +- The entry is under the latest `## Version ... (...)` section. +- The category heading is appropriate and matches nearby release note style. +- Issue-backed entries use bare issue references. +- PR-backed entries use bare PR references. +- The entry is not duplicated. diff --git a/.agents/skills/generate-release-note/agents/openai.yaml b/.agents/skills/generate-release-note/agents/openai.yaml new file mode 100644 index 00000000..bc459dbe --- /dev/null +++ b/.agents/skills/generate-release-note/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Generate release note" + short_description: "Generate user-facing release notes" + default_prompt: "Use $generate-release-note to generate and insert a user-facing release note for this PR." diff --git a/.agents/skills/generate-release-note/scripts/insert_release_note.py b/.agents/skills/generate-release-note/scripts/insert_release_note.py new file mode 100644 index 00000000..ee4906a8 --- /dev/null +++ b/.agents/skills/generate-release-note/scripts/insert_release_note.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path + +DEFAULT_RELEASE_NOTES = Path("docs/about/release-notes.md") +VERSION_HEADING_RE = re.compile(r"^## Version .+ \([^)]+\)\s*$", re.MULTILINE) +CATEGORY_HEADING_RE = re.compile(r"^### (?P.+?)\s*$", re.MULTILINE) + + +def normalize_note(note: str) -> str: + note = note.strip() + if not note: + raise SystemExit("Release note cannot be empty.") + if not note.startswith("* "): + note = f"* {note}" + return note + + +def find_latest_release_section(text: str) -> tuple[int, int]: + matches = list(VERSION_HEADING_RE.finditer(text)) + if not matches: + raise SystemExit("No '## Version ... (...)' release section found.") + + start = matches[0].start() + end = matches[1].start() if len(matches) > 1 else len(text) + return start, end + + +def find_category(section: str, category: str) -> tuple[int, int] | None: + matches = list(CATEGORY_HEADING_RE.finditer(section)) + for index, match in enumerate(matches): + if match.group("category").strip().casefold() == category.casefold(): + start = match.end() + end = ( + matches[index + 1].start() if index + 1 < len(matches) else len(section) + ) + return start, end + return None + + +def append_to_category(section: str, category: str, note: str) -> str: + existing = find_category(section, category) + if existing is None: + section = section.rstrip() + return f"{section}\n\n### {category}\n\n{note}\n\n" + + _category_start, category_end = existing + before = section[:category_end].rstrip() + after = section[category_end:].lstrip("\n") + + if note in before: + return section + + inserted = f"{before}\n{note}\n" + if after: + return f"{inserted}\n{after}" + return f"{inserted}\n" + + +def insert_release_note(text: str, category: str, note: str) -> str: + section_start, section_end = find_latest_release_section(text) + section = text[section_start:section_end] + new_section = append_to_category(section, category, note) + return f"{text[:section_start]}{new_section}{text[section_end:]}" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Insert a release note bullet into the latest release section." + ) + parser.add_argument("--note", required=True, help="Release note bullet to insert.") + parser.add_argument( + "--category", + required=True, + help="Target category heading, for example Fixed, Added, Changed, or Maintenance.", + ) + parser.add_argument( + "--file", + type=Path, + default=DEFAULT_RELEASE_NOTES, + help=f"Release notes file to update. Defaults to {DEFAULT_RELEASE_NOTES}.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print the updated file content without writing it.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + note = normalize_note(args.note) + text = args.file.read_text(encoding="utf-8") + updated = insert_release_note(text, args.category.strip(), note) + + if args.dry_run: + sys.stdout.write(updated) + return + + if updated != text: + args.file.write_text(updated, encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..306d032f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,19 @@ +# Development Rules + +## Quality Gate + +* Before every final commit, run `uvx prek run -a`. +* Pre-commit must pass before committing. If it fails, fix the reported issues and rerun `uvx prek run -a` until it passes. +* Do not use `git commit --no-verify` to bypass required checks. + +## Git Safety + +* Run `git status` before staging or committing. +* Stage only files changed for the current task, using explicit paths. +* Do not use `git add .` or `git add -A`, because they can include unrelated work. +* Do not run destructive commands such as `git reset --hard`, `git checkout .`, or `git clean -fd` unless the user explicitly asks for them. + +## Project Notes + +* When updating release notes, edit `docs/about/release-notes.md` and add entries to the latest version section near the top of the file. +* Keep release note entries user-facing and consistent with the surrounding section style. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2af0710a..a4579b22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,191 +1,93 @@ -# Contributing to MkDocs +# Contributing to MkDocs NG -An introduction to contributing to the MkDocs project. - -The MkDocs project welcomes contributions from developers and -users in the open source community. Contributions can be made in a number of -ways, a few examples are: - -- Code patches via pull requests -- Documentation improvements -- Bug reports and patch reviews +Thanks for wanting to contribute to MkDocs NG. This guide is intentionally +short; it exists to help contributors send focused, reviewable changes. Use [GitHub Discussions] for questions and general help. Use [GitHub issues] for bug reports and feature requests. -## Reporting an Issue - -Please include as much detail as you can. Let us know your platform and MkDocs -version. If the problem is visual (for example a theme or design issue), please -add a screenshot. If you get an error, please include the full error message and -traceback. - -It is particularly helpful if an issue report touches on all of these aspects: - -1. What are you trying to achieve? - -2. What is your `mkdocs.yml` configuration (+ other relevant files)? Preferably reduced to the minimal reproducible example. +## The Main Rule -3. What did you expect to happen when applying this setup? - -4. What happened instead and how didn't it match your expectation? - -## Trying out the Development Version - -If you want to just install and try out the latest development version of -MkDocs (in case it already contains a fix for your issue), -you can do so with the following command. This can be useful if you -want to provide feedback for a new feature or want to confirm if a bug you -have encountered is fixed in the latest development version. It is **strongly** recommended -that you do this within a [virtualenv]. - -```bash -pip install git+https://github.com/mkdocs-ng/mkdocs.git -``` +You must understand the code you contribute. -## Installing for Development +Using AI to help write code is fine. What is not fine is submitting code you do +not understand. If you cannot explain what your changes do, why they are needed, +and how they interact with the rest of the project, your PR may be closed. -Note that for development you can just use [Hatch] directly as described below. If you wish to install a local clone of MkDocs anyway, you can run `pip install --editable .`. It is **strongly** recommended that you do this within a [virtualenv]. +If you use an AI coding agent, run it from the repository root so it can pick up +`AGENTS.md`, and review its output carefully before opening a PR. -## Installing Hatch +## Reporting Issues -The main tool that is used for development is [Hatch]. It manages dependencies (in a virtualenv that is created on the fly) and is also the command runner. +Please include enough detail for someone else to reproduce or understand the +problem: -So first, [install it][install Hatch]. Ideally in an isolated way with **`pipx install hatch`** (after [installing `pipx`]), or just `pip install hatch` as a more well-known way. +* What you were trying to do. +* Your MkDocs version, Python version, platform, and relevant `mkdocs.yml`. +* What you expected to happen. +* What happened instead, including the full error message or traceback. +* Screenshots for visual or theme issues. -For repository hooks, install [pre-commit] as well. Ideally use **`pipx install pre-commit`**, or `pip install pre-commit` if you prefer. The Markdown and JavaScript hooks also require Node.js to be available on your `PATH`. +For large feature ideas, open a discussion or issue before starting the PR. -## Running repository hooks +## Development Setup -To run the same repository-wide hooks as CI, use the following command in the cloned MkDocs repository: +Install [Hatch] to run the development environments: ```bash -pre-commit run --all-files +pipx install hatch ``` -This runs the hooks defined in `.pre-commit-config.yaml`, including Python formatting and linting, spelling, YAML/TOML validation, Markdown linting, and JavaScript linting. - -If you want these hooks to run automatically on each commit, install them into your local Git checkout: +Install [prek] or use it through `uvx` to run repository hooks: ```bash -pre-commit install +uvx prek run -a ``` -### Running tests +The hooks cover formatting, linting, spelling, YAML/TOML validation, Markdown +linting, and JavaScript linting. + +## Useful Checks -To run the test suite for MkDocs, run the following commands: +Run the checks that match your change before opening a PR: ```bash +uvx prek run -a hatch run test:test hatch run integration:test -``` - -It will attempt to run the tests against all of the Python versions we -support. So don't be concerned if you are missing some. The rest -will be verified by [GitHub Actions] when you submit a pull request. - -### Python code style - -Python code within MkDocs' code base is formatted using Ruff's formatter and [Isort] and lint-checked using [Ruff]. The shared repository hooks live in `.pre-commit-config.yaml`, and GitHub Actions runs those hooks directly to avoid drift. - -You can check and automatically format the repository according to these tools with the following command: - -```bash -pre-commit run --all-files -``` - -The code is also type-checked using [mypy] - also configured in `pyproject.toml`, it can be run like this: - -```bash hatch run types:check ``` -### Documentation of MkDocs itself - -After making edits to files under the `docs/` dir, you can preview the site locally using the following command: +If you changed documentation, preview it locally: ```bash hatch run docs:serve ``` -Note that any 'WARNING' should be resolved before submitting a contribution. - -Documentation files are also checked by the repository hooks, so you should run this as well: - -```bash -pre-commit run --all-files -``` - -If you add a new plugin to mkdocs.yml, you don't need to add it to any "requirements" file, because that is managed automatically. - -> INFO: If you don't want to use Hatch, for documentation you can install dependencies into a virtualenv (with `.venv` being the virtualenv directory): -> -> ```bash -> .venv/bin/pip install --editable . -> .venv/bin/pip install $(mkdocs get-deps) # Additional dependencies from mkdocs.yml plugins/themes. -> ``` - -## Translating themes +Resolve documentation warnings before submitting documentation changes. -To localize a theme to your favorite language, follow the guide on [Translating Themes]. We welcome translation pull requests! +## Pull Requests -## Submitting Pull Requests +Keep PRs focused and easy to review. A PR should usually do one thing. -If you're considering a large code contribution to MkDocs, please start a -discussion or open an issue first so you can get early feedback on the idea. +For changes that affect behavior, add or update tests. For user-facing changes, +update documentation when appropriate. -Once you think the code is ready to be reviewed, push -it to your fork and send a pull request. For a change to be accepted it will -most likely need to have tests and documentation if it is a new feature. +Do not force-push during review unless there is a clear reason. Review history +is easier to follow when updates are pushed as normal commits; PRs are typically +squash-merged. -When working with a pull request branch: -Unless otherwise agreed, prefer `commit` over `amend`, and `merge` over `rebase`. Avoid force-pushes, otherwise review history is much harder to navigate. For the end result, the "unclean" history is fine because most pull requests are squash-merged on GitHub. - -Do *not* add to *release-notes.md*, this will be written later. - -### Submitting changes to the builtin themes - -When installed with `i18n` support (`pip install 'mkdocs[i18n]'`), MkDocs allows -themes to support being translated into various languages (referred to as -locales) if they respect [Jinja's i18n extension] by wrapping text placeholders -with `{% trans %}` and `{% endtrans %}` tags. - -Each time a translatable text placeholder is added, removed or changed in a -theme template, the theme's Portable Object Template (`pot`) file needs to be -updated by running the `extract_messages` command. To update the -`pot` file for both built-in themes, run these commands: - -```bash -pybabel extract --project=MkDocs --copyright-holder=MkDocs --msgid-bugs-address='https://github.com/mkdocs-ng/mkdocs/issues' --no-wrap --version="$(hatch version)" --mapping-file mkdocs/themes/babel.cfg --output-file mkdocs/themes/mkdocs/messages.pot mkdocs/themes/mkdocs -pybabel extract --project=MkDocs --copyright-holder=MkDocs --msgid-bugs-address='https://github.com/mkdocs-ng/mkdocs/issues' --no-wrap --version="$(hatch version)" --mapping-file mkdocs/themes/babel.cfg --output-file mkdocs/themes/readthedocs/messages.pot mkdocs/themes/readthedocs -``` - -The updated `pot` file should be included in a PR with the updated template. -The updated `pot` file will allow translation contributors to propose the -translations needed for their preferred language. See the guide on [Translating -Themes] for details. - -NOTE: -Contributors are not expected to provide translations with their changes to -a theme's templates. However, they are expected to include an updated `pot` -file so that everything is ready for translators to do their job. +If release notes are needed, update `docs/about/release-notes.md` in the latest +version section near the top of the file. Keep entries user-facing and +consistent with the surrounding style. ## Code of Conduct -Everyone interacting in the MkDocs project's codebases, issue trackers, chat -rooms, and mailing lists is expected to follow the [PyPA Code of Conduct]. +Everyone interacting in MkDocs NG spaces is expected to follow the +[PyPA Code of Conduct]. -[virtualenv]: https://virtualenv.pypa.io/en/latest/user_guide.html -[Hatch]: https://hatch.pypa.io/ -[install Hatch]: https://hatch.pypa.io/latest/install/#pip -[pre-commit]: https://pre-commit.com/ -[installing `pipx`]: https://pypa.github.io/pipx/installation/ -[GitHub Actions]: https://docs.github.com/actions [GitHub Discussions]: https://github.com/orgs/mkdocs-ng/discussions [GitHub issues]: https://github.com/mkdocs-ng/mkdocs/issues +[Hatch]: https://hatch.pypa.io/ +[prek]: https://github.com/j178/prek [PyPA Code of Conduct]: https://www.pypa.io/en/latest/code-of-conduct/ -[Translating Themes]: https://mkdocs-ng.github.io/mkdocs/dev-guide/translations/ -[Jinja's i18n extension]: https://jinja.palletsprojects.com/en/latest/extensions/#i18n-extension -[Ruff]: https://docs.astral.sh/ruff/ -[Isort]: https://pycqa.github.io/isort/ -[mypy]: https://mypy-lang.org/ diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 2ace8d11..4fd43a10 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -33,6 +33,7 @@ The current members of the MkDocs-NG team. ### Maintenance * Remove the unmaintained `mergedeep` dependency by replacing it with an internal deep-merge helper for inherited YAML configuration. #29 +* Add a release-note generation skill and repository agent instructions to make maintainer workflows more consistent. #35 ## Version 1.7.0 (2026-04-24)