-
Notifications
You must be signed in to change notification settings - Fork 0
141 lines (123 loc) · 5.06 KB
/
Copy pathdevcontainer-cache.yml
File metadata and controls
141 lines (123 loc) · 5.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
name: DevContainer Prebuild
on:
push:
branches:
- main
paths:
- '.devcontainer/**'
- '.github/workflows/devcontainer-cache.yml'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
id-token: write
env:
DEVCONTAINER_IMAGE: ghcr.io/crunchloop/devcontainer/devcontainer
jobs:
prebuild:
name: Build devcontainer prebuild (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
suffix: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
suffix: arm64
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# devcontainers/ci doesn't cleanly expose digest-only push, so we push
# per-arch tags and merge them into a multi-arch manifest below.
- name: Build devcontainer prebuild image
uses: devcontainers/ci@v0.3
with:
configFile: .devcontainer/devcontainer-build.json
imageName: ${{ env.DEVCONTAINER_IMAGE }}
imageTag: build-${{ github.run_id }}-${{ matrix.suffix }}
platform: ${{ matrix.platform }}
cacheFrom: ${{ env.DEVCONTAINER_IMAGE }}:buildcache-${{ matrix.suffix }}
push: always
merge:
name: Merge multi-arch manifest
needs: prebuild
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest list and push
run: |
# Tag the multi-arch manifest as :latest (the rolling tag developers
# pull) and as an immutable :sha-<commit> tag for pinning/rollback.
short_sha="${GITHUB_SHA::12}"
docker buildx imagetools create \
-t ${{ env.DEVCONTAINER_IMAGE }}:latest \
-t ${{ env.DEVCONTAINER_IMAGE }}:sha-${short_sha} \
${{ env.DEVCONTAINER_IMAGE }}:build-${{ github.run_id }}-amd64 \
${{ env.DEVCONTAINER_IMAGE }}:build-${{ github.run_id }}-arm64
- name: Inspect manifest
run: docker buildx imagetools inspect ${{ env.DEVCONTAINER_IMAGE }}:latest
# Best-effort cleanup of the per-arch build-<run_id>-* intermediates that
# devcontainers/ci has to push (it can't push digest-only). This is
# non-fatal: a failure here must never block a published :latest.
#
# Safety: the current run's intermediates share their image digests with
# the children of the :latest manifest list we just pushed — deleting
# those versions would break :latest. So we only delete build-* versions
# whose digest is NOT referenced by the current :latest, and never touch
# versions tagged latest or sha-*.
- name: Prune stale per-arch build intermediates
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -uo pipefail
owner="${GITHUB_REPOSITORY_OWNER}"
pkg="devcontainer%2Fdevcontainer" # url-encoded "devcontainer/devcontainer"
# Digests referenced by the just-pushed :latest manifest list. These
# must be preserved.
mapfile -t keep_digests < <(
docker buildx imagetools inspect --raw "${DEVCONTAINER_IMAGE}:latest" \
| jq -r '.manifests[].digest'
)
echo "Protected digests (referenced by :latest):"
printf ' %s\n' "${keep_digests[@]}"
gh api --paginate "/orgs/${owner}/packages/container/${pkg}/versions" \
| jq -c '.[]' | while read -r v; do
id=$(jq -r '.id' <<<"$v")
digest=$(jq -r '.name' <<<"$v")
tags=$(jq -r '(.metadata.container.tags // []) | join(",")' <<<"$v")
# Only consider intermediates; never delete latest/sha-* versions.
case ",${tags}," in
*",latest,"*) continue ;;
esac
[[ "${tags}" == *sha-* ]] && continue
[[ "${tags}" == *build-* ]] || continue
# Preserve anything still referenced by the current :latest.
for d in "${keep_digests[@]}"; do
[ "${d}" = "${digest}" ] && { echo "keep ${id} (${tags}) — in :latest"; continue 2; }
done
echo "prune ${id} (tags: ${tags})"
gh api -X DELETE "/orgs/${owner}/packages/container/${pkg}/versions/${id}" \
&& echo " deleted" || echo " delete failed (non-fatal)"
done