Shared GitHub Actions and reusable workflows for Citrine's Python repositories.
To use these shared workflows in your repository, call them from a workflow file (e.g. .github/workflows/pr.yml):
name: PR
on:
pull_request:
jobs:
checks:
uses: CitrineInformatics/common-gh-actions/.github/workflows/repo-checks.yml@v3
secrets: inherit
with:
src: src/my_package
tests:
uses: CitrineInformatics/common-gh-actions/.github/workflows/matrix-tests.yml@v3
secrets: inherit
with:
src: src/my_packagePin to a major version tag (e.g. @v3) to receive non-breaking updates automatically.
If you want private repository access (i.e., your repository depends on another private repository for its testing), your repository must have the following Actions secrets configured:
AUTOMATION_APP_ID— The GitHub App IDAUTOMATION_APP_PRIVATE_KEY— The GitHub App private key
The initialize action will use them to generate a GitHub App token for private repository access. Pass them to reusable workflows with secrets: inherit.
Checks out the caller's repository, sets up Python (via actions/setup-python), and installs project dependencies using uv.
Other Actions (extract-version, check-version-bump, check-deprecations) do not include checkout — callers must handle it themselves.
This is intentional so that build issues do not interfere with other checks.
| Name | Required | Default | Description |
|---|---|---|---|
app-id |
no | "" |
GitHub App ID for private repository access. If omitted, private repo access is not configured. |
app-private-key |
no | "" |
GitHub App private key for private repository access. If omitted, private repo access is not configured. |
python_version |
no | (reads .python-version) |
Python version to install |
resolution |
no | "" |
Passed to uv --resolution ("highest" or "lowest-direct") |
latest-citrine |
no | true |
Install citrine from the main branch. Skipped if resolution is not "highest" or if the project itself is citrine-python (citrine is an editable dependency) |
latest-gemd |
no | true |
Install gemd from the main branch. Skipped if resolution is not "highest" or if the project itself is gemd-python (gemd is an editable dependency) |
documentation |
no | false |
Include the docs dependency group in uv sync |
Extracts the project version.
The script assumes that you have a pyproject.toml. It support both static and dynamic version strings (both file and attr), but only supports constant values. It runs in its own Python 3.12 environment so that build problems do not interfere with checks.
| Name | Required | Description |
|---|---|---|
path |
yes | Path to the project root containing pyproject.toml |
| Name | Description |
|---|---|
version |
The extracted PEP 440 version string |
Verifies that the pyproject.toml version on the PR branch is strictly greater than the version on main. Uses extract-version internally.
| Name | Required | Description |
|---|---|---|
pr_path |
yes | Path to the PR checkout containing pyproject.toml |
main_path |
yes | Path to the main checkout containing pyproject.toml |
Scans Python source files for @deprecation.deprecated decorators and warnings.warn(..., DeprecationWarning) calls, then checks whether any have passed their removal version. Uses extract-version internally.
| Name | Required | Default | Description |
|---|---|---|---|
src |
yes | Path to the source directory to scan | |
root |
no | "." |
Path to the project root containing pyproject.toml |
Runs standard PR checks: linting, version bump verification, and deprecation scanning.
| Name | Required | Default | Description |
|---|---|---|---|
src |
yes | Path to the source directory | |
linter |
no | "ruff" |
Which linter to run. Must be one of: ruff, flake8, black, none |
check_version_bump |
no | true |
Verify the version in pyproject.toml has been incremented |
check_deprecation |
no | true |
Check for expired deprecations |
- validate-inputs -- Fails fast if
linteris not a valid option. - linting --
ruff checkandruff format --checkwith the project-pinned version (whenlinterisruff). - linting-latest -- Same checks with the latest
ruffrelease (whenlinterisruff). - linting-flake8 --
flake8lint check (whenlinterisflake8). - linting-black --
black --checkformatting check (whenlinterisblack). - version-bump -- Compares PR vs. main version (skippable).
- deprecation-check -- Scans for expired deprecations (skippable).
Runs unit tests across a Python version / OS matrix, with coverage enforcement.
The default matrix includes Python 3.10–3.14; use the skip_* and include_* inputs to customize.
Coverage is only enforced on the pinned Python version (run-tests-default job).
| Name | Required | Default | Description |
|---|---|---|---|
src |
yes | Path to the source directory | |
skip_38 |
no | true |
Omit Python 3.8 from the matrix |
skip_39 |
no | true |
Omit Python 3.9 from the matrix |
skip_310 |
no | false |
Omit Python 3.10 from the matrix |
skip_311 |
no | false |
Omit Python 3.11 from the matrix |
include_313 |
no | true |
Include Python 3.13 in the matrix |
include_314 |
no | true |
Include Python 3.14 in the matrix |
include_315 |
no | false |
Include Python 3.15 in the matrix |
coverage_fails_under |
no | 100 |
Minimum required coverage percentage |
branch_coverage |
no | false |
Include --cov-branch in coverage flags at the percentage provided |
include_windows |
no | true |
Include windows-latest in the test matrix |
include_macos |
no | true |
Include macos-latest in the test matrix |
- run-tests-default -- Tests with default dependencies and coverage threshold.
- run-tests -- Matrix across Python versions and OSes with
lowest-directresolution. - run-tests-against-latest -- Matrix across Python versions and OSes with
highestresolution, potentially including the main branches of citrine-python and gemd-python.
Builds Sphinx documentation and deploys to GitHub Pages.
These are workflows that cannot be shared as reusable workflows due to external limitations. Copy them into your repository.
Packages and publishes to PyPI using an API token. Requires a repository secret called PYPI_API_TOKEN.
Cannot be a reusable workflow because PyPI's Trusted Publishing uses the calling repo's identity. See pypi/warehouse#11096.
This repo uses its own shared workflows for CI. To work locally:
uv sync --dev
uv run ruff check .github/actions tests
uv run ruff format --check .github/actions tests
uv run pytest