diff --git a/.github/actions/setup-codeartifact/README.md b/.github/actions/setup-codeartifact/README.md index e04ceaf..4cc90a3 100644 --- a/.github/actions/setup-codeartifact/README.md +++ b/.github/actions/setup-codeartifact/README.md @@ -1,6 +1,6 @@ # Authenticate with AWS CodeArtifact -A composite GitHub Action that assumes an IAM role via OIDC, acquires an AWS CodeArtifact authorization token, and writes a Maven `settings.xml` so subsequent `mvn` commands can resolve and deploy artifacts against CodeArtifact. +A composite GitHub Action that assumes an IAM role via OIDC, acquires an AWS CodeArtifact authorization token, and configures the requested package format: a Maven `settings.xml` so subsequent `mvn` commands can resolve and deploy artifacts (`format: maven`, the default), or pypi index/publish URLs for pip and uv (`format: pypi`). - [How-to guides](#how-to-guides) - [Reference](#reference) @@ -35,23 +35,55 @@ jobs: > Pin to a commit SHA rather than `@main` for reproducible builds, e.g. > `uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@`. +### Authenticate for pypi + +Set `format: pypi` to skip the Maven `settings.xml` and get pypi URLs instead. +The `pypi-index-url` output embeds the token as credentials (masked in logs) +for `pip`/`uv --index-url`; the `pypi-publish-url` output carries no +credentials, so pass the token separately when publishing: + +```yaml +- name: Authenticate with CodeArtifact + id: ca + uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@main + with: + aws-role-arn: arn:aws:iam::123456789012:role/codeartifact-publisher + codeartifact-domain: overture-pypi + codeartifact-domain-owner: "123456789012" + codeartifact-repository: overture + format: pypi + +- name: Publish + env: + UV_PUBLISH_USERNAME: aws + UV_PUBLISH_PASSWORD: ${{ steps.ca.outputs.token }} + run: uv publish --publish-url "${{ steps.ca.outputs.pypi-publish-url }}" +``` + +To dual-publish the same package to two CodeArtifact accounts (e.g. a legacy +domain and the MCD domain during a migration), call the action twice in the +same job with distinct step ids and role/domain/owner inputs, and run the +publish step once per id. + ## Reference ### Inputs - `aws-role-arn` (**required**): IAM role ARN to assume via OIDC. - `aws-region` (optional): AWS region where CodeArtifact is hosted. Default `us-west-2`. +- `format` (optional): CodeArtifact package format, `maven` or `pypi`. Default `maven`, with behavior identical to before this input existed. `pypi` skips the Maven `settings.xml` and populates the `pypi-index-url`/`pypi-publish-url` outputs instead. Both formats share the same OIDC role assumption and token acquisition. - `codeartifact-domain` (**required**): CodeArtifact domain name. - `codeartifact-domain-owner` (**required**): AWS account ID that owns the CodeArtifact domain. - `codeartifact-repository` (**required**): CodeArtifact repository name. -- `maven-repository-id` (optional): The Maven ``/`` id written to `settings.xml`. Default `codeartifact`. Must match the id your `pom.xml`'s `` declares, otherwise Maven silently skips attaching CodeArtifact credentials when *resolving* dependencies (deploys are unaffected — see [The repository id must match](#the-repository-id-must-match) below). Only override this if your repo's convention differs from `codeartifact`. +- `maven-repository-id` (optional): The Maven ``/`` id written to `settings.xml`. Default `codeartifact`. Ignored when `format: pypi`. Must match the id your `pom.xml`'s `` declares, otherwise Maven silently skips attaching CodeArtifact credentials when *resolving* dependencies (deploys are unaffected — see [The repository id must match](#the-repository-id-must-match) below). Only override this if your repo's convention differs from `codeartifact`. - `token-env-var` (optional): Name of the environment variable the masked CodeArtifact token is exported to via `$GITHUB_ENV`, available to every later step in the job. Default `CODEARTIFACT_AUTH_TOKEN`. Set to an empty string to skip the export and rely on the `token` output instead (see [Using the token with tools that wrap Maven](#using-the-token-with-tools-that-wrap-maven)). ### Outputs The action's primary effect is environmental: it acquires a CodeArtifact authorization token (masked, exported to `$GITHUB_ENV` under `token-env-var`) -and writes `~/.m2/settings.xml`. It also echoes the CodeArtifact metadata back +and, for `format: maven`, writes `~/.m2/settings.xml`. It also echoes the +CodeArtifact metadata back as outputs so later steps can pipe from a single source of truth instead of re-specifying it: @@ -59,11 +91,19 @@ re-specifying it: - `codeartifact-domain-owner` — the owning AWS account ID. - `codeartifact-repository` — the repository name. - `aws-region` — the AWS region. -- `repository-url` — the fully-composed Maven repository URL - (`https://-.d.codeartifact..amazonaws.com/maven//`). +- `repository-url` — the fully-composed repository URL for the requested + format (`https://-.d.codeartifact..amazonaws.com///`). + For pypi, prefer the two outputs below. +- `pypi-index-url` — pypi index URL with the token embedded as credentials + (`https://aws:TOKEN@.../pypi//simple/`), masked in logs, for + `pip`/`uv --index-url`. Empty unless `format: pypi`. +- `pypi-publish-url` — pypi publish endpoint without credentials + (`https://.../pypi//`), for `uv publish --publish-url` with the token + passed separately. Empty unless `format: pypi`. - `token` — the masked CodeArtifact authorization token. Only needed if you set `token-env-var` to an empty string and want the token scoped to a single - step instead of the whole job (see below). + step instead of the whole job (see below), or as the publish password in + pypi mode. ```yaml - name: Authenticate with CodeArtifact @@ -153,7 +193,8 @@ The authorization token is masked in logs and exported two ways: to `$GITHUB_ENV` under `token-env-var` (default `CODEARTIFACT_AUTH_TOKEN`), and as this action's `token` output — then embedded into the `~/.m2/settings.xml` written by an inline bash step (a `cat < Assumes an IAM role via OIDC, acquires a CodeArtifact authorization token, - and writes Maven settings.xml so subsequent mvn commands can resolve and - deploy artifacts. Must be called before any Maven step that touches CodeArtifact. + and configures the requested package format: writes Maven settings.xml so + subsequent mvn commands can resolve and deploy artifacts (format: maven), + or composes pypi index/publish URLs for pip/uv (format: pypi). Must be + called before any step that touches CodeArtifact. inputs: aws-role-arn: description: IAM role ARN to assume via OIDC required: true + format: + description: > + CodeArtifact package format to configure: "maven" (default) or "pypi". + Maven mode writes ~/.m2/settings.xml; behavior is unchanged from before + this input existed. Pypi mode skips settings.xml entirely and instead + exposes the pypi-index-url and pypi-publish-url outputs for pip/uv. + Both modes share the same OIDC role assumption and token acquisition. + required: false + default: maven aws-region: description: AWS region where CodeArtifact is hosted required: false @@ -28,7 +39,8 @@ inputs: otherwise Maven will not attach CodeArtifact credentials when resolving dependencies (deploys are unaffected since they pipe the URL directly via -DaltDeploymentRepository). Defaults to "codeartifact", the - convention documented across consuming repos. + convention documented across consuming repos. Ignored when format is + "pypi". required: false default: codeartifact token-env-var: @@ -60,9 +72,26 @@ outputs: value: ${{ inputs.aws-region }} repository-url: description: > - Fully-composed CodeArtifact Maven repository URL. Pipe this to a later - mvn step (e.g. -DaltDeploymentRepository / -Durl) instead of rebuilding it. - value: "https://${{ inputs.codeartifact-domain }}-${{ inputs.codeartifact-domain-owner }}.d.codeartifact.${{ inputs.aws-region }}.amazonaws.com/maven/${{ inputs.codeartifact-repository }}/" + Fully-composed CodeArtifact repository URL for the requested format. + For maven, pipe this to a later mvn step (e.g. -DaltDeploymentRepository + / -Durl) instead of rebuilding it. For pypi, prefer the pypi-index-url + and pypi-publish-url outputs, which carry the /simple/ suffix and + credential handling that pip/uv expect. + value: "https://${{ inputs.codeartifact-domain }}-${{ inputs.codeartifact-domain-owner }}.d.codeartifact.${{ inputs.aws-region }}.amazonaws.com/${{ inputs.format }}/${{ inputs.codeartifact-repository }}/" + pypi-index-url: + description: > + Pypi index URL with the authorization token embedded as credentials + (https://aws:TOKEN@.../pypi//simple/), masked in logs. + Pass it to pip/uv via --index-url. Empty unless format is "pypi". + value: ${{ steps.pypi.outputs.index-url }} + pypi-publish-url: + description: > + Pypi publish endpoint URL without embedded credentials + (https://.../pypi//). Pass it to `uv publish --publish-url` + with the token supplied separately (username "aws", password from the + `token` output or the env var named by `token-env-var`). Empty unless + format is "pypi". + value: ${{ steps.pypi.outputs.publish-url }} token: description: > The masked CodeArtifact authorization token. By default this is also @@ -75,6 +104,19 @@ outputs: runs: using: "composite" steps: + - name: Validate format + shell: bash + env: + FORMAT: ${{ inputs.format }} + run: | + case "$FORMAT" in + maven|pypi) ;; + *) + echo "::error::Unsupported format '$FORMAT': must be 'maven' or 'pypi'." + exit 1 + ;; + esac + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 with: @@ -92,9 +134,9 @@ runs: # step in the job, same as any other setup action's credentials — # callers who'd rather scope it to a single step can set # `token-env-var: ""` to skip the export and use the `token` output - # directly instead. Either way it's still embedded into - # ~/.m2/settings.xml, so the runner must be trusted for the job's - # duration regardless. + # directly instead. Either way the token is still embedded into + # ~/.m2/settings.xml (maven) or the masked index URL (pypi), so the + # runner must be trusted for the job's duration regardless. env: CODEARTIFACT_DOMAIN: ${{ inputs.codeartifact-domain }} CODEARTIFACT_DOMAIN_OWNER: ${{ inputs.codeartifact-domain-owner }} @@ -113,6 +155,30 @@ runs: echo "${TOKEN_ENV_VAR}=${TOKEN}" >> "$GITHUB_ENV" fi + # The index URL embeds the token as credentials (https://aws:TOKEN@...), + # the form pip/uv expect for --index-url, so it's masked as a whole in + # addition to the token itself. The publish URL carries no credentials: + # `uv publish` takes them separately (--username aws / --password TOKEN), + # so it stays unmasked and readable in logs. + - name: Compose pypi URLs + if: ${{ inputs.format == 'pypi' }} + id: pypi + shell: bash + env: + CODEARTIFACT_DOMAIN: ${{ inputs.codeartifact-domain }} + CODEARTIFACT_DOMAIN_OWNER: ${{ inputs.codeartifact-domain-owner }} + CODEARTIFACT_REPOSITORY: ${{ inputs.codeartifact-repository }} + AWS_REGION: ${{ inputs.aws-region }} + CODEARTIFACT_TOKEN: ${{ steps.token.outputs.token }} + run: | + BASE_URL="https://${CODEARTIFACT_DOMAIN}-${CODEARTIFACT_DOMAIN_OWNER}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${CODEARTIFACT_REPOSITORY}/" + + INDEX_URL="https://aws:${CODEARTIFACT_TOKEN}@${CODEARTIFACT_DOMAIN}-${CODEARTIFACT_DOMAIN_OWNER}.d.codeartifact.${AWS_REGION}.amazonaws.com/pypi/${CODEARTIFACT_REPOSITORY}/simple/" + echo "::add-mask::$INDEX_URL" + echo "index-url=$INDEX_URL" >> "$GITHUB_OUTPUT" + + echo "publish-url=$BASE_URL" >> "$GITHUB_OUTPUT" + # settings.xml is written inline rather than via actions/setup-java's # generator. CodeArtifact needs both a (credentials) and a # (resolve URL) in settings.xml; setup-java emits only @@ -129,6 +195,7 @@ runs: # maven-repository-id input (default "codeartifact") instead, so the id is # stable and matches the convention already documented in consuming repos. - name: Configure Maven Settings for CodeArtifact + if: ${{ inputs.format == 'maven' }} shell: bash env: MAVEN_REPOSITORY_ID: ${{ inputs.maven-repository-id }}