Skip to content
Open
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
55 changes: 48 additions & 7 deletions .github/actions/setup-codeartifact/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Authenticate with AWS CodeArtifact <!-- omit in toc -->

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)
Expand Down Expand Up @@ -35,35 +35,75 @@ jobs:
> Pin to a commit SHA rather than `@main` for reproducible builds, e.g.
> `uses: OvertureMaps/workflows/.github/actions/setup-codeartifact@<sha>`.

### 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 `<server>`/`<repository>` id written to `settings.xml`. Default `codeartifact`. Must match the id your `pom.xml`'s `<repositories><repository>` 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 `<server>`/`<repository>` id written to `settings.xml`. Default `codeartifact`. Ignored when `format: pypi`. Must match the id your `pom.xml`'s `<repositories><repository>` 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:

- `codeartifact-domain` — the domain name.
- `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://<domain>-<owner>.d.codeartifact.<region>.amazonaws.com/maven/<repo>/`).
- `repository-url` — the fully-composed repository URL for the requested
format (`https://<domain>-<owner>.d.codeartifact.<region>.amazonaws.com/<format>/<repo>/`).
For pypi, prefer the two outputs below.
- `pypi-index-url` — pypi index URL with the token embedded as credentials
(`https://aws:TOKEN@.../pypi/<repo>/simple/`), masked in logs, for
`pip`/`uv --index-url`. Empty unless `format: pypi`.
- `pypi-publish-url` — pypi publish endpoint without credentials
(`https://.../pypi/<repo>/`), for `uv publish --publish-url` with the token
passed separately. Empty unless `format: pypi`.
Comment thread
lowlydba marked this conversation as resolved.
- `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
Expand Down Expand Up @@ -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 <<EOF` heredoc — no third-party
action). The `$GITHUB_ENV` export follows the same convention used by
action) in maven mode, or into the masked `pypi-index-url` output in pypi
mode. The `$GITHUB_ENV` export follows the same convention used by
`aws-actions/configure-aws-credentials` and AWS's own CodeArtifact docs: it
makes the token ambiently available to every later step in the job, the same
way those tokens are meant to be consumed by arbitrary AWS/Maven tooling.
Expand Down
85 changes: 76 additions & 9 deletions .github/actions/setup-codeartifact/action.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
name: Authenticate with AWS CodeArtifact
description: >
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
Expand All @@ -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:
Expand Down Expand Up @@ -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/<repository>/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/<repository>/). 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".
Comment thread
lowlydba marked this conversation as resolved.
value: ${{ steps.pypi.outputs.publish-url }}
token:
description: >
The masked CodeArtifact authorization token. By default this is also
Expand All @@ -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:
Expand All @@ -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 }}
Expand All @@ -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 <server> (credentials) and a
# <repository> (resolve URL) in settings.xml; setup-java emits only
Expand All @@ -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 }}
Expand Down