mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 10:45:56 +00:00
* Add python-pypi-package-builder skill for Python packaging - Created `SKILL.md` defining decision-driven workflow for building, testing, versioning, and publishing Python packages. - Added reference modules covering PyPA packaging, architecture patterns, CI/CD, testing, versioning strategy, and release governance. - Implemented scaffold script to generate complete project structure with pyproject.toml, CI workflows, tests, and configuration. - Included support for multiple build backends (setuptools_scm, hatchling, flit, poetry) with clear decision rules. - Added secure release practices including tag-based versioning, branch protection, and OIDC Trusted Publishing. * fix: correct spelling issues detected by codespell
13 KiB
13 KiB
Community Docs, PR Checklist, Anti-patterns, and Release Checklist
Table of Contents
- README.md required sections
- Docstrings — Google style
- CONTRIBUTING.md template
- SECURITY.md template
- GitHub Issue Templates
- PR Checklist
- Anti-patterns to avoid
- Master Release Checklist
1. README.md Required Sections
A good README is the single most important file for adoption. Users decide in 30 seconds whether to use your library based on the README.
# your-package
> One-line description — what it does and why it's useful.
[](https://pypi.org/project/your-package/)
[](https://pypi.org/project/your-package/)
[](https://github.com/you/your-package/actions/workflows/ci.yml)
[](https://codecov.io/gh/you/your-package)
[](LICENSE)
## Installation
pip install your-package
# With Redis backend:
pip install "your-package[redis]"
## Quick Start
(A copy-paste working example — no setup required to run it)
from your_package import YourClient
client = YourClient(api_key="sk-...")
result = client.process({"input": "value"})
print(result)
## Features
- Feature 1
- Feature 2
## Configuration
| Parameter | Type | Default | Description |
|---|---|---|—--|
| api_key | str | required | Authentication credential |
| timeout | int | 30 | Request timeout in seconds |
| retries | int | 3 | Number of retry attempts |
## Backends
Brief comparison — in-memory vs Redis — and when to use each.
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md)
## Changelog
See [CHANGELOG.md](./CHANGELOG.md)
## License
MIT — see [LICENSE](./LICENSE)
2. Docstrings — Google Style
Use Google-style docstrings for every public class, method, and function. IDEs display these as tooltips, mkdocs/sphinx can auto-generate documentation from them, and they convey intent clearly to contributors.
class YourClient:
"""
Main client for <purpose>.
Args:
api_key: Authentication credential.
timeout: Request timeout in seconds. Defaults to 30.
retries: Number of retry attempts. Defaults to 3.
Raises:
ValueError: If api_key is empty or timeout is non-positive.
Example:
>>> from your_package import YourClient
>>> client = YourClient(api_key="sk-...")
>>> result = client.process({"input": "value"})
"""
3. CONTRIBUTING.md
# Contributing to your-package
## Development Setup
git clone https://github.com/you/your-package
cd your-package
pip install -e ".[dev]"
pre-commit install
## Running Tests
pytest
## Running Linting
ruff check .
black . --check
mypy your_package/
## Submitting a PR
1. Fork the repository
2. Create a feature branch: `git checkout -b feat/your-feature`
3. Make changes with tests
4. Ensure CI passes: `pre-commit run --all-files && pytest`
5. Update `CHANGELOG.md` under `[Unreleased]`
6. Open a PR — use the PR template
## Commit Message Format (Conventional Commits)
- `feat: add Redis backend`
- `fix: correct retry behavior on timeout`
- `docs: update README quick start`
- `chore: bump ruff to 0.5`
- `test: add edge cases for memory backend`
## Reporting Bugs
Use the GitHub issue template. Include Python version, package version,
and a minimal reproducible example.
4. SECURITY.md
# Security Policy
## Supported Versions
| Version | Supported |
|---|---|
| 1.x.x | Yes |
| < 1.0 | No |
## Reporting a Vulnerability
Do NOT open a public GitHub issue for security vulnerabilities.
Report via: GitHub private security reporting (preferred)
or email: security@yourdomain.com
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We aim to acknowledge within 48 hours and resolve within 14 days.
5. GitHub Issue Templates
.github/ISSUE_TEMPLATE/bug_report.md
---
name: Bug Report
about: Report a reproducible bug
labels: bug
---
**Python version:**
**Package version:**
**Describe the bug:**
**Minimal reproducible example:**
```python
# paste code here
Expected behavior:
Actual behavior:
### `.github/ISSUE_TEMPLATE/feature_request.md`
```markdown
---
name: Feature Request
about: Suggest a new feature or enhancement
labels: enhancement
---
**Problem this would solve:**
**Proposed solution:**
**Alternatives considered:**
6. PR Checklist
All items must be checked before requesting review. CI must be fully green.
Code Quality Gates
[ ] ruff check . — zero errors
[ ] black . --check — zero formatting issues
[ ] isort . --check-only — imports sorted correctly
[ ] mypy your_package/ — zero type errors
[ ] pytest — all tests pass
[ ] Coverage >= 80% (enforced by fail_under in pyproject.toml)
[ ] All GitHub Actions workflows green
Structure
[ ] pyproject.toml: name, dynamic/version, description, requires-python, license, authors,
keywords (10+), classifiers, dependencies, all [project.urls] filled in
[ ] dynamic = ["version"] if using setuptools_scm
[ ] [tool.setuptools_scm] with local_scheme = "no-local-version"
[ ] setup.py shim present (if using setuptools_scm)
[ ] py.typed marker file exists in the package directory (empty file)
[ ] py.typed listed in [tool.setuptools.package-data]
[ ] "Typing :: Typed" classifier in pyproject.toml
[ ] __init__.py has __all__ listing all public symbols
[ ] __version__ via importlib.metadata (not hardcoded string)
Testing
[ ] conftest.py has shared fixtures for client and backend
[ ] Core happy path tested
[ ] Error conditions and edge cases tested
[ ] Each backend tested independently in isolation
[ ] Redis backend tested in separate CI job with redis service (if applicable)
[ ] asyncio_mode = "auto" in pyproject.toml (for async tests)
[ ] fetch-depth: 0 in all CI checkout steps
Optional Backend (if applicable)
[ ] BaseBackend abstract class defines the interface
[ ] MemoryBackend works with zero extra deps
[ ] RedisBackend raises ImportError with clear pip install hint if redis not installed
[ ] Both backends unit-tested independently
[ ] redis extra declared in [project.optional-dependencies]
[ ] README shows both install paths (base and [redis])
Changelog & Docs
[ ] CHANGELOG.md updated under [Unreleased]
[ ] README has: description, install, quick start, config table, badges, license
[ ] All public symbols have Google-style docstrings
[ ] CONTRIBUTING.md: dev setup, test/lint commands, PR instructions
[ ] SECURITY.md: supported versions, reporting process
[ ] .github/ISSUE_TEMPLATE/bug_report.md
[ ] .github/ISSUE_TEMPLATE/feature_request.md
CI/CD
[ ] ci.yml: lint + mypy + test matrix (all supported Python versions)
[ ] ci.yml: separate job for Redis backend with redis service
[ ] publish.yml: triggered on v*.*.* tags, uses Trusted Publishing (OIDC)
[ ] fetch-depth: 0 in all workflow checkout steps
[ ] pypi environment created in GitHub repo Settings → Environments
[ ] No API tokens in repository secrets
7. Anti-patterns to Avoid
| Anti-pattern | Why it's bad | Correct approach |
|---|---|---|
__version__ = "1.0.0" hardcoded with setuptools_scm |
Goes stale after first git tag | Use importlib.metadata.version() |
Missing fetch-depth: 0 in CI checkout |
setuptools_scm can't find tags → version = 0.0.0+dev |
Add fetch-depth: 0 to every checkout step |
local_scheme not set |
+g<hash> suffix breaks PyPI uploads (local versions rejected) |
local_scheme = "no-local-version" |
Missing py.typed file |
IDEs and mypy don't see package as typed | Create empty py.typed in package root |
py.typed not in package-data |
File missing from installed wheel — useless | Add to [tool.setuptools.package-data] |
| Importing optional dep at module top | ImportError on import your_package for all users |
Lazy import inside the function/class that needs it |
Duplicating metadata in setup.py |
Conflicts with pyproject.toml; drifts |
Keep setup.py as 3-line shim only |
No fail_under in coverage config |
Coverage regressions go unnoticed | Set fail_under = 80 |
| No mypy in CI | Type errors silently accumulate | Add mypy step to ci.yml |
| API tokens in GitHub Secrets for PyPI | Security risk, rotation burden | Use Trusted Publishing (OIDC) |
Committing directly to main/master |
Bypasses CI checks | Enforce via no-commit-to-branch pre-commit hook |
Missing [Unreleased] section in CHANGELOG |
Changes pile up and get forgotten at release time | Keep [Unreleased] updated every PR |
| Pinning exact dep versions in a library | Breaks dependency resolution for users | Use >= lower bounds only; avoid == |
No __all__ in __init__.py |
Users can accidentally import internal helpers | Declare __all__ with every public symbol |
from your_package import * in tests |
Tests pass even when imports are broken | Always use explicit imports |
No SECURITY.md |
No path for responsible vulnerability disclosure | Add file with response timeline |
Any everywhere in type hints |
Defeats mypy entirely | Use object for truly arbitrary values |
Union return types |
Forces every caller to write isinstance() checks |
Return concrete types; use overloads |
setup.cfg + pyproject.toml both active |
Conflicts and confusing for contributors | Migrate everything to pyproject.toml |
| Releasing on untagged commits | Version number is meaningless | Always tag before release |
| Not testing on all supported Python versions | Breakage discovered by users, not you | Matrix test in CI |
license = {text = "MIT"} (old form) |
Deprecated; PEP 639 uses SPDX strings | license = "MIT" |
| No issue templates | Bug reports are inconsistent | Add bug_report.md + feature_request.md |
8. Master Release Checklist
Run through every item before pushing a release tag. CI must be fully green.
Code Quality
[ ] ruff check . — zero errors
[ ] ruff format . --check — zero formatting issues
[ ] mypy src/your_package/ — zero type errors
[ ] pytest — all tests pass
[ ] Coverage >= 80% (fail_under enforced in pyproject.toml)
[ ] All GitHub Actions CI jobs green (lint + test matrix)
Project Structure
[ ] pyproject.toml — name, description, requires-python, license (SPDX string), authors,
keywords (10+), classifiers (Python versions + Typing :: Typed), urls (all 5 fields)
[ ] dynamic = ["version"] set (if using setuptools_scm or hatch-vcs)
[ ] [tool.setuptools_scm] with local_scheme = "no-local-version"
[ ] setup.py shim present (if using setuptools_scm)
[ ] py.typed marker file exists (empty file in package root)
[ ] py.typed listed in [tool.setuptools.package-data]
[ ] "Typing :: Typed" classifier in pyproject.toml
[ ] __init__.py has __all__ listing all public symbols
[ ] __version__ reads from importlib.metadata (not hardcoded)
Testing
[ ] conftest.py has shared fixtures for client and backend
[ ] Core happy path tested
[ ] Error conditions and edge cases tested
[ ] Each backend tested independently in isolation
[ ] asyncio_mode = "auto" in pyproject.toml (for async tests)
[ ] fetch-depth: 0 in all CI checkout steps
CHANGELOG and Docs
[ ] CHANGELOG.md: [Unreleased] entries moved to [x.y.z] - YYYY-MM-DD
[ ] README has: description, install commands, quick start, config table, badges
[ ] All public symbols have Google-style docstrings
[ ] CONTRIBUTING.md: dev setup, test/lint commands, PR instructions
[ ] SECURITY.md: supported versions, reporting process with timeline
Versioning
[ ] All CI checks pass on the commit you plan to tag
[ ] CHANGELOG.md updated and committed
[ ] Git tag follows format v1.2.3 (semver, v prefix)
[ ] No stale local_scheme suffixes will appear in the built wheel name
CI/CD
[ ] ci.yml: lint + mypy + test matrix (all supported Python versions)
[ ] publish.yml: triggered on v*.*.* tags, uses Trusted Publishing (OIDC)
[ ] pypi environment created in GitHub repo Settings → Environments
[ ] No API tokens stored in repository secrets
The Release Command Sequence
# 1. Run full local validation
ruff check . ; ruff format . --check ; mypy src/your_package/ ; pytest
# 2. Update CHANGELOG.md — move [Unreleased] to [x.y.z]
# 3. Commit the changelog
git add CHANGELOG.md
git commit -m "chore: prepare release vX.Y.Z"
# 4. Tag and push — this triggers publish.yml automatically
git tag vX.Y.Z
git push origin main --tags
# 5. Monitor: https://github.com/<you>/<pkg>/actions
# 6. Verify: https://pypi.org/project/your-package/