Skip to content

Commit 28daf2e

Browse files
committed
add build-devcontainer.yaml workflow
1 parent 9b3ec6b commit 28daf2e

2 files changed

Lines changed: 232 additions & 0 deletions

File tree

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
on:
2+
workflow_call:
3+
inputs:
4+
push:
5+
type: string
6+
default: true
7+
description: "Whether to push the image."
8+
repo:
9+
type: string
10+
required: true
11+
description: "Devcontainer image repository."
12+
tag:
13+
type: string
14+
required: true
15+
description: "Devcontainer image tag."
16+
workspace-dir:
17+
type: string
18+
default: '.'
19+
description: "Devcontainer workspace directory."
20+
devcontainer-json:
21+
type: string
22+
required: true
23+
description: "Path to the devcontainer.json file."
24+
timeout-minutes:
25+
type: number
26+
default: 360
27+
description: "Maximum time (in minutes) allowed for a run of this workflow."
28+
retries:
29+
type: string
30+
default: '10'
31+
description: "Number of times to retry the image build"
32+
runs-on:
33+
type: string
34+
default: "ubuntu-latest"
35+
description: "GHA runner label."
36+
outputs:
37+
version:
38+
type: string
39+
value: ${{ jobs.build.outputs.version }}
40+
41+
42+
permissions:
43+
actions: read
44+
checks: none
45+
contents: read
46+
deployments: none
47+
discussions: none
48+
issues: none
49+
packages: write
50+
pages: none
51+
pull-requests: read
52+
repository-projects: none
53+
security-events: none
54+
statuses: none
55+
56+
jobs:
57+
build:
58+
timeout-minutes: ${{ inputs.timeout-minutes }}
59+
strategy:
60+
fail-fast: false
61+
matrix:
62+
arch: [amd64, arm64]
63+
runs-on: ${{ fromJSON(github.actor != 'rapidsai' && '"ubuntu-latest"' || format('"${{ inputs.runs-on }}"', matrix.arch)) }}
64+
name: "${{ inputs.tag }} (${{ matrix.arch }})"
65+
outputs:
66+
hash_amd64: ${{ steps.build.outputs.hash_amd64 }}
67+
hash_arm64: ${{ steps.build.outputs.hash_arm64 }}
68+
name: ${{ steps.build.outputs.name }}
69+
repo: ${{ steps.build.outputs.repo }}
70+
tag: ${{ steps.build.outputs.tag }}
71+
version: ${{ steps.setup.outputs.version }}
72+
steps:
73+
- uses: actions/checkout@v6
74+
with:
75+
fetch-depth: 0
76+
persist-credentials: false
77+
78+
- id: setup
79+
name: Setup versions
80+
run: |
81+
cat <<EOF | tee -a "$GITHUB_OUTPUT"
82+
version=$(git describe --abbrev=0 --tags | sed 's/[a-zA-Z]//g' | cut -d '.' -f -2)
83+
EOF
84+
85+
- if: runner.environment != 'self-hosted'
86+
name: Setup proxy cache
87+
uses: nv-gha-runners/setup-proxy-cache@main
88+
continue-on-error: true
89+
with:
90+
enable-apt: true
91+
92+
- name: Login to ghcr.io
93+
if: inputs.push == 'true'
94+
uses: docker/login-action@v4
95+
with:
96+
registry: "ghcr.io"
97+
username: "${{ github.actor }}"
98+
password: "${{ github.token }}"
99+
100+
- id: build
101+
name: Build devcontainer (${{ matrix.arch }})
102+
uses: rapidsai/shared-actions/build-devcontainer@fea/build-devcontainer
103+
with:
104+
arch: "${{ matrix.arch }}"
105+
repo: "ghcr.io/${{ inputs.repo }}"
106+
push: "${{ inputs.push }}"
107+
retries: "${{ inputs.retries }}"
108+
tag: "${{ steps.setup.outputs.version }}-${{ inputs.tag }}"
109+
workspace-dir: "${{ inputs.workspace-dir }}"
110+
devcontainer-json: "${{ inputs.devcontainer-json }}"
111+
112+
push:
113+
if: inputs.push == 'true'
114+
name: Push to ghcr.io
115+
needs: [build]
116+
runs-on: ubuntu-latest
117+
steps:
118+
- name: Login to ghcr.io
119+
uses: docker/login-action@v4
120+
with:
121+
registry: "ghcr.io"
122+
username: "${{ github.actor }}"
123+
password: "${{ github.token }}"
124+
125+
- id: push
126+
name: Push manifest to ghcr.io
127+
shell: bash --noprofile --norc -x -eo pipefail {0}
128+
env:
129+
hash_amd64: "${{ needs.build.outputs.hash_amd64 }}"
130+
hash_arm64: "${{ needs.build.outputs.hash_arm64 }}"
131+
name: "${{ needs.build.outputs.name }}"
132+
run: |
133+
# Create the multiarch manifest
134+
docker buildx imagetools create --tag "${name}" "${hash_amd64}" "${hash_arm64}";
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Build devcontainers
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
cuda:
7+
description: |
8+
Stringified JSON array of CUDA versions to run this workflow for.
9+
This is used to select .devcontainer/ directories local to wherever this workflow is invoked from.
10+
For example, if a repository has directories '.devcontainer/cuda12.9-pip/' and '.devcontainer/cuda13.1-pip/',
11+
'["12.9", "13.1"]' could be passed here to build both of those devcontainers in CI.
12+
type: string
13+
default: '["12.9", "13.1"]'
14+
python_package_manager:
15+
description: |
16+
Stringified JSON array of Python package managers to run devcontainer builds for.
17+
One of: '["conda"]', '["pip"]', '["conda", "pip"]'.
18+
type: string
19+
default: '["conda", "pip"]'
20+
retries:
21+
type: string
22+
default: '10'
23+
description: "Number of times to retry the image build"
24+
push:
25+
type: string
26+
default: true
27+
28+
jobs:
29+
build:
30+
secrets: inherit
31+
uses: ./.github/workflows/build-devcontainer.yaml
32+
permissions:
33+
actions: read
34+
packages: write
35+
contents: read
36+
pull-requests: read
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
cuda: ${{ fromJSON(inputs.cuda) }}
41+
python_package_manager: ${{ fromJSON(inputs.python_package_manager) }}
42+
with:
43+
runs-on: 'linux-{0}-cpu4'
44+
push: "${{ inputs.push }}"
45+
retries: "${{ inputs.retries }}"
46+
repo: "${{ github.repository }}/devcontainer"
47+
tag: "cuda${{ matrix.cuda }}-${{ matrix.python_package_manager }}"
48+
devcontainer-json: ".devcontainer/cuda${{ matrix.cuda }}-${{ matrix.python_package_manager }}/devcontainer.json"
49+
50+
cleanup:
51+
needs: [build]
52+
name: Clean up untagged images
53+
runs-on: ubuntu-latest
54+
outputs:
55+
digests: ${{ steps.values.outputs.digests }}
56+
name: ${{ steps.values.outputs.name }}
57+
tags: ${{ steps.values.outputs.tags }}
58+
steps:
59+
- id: vars
60+
name: Get image name, tags, and digests
61+
env:
62+
VERSION: "${{ needs.build.outputs.version }}"
63+
NAME: "ghcr.io/${{ github.repository }}/devcontainer"
64+
run: |
65+
set -xeuo pipefail
66+
67+
declare -a TAGS="($(jq -cnr \
68+
--arg vers "${VERSION}" \
69+
--argjson cuda '${{ inputs.cuda }}' \
70+
--argjson pkgr '${{ inputs.python_package_manager }}' \
71+
'[[$cuda, $pkgr] | combinations | [$vers, "cuda" + .[0], .[1]] | join("-")] | join(" ")'))"
72+
73+
declare -a DIGESTS=()
74+
for TAG in "${TAGS[@]}"; do
75+
mapfile -O ${#DIGESTS} -t DIGESTS < <(
76+
docker buildx imagetools inspect --raw "${NAME}:${TAG}" | jq -r '.manifests.[] | .digest'
77+
)
78+
done
79+
80+
NAME=${NAME#ghcr.io/${{ github.actor }}/}
81+
82+
# Set values to control ghcr.io cleanup below
83+
cat <<EOF >> "$GITHUB_OUTPUT"
84+
digests=${DIGESTS[*]}
85+
name=${NAME}
86+
tags=${TAGS[*]}
87+
EOF
88+
89+
- name: Clean up untagged images
90+
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb
91+
with:
92+
cut-off: 1hr
93+
tag-selection: untagged
94+
token: "${{ github.token }}"
95+
image-tags: "${{ steps.vars.outputs.tags }}"
96+
image-names: "${{ steps.vars.outputs.name }}"
97+
skip-shas: "${{ steps.vars.outputs.digests }}"
98+
account: "${{ github.actor == github.triggering_actor && 'user' || github.actor }}"

0 commit comments

Comments
 (0)