Skip to content
Closed
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
19 changes: 17 additions & 2 deletions .github/workflows/image-multiarch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ on:
type: string
required: false
default: "ci-base-scale-set"
codeartifact:
description: "Pass an AWS CodeArtifact token to the build as the CODEARTIFACT_TOKEN secret"
type: boolean
required: false
default: false
env:
VERSION_PREFIX: ''
VERSION_LATEST: latest
Expand All @@ -61,6 +66,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Get CodeArtifact token
id: codeartifact
if: ${{ inputs.codeartifact }}
uses: Typeform/.github/shared-actions/codeartifact-token@v1
with:
export-env: "false"
- name: Set up QEMU
if: ${{ contains(inputs.platforms, ',') }}
uses: docker/setup-qemu-action@v4
Expand Down Expand Up @@ -89,7 +100,9 @@ jobs:
tags: |
${{ vars.IMAGE_REGISTRY }}/${{ inputs.service }}:${{env.VERSION_PREFIX}}${{ inputs.version }}
${{ vars.IMAGE_REGISTRY }}/${{ inputs.service }}:${{env.VERSION_PREFIX}}${{ github.sha }}
secrets: ${{ secrets.build-secrets }}
secrets: |
${{ secrets.build-secrets }}
${{ inputs.codeartifact && format('CODEARTIFACT_TOKEN={0}', steps.codeartifact.outputs.token) || '' }}
file: ${{ inputs.file }}
- name: Set configured prefix on latest tag
if: ${{ inputs.prefix != '' }}
Expand All @@ -105,5 +118,7 @@ jobs:
push: true
tags: |
${{ vars.IMAGE_REGISTRY }}/${{ inputs.service }}:${{ env.VERSION_LATEST }}
secrets: ${{ secrets.build-secrets }}
secrets: |
${{ secrets.build-secrets }}
${{ inputs.codeartifact && format('CODEARTIFACT_TOKEN={0}', steps.codeartifact.outputs.token) || '' }}
file: ${{ inputs.file }}
51 changes: 51 additions & 0 deletions shared-actions/codeartifact-token/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# CodeArtifact token

Gets an AWS CodeArtifact authorization token using the job's AWS credentials. It doesn't need the `aws` CLI, which the ARC runner images don't ship: it uses `uv` and boto3.

The job must already have AWS credentials, either from the runner's IAM role (the ARC scale sets can read the `typeform` domain) or from an earlier `aws-actions/configure-aws-credentials` step.

## Usage

### Install packages in CI

```yaml
- uses: Typeform/.github/shared-actions/codeartifact-token@v1
- run: pip install -r requirements.txt # PIP_INDEX_URL / UV_INDEX_URL are set
```

By default the action exports:

| Variable | Value |
| --- | --- |
| `CODEARTIFACT_TOKEN` | The token (masked in logs) |
| `PIP_INDEX_URL` | `https://aws:<token>@<domain>-<owner>.d.codeartifact.<region>.amazonaws.com/pypi/<repository>/simple/` |
| `UV_INDEX_URL` | Same as `PIP_INDEX_URL` |

Use `PIP_INDEX_URL`, not `PIP_EXTRA_INDEX_URL`. CodeArtifact also serves public PyPI through its upstream, and a single index is what keeps internal package names from being resolved from public PyPI.

### Docker builds

Set `codeartifact: true` on the `image-multiarch` reusable workflow. It passes the token as the BuildKit secret `CODEARTIFACT_TOKEN`:

```dockerfile
RUN --mount=type=secret,id=CODEARTIFACT_TOKEN \
pip install --index-url "https://aws:$(cat /run/secrets/CODEARTIFACT_TOKEN)@typeform-567716553783.d.codeartifact.us-east-1.amazonaws.com/pypi/pypi/simple/" -r requirements.txt
```

## Inputs

| Input | Default | Description |
| --- | --- | --- |
| `domain` | `typeform` | CodeArtifact domain |
| `domain-owner` | `567716553783` | AWS account that owns the domain |
| `region` | `us-east-1` | Region of the domain |
| `repository` | `pypi` | Repository used for the index URL |
| `duration-seconds` | `3600` | Token lifetime, 900 to 43200 |
| `export-env` | `true` | Export the variables above. Set `false` to only set outputs. |

## Outputs

| Output | Description |
| --- | --- |
| `token` | The token (masked) |
| `pypi-index-url` | Index URL without credentials |
75 changes: 75 additions & 0 deletions shared-actions/codeartifact-token/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: 'CodeArtifact token'
description: 'Get an AWS CodeArtifact authorization token using the job AWS credentials, without the aws CLI'
inputs:
domain:
description: 'CodeArtifact domain'
required: false
default: 'typeform'
domain-owner:
description: 'AWS account ID that owns the domain'
required: false
default: '567716553783'
region:
description: 'AWS region of the domain'
required: false
default: 'us-east-1'
repository:
description: 'PyPI repository used for the index URL outputs and env vars'
required: false
default: 'pypi'
duration-seconds:
description: 'Token lifetime in seconds (900-43200)'
required: false
default: '3600'
export-env:
description: 'Export CODEARTIFACT_TOKEN, PIP_INDEX_URL and UV_INDEX_URL to later steps'
required: false
default: 'true'

outputs:
token:
description: 'CodeArtifact authorization token (masked)'
value: ${{ steps.token.outputs.token }}
pypi-index-url:
description: 'PyPI simple index URL of the repository, without credentials'
value: ${{ steps.token.outputs.pypi-index-url }}

runs:
using: 'composite'
steps:
- name: Setup uv
uses: astral-sh/setup-uv@v6
- name: Get CodeArtifact token
id: token
shell: bash
env:
CA_DOMAIN: ${{ inputs.domain }}
CA_OWNER: ${{ inputs.domain-owner }}
CA_REGION: ${{ inputs.region }}
CA_REPOSITORY: ${{ inputs.repository }}
CA_DURATION: ${{ inputs.duration-seconds }}
CA_EXPORT_ENV: ${{ inputs.export-env }}
run: |
set -euo pipefail
token=$(uv run --no-project --quiet --with 'boto3>=1.34' python -c '
import os, boto3
print(boto3.client("codeartifact", region_name=os.environ["CA_REGION"]).get_authorization_token(
domain=os.environ["CA_DOMAIN"],
domainOwner=os.environ["CA_OWNER"],
durationSeconds=int(os.environ["CA_DURATION"]),
)["authorizationToken"])
')
echo "::add-mask::${token}"
host="${CA_DOMAIN}-${CA_OWNER}.d.codeartifact.${CA_REGION}.amazonaws.com"
path="pypi/${CA_REPOSITORY}/simple/"
{
echo "token=${token}"
echo "pypi-index-url=https://${host}/${path}"
} >> "$GITHUB_OUTPUT"
if [ "${CA_EXPORT_ENV}" = "true" ]; then
{
echo "CODEARTIFACT_TOKEN=${token}"
echo "PIP_INDEX_URL=https://aws:${token}@${host}/${path}"
echo "UV_INDEX_URL=https://aws:${token}@${host}/${path}"
} >> "$GITHUB_ENV"
fi
Loading