Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8191043
fix(python): keep the test key source out of wheels, and tighten CI
yujiyokoo Nov 12, 2026
6b5b687
fix(python): refuse hand-written methods and read declarations correctly
yujiyokoo Nov 12, 2026
20a82b6
fix(python): give encrypt and decrypt the right error for each bad input
yujiyokoo Nov 12, 2026
0f9a4d3
test(python): cover the paths the review found without a test
yujiyokoo Nov 12, 2026
a743fdf
fix(python): small fixes from the review, with tighter tests
yujiyokoo Nov 12, 2026
8593093
fix(python): say how to fix a bare ClassVar on Python 3.9
yujiyokoo Nov 12, 2026
c7d6eb1
fix(python): build a new engine client in a forked child
yujiyokoo Nov 12, 2026
3c2b14e
fix(python): refuse a forked child the client its parent built
yujiyokoo Nov 12, 2026
7c7a534
fix(python): resolve the root workspace's vitaminc, and test that it …
yujiyokoo Nov 12, 2026
6ae2866
ci(python): give the CS_* credentials to the live test step alone
yujiyokoo Nov 12, 2026
138ddb6
fix(python): let the fuzz seeds reach the value frame read check
yujiyokoo Nov 12, 2026
11cf614
docs(python): add the customer directory example's findings
yujiyokoo Nov 12, 2026
d7cb6dd
docs(python): say which decrypt errors do not name the row
yujiyokoo Nov 12, 2026
260902b
refactor(python): read the endpoint and client key with the engine's …
yujiyokoo Nov 12, 2026
75c7810
build(python): lock maturin, and build with the locked copy
yujiyokoo Nov 12, 2026
8187e43
chore(ci): say that Dependabot updates the locked maturin
yujiyokoo Nov 12, 2026
5b9b1fd
fix(python): clearer refusal messages, and seal Pydantic's other prin…
yujiyokoo Nov 12, 2026
af68a55
test(python): pin the value numbers, the commit rules, and the genera…
yujiyokoo Nov 12, 2026
eb5a4cc
test(python): close the gaps the review marked low
yujiyokoo Nov 12, 2026
47bbb9f
ci(python): cache both workspaces in one step, and check two more steps
yujiyokoo Nov 12, 2026
bf84840
docs(python): close the example's first four findings
yujiyokoo Nov 12, 2026
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
15 changes: 9 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,15 @@ updates:
update-types:
- version-update:semver-major

# ── uv (languages/python — the Python SDK's test dependencies) ──
# The package has no runtime dependency, so uv.lock holds only the `test`
# extra: pytest, attrs, Pydantic, SQLAlchemy and psycopg, and what they
# need. tests-python.yml installs it with `uv sync --locked`. The extra has
# lower bounds and no upper bounds, so a new minor or patch release changes
# only the lock.
# ── uv (languages/python — the Python SDK's test and build tools) ──
# The package has no runtime dependency. So uv.lock contains two sets of
# tools: the `test` extra (pytest, attrs, Pydantic, SQLAlchemy, psycopg and
# Alembic), and the `build` dependency group (maturin, the build backend),
# with what they need. `mise run build` installs both with
# `uv sync --locked`. Dependabot's uv parser reads `[dependency-groups]`
# as well as the extras, so maturin is updated with the rest. Both have
# lower bounds and no major upper bound, so a new minor or patch release
# changes only the lock.
- package-ecosystem: uv
directory: /languages/python
# Monthly, matching the other non-npm toolchains. No `day:`, for the
Expand Down
66 changes: 49 additions & 17 deletions .github/workflows/tests-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ on:
pull_request:
paths:
- languages/python/**
# The example has its own lock and its own tests, which CI does not
# run. A change only there must not start this workflow.
- '!languages/python/examples/**'
# The crates the workspace compiles by path, in the order Cargo.lock
# lists them. The last two are what stack-kms's `profile` feature pulls
# in. The root manifest
Expand All @@ -38,6 +41,9 @@ on:
- packages/stack-auth/**
- packages/stack-profile/**
- Cargo.toml
# Cargo reads the root .cargo/config.toml for this workspace too. A
# [patch] or [build] change there changes what this workspace compiles.
- .cargo/**
# The EQL codegen and the catalog it reads. The SDK's marker names are
# generated from the catalog (#1385). A change to either must rerun the
# suite that reads them.
Expand All @@ -59,6 +65,9 @@ on:
# Go workflow's eql-postgres job does, and runs the secrets pre-flight.
- packages/eql/src/**
- packages/eql/mise.toml
# The EQL build compiles eql-codegen, whose versions these two choose.
- packages/eql/Cargo.toml
- packages/eql/Cargo.lock
- packages/eql/tasks/**
- .github/actions/require-cs-secrets/**
- .github/workflows/tests-python.yml
Expand Down Expand Up @@ -206,15 +215,15 @@ jobs:
live:
name: "Python live: encrypt into PostgreSQL with EQL v4"
# A fork's pull request has no secrets: skip it rather than fail it, as
# the integration workflows do.
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
# the integration workflows do. Dependabot's pull requests come from this
# repository but get no Actions secrets either, so they skip too.
if: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' }}
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 45
env:
CS_WORKSPACE_CRN: ${{ vars.CS_WORKSPACE_CRN }}
CS_CLIENT_ID: ${{ vars.CS_CLIENT_ID }}
CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }}
CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }}
# The CS_* credentials are given to the `Live tests` step alone, not the
# job. Every other step, the builds above all, runs third-party code: each
# dependency's build.rs and proc-macros, and maturin. None of it runs with
# the credentials set.
services:
postgres:
image: postgres:17
Expand Down Expand Up @@ -268,22 +277,33 @@ jobs:
# Must name runs-on's image; see scripts/__tests__/workflow-mise-cache-image.test.mjs
cache_key_prefix: mise-v1-ubuntu-2404

# One cache per workspace: this one, and EQL's, whose eql-codegen
# writes the bundle.
# One step for both workspaces: this one, and EQL's, whose eql-codegen
# writes the bundle. Two steps would each prune the shared cargo
# registry to their own crates before they save, so each save would
# lose the other workspace's crates.
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: languages/python
workspaces: |
languages/python
packages/eql

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: packages/eql
# The builds, with no credentials: the extension into .venv, and the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor The build step and the Live tests step run on the same runner and filesystem. Anything the build step leaves behind therefore runs with CS_CLIENT_KEY and CS_CLIENT_ACCESS_KEY set: a .pth in .venv, a $GITHUB_ENV or $GITHUB_PATH line, an edited mise.toml, or a changed shim. Dependency code compiled into _engine.so does too. This is still better than the job-level secrets it replaces, but this comment, mise.toml:108-111 and the AGENTS.md line ("no dependency's build script runs with the credentials set") claim more than the change delivers. Either reword them, or build the wheel in a job with no secrets and install only that wheel into a fresh venv in the live job.

Source: code-review · claude-opus-5-5


Generated by Claude Code

# EQL v4 bundle.
- name: Build the extension and the EQL bundle
run: |
mise run build
mise run build:eql-bundle

# Builds the wheel and the bundle, installs the bundle, and runs the
# live tests; a skip fails the task.
# Installs the bundle and runs the live tests, building nothing; a skip
# fails the task. The only step with the credentials.
- name: Live tests
env:
CS_WORKSPACE_CRN: ${{ vars.CS_WORKSPACE_CRN }}
CS_CLIENT_ID: ${{ vars.CS_CLIENT_ID }}
CS_CLIENT_KEY: ${{ secrets.CS_CLIENT_KEY }}
CS_CLIENT_ACCESS_KEY: ${{ secrets.CS_CLIENT_ACCESS_KEY }}
EQL_TEST_DATABASE_URL: "host=localhost port=${{ job.services.postgres.ports['5432'] }} user=postgres password=postgres dbname=postgres sslmode=disable"
run: mise run test:live
run: mise run test:live:run

# One abi3-py39 wheel per platform the FFI ships for, by the same Rust
# triples. python-ci.test.mjs holds the list to the protect-ffi platform
Expand Down Expand Up @@ -439,9 +459,14 @@ jobs:
# wheel names its engine the same way. The checkout is clean here, so
# the `-dirty` check the script skips with the override has nothing to
# add. python-ci.test.mjs holds the variable name to build.rs.
# On a pull request, HEAD is GitHub's test merge commit, which is on no
# branch. The pull request's head commit is the one a bug report can
# name, so the wheel records that one.
- name: Read the engine commit
id: engine
run: echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
env:
COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor The wheels checkout (line 387) has no ref:, so on a pull request it compiles refs/pull/N/merge, while this records head.sha. build.rs trusts the override and skips the -dirty check. So when the base branch has moved under packages/stack-*, __engine__ names a commit the wheel was not built from. Fix: add ref: ${{ github.event.pull_request.head.sha || github.sha }} to that checkout.

Source: code-review · claude-opus-5-5


Generated by Claude Code

run: echo "commit=$COMMIT" >> "$GITHUB_OUTPUT"

# `--locked`: the Cargo.lock in the tree is the one built, as in every
# other check of this workspace. `--out dist` puts the wheel where the
Expand All @@ -459,6 +484,9 @@ jobs:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
rust-toolchain: ${{ steps.rust.outputs.version }}
# A fixed maturin, inside pyproject.toml's range. Without it the
# action downloads the newest release on every run.
maturin-version: v1.16.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor Nothing keeps this pin equal to maturin in uv.lock (1.16.0). Dependabot's uv group will bump the lock alone, and then mise run build and the live job build with the new maturin while the shipped wheels stay on 1.16.0, with no failure. Add an assertion in scripts/__tests__/python-ci.test.mjs that this value, without the v, equals the uv.lock version. The dependabot.yml comment that says uv.lock "holds only the test extra" is now stale as well.

Source: code-review · claude-opus-5-5


Generated by Claude Code

docker-options: -e STACKENCRYPT_ENGINE_COMMIT

# The build time is the number this job exists to record. The spec's
Expand Down Expand Up @@ -508,6 +536,8 @@ jobs:
set -euo pipefail
python -m pip install --no-index --no-deps languages/python/dist/*.whl
python -c 'import stackencrypt as s; print("stackencrypt", s.__version__, "engine", s.__engine__)'
# A wheel must not carry the deterministic key source.
python -c 'from stackencrypt import _engine; assert not hasattr(_engine, "_use_deterministic_keys"), "this wheel has the deterministic-kms test feature"'

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
Expand All @@ -530,6 +560,8 @@ jobs:
wheels-summary:
name: one wheel per platform, build time per platform
needs: [wheels]
# Run when a leg fails too, so the table still shows the legs that built.
if: ${{ !cancelled() }}
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
steps:
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md

Large diffs are not rendered by default.

Loading
Loading