You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Rhiza supports public PyPI publishing through trusted publishing and custom package feeds through PYPI_REPOSITORY_URL plus a static PYPI_TOKEN. That model is not sufficient for AWS CodeArtifact.
CodeArtifact authorization tokens are short-lived. They should be generated inside each GitHub Actions job after assuming an IAM role through OIDC, not stored as repository or organization secrets. This creates two related gaps:
Publishing: a private wheel can be published today only with a repository-owned workflow that duplicates part of Rhiza's release pipeline.
Consumption: a project depending on a private wheel cannot authenticate before uv lock, uv sync, or another dependency-installing command inside Rhiza's job-level reusable workflows. A caller cannot insert setup steps into a reusable-workflow job, and credentials generated in a separate job do not carry into downstream jobs.
The current custom-feed support therefore handles feeds with durable tokens, but not CodeArtifact's OIDC and short-lived-token model.
Proposed solution
Add AWS CodeArtifact as an optional, first-class package repository provider for both publishing and dependency consumption. Keep it disabled by default and configure it entirely through generic repository or organization variables.
A possible configuration surface is:
Setting
Purpose
PACKAGE_REPOSITORY
none, pypi, codeartifact, or custom
AWS_REGION
AWS region containing the feed
AWS_ROLE_TO_ASSUME_PUBLISH
OIDC role ARN with publish permissions
AWS_ROLE_TO_ASSUME_READ
OIDC role ARN with read-only permissions
CODEARTIFACT_DOMAIN
CodeArtifact domain
CODEARTIFACT_DOMAIN_OWNER
AWS account that owns the domain
CODEARTIFACT_REPOSITORY
CodeArtifact repository
CODEARTIFACT_UV_INDEX
Optional uv index name used by consuming projects
No AWS access key, secret key, or CodeArtifact authorization token should be stored in GitHub.
Publishing
When PACKAGE_REPOSITORY == 'codeartifact', the release workflow should:
Retain the existing tag validation, build, SBOM, provenance, and GitHub Release behavior.
Reuse the distributions produced by the standard build job rather than rebuilding them in a second workflow.
Grant id-token: write only to the publishing job.
Assume AWS_ROLE_TO_ASSUME_PUBLISH using aws-actions/configure-aws-credentials.
Resolve the upload endpoint at runtime with aws codeartifact get-repository-endpoint --format pypi.
Resolve a short-lived authorization token with aws codeartifact get-authorization-token and mask it immediately.
Fail clearly when required provider settings are absent or when publication fails.
Preserve the existing pypi and custom behavior when another provider is selected.
The following standalone workflow shape has been verified end to end. It is included as a
concrete implementation reference; native Rhiza integration should reuse the standard build
job's distributions rather than running uv build a second time.
The existing build job already uploads the wheel and source distribution as the dist
artifact. Add a codeartifact job alongside pypi, with this shape:
needs: [tag, build, draft-release];
the existing release environment;
contents: read and id-token: write permissions;
the same hardened-runner and checkout conventions as the other release jobs;
download the existing dist artifact rather than rebuilding;
validate the selected provider and required CodeArtifact settings;
assume the publisher role, resolve the endpoint and token, mask the token, and run uv publish dist/* against the resolved endpoint;
expose should_publish and, if useful, package/version outputs for final release notes.
Provider selection should be explicit:
pypi: retain trusted publishing and reject Private :: Do Not Upload;
custom: retain the current URL plus durable-token behavior;
codeartifact: use AWS OIDC and permit packages marked private;
none: skip package publication while retaining the remaining release outputs.
finalise-release should add codeartifact to needs, include a successful CodeArtifact job
in its completion condition, and report private-feed publication without printing the endpoint
or credentials. The conda job currently depends on public PyPI metadata; it should remain
disabled unless the selected provider supports that lookup.
Update tests/api/test_release_workflow.py to cover the new job, provider branches,
permissions, artifact reuse, step order, and finalization behavior. Existing tests that assert
the live and bundled release workflows remain synchronized should continue to cover both files.
The initial implementation can be GitHub Actions-specific; GitLab behavior should be left
unchanged or explicitly documented as unsupported rather than implying GitHub OIDC works there.
The Private :: Do Not Upload classifier should continue preventing accidental public PyPI publication, but it should not suppress an explicitly selected private CodeArtifact publication. The provider selection and classifier check therefore need to be evaluated together.
The publisher IAM role should be independently configurable and limited to the required token, endpoint, package-version publishing, and package-metadata operations. Its trust policy can restrict assumption to the appropriate repository and release refs.
Consumption
Reusable workflows that install project dependencies should optionally authenticate with CodeArtifact before the first uv operation that can resolve or download project packages.
For each relevant job:
Grant id-token: write only when CodeArtifact consumption is enabled.
Assume AWS_ROLE_TO_ASSUME_READ with aws-actions/configure-aws-credentials.
Request and mask a fresh CodeArtifact authorization token.
Expose credentials to uv for the remainder of that job.
The underlying AWS token and uv named-index commands have been verified locally with a private
wheel. The complete job-scoped sequence below has also been verified successfully in GitHub
Actions, including OIDC role assumption, token retrieval, locked synchronization, and package
import:
Here <NORMALIZED_INDEX_NAME> is the configured uv index name uppercased with non-alphanumeric characters converted to underscores. This keeps only private packages on CodeArtifact while public dependencies remain locked to PyPI. It also avoids embedding credentials in a URL.
Authentication must run independently in every job that consumes dependencies; $GITHUB_ENV, AWS session credentials, and uv credentials do not cross job boundaries. This likely includes the reusable CI, documentation, notebook, benchmark, weekly compatibility, devcontainer, and release/SBOM paths wherever they execute uv lock, uv sync, or an equivalent project install.
This should not mean copying the full authentication script into every workflow job. Prefer a
single maintained composite action, for example jebel-quant/rhiza/.github/actions/codeartifact-auth@<version>, that:
validates the role, region, domain, owner, and uv index-name inputs;
invokes aws-actions/configure-aws-credentials;
requests and masks the CodeArtifact token; and
writes the normalized uv index credentials to $GITHUB_ENV.
Each consuming workflow job would then contain a small call like:
Each dependency-consuming job must still grant id-token: write and invoke that action before
its first project-resolving uv command. A composite action removes duplicated implementation,
but it cannot share credentials between jobs or grant job-level permissions on the caller's
behalf. The workflow tests should enumerate dependency-consuming jobs and assert that each one
contains the authentication action before its install step, preventing new jobs from silently
omitting private-feed setup.
The reader IAM role should be separate from the publisher role and limited to the token, endpoint, repository-read, and package-file operations required for installation.
GitHub OIDC subject configuration
The repository's GitHub OIDC subject format must match the IAM role's trust policy. If the trust
policy uses GitHub's stable organization and repository IDs, enable immutable subjects for each
repository that assumes the role:
gh api --method PUT repos/OWNER/REPOSITORY/actions/oidc/customization/sub \
-F use_default=true \
-F use_immutable_subject=true
Verify the resulting prefix before running CI:
gh api repos/OWNER/REPOSITORY/actions/oidc/customization/sub
With immutable subjects disabled, GitHub emits the mutable repo:OWNER/REPOSITORY:... subject.
An IAM policy expecting the immutable repo:OWNER@ORG_ID/REPOSITORY@REPOSITORY_ID:... form then
rejects an otherwise valid token with Not authorized to perform sts:AssumeRoleWithWebIdentity. Rhiza's documentation should show both subject formats and require
the repository setting and IAM condition to agree; it should not prescribe weakening the IAM
trust pattern to work around a mismatched setting.
Pull requests from forks
OIDC access to a private feed must not be exposed to untrusted fork code. The implementation should define an explicit policy for fork pull requests, such as skipping private-package jobs with a clear explanation. Same-repository branches and trusted release refs should continue normally when permitted by the IAM trust policy.
Why the current alternatives are insufficient
PYPI_TOKEN or UV_EXTRA_INDEX_URL secret: CodeArtifact tokens expire, normally within hours, so a stored value requires continuous secret rotation and will fail unpredictably.
Generate the token in a prerequisite job: GitHub Actions credentials and environment files do not cross jobs. Passing an authorization token as a job output also expands its exposure and should not be the authentication design.
Repository-owned workflow copies: These work, but duplicate Rhiza's release and quality workflows and drift as Rhiza evolves.
Use CodeArtifact as the global uv index: This can rewrite all public dependency sources in uv.lock to the private proxy. Named explicit indexes let only designated private packages use CodeArtifact.
Static AWS credentials: Long-lived AWS access keys are unnecessary when GitHub OIDC is available and should not be introduced.
Acceptance criteria
CodeArtifact support is optional and disabled by default.
Publishing and consumption use GitHub OIDC and short-lived CodeArtifact tokens.
Publisher and reader roles are configured separately.
The standard release build artifacts are reused for CodeArtifact publication.
Selecting CodeArtifact publishes private distributions without enabling public PyPI publication.
Every reusable workflow job that resolves project dependencies authenticates in that same job.
Named uv indexes are supported without routing all public dependencies through CodeArtifact.
Tokens are masked, never persisted as GitHub secrets, never embedded in committed files, and never printed.
Documentation includes IAM permissions, repository variables, publishing, consumption, and local-development examples using placeholders.
Additional context
A standalone OIDC workflow has validated the publishing sequence end to end, including endpoint discovery, token generation, and uv publish. A downstream project has also validated local installation through a named explicit uv index. The remaining blocker is integrating those steps into Rhiza's reusable jobs so consumers do not need to fork or duplicate the workflows.
Problem / motivation
Rhiza supports public PyPI publishing through trusted publishing and custom package feeds through
PYPI_REPOSITORY_URLplus a staticPYPI_TOKEN. That model is not sufficient for AWS CodeArtifact.CodeArtifact authorization tokens are short-lived. They should be generated inside each GitHub Actions job after assuming an IAM role through OIDC, not stored as repository or organization secrets. This creates two related gaps:
uv lock,uv sync, or another dependency-installing command inside Rhiza's job-level reusable workflows. A caller cannot insert setup steps into a reusable-workflow job, and credentials generated in a separate job do not carry into downstream jobs.The current custom-feed support therefore handles feeds with durable tokens, but not CodeArtifact's OIDC and short-lived-token model.
Proposed solution
Add AWS CodeArtifact as an optional, first-class package repository provider for both publishing and dependency consumption. Keep it disabled by default and configure it entirely through generic repository or organization variables.
A possible configuration surface is:
PACKAGE_REPOSITORYnone,pypi,codeartifact, orcustomAWS_REGIONAWS_ROLE_TO_ASSUME_PUBLISHAWS_ROLE_TO_ASSUME_READCODEARTIFACT_DOMAINCODEARTIFACT_DOMAIN_OWNERCODEARTIFACT_REPOSITORYCODEARTIFACT_UV_INDEXNo AWS access key, secret key, or CodeArtifact authorization token should be stored in GitHub.
Publishing
When
PACKAGE_REPOSITORY == 'codeartifact', the release workflow should:id-token: writeonly to the publishing job.AWS_ROLE_TO_ASSUME_PUBLISHusingaws-actions/configure-aws-credentials.aws codeartifact get-repository-endpoint --format pypi.aws codeartifact get-authorization-tokenand mask it immediately.pypiandcustombehavior when another provider is selected.The following standalone workflow shape has been verified end to end. It is included as a
concrete implementation reference; native Rhiza integration should reuse the standard build
job's distributions rather than running
uv builda second time.Expected Rhiza release-workflow changes
The integration should update both authoritative GitHub workflow copies:
.github/workflows/rhiza_release.ymlbundles/github/.github/workflows/rhiza_release.ymlThe existing
buildjob already uploads the wheel and source distribution as thedistartifact. Add a
codeartifactjob alongsidepypi, with this shape:needs: [tag, build, draft-release];releaseenvironment;contents: readandid-token: writepermissions;distartifact rather than rebuilding;uv publish dist/*against the resolved endpoint;should_publishand, if useful, package/version outputs for final release notes.Provider selection should be explicit:
pypi: retain trusted publishing and rejectPrivate :: Do Not Upload;custom: retain the current URL plus durable-token behavior;codeartifact: use AWS OIDC and permit packages marked private;none: skip package publication while retaining the remaining release outputs.finalise-releaseshould addcodeartifacttoneeds, include a successful CodeArtifact jobin its completion condition, and report private-feed publication without printing the endpoint
or credentials. The
condajob currently depends on public PyPI metadata; it should remaindisabled unless the selected provider supports that lookup.
Update
tests/api/test_release_workflow.pyto cover the new job, provider branches,permissions, artifact reuse, step order, and finalization behavior. Existing tests that assert
the live and bundled release workflows remain synchronized should continue to cover both files.
The initial implementation can be GitHub Actions-specific; GitLab behavior should be left
unchanged or explicitly documented as unsupported rather than implying GitHub OIDC works there.
The
Private :: Do Not Uploadclassifier should continue preventing accidental public PyPI publication, but it should not suppress an explicitly selected private CodeArtifact publication. The provider selection and classifier check therefore need to be evaluated together.The publisher IAM role should be independently configurable and limited to the required token, endpoint, package-version publishing, and package-metadata operations. Its trust policy can restrict assumption to the appropriate repository and release refs.
Consumption
Reusable workflows that install project dependencies should optionally authenticate with CodeArtifact before the first uv operation that can resolve or download project packages.
For each relevant job:
id-token: writeonly when CodeArtifact consumption is enabled.AWS_ROLE_TO_ASSUME_READwithaws-actions/configure-aws-credentials.The underlying AWS token and uv named-index commands have been verified locally with a private
wheel. The complete job-scoped sequence below has also been verified successfully in GitHub
Actions, including OIDC role assumption, token retrieval, locked synchronization, and package
import:
For projects using a named explicit index in
pyproject.toml, prefer uv's index-specific environment variables:Here
<NORMALIZED_INDEX_NAME>is the configured uv index name uppercased with non-alphanumeric characters converted to underscores. This keeps only private packages on CodeArtifact while public dependencies remain locked to PyPI. It also avoids embedding credentials in a URL.Authentication must run independently in every job that consumes dependencies;
$GITHUB_ENV, AWS session credentials, and uv credentials do not cross job boundaries. This likely includes the reusable CI, documentation, notebook, benchmark, weekly compatibility, devcontainer, and release/SBOM paths wherever they executeuv lock,uv sync, or an equivalent project install.This should not mean copying the full authentication script into every workflow job. Prefer a
single maintained composite action, for example
jebel-quant/rhiza/.github/actions/codeartifact-auth@<version>, that:aws-actions/configure-aws-credentials;$GITHUB_ENV.Each consuming workflow job would then contain a small call like:
Each dependency-consuming job must still grant
id-token: writeand invoke that action beforeits first project-resolving uv command. A composite action removes duplicated implementation,
but it cannot share credentials between jobs or grant job-level permissions on the caller's
behalf. The workflow tests should enumerate dependency-consuming jobs and assert that each one
contains the authentication action before its install step, preventing new jobs from silently
omitting private-feed setup.
The reader IAM role should be separate from the publisher role and limited to the token, endpoint, repository-read, and package-file operations required for installation.
GitHub OIDC subject configuration
The repository's GitHub OIDC subject format must match the IAM role's trust policy. If the trust
policy uses GitHub's stable organization and repository IDs, enable immutable subjects for each
repository that assumes the role:
Verify the resulting prefix before running CI:
With immutable subjects disabled, GitHub emits the mutable
repo:OWNER/REPOSITORY:...subject.An IAM policy expecting the immutable
repo:OWNER@ORG_ID/REPOSITORY@REPOSITORY_ID:...form thenrejects an otherwise valid token with
Not authorized to perform sts:AssumeRoleWithWebIdentity. Rhiza's documentation should show both subject formats and requirethe repository setting and IAM condition to agree; it should not prescribe weakening the IAM
trust pattern to work around a mismatched setting.
Pull requests from forks
OIDC access to a private feed must not be exposed to untrusted fork code. The implementation should define an explicit policy for fork pull requests, such as skipping private-package jobs with a clear explanation. Same-repository branches and trusted release refs should continue normally when permitted by the IAM trust policy.
Why the current alternatives are insufficient
PYPI_TOKENorUV_EXTRA_INDEX_URLsecret: CodeArtifact tokens expire, normally within hours, so a stored value requires continuous secret rotation and will fail unpredictably.uv.lockto the private proxy. Named explicit indexes let only designated private packages use CodeArtifact.Acceptance criteria
Additional context
A standalone OIDC workflow has validated the publishing sequence end to end, including endpoint discovery, token generation, and
uv publish. A downstream project has also validated local installation through a named explicit uv index. The remaining blocker is integrating those steps into Rhiza's reusable jobs so consumers do not need to fork or duplicate the workflows.