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
132 changes: 132 additions & 0 deletions .github/workflows/build-devcontainer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
on:
workflow_call:
inputs:
push:
type: string
default: true
description: "Whether to push the image."
repo:
type: string
required: true
description: "Devcontainer image repository."
tag:
type: string
required: true
description: "Devcontainer image tag."
workspace-dir:
type: string
default: '.'
description: "Devcontainer workspace directory."
devcontainer-json:
type: string
required: true
description: "Path to the devcontainer.json file."
timeout-minutes:
type: number
default: 360
description: "Maximum time (in minutes) allowed for a run of this workflow."
retries:
type: string
default: '3'
description: "Number of times to retry the image build"
runs-on:
type: string
default: "ubuntu-latest"
description: "GHA runner label."
outputs:
version:
value: ${{ jobs.build.outputs.version }}


permissions:
actions: none
checks: none
contents: none
deployments: none
discussions: none
issues: none
packages: write
pages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none

jobs:
build:
timeout-minutes: ${{ inputs.timeout-minutes }}
strategy:
fail-fast: false
matrix:
arch: [amd64, arm64]
runs-on: ${{ fromJSON(github.actor != 'rapidsai' && '"ubuntu-latest"' || format('"${{ inputs.runs-on }}"', matrix.arch)) }}
name: "${{ inputs.tag }} (${{ matrix.arch }})"
outputs:
hash_amd64: ${{ steps.build.outputs.hash_amd64 }}
hash_arm64: ${{ steps.build.outputs.hash_arm64 }}
name: ${{ steps.build.outputs.name }}
repo: ${{ steps.build.outputs.repo }}
tag: ${{ steps.build.outputs.tag }}
version: ${{ steps.setup.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false

- id: setup
name: Setup versions
run: |
cat <<EOF | tee -a "$GITHUB_OUTPUT"
version=$(git describe --abbrev=0 --tags | sed 's/[a-zA-Z]//g' | cut -d '.' -f -2)
EOF

- name: Login to ghcr.io
if: inputs.push == 'true'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: "ghcr.io"
username: "${{ github.actor }}"
password: "${{ github.token }}"

- id: build
name: Build devcontainer (${{ matrix.arch }})
uses: rapidsai/shared-actions/build-devcontainer@main
with:
arch: "${{ matrix.arch }}"
repo: "ghcr.io/${{ inputs.repo }}"
push: "${{ inputs.push }}"
retries: "${{ inputs.retries }}"
tag: "${{ inputs.tag }}"
version: "${{ steps.setup.outputs.version }}"
workspace-dir: "${{ inputs.workspace-dir }}"
devcontainer-json: "${{ inputs.devcontainer-json }}"

push:
if: inputs.push == 'true'
name: Push to ghcr.io
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Login to ghcr.io
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: "ghcr.io"
username: "${{ github.actor }}"
password: "${{ github.token }}"

- id: push
name: Push manifest to ghcr.io
shell: bash --noprofile --norc -x -eo pipefail {0}
env:
hash_amd64: "${{ needs.build.outputs.hash_amd64 }}"
hash_arm64: "${{ needs.build.outputs.hash_arm64 }}"
name: "${{ needs.build.outputs.name }}"
name_latest: "${{ needs.build.outputs.name_latest }}"
ref_name: "${{ github.ref_name }}"
run: |
# Create the multiarch manifest
docker buildx imagetools create --tag "${name}" "${hash_amd64}" "${hash_arm64}";
if [[ "${ref_name}" == main ]]; then
docker buildx imagetools create --tag "${name_latest}" "${hash_amd64}" "${hash_arm64}";
fi
94 changes: 94 additions & 0 deletions .github/workflows/build-devcontainers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Build devcontainers

on:
workflow_call:
inputs:
cuda:
description: |
Stringified JSON array of CUDA versions to run this workflow for.
This is used to select .devcontainer/ directories local to wherever this workflow is invoked from.
For example, if a repository has directories '.devcontainer/cuda12.9-pip/' and '.devcontainer/cuda13.1-pip/',
'["12.9", "13.1"]' could be passed here to build both of those devcontainers in CI.
type: string
default: '["12.9", "13.1"]'
python_package_manager:
description: |
Stringified JSON array of Python package managers to run devcontainer builds for.
One of: '["conda"]', '["pip"]', '["conda", "pip"]'.
type: string
default: '["conda", "pip"]'
retries:
type: string
default: '3'
description: "Number of times to retry the image build"
push:
type: string
default: true

jobs:
build:
uses: ./.github/workflows/build-devcontainer.yaml
permissions:
packages: write
strategy:
fail-fast: false
matrix:
cuda: ${{ fromJSON(inputs.cuda) }}
python_package_manager: ${{ fromJSON(inputs.python_package_manager) }}
with:
runs-on: 'linux-{0}-cpu4'
push: "${{ inputs.push }}"
retries: "${{ inputs.retries }}"
repo: "${{ github.repository }}/devcontainer"
tag: "cuda${{ matrix.cuda }}-${{ matrix.python_package_manager }}"
devcontainer-json: ".devcontainer/cuda${{ matrix.cuda }}-${{ matrix.python_package_manager }}/devcontainer.json"

cleanup:
if: inputs.push == 'true'
needs: [build]
name: Clean up untagged images
runs-on: ubuntu-latest
steps:
- id: vars
name: Get image name, tags, and digests
env:
CUDA: "${{ inputs.cuda }}"
NAME_PREFIX: "ghcr.io/${{ github.actor }}"
NAME: "ghcr.io/${{ github.repository }}/devcontainer"
PYTHON_PACKAGE_MANAGER: "${{ inputs.python_package_manager }}"
VERSIONS: '["latest", "${{ needs.build.outputs.version }}"]'
run: |
set -xeuo pipefail

declare -a TAGS="($(jq -cnr \
--argjson vers "${VERSIONS}" \
--argjson cuda "${CUDA}" \
--argjson pkgr "${PYTHON_PACKAGE_MANAGER}" \
'[[$vers, $cuda, $pkgr] | combinations | [.[0], "cuda" + .[1], .[2]] | join("-")] | join(" ")'))"

declare -a DIGESTS=()
for TAG in "${TAGS[@]}"; do
mapfile -O "${#DIGESTS[@]}" -t DIGESTS < <(
docker buildx imagetools inspect --raw "${NAME}:${TAG}" | jq -r '.manifests.[] | .digest'
)
done

NAME="${NAME#${NAME_PREFIX}/}"

# Set values to control ghcr.io cleanup below
cat <<EOF >> "$GITHUB_OUTPUT"
digests=${DIGESTS[*]}
name=${NAME}
tags=${TAGS[*]}
EOF

- name: Clean up untagged images
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb
with:
cut-off: 1hr
tag-selection: untagged
token: "${{ github.token }}"
image-tags: "${{ steps.vars.outputs.tags }}"
image-names: "${{ steps.vars.outputs.name }}"
skip-shas: "${{ steps.vars.outputs.digests }}"
account: "${{ github.actor == github.triggering_actor && 'user' || github.actor }}"