Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/CONFIG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# GitHub Actions Configuration

This document describes the secrets used by the Rhiza-provided GitHub Actions workflows
(`.github/workflows/rhiza_*.yml`) and how to configure them.

## PAT_TOKEN

Some workflows may need to push changes to files under `.github/workflows/`. The
automatic `github.token` **cannot** do that — GitHub rejects such pushes unless
the token carries the `workflow` scope. If you need it, create a Personal Access
Token (PAT) with the `workflow` scope and store it as a repository secret named
`PAT_TOKEN`.

If `PAT_TOKEN` is not configured, workflows fall back to `github.token`.

### Creating the token

**Fine-grained PAT** (recommended):

1. Go to **Settings → Developer settings → Fine-grained tokens → Generate new token**
(<https://github.com/settings/personal-access-tokens/new>).
2. Restrict **Repository access** to the repository (or repositories) using Rhiza.
3. Under **Repository permissions**, grant:
- **Contents**: Read and write
- **Workflows**: Read and write
- **Pull requests**: Read and write (needed for the scheduled sync-PR mode)
4. Generate the token and copy it.

**Classic PAT** (alternative):

1. Go to **Settings → Developer settings → Tokens (classic) → Generate new token**.
2. Select the `repo` and `workflow` scopes.
3. Generate the token and copy it.

### Storing the secret

In the repository that consumes Rhiza:

1. Go to **Settings → Secrets and variables → Actions → New repository secret**.
2. Name: `PAT_TOKEN`
3. Value: the token created above.

Or with the GitHub CLI:

```bash
gh secret set PAT_TOKEN
```

A PAT expires; when sync pushes start failing with a `refusing to allow ... workflow` error,
regenerate the token and update the secret.

## Release workflow secrets (optional)

The release workflow (`.github/workflows/rhiza_release.yml`) supports additional secrets, all
optional depending on which release features you use:

| Secret | Purpose |
| --- | --- |
| `PYPI_TOKEN` | Publish the built package to PyPI. Not needed when using trusted publishing (OIDC). |
| `GH_PAT` | Git authentication for installing private dependencies during the release build. |
| `UV_EXTRA_INDEX_URL` | Extra package index URL (with credentials) for private dependencies. |

`GITHUB_TOKEN` is provided automatically by GitHub Actions and needs no configuration.
2 changes: 1 addition & 1 deletion .github/workflows/rhiza_benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ on:

jobs:
benchmark:
uses: jebel-quant/rhiza/.github/workflows/rhiza_benchmark.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_benchmark.yml@v1.5.1
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/rhiza_book.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ on:

jobs:
book:
uses: jebel-quant/rhiza/.github/workflows/rhiza_book.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_book.yml@v1.5.1
secrets: inherit
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rhiza_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ on:

jobs:
ci:
uses: jebel-quant/rhiza/.github/workflows/rhiza_ci.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_ci.yml@v1.5.1
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/rhiza_codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ on:

jobs:
codeql:
uses: jebel-quant/rhiza/.github/workflows/rhiza_codeql.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_codeql.yml@v1.5.1
secrets: inherit
permissions:
security-events: write # Upload CodeQL results to code scanning
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rhiza_marimo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ on:

jobs:
marimo:
uses: jebel-quant/rhiza/.github/workflows/rhiza_marimo.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_marimo.yml@v1.5.1
secrets: inherit
16 changes: 13 additions & 3 deletions .github/workflows/rhiza_paper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
# Workflow: Paper
#
# Purpose: Compile the LaTeX paper (docs/paper/*.tex) to a PDF and publish it
# as a downloadable workflow artifact. Pushes the PDF to a paper branch.
# as a downloadable workflow artifact. The durable copy is published by
# the book: `paper` is a prerequisite of `book`, and docs/paper/ sits
# inside the docs tree, so the PDF ships as a site asset.
#
# It no longer pushes to an orphan `paper` branch (rhiza #1494): that
# ref cannot coexist with any `paper/<topic>` branch, so the push failed
# in exactly the repositories most likely to have one. A repository that
# still has the branch gets a warning naming it; delete it when nothing
# links to it.
#
# Trigger: On push/PR to main/master when docs/paper/** changes, or manual dispatch.

Expand All @@ -28,7 +36,9 @@ on:

jobs:
paper:
uses: jebel-quant/rhiza/.github/workflows/rhiza_paper.yml@v1.4.2
uses: jebel-quant/rhiza/.github/workflows/rhiza_paper.yml@v1.5.1
secrets: inherit
# `contents: read` only. The `write` scope this stub used to grant existed solely for
# the retired branch push; compiling and uploading an artifact need no write access.
permissions:
contents: write
contents: read
19 changes: 18 additions & 1 deletion .github/workflows/rhiza_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,24 @@ jobs:
name: Finalise Release
runs-on: ubuntu-latest
needs: [tag, pypi, conda, devcontainer]
if: needs.pypi.result == 'success' || needs.conda.result == 'success' || needs.devcontainer.result == 'success'
# `!cancelled() &&` is what makes the rest of this condition mean anything (#1537).
# GitHub combines a job's own `if` with an implicit `success()` over every entry in
# `needs` *unless* the expression names a status function -- so the OR below was
# evaluated only in runs where conda had already succeeded, and a conda failure skipped
# this job however true the OR was. That is not theoretical: releasing jointview v0.2.0,
# PyPI published successfully, grayskull 404'd on metadata that had not propagated, and
# the GitHub release was stranded as the draft `untagged-547fe31ec1ed6de3ef9b` holding
# SBOM and provenance assets that never became visible.
#
# `!cancelled()` rather than `always()`, deliberately: `always()` would finalise a
# release during a run somebody had cancelled, which is the one case where publishing is
# certainly wrong. A cancelled run still skips this job.
#
# The OR is unchanged, so the failure modes it encodes are too: if every publishing job
# fails or is skipped there is nothing to finalise, and this job skips. Recipe generation
# is the only one of the three that is a downstream convenience -- the workflow header
# has always called it optional, and now the failure path agrees.
if: ${{ !cancelled() && (needs.pypi.result == 'success' || needs.conda.result == 'success' || needs.devcontainer.result == 'success') }}
permissions:
contents: write # Needed to undraft/publish the GitHub release
steps:
Expand Down
25 changes: 11 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ __marimo__

_tests
_book
_marimushka
_pdoc
docs/notebooks/*.html
docs/reports
docs/reports.md
_mkdocs
_benchmarks
_jupyter
Expand All @@ -32,6 +35,9 @@ docs/paper/*.fls
docs/paper/*.log
docs/paper/*.out
docs/paper/*.toc
docs/paper/*.pdf
docs/paper/*.bbl
docs/paper/*.blg

# temp file used by Junie
.output.txt
Expand Down Expand Up @@ -103,25 +109,16 @@ cover/
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Cython debug symbols
cython_debug/

# Makefile
local.mk
# Makefile -- `local.mk` is deliberately not ignored. The Makefile is template-owned and
# overwritten by every sync, so `local.mk` is the only place a repo's own make targets can
# live, and anything CI invokes has to be committed. rhiza's own `make e2e`, which
# rhiza_e2e.yml runs in all three language jobs, is exactly that case.

.bandit-baseline.json


# Rust (rust-core bundle). Kept in core rather than the language layer because
# .gitignore has one owner and git opens it with O_NOFOLLOW, so it cannot be a
# per-layer file. These entries are inert in a Python repo.
Expand Down
38 changes: 33 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ repos:
rev: v6.0.0
hooks:
- id: check-toml
groups: ["fast"]
Comment on lines 14 to +15
- id: check-yaml
args: ['--unsafe']
exclude: ^recipe/meta\.yaml$
groups: ["fast"]

- repo: local
hooks:
Expand All @@ -23,12 +25,14 @@ repos:
language: fail
entry: Python cache files (__pycache__, .pyc, .pyo, .pyd) must not be committed. Run 'make clean' to remove them.
files: '(/__pycache__/|\.py[cod]$)'
groups: ["python", "fast"]

- id: no-rej-files
name: Reject .rej files
entry: "bash -c 'find . -type f -name \"*.rej\" | grep -q . && { echo \"ERROR: .rej files detected\"; find . -type f -name \"*.rej\"; exit 1; } || exit 0'"
language: system
pass_filenames: false
groups: ["fast"]

# The architectural import contracts in [tool.importlinter]: the analytics
# subpackages (_stats, _plots, _reports, _utils) annotate against the structural
Expand Down Expand Up @@ -61,73 +65,97 @@ repos:
files: '^(src/.*\.py|pyproject\.toml)$'

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.16.2'
rev: 'v0.16.4'
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix, --unsafe-fixes ]
groups: ["python", "lint", "fast"]

# Run the formatter
- id: ruff-format
groups: ["python", "format", "fast"]

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.49.0
hooks:
- id: markdownlint
args: ["--disable", "MD013"]
groups: ["lint", "fast"]

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.38.0
hooks:
- id: check-renovate
args: [ "--verbose" ]
groups: ["fast"]

- id: check-github-workflows
args: ["--verbose"]
groups: ["fast"]

- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
hooks:
- id: actionlint
groups: ["lint", "fast"]

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.25
rev: 0.26
hooks:
- id: validate-pyproject
groups: ["python", "fast"]

- repo: https://github.com/PyCQA/bandit
rev: 1.9.4
hooks:
- id: bandit
# Scope deliberately lives in .bandit, not here — see that file (#1493).
args: ["--ini", ".bandit"]
groups: ["python", "security", "slow"]

- repo: https://github.com/betterleaks/betterleaks
rev: v1.7.4
rev: v1.8.1
hooks:
- id: betterleaks
groups: ["security", "slow"]

- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.12.3
rev: 0.12.5
hooks:
- id: uv-lock
groups: ["python", "fast"]

- repo: https://github.com/econchick/interrogate
rev: 1.7.0
hooks:
- id: interrogate
args: [--config=pyproject.toml]
files: ^src/
groups: ["python", "lint", "slow"]

- repo: https://github.com/Jebel-Quant/rhiza-hooks
rev: v1.2.0 # Use the latest release
rev: v1.3.0 # Use the latest release
hooks:
# Migrated from rhiza
- id: check-rhiza-workflow-names
groups: ["fast"]
- id: update-readme-help
groups: ["fast"]
# Additional utility hooks
- id: check-rhiza-config
groups: ["fast"]
# check-managed-files refuses a commit touching any path in .rhiza/template.lock's
# `files:` list (minus `exclude:` in template.yml), enforcing the rule every managed
# repo's CLAUDE.md opens with. Only paths differing from HEAD are reported, so
# `make fmt` and CI stay green on a clean tree under --all-files. Its adoption was
# gated on the sync commit gaining `SKIP=check-managed-files` -- see CLAUDE.md's
# "Enforcing template ownership" for why that order is not optional.
- id: check-managed-files
groups: ["fast"]
- id: check-makefile-targets
groups: ["fast"]
- id: check-python-version-consistency
groups: ["python", "fast"]
# check-bumpversion-config asserts that bump-my-version can actually discover the
# project's version config (issue #1453) — for this layer, the [tool.bumpversion]
# table in pyproject.toml, since python-core deliberately ships no .bumpversion.toml.
Expand Down
2 changes: 1 addition & 1 deletion .rhiza/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Append `!` after the type/scope and add a `BREAKING CHANGE:` footer:
```
feat!: rename make target from `book` to `docs`

BREAKING CHANGE: `make book` no longer exists; use `make docs`.
BREAKING CHANGE: the `sync` make target no longer exists; use `/rhiza:update`.
```

## Code style
Expand Down
8 changes: 4 additions & 4 deletions .rhiza/template.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
sha: 31e1a11237419f746770c5339f8aa468fd1c0deb
sha: de568b1e79e5871c58b4349762ce9dfd31d73e99
repo: jebel-quant/rhiza
host: github
ref: v1.4.2
ref: v1.5.1
include: []
exclude:
- .github/ISSUE_TEMPLATE
Expand All @@ -28,7 +28,6 @@ files:
- .github/workflows/rhiza_ci.yml
- .github/workflows/rhiza_codeql.yml
- .github/workflows/rhiza_marimo.yml
- .github/workflows/rhiza_mutation.yml
- .github/workflows/rhiza_paper.yml
- .github/workflows/rhiza_release.yml
- .gitignore
Expand All @@ -38,6 +37,7 @@ files:
- .rhiza/CONTRIBUTING.md
- .rhiza/semgrep.yml
- LICENSE
- Makefile
- SECURITY.md
- cliff.toml
- docs/assets/rhiza-logo.svg
Expand All @@ -49,5 +49,5 @@ files:
- pytest.ini
- ruff.toml
- tests/test_rhiza_packaging.py
synced_at: '2026-08-22T12:00:46Z'
synced_at: '2026-08-22T14:38:47Z'
strategy: merge
2 changes: 1 addition & 1 deletion .rhiza/template.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
repository: "jebel-quant/rhiza"
ref: "v1.4.2"
ref: "v1.5.1"

profiles:
- github-project
Expand Down
Loading