Conversation
The publish job authenticated with the long-lived PYPI_TOKEN secret. It now passes no password, so pypa/gh-action-pypi-publish exchanges the job's OIDC token for a short-lived upload token. The zizmor ignore for use-trusted-publishing and its TODO are gone. PyPI must list permitio/permit-python, workflow python-sdk-publish.yml and environment pypi as a trusted publisher of the permit project before this is released, or the upload is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
trivy-action caches by default, and on a cache hit setup-trivy puts the cached Trivy binary on PATH without checking it. The release gate then scans with whatever binary the Actions cache held. With the cache off, each release downloads Trivy and checks it against its release's checksums, as the build and scan jobs already do for uv. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pytest jobs ran permitio/pdp-v2:latest, so a new PDP release could fail a required check with no change in this repository. They now run 0.9.16 by the digest of its multi-arch index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both ran with GitHub's default limit of six hours. A run of pytest takes 5-6 minutes and one of compatibility under one, so 30 and 15 minutes leave room for a slow PDP start or rate-limit retries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build job checked that the artifacts carry py.typed and _sync_types.pyi, but not what else they hold. permit 2.8.3's wheel installed a top-level `tests` package into site-packages. The same step now also fails when the wheel's top level holds anything but permit/ and its own .dist-info, or the sdist holds a directory other than permit/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build, scan and publish jobs had none, so a hung step would hold the release for GitHub's six-hour default. Each usually finishes in under a minute; they now stop after 10, 15 and 10 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new job, e2e-unpinned-pdp, which is not a required check, runs after both pytest lanes pass, with a scratch environment per leg: - e2e (latest PDP image): the suite against permitio/pdp-v2:latest on pydantic 2, so a PDP release that breaks the SDK shows up before the pin moves to it. It prints the digest :latest resolved to. - e2e (cloud PDP): tests/test_abac_pdp.py against the hosted cloud PDP. Those three tests skip everywhere else, so until now no CI job ran them. The leg fails if any of them is skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in per-16676/impl-publish: trusted publishing for the PyPI upload, no Trivy cache in the release scan, a check that the wheel and sdist ship only permit, and a timeout on each release job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in per-16676/impl-ci: the required e2e jobs run a PDP image pinned by version and digest, two jobs that are not required checks run the e2e tests on the latest PDP image and on the cloud PDP, and the pytest and compatibility jobs get timeouts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The compatibility job in test.yml now runs the same artifact check as the release build, so a packaging change that ships a package beside permit fails a pull request, not the next release. The two scripts are identical, and each workflow points at the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment read as if PyPI took uploads for permit only from this publisher, which is not so while a project API token exists. It now says what makes this job's own upload accepted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTRIBUTING.md gets a Releasing section: which tags the release workflow accepts, what each of its three jobs checks, that the upload uses PyPI trusted publishing, and which names on pypi.org must change with the workflow file or the environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTRIBUTING.md now says which e2e jobs CI runs and which are required, how to reproduce them locally on the pinned PDP image, the latest one or the cloud PDP, and how to move the PDP pin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module comment said CI only ever skips these tests. The new e2e (cloud PDP) job runs them and fails if any is skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three tests in tests/test_abac_pdp.py passed on any PermitConnectionError, which the SDK also raises for a rejected key, a server error and a PDP it cannot reach. The e2e (cloud PDP) job was then green without the cloud PDP ever answering. Each test now requires the status code 501 in the error message, and the workflow comment and CONTRIBUTING.md say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The text said three, but the list under it names four: the two pytest lanes, e2e (latest PDP image) and e2e (cloud PDP). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PyPI trusted publishing matches the repository, the workflow file name and the environment, not the ref, so a branch that edits the publish workflow to run on push could upload. The publish job's comment and the Releasing section now say that the pypi environment's deployment rules, a repository setting, are what prevent that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both workflows ran only on PRs into main or master. A stacked PR, whose base is another PR's branch, got no tests, no dependency audit and no workflow checks until it was retargeted. Drop the base filter so every PR gets the same checks before it merges. Push runs are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependency Security AuditScanned: pyproject.toml dependencies + dev group, resolved at Python 3.10 (the current resolution, and the lowest versions the published specs permit under each pydantic major) ✅ No known vulnerabilities found. Both the resolved dependency set and the lowest versions the published specs permit are clean at HIGH and CRITICAL. |
A PDP that does not implement check, get_user_permissions or filter_objects answers 501. The SDK must then raise PermitConnectionError with that status in the message, not return a decision. The cloud PDP tests asserted this against the hosted PDP, which now answers these calls. This test keeps the contract covered against a local pytest-httpserver PDP. The status is matched where each message reports it, since the message also holds the PDP's URL, whose random port can contain 501. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cloud PDP tests expected the hosted PDP to answer check, get_user_permissions and filter_objects with 501. It now answers them with 200, so all three failed the first time CI ran them. tests/test_cloud_pdp_e2e.py replaces tests/test_abac_pdp.py. Each test creates a small RBAC policy with per-run keys in the scratch environment: a resource type with two actions, a role that grants one of them, a tenant where the user has that role and a tenant where it has none. It waits, with a bounded poll, until the cloud PDP allows the granted action, then asserts the exact answers of check, bulk_check, get_user_permissions and filter_objects. Teardown deletes every object it created and treats a 404 as success. The module still skips unless PDP_URL is the cloud PDP. The e2e (cloud PDP) job now runs the new path and still fails if any of its tests is skipped. The workflow comments and CONTRIBUTING.md describe what the job now tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cloud PDP lists its built-in "tenant-association" role after the roles assigned to a user who belongs to the tenant. The first CI run against it returned ["<role>", "tenant-association"] on every poll, so the expected value now includes it. check, bulk_check and filter_objects already matched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The invites target a resource instance, and the API now refuses to approve an invite whose role belongs to another resource (PER-15743). The test gave them a tenant role; it now creates a role on the invited resource and deletes it before the resource. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PDP can allow a check before its list of role assignments shows the grant, so the tests read that list once and sometimes saw none. They now poll for it, as they do for decisions, before asserting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear issues
Stacked on #128.
Why
permitio/pdp-v2:latest, so a new PDP release could fail every pull request.tests/test_abac_pdp.py. They assumed the cloud PDP answers 501 to ABAC calls, which it no longer does, and set up no policy of their own.permitpackage ships.What changed
Release workflow (
.github/workflows/python-sdk-publish.yml)password, keepsenvironment: pypi, and its only permission isid-token: write.zizmor: ignore[use-trusted-publishing]and its TODO are removed.pypienvironment's deployment rules are what limit uploads to release tags.cache: false, so every release downloads Trivy and checks its checksum.permit/andpermit-<version>.dist-info, orpermit/.Already in place and unchanged:
needs:. The scan gates the publish and keeps its report for 90 days.py.typedand_sync_types.pyiare asserted in both artifacts.env.release: published.persist-credentials: false, top-levelcontents: read,ubuntu-24.04, and SHA-pinned actions.CI (
.github/workflows/test.yml,.github/workflows/security.yml)main/master, so a stacked PR, whose base is another PR's branch, got no tests, dependency audit or workflow checks. The base filter is dropped. Push runs are unchanged.pytestjobs runPINNED_PDP_IMAGE,permitio/pdp-v2:0.9.16@sha256:e3cf30794ec2d256636b4714641df46e51ee58a3f1f0d24c606e214e0bf8669a, the multi-arch index digest. Job name, matrix and step names are unchanged.e2e-unpinned-pdp. It is not a required check and starts after bothpytestlanes pass. It has two legs:e2e (latest PDP image)runs the whole suite on pydantic 2 againstpermitio/pdp-v2:latestand logs the digest:latestresolved to.e2e (cloud PDP)runstests/test_cloud_pdp_e2e.pywithPDP_URL=https://cloudpdp.api.permit.ioand no container. It fails if any test is skipped, which a check on the junit report enforces.python-sdk-ci-<run_id>-<run_attempt>-<leg>. It masks the key before export and deletes the environment in analways()step. The leg keeps the 300 s PDP wait and the filtered PDP log, and has the same permissions, pinned actions,persist-credentials: falseand env-passing aspytest.compatibilityjob. Its artifact check is now the same script as the release check.pytest30,compatibility15,e2e-unpinned-pdp30 minutes.Tests
tests/test_abac_pdp.pyis replaced bytests/test_cloud_pdp_e2e.py. It creates its own resource type (actionsreadandwrite), a role grantingread, two tenants, a user and a role assignment, waits until the cloud PDP allowsread, then asserts exact decisions fromcheck,bulk_check,get_user_permissionsandfilter_objects, including the denials.check,get_user_permissionsorfilter_objectsmakes the SDK raisePermitConnectionErrorwith the status in the message.Docs
CONTRIBUTING.mdhas a new "Releasing" section.Behaviour changes
PYPI_TOKEN. The trusted publisher must be registered on pypi.org first.compatibilityrun, fails if the wheel or sdist ships anything besidespermit. Today's build passes.:latest.pytest,compatibilityand new e2e jobs have timeouts.e2e (cloud PDP)passes only if the cloud PDP returns the expected allow and deny decisions for the policy the tests create.How it was tested
uv lock --checkpasses.pre-commit run --all-files: all hooks pass.mypyreports no issues in 89 files, on both pydantic 2 and pydantic 1 (1.10.26).pytest -m "not e2e": 308 passed, 3 skipped, no warnings, on pydantic 2 and on pydantic 1.actionlintreports nothing.zizmor .github/with online audits reports no findings and no ignores. Before this PR it had one ignore, for trusted publishing.e2e (latest PDP image)ande2e (cloud PDP). Both requiredpytest (Pydantic …)contexts are unchanged.pytest --collect-onlycollects the 3 tests.--setup-plan -rsskips all 3 withPDP_URL=http://localhost:7766and skips none withPDP_URL=https://cloudpdp.api.permit.io.check,get_user_permissions,filter_objects) against pytest-httpserver answering 501. Six mutations of the enforcer, treating 501 as success or dropping the status from the message, each fail exactly the case they target.tests/package. A mutant without the exit lets that wheel through. The release andcompatibilitycopies are identical.e2e (cloud PDP)is not a required check.Owner actions before merge
On pypi.org, under project
permit, Manage, Publishing, add a GitHub Actions trusted publisher: ownerpermitio, repositorypermit-python, workflowpython-sdk-publish.yml, environmentpypi.Create a tag ruleset that lets only admins create, move or delete tags matching
refs/tags/v[0-9]*andrefs/tags/[0-9]*. Save the JSON below asrelease-tag-ruleset.jsonand rungh api repos/permitio/permit-python/rulesets -X POST --input release-tag-ruleset.json.release-tag-ruleset.json
{ "name": "Release tags: admins only", "target": "tag", "enforcement": "active", "bypass_actors": [ { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always" }, { "actor_id": 1, "actor_type": "OrganizationAdmin", "bypass_mode": "always" } ], "conditions": { "ref_name": { "include": ["refs/tags/v[0-9]*", "refs/tags/[0-9]*"], "exclude": [] } }, "rules": [ { "type": "creation" }, { "type": "update", "parameters": { "update_allows_fetch_and_merge": false } }, { "type": "deletion" } ] }Protect the
pypienvironment:v[0-9]*and[0-9]*;After the first release published by OIDC succeeds, delete the
PYPI_TOKENsecret and revoke that token on pypi.org.🤖 Generated with Claude Code