jenstroeger/python-package-template

GitHub: jenstroeger/python-package-template

这是一个集成了安全合规、代码质量保证与自动化发布流程的Python项目模板,专为构建高标准的软件供应链而设计。

Stars: 35 | Forks: 11

![license](https://img.shields.io/badge/license-MIT-blue) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-yellow?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![conventional-commits](https://img.shields.io/badge/conventional%20commits-1.0.0-yellow)](https://www.conventionalcommits.org/en/v1.0.0/) [![black](https://img.shields.io/badge/code%20style-black-000000)](https://github.com/psf/black) [![mypy](https://img.shields.io/badge/mypy-checked-brightgreen)](http://mypy-lang.org/) [![pylint](https://img.shields.io/badge/pylint-required%2010.0-brightgreen)](http://pylint.org/) [![pytest](https://img.shields.io/badge/pytest-enabled-brightgreen)](https://github.com/pytest-dev/pytest) [![coverage](https://img.shields.io/badge/coverage-required%20100%25-brightgreen)](https://github.com/nedbat/coveragepy) [![hypothesis](https://img.shields.io/badge/hypothesis-tested-brightgreen.svg)](https://hypothesis.readthedocs.io/) # Python 包模板 此仓库旨在作为一个基础模板,即用于新 Python 包项目的 cookiecutter,同时兼顾 [PEP518](https://www.python.org/dev/peps/pep-0518/)。由于托管在 Github 上,它已经利用了几个 [Github Actions](https://docs.github.com/en/actions),在仓库端执行持续集成的检查,并实现了语义化版本的发布设置。虽然此包是具有良好工程实践的 Python 项目的起点,但它旨在通过各种方式进行改进和扩展 — 更多建议请参阅 [Wiki](https://github.com/jenstroeger/python-package-template/wiki)。 ## 目录 [功能](#features)  [类型标注](#typing)  [质量保证](#quality-assurance)  [广泛的测试](#extensive-testing)  [文档](#documentation)  [版本控制与发布](#versioning-and-publishing)  [依赖分析](#dependency-analysis)  [安全分析](#security-analysis)  [包还是应用程序?](#package-or-application) [如何使用此仓库](#how-to-use-this-repository) [更新依赖包](#updating-dependent-packages) [Git 钩子](#git-hooks) [测试](#testing) [生成文档](#generating-documentation) [与此模板仓库同步](#synchronizing-with-this-template-repo) [版本控制、发布和分发](#versioning-publishing-and-distributions)  [从源码分发包构建](#building-from-a-source-distribution-package)  [使用 Simple Index](#using-the-simple-index) [使用 SLSA 框架确保构建完整性](#build-integrity-using-slsa-framework) [清理](#cleaning-up) [常见问题](#frequently-asked-questions) ## 功能 上方的徽标为您展示了此项目模板所提供的内容。这项工作仍在进行中,我尝试通过 [git hooks](https://git-scm.com/docs/githooks)(参见[下方](#git-hooks))和 Github Actions 尽可能多地启用合理的工程实践。 ### 类型标注 此包至少需要 [Python 3.10](https://www.python.org/downloads/release/python-31015/),并支持 [Python 3.11](https://www.python.org/downloads/release/python-31110/)、[Python 3.12](https://www.python.org/downloads/release/python-3127/) 和 [Python 3.13](https://www.python.org/downloads/release/python-3130/)(默认)。所有代码都需要全面的 [类型标注](https://docs.python.org/3/library/typing.html)。[mypy](http://mypy-lang.org/) 静态类型检查器和 [flake8-pyi](https://github.com/PyCQA/flake8-pyi) 插件通过 git hooks 和 Github Action 调用,以强制对 Python 源代码和 [stub 文件](https://peps.python.org/pep-0484/#stub-files) 进行持续的类型检查。请确保在代码中添加类型提示,或使用 [stub 文件](https://mypy.readthedocs.io/en/stable/stubs.html) 来定义类型,以确保您包的用户可以 `import` 并对您的代码进行类型检查(另请参阅 [PEP 561](https://www.python.org/dev/peps/pep-0561/))。 ### 质量保证 许多 git hooks 在提交前后以及推送前被调用。这些钩子均由 [pre-commit](https://pre-commit.com/) 工具管理,并强制执行多项 [软件质量保证](https://en.wikipedia.org/wiki/Software_quality_assurance) 措施(参见[下方](#git-hooks))。 ### 广泛的测试 使用 [pytest](https://pytest.org/) 作为测试运行器启用了全面的测试,并结合了 [doctest](https://docs.python.org/3/library/doctest.html)、[Hypothesis](https://hypothesis.works/)(用于支持[基于属性的测试](https://en.wikipedia.org/wiki/Software_testing#Property_testing))、[Faker](https://github.com/joke2k/faker) 和 [Cases](https://github.com/smarie/python-pytest-cases)(用于生成有效的、本地化的测试用例数据),同时使用 [coverage](https://github.com/nedbat/coveragepy) 衡量代码覆盖率和分支覆盖率(参见[下方](#testing))。 ### 文档 文档非常重要,[Sphinx](https://www.sphinx-doc.org/en/master/) 已配置为假设代码包含 [reStructuredText 格式的 docstrings](https://www.python.org/dev/peps/pep-0287/),从而为包生成标准的 HTML 和 Markdown 格式文档;生成的 Markdown 文档也可以选择性地推送到仓库的 Github Wiki(参见[下方](#generating-documentation))。 ### 版本控制与发布 使用 Github Actions 启用了自动包版本控制和标记、发布到 [PyPI](https://pypi.org/) 以及生成 [Changelog](https://en.wikipedia.org/wiki/Changelog)。此外,一个可选的 [Release Notification](https://github.com/jenstroeger/python-package-template/tree/main/.github/workflows/release-notifications.yaml) Action 允许 Github 向您选择的 [Slack bot](https://api.slack.com/bot-users) 推送更新通知。有关设置说明,请参阅[下方](#versioning-publishing-and-changelog)。 ### 依赖分析 已启用 [Dependabot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates) 来扫描依赖项,并在有可用更新版本时自动创建拉取请求。 ### 安全分析 已启用 [CodeQL](https://codeql.github.com/) 来扫描 Python 代码中的安全漏洞。您可以调整 `.github/workflows/codeql-analysis.yaml` 中的 GitHub Actions 工作流和 `.github/codeql/codeql-config.yaml` 中的配置文件,以添加更多语言、更改默认路径、扫描计划和查询。 [OSSF Security Scorecards](https://github.com/ossf/scorecard) 作为 GitHub Actions 工作流已启用,旨在向消费者提供有关此项目供应链安全态势的信息,评分为 0–10 分。我们在每次运行后将结果作为 SARIF(静态分析结果交换格式)工件上传,结果可以在此 GitHub 项目的 Security 选项卡中找到。我们还允许在 [OpenSSF](https://metrics.openssf.org/) 上发布数据。我们使用这些数据持续改进项目的安全态势。请注意,此配置支持 ``main``(默认)分支,并要求仓库是公开的且未被复刻。 [Macaron](https://oracle.github.io/macaron/pages/macaron_action.html) 作为 GitHub Actions 工作流已启用,用于供应链安全检测。它运行 `check-github-actions` 策略来分析 GitHub 工作流中的不安全模式和潜在风险。当检测到问题时,工作流摘要中会包含详细发现和修复建议。完整报告也会作为工作流工件上传,以供进一步审查。 [pip-audit](https://github.com/pypa/pip-audit) 是默认 Python 虚拟环境的一部分,可用于通过查询 [Python Packaging Advisory Database](https://github.com/pypa/advisory-database) 来检查所有已安装的包是否存在已记录的 [CVE](https://www.cve.org/)。`_build.yaml` 工作流始终在构建工件之前运行包审计。在自动化的生产环境中,在极少数情况下_可能_有必要暂停包审计,在这种情况下,您可以向仓库[添加一个仓库变量](https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository) `DISABLE_PIP_AUDIT`,值为 `true`,以显式禁用运行 pip-audit。 ### 包还是应用程序? _共享包_(Shared package)或库旨在供另一个包或应用程序导入;_应用程序_(Application)是一个独立的、可运行的包。不幸的是,Python 的打包生态系统主要集中在打包共享包(库)上,而对 Python 应用程序的打包支持不佳([讨论](https://discuss.python.org/t/help-packaging-optional-application-features-using-extras/14074/7))。然而,此模板同时支持这两种场景。 **共享包**:此模板开箱即用作为共享包。对其他包的直接依赖在 `pyproject.toml` 中声明(参见 [`dependencies`](https://flit.pypa.io/en/latest/pyproject_toml.html#dependencies) 字段),并应允许尽可能宽的版本范围,以确保此包及其依赖项可以被其他包和应用程序安装并存,而不会发生版本冲突。 **应用程序**:[`__main__.py`](https://docs.python.org/3/library/__main__.html#main-py-in-python-packages) 文件确保了使用 Python 的 [-m](https://docs.python.org/3/using/cmdline.html#cmdoption-m) 命令行选项将此包作为独立应用程序运行的入口点。通过 `make setup` 或 `make upgrade`,还会生成一个名为 `something` 的包装脚本,作为 [此包的入口点](https://flit.pypa.io/en/latest/pyproject_toml.html#scripts-section)。除了在 `pyproject.toml` 中指定直接依赖包及其版本范围外,应用程序应使用 [`requirements.txt`](https://pip.pypa.io/en/latest/user_guide/#requirements-files) _锁定_(pin)其整个环境。如果您正在构建应用程序,请使用 `make requirements` 命令生成该文件。 生成的 `requirements.txt` 文件及其每个依赖包的完整性哈希值用于生成 [CycloneDX 格式](https://cyclonedx.org/) 的 [软件物料清单 (SBOM)](https://www.cisa.gov/sbom)。这是提供打包过程透明度的重要来源材料(另请参阅 [SBOM + SLSA](https://slsa.dev/blog/2022/05/slsa-sbom))。该 `requirements.txt` 文件连同 SBOM 一起,也会作为每个包版本的构建工件存储。 ## 如何使用此仓库 如果您想为项目模板做出贡献,请提出 issue 进行讨论或提交拉取请求。 如果您想从头开始自己的 Python 项目,您可以将此仓库的内容复制到新的项目文件夹中,或者复刻此仓库。无论哪种方式,请考虑对您的本地副本进行以下调整: - 根据您的需要更改 `LICENSE.md` 文件和许可证徽标,并调整 `SECURITY.md` 文件以满足您的需求(更多详细信息请参阅[此处](https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository))。删除 `CHANGELOG.md` 文件中的所有内容。 - 将 `src/package/` 文件夹重命名为您自己的包名,调整 `.github/workflows/` 中的 Github Actions,并相应地审查 `Makefile`、`pyproject.toml`、`.pre-commit-config.yaml` 文件以及测试。**注意**:默认情况下,所有 Actions 在三种不同的主机类型(Linux、MacOS 和 Windows)上运行,其[速率差异很大](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#minute-multipliers),因此如果您使用的是私有仓库,请确保相应地禁用或做好预算规划! - 如果不存在,应创建一个新的受保护的 `release` 分支。应为此分支配置适当的安全策略和基本检查,以确保发布过程的完整性和稳定性。 - 根据您的需要调整 `pyproject.toml` 文件的内容,并确保填写项目 URL、维护者和作者信息。不要忘记在 `src/package/__init__.py` 中重置包的版本号。 - 如果您将不提供类型提示的包导入到新仓库中,则需要相应地配置 `mypy`:使用 [`ignore_missing_imports`](https://mypy.readthedocs.io/en/stable/config_file.html#confval-ignore_missing_imports) 选项将这些包添加到 `pyproject.toml` 文件中。 - 如果您想将包发布到 PyPI,请取消 [`release.yaml`](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/release.yaml) Action 中的代码注释,并添加适当的环境变量。 - 调整 `.github/dependabot.yaml` 中的 Dependabot 设置,设置为您希望 Dependabot 监控的目标分支。 - 为您的 Github 用户帐户创建以下具有特定[范围](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes)的 [Personal Access Tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) (PAT),然后为新的 Github 仓库[创建密钥](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository),其值为这些新的 PAT: - 一个具有 `workflow` 和 `repo` 范围(包括 _所有_ `repo` 权限)的 PAT,用于名为 `REPO_ACCESS_TOKEN` 的密钥;此密钥由 [Release Action](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/release.yaml) 使用,用于推送发布提交并将资源附加到生成的 [Github release](https://github.com/jenstroeger/python-package-template/releases) 上。 - 一个具有 `public_repo`、`read:discussion`、`read:org` 和 `read:repo_hook` 范围([详细文档](https://github.com/ossf/scorecard-action#authentication-with-pat-optional))的 PAT,用于名为 `SCORECARD_READ_TOKEN` 的密钥;此密钥由 [Scorecard Action](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/scorecards-analysis.yaml) 使用,用于分析代码并将其结果添加到您的仓库中。 - 一个具有 `repo` 范围的 PAT,用于名为 `DEPENDABOT_AUTOMERGE_TOKEN` 的密钥;此密钥由 [Dependabot Automerge Action](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/dependabot-automerge.yaml) 使用,用于在 Dependabot PR 上添加评论。 - 为您的新仓库创建一个 Wiki 和第一个空的 Wiki 页面。使用 [Wiki Documentation](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/_wiki-documentation.yaml) Action,仓库的 Wiki 将作为发布新版本的一部分自动更新。 要开发您的新包,首先使用 [Makefile](https://www.gnu.org/software/make/manual/make.html#toc-An-Introduction-to-Makefiles) 创建一个 [虚拟环境](https://docs.python.org/3/tutorial/venv.html): ``` make venv # Create a new virtual environment in .venv folder using Python 3.13. ``` 或者针对特定版本的 Python: ``` PYTHON=python3.12 make venv # Same virtual environment for a different Python version. ``` 或者手动创建: ``` python3.13 -m venv .venv # Or use .venv313 for more than one local virtual environments. ``` 使用此 Makefile 时_始终 [激活虚拟环境](https://docs.python.org/3/library/venv.html) 是很重要的_,因为某些 [git hooks](#git-hooks)(见下文)依赖于这一点: ``` . .venv/bin/activate # Where . is a bash shortcut for the source command. ``` 最后,设置包含所有额外组件的新包并初始化本地 git hooks: ``` make setup ``` 完成这些后,您就可以构建自己的包了! ## 更新依赖包 在开发过程中,您很可能会在 `pyproject.toml` 文件中添加或更新依赖包,这需要更新虚拟环境: ``` make upgrade ``` ## Git 钩子 使用 pre-commit 工具及其 `.pre-commit-config.yaml` 配置,此仓库中激活了以下 git hooks: - 提交代码时,许多 [pre-commit hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks) 确保您的代码根据 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 使用 [`black`](https://github.com/psf/black) 工具进行格式化,并调用 [`flake8`](https://github.com/PyCQA/flake8)(及各种插件)、[`pylint`](https://github.com/PyCQA/pylint) 和 [`mypy`](https://github.com/python/mypy) 来检查 lint 和正确的类型。还有更多检查,但这两个是最重要的。您可以在 `pyproject.toml` 或 `.flake8` 配置文件中调整这些工具的设置。 - [提交消息钩子](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks) 强制执行 [约定式提交消息](https://www.conventionalcommits.org/),这反过来又在 Github 端启用了此包的 _语义化版本发布_:将更改合并到 `release` 分支后,[release action](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/release.yaml) 使用 [Commitizen 工具](https://commitizen-tools.github.io/commitizen/) 生成 [changelog](https://en.wikipedia.org/wiki/Changelog),并计算此包的下一个版本并发布一个版本 — 所有这些均基于发布的提交消息。 - 使用 [pre-push hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_other_client_hooks),此包还设置为运行 [`pytest`](https://github.com/pytest-dev/pytest);此外,[`coverage`](https://github.com/nedbat/coveragepy) 插件确保 _所有_ 包代码都由单元测试覆盖,并且 [Hypothesis](https://hypothesis.works/) 和 [Faker](https://github.com/joke2k/faker) 已安装,以帮助生成测试用例有效负载。 - [`actionlint`](https://github.com/Mateusz-Grzelinski/actionlint-py) 钩子设置为对 GitHub Actions 工作流进行 lint 检查。如果系统上安装了 [`shellcheck`](https://github.com/koalaman/shellcheck),`actionlint` 将运行 `shellcheck` 来对 GitHub Actions 中的 `run` 步骤进行 lint 检查。请注意,[Ubuntu GitHub Actions runners](https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md) 上默认提供 `shellcheck`。 您还可以手动运行这些 hooks,这在日常开发任务中非常方便。例如 ``` make check-code ``` 运行所有代码检查(即 `bandit`、`flake8`、`pylint`、`mypy`、`actionlint`),而 ``` make check ``` 对您的代码运行 _所有_ 已安装的 git hooks。为了对代码检查进行更多控制,Makefile 还实现了 `check-bandit`、`check-flake8`、`check-lint`、`check-mypy` 和 `check-actionlint` 目标。 ## 测试 如上所述,此仓库已设置为使用 [pytest](https://pytest.org/) 作为测试运行器,既可以独立运行,也可以作为 pre-push git hook 运行。测试存储在 `tests/` 文件夹中,并组织为单元测试、集成测试和性能测试。您可以像这样手动运行测试: ``` make test-all # Run all tests: unit, integration, performance. ``` 这会在您的本地 Python 虚拟环境中运行所有测试。有关更多选项,请参阅 [pytest 命令行标志](https://docs.pytest.org/en/7.4.x/reference/reference.html#command-line-flags)。 有三种类型的测试: 1. [单元测试](https://en.wikipedia.org/wiki/Unit_testing) 通过 `make test` 或 `make test-unit` 调用; 2. [集成测试](https://en.wikipedia.org/wiki/Integration_testing) 通过 `make test-integration` 调用;以及 3. [性能测试](https://en.wikipedia.org/wiki/Software_performance_testing)(使用 [pytest-benchmark](https://github.com/ionelmc/pytest-benchmark) 插件)通过 `make test-performance` 调用。 请注意,单元测试包括 [doctest](https://docs.python.org/3/library/doctest.html),这意味着模块和函数 [docstrings](https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring) 以及文档可能包含作为单元测试一部分执行的测试代码。当将更改推送到远程时,pre-push hook *仅*运行单元测试,而 *所有* 三种类型的测试都作为 Github CI 的一部分运行。 使用 [coverage](https://github.com/nedbat/coveragepy) 和 pytest 的 [pytest-cov](https://github.com/pytest-dev/pytest-cov) 插件跟踪语句和分支覆盖率,并在运行单元测试时衡量 `src/package/` 文件夹中有多少代码被测试覆盖: ``` =========================================== test session starts =========================================== platform darwin -- Python 3.13.9, pytest-8.4.2, pluggy-1.6.0 -- /path/to/python-package-template/.venv/bin/python cachedir: .pytest_cache hypothesis profile 'default-with-verbose-verbosity' -> verbosity=Verbosity.verbose benchmark: 5.2.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) rootdir: /path/to/python-package-template configfile: pyproject.toml plugins: cases-3.9.1, hypothesis-6.138.16, env-1.1.5, cov-6.3.0, custom-exit-code-0.3.0, doctestplus-1.4.0, Faker-37.6.0, benchmark-5.2.0 collected 6 items / 2 deselected / 4 selected src/package/something.py::package.something.Something.do_something PASSED [ 25%] tests/unit/test_something.py::test_something_hypothesis PASSED [ 50%] tests/unit/test_something.py::test_something_cases[_case_boolean] PASSED [ 75%] docs/source/index.rst::index.rst PASSED [100%] ============================================= tests coverage ============================================== ____________________________ coverage: platform darwin, python 3.13.9-final-0 _____________________________ Name Stmts Miss Branch BrPart Cover Missing ---------------------------------------------------------------------- src/package/__init__.py 1 0 0 0 100% src/package/something.py 4 0 0 0 100% ---------------------------------------------------------------------- TOTAL 5 0 0 0 100% Required test coverage of 100.0% reached. Total coverage: 100.00% ========================================== Hypothesis Statistics ========================================== tests/unit/test_something.py::test_something_hypothesis: - during generate phase (0.00 seconds): - Typical runtimes: ~ 0-1 ms, of which < 1ms in data generation - 2 passing examples, 0 failing examples, 0 invalid examples - Stopped because nothing left to do ============================================ slowest durations ============================================ 0.10s setup src/package/something.py::package.something.Something.do_something 0.01s call tests/unit/test_something.py::test_something_hypothesis 0.00s call src/package/something.py::package.something.Something.do_something 0.00s setup tests/unit/test_something.py::test_something_cases[_case_boolean] 0.00s call docs/source/index.rst::index.rst 0.00s setup docs/source/index.rst::index.rst 0.00s teardown src/package/something.py::package.something.Something.do_something 0.00s teardown tests/unit/test_something.py::test_something_cases[_case_boolean] 0.00s call tests/unit/test_something.py::test_something_cases[_case_boolean] 0.00s setup tests/unit/test_something.py::test_something_hypothesis 0.00s teardown docs/source/index.rst::index.rst 0.00s teardown tests/unit/test_something.py::test_something_hypothesis ===================================== 4 passed, 2 deselected in 0.23s ===================================== ``` 请注意,未被测试覆盖的代码列在 `Missing` 列下,未执行的分支也是如此。强制执行 100% 的代码和分支覆盖率的实际效果是,每个新的主要和次要功能、每个代码更改和每个修复都经过了测试(请记住,高 _覆盖率_ 并不意味着全面、有意义的 _测试数据_)。 Hypothesis 是一个实现 [基于属性的测试](https://en.wikipedia.org/wiki/Software_testing#Property_testing) 的包,并根据策略描述为您的测试提供有效负载生成([更多](https://hypothesis.works/#what-is-hypothesis))。使用其 [pytest 插件](https://hypothesis.readthedocs.io/en/latest/details.html#the-hypothesis-pytest-plugin),Hypothesis 已准备好用于此包。同样,[Faker](https://github.com/joke2k/faker) 包及其 [pytest 插件](https://faker.readthedocs.io/en/master/#pytest-fixtures) 已安装,以提供有效的、本地化的测试用例数据(有关如何分离测试和测试用例的更多详细信息,另请参阅 [pytest-cases](https://github.com/smarie/python-pytest-cases) 插件)。 ## 生成文档 如上所述,所有包代码都应使用 [reStructured text 格式](https://www.python.org/dev/peps/pep-0287/) 的 [Python docstrings](https://www.python.org/dev/peps/pep-0257/)。使用这些 docstrings 和 `docs/source/` 文件夹中的文档模板,您可以使用 [Sphinx](https://github.com/sphinx-doc/sphinx/) 工具生成不同格式的适当文档: ``` make docs ``` 此示例生成 HTML 格式的文档,可以在此处找到: ``` open docs/_build/html/index.html ``` 除了默认的 HTML 之外,Sphinx 还生成兼容 [Github Wiki](https://docs.github.com/en/communities/documenting-your-project-with-wikis) 的 Markdown 文档,并且 [Wiki Documentation](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/_wiki-documentation.yaml) Action 会自动更新项目仓库的 Wiki。 ## 与此模板仓库同步 [sync-with-upstream.yaml](https://github.com/jenstroeger/python-package-template/blob/main/.github/workflows/sync-with-upstream.yaml) GitHub Actions 工作流每天检查此模板仓库,如果有新版本,会自动在下游仓库中创建一个拉取请求。请确保在此工作流中相应地设置 Github 用户名和电子邮件地址。 ## 版本控制、发布和分发 要启用 [语义化版本控制](https://semver.org/)、包发布和 changelog 生成的自动化,使用有意义的 [约定式提交消息](https://www.conventionalcommits.org/) 至关重要!此包模板已内置语义化版本发布支持,设置为处理这三个方面 — 每次将更改推送到 `release` 分支时。 每次发布包时,都会向 `release` 分支推送一个新的 `bump:` 提交,并标记包的新版本。此外,`main` 分支(此仓库用于将合并的拉取请求暂存到其中以供下一版本发布)会自动基于更新后的 `release` 分支进行变基,以便在保持 [线性历史](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches#require-linear-history) 的同时合并后续的拉取请求。 如果您希望在发布新版本时收到 Slack 通知,请遵循 [Release Notification](https://github.com/jenstroeger/python-package-template/tree/main/.github/workflows/_release-notifications.yaml) Action 中的注释,并按照[此处的说明](https://github.com/slackapi/slack-github-action#setup-2) 设置 Slack bot。 为了在本地构建包的分发版本,而不是通过 Github Actions 工作流发布,您可以简单地调用: ``` make dist ``` 这会构建一个源码包([sdist](https://packaging.python.org/en/latest/discussions/package-formats/#what-is-a-source-distribution))和一个二进制分发([wheel](https://packaging.python.org/en/latest/discussions/package-formats/#what-is-a-wheel)),并将文件存储在您的本地 `dist/` 文件夹中。 您还可以使用 commitizen(已作为开发依赖项安装)在本地手动生成 changelog 并提升版本,例如: ``` cz changelog cz bump ``` ## 从源码分发包构建 源码分发包([sdist](https://packaging.python.org/en/latest/discussions/package-formats/#what-is-a-source-distribution))包含检查、测试和构建二进制分发([wheel](https://packaging.python.org/en/latest/discussions/package-formats/#what-is-a-wheel))及其文档所需的一切;这对于使用自定义流程构建自己的软件分发包的第三方打包服务特别有用。 要从源码分发包构建,只需按照以下步骤操作: ``` tar zxvf package.tar.gz # Unpack the sdist tar file. cd package/ git init # We need this to be a Git repository to run checks. git add . # Add all files so tools find them via the VCS. ``` 我们需要将包文件夹初始化为 Git 仓库,以确保 Makefile 能够通过 hooks 调用各种检查器。完成后,我们可以像以前一样使用 `make`: ``` SKIP=check-hooks-apply,check-useless-excludes,actionlint make dist ``` 请注意,我们跳过了从源码分发构建时不必要的 Git hooks。如上所述,这会构建源码包和二进制分发,并将两者都存储在 `dist/` 文件夹中。而且,正如预期的那样,将 `SOURCE_DATE_EPOCH` 环境变量设置为原始 sdist 和 wheel 构建的构建 epoch 值,将产生位完全相同的二进制分发包! ## 使用 Simple Index 一旦构建了源码和/或二进制分发包,就可以使用兼容 [PEP 503](https://peps.python.org/pep-0503/) 的包仓库来提供它们。只需调用 ``` make simple-index ``` 在 `dist/` 文件夹中创建包仓库,然后例如使用 [pip](https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-extra-index-url) 及其 `--extra-index-url` 参数来使用它。 ## 使用 SLSA 框架确保构建完整性 此仓库中的构建过程遵循 [SLSA 框架](https://slsa.dev/) 的要求,以符合 3 级标准。SLSA 改善供应链安全态势的一个重要方面是为构建流程生成可验证的来源证明。这种来源证明可用于验证构建者,并让消费者检查构建工件时使用的材料和配置。在此仓库中,我们使用 [通用来源证明生成器可重用工作流](https://github.com/slsa-framework/slsa-github-generator) 来生成来源证明,该证明可以证明每个版本中的以下工件: - 二进制分发(wheel) - 源码分发(tarball) - SBOM(CycloneDx 格式) - HTML 和 Markdown 文档 - 构建时间的 [UNIX epoch](https://en.wikipedia.org/wiki/Unix_time) 时间戳文件,用于[可重现构建](https://reproducible-builds.org/) 要使用来源证明验证工件,请按照 [SLSA verifier](https://github.com/slsa-framework/slsa-verifier) 项目中的说明安装验证器工具。下载工件和来源证明后,逐一验证每个工件,例如: ``` slsa-verifier -artifact-path ~/Downloads/package-2.2.0.tar.gz -provenance attestation.intoto.jsonl -source github.com/jenstroeger/python-package-template ``` 这应该会通过并提供验证详细信息。 ## 清理 有时清理陈旧文件、`mypy` 等工具留下的缓存,甚至是彻底清除整个虚拟环境是很有用的(甚至是必要的): - **移除分发工件**:`make dist-clean` - 此外,**移除工具缓存和文档**:`make clean` - 此外,**移除 Python 代码缓存和 git hooks**:`make nuke-caches` - 此外,为了**重置所有内容**,以恢复一个干净的包并重新开始:`make nuke` 清除环境时请务必小心,并确保您知道自己在做什么。 ## 常见问题 - **问题**:为什么不使用像 [tox](https://github.com/tox-dev/tox) 或 [nox](https://github.com/theacodes/nox) 这样的工具来编排测试? **回答**:我们根据 [issue #100](https://github.com/jenstroeger/python-package-template/issues/100) 和 [PR #102](https://github.com/jenstroeger/python-package-template/pull/102) 中的讨论移除了 `tox`。简而言之:我们要使用 `pytest` 在开发 venv 中运行测试,并使用 Github Actions 通过广泛的测试矩阵运行更多测试。
标签:Anchore, Black, Cookiecutter, GitHub Actions, Hypothesis, Mypy, PEP518, Pre-commit, Pylint, Pytest, Python, SBOM, SLSA, WebSocket, 代码规范, 依赖分析, 单元测试, 可复现构建, 安全扫描, 开源框架, 持续集成, 无后门, 时序注入, 测试覆盖率, 硬件无关, 类型检查, 脚手架, 自动化发布, 自动笔记, 语义化版本, 软件供应链安全, 远程方法调用, 逆向工具, 项目模板