1+ name : image
2+ on :
3+ workflow_dispatch :
4+ release :
5+ types : [published]
6+
7+ permissions :
8+ contents : read
9+ packages : write
10+ attestations : write
11+ id-token : write
12+
13+ defaults :
14+ run :
15+ shell : bash
16+
17+ concurrency :
18+ group : " image"
19+ cancel-in-progress : true
20+
21+ env :
22+ REGISTRY : ghcr.io
23+ REGISTRY_IMAGE : ghcr.io/borrowsanitizer/rust
24+ COMPRESSION : .tar.xz
25+
26+ jobs :
27+ release-info :
28+ # Every time we publish a new release, we want to build a corresponding
29+ # Docker image. However, we also want to be able to manually trigger this workflow
30+ # for debugging purposes. For this reason, instead of relying on the properties of `github.event`,
31+ # we query the GitHub API manually.
32+ name : Gather release info
33+ outputs :
34+ repo : ${{ steps.release-info.outputs.repo }}
35+ sha : ${{ steps.release-info.outputs.sha }}
36+ tag : ${{ steps.release-info.outputs.tag }}
37+ runs-on : ubuntu-latest
38+ steps :
39+ - name : Gather release info
40+ id : release-info
41+ env :
42+ GH_TOKEN : ${{ github.token }}
43+ run : |
44+ RELEASE_TAG=$(gh api repos/${{ github.repository }}/releases --jq 'sort_by(.created_at) | reverse | .[0].tag_name')
45+ RELEASE_SHA=$(gh api repos/${{ github.repository }}/git/refs/tags/$RELEASE_TAG --jq '.object.sha')
46+ echo "repo=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
47+ sha=$(echo "$RELEASE_SHA" | cut -c1-7)
48+ echo "tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
49+ echo "sha=$sha" >> $GITHUB_OUTPUT
50+
51+ # Builds a series of Docker images; one for each supported architecture
52+ # Each image has a corresponding "digest" that's saved as an artifact.
53+ # Each digest is combined into a single, multi-architecture image in the final stage.
54+ build :
55+ needs : release-info
56+ runs-on : ubuntu-latest
57+ strategy :
58+ fail-fast : false
59+ matrix :
60+ config :
61+ - platform : linux/amd64
62+ target : x86_64-unknown-linux-gnu
63+ steps :
64+ - name : Checkout
65+ uses : actions/checkout@v4
66+ - name : Prepare
67+ run : |
68+ platform=${{ matrix.config.platform }}
69+ echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
70+
71+ - name : Docker meta
72+ id : meta
73+ uses : docker/metadata-action@v5
74+ with :
75+ images : ${{ env.REGISTRY_IMAGE }}
76+
77+ # Our releases artifacts will always be a series of compressed archives
78+ # with names matching each supported target. For example, when we're building
79+ # an image for `x86_64-unknown-linux-gnu`, we want `x86_64-unknown-linux-gnu.tar.xz`.
80+ - name : Resolve URLs
81+ id : resolve_urls
82+ env :
83+ GH_TOKEN : ${{ github.token }}
84+ run : |
85+ QUERY=".assets[] | select(.url | endswith(\"${{matrix.config.target}}${{ env.COMPRESSION }}\")) | \"\(.url)\""
86+ url=$(gh release view ${{ needs.release-info.outputs.tag }} --repo ${{ github.repository }} --json assets -q "$QUERY")
87+ if [ ! "$url" ]; then
88+ echo "Unable to resolve url for release asset."
89+ exit 1
90+ fi
91+ echo "url_basename=$(basename $url ${{ env.COMPRESSION }})" >> "$GITHUB_OUTPUT"
92+ echo "url=$url" >> "$GITHUB_OUTPUT"
93+
94+ - name : Login
95+ uses : docker/login-action@v3
96+ with :
97+ registry : ${{ env.REGISTRY }}
98+ username : ${{ github.actor }}
99+ password : ${{ secrets.GITHUB_TOKEN }}
100+
101+ - name : Set up QEMU
102+ uses : docker/setup-qemu-action@v3
103+
104+ - name : Set up Docker Buildx
105+ uses : docker/setup-buildx-action@v3
106+
107+ - name : Build and push by digest
108+ id : build
109+ uses : docker/build-push-action@v6
110+ with :
111+ context : .
112+ file : ./src/ci/bsan/Dockerfile.bsan
113+ platforms : ${{ matrix.config.platform }}
114+ labels : ${{ steps.meta.outputs.labels }}
115+ tags : ${{ env.REGISTRY_IMAGE }}
116+ outputs : type=image,push-by-digest=true,name-canonical=true,push=true
117+ # URL - The URL of the compressed toolchain, which is downloaded, extracted, and installed when building the image.
118+ # TARGET - The current target; e.g. `aarch64-apple-darwin`.
119+ # PREFIX - A string placed before the name of the target for the toolchain installed within the container.
120+ # For example, "$PREFIX-$TARGET" will be listed as the only toolchain installed.
121+ build-args : |
122+ URL=${{ steps.resolve_urls.outputs.url }}
123+ TARGET=${{ matrix.config.target }}
124+ PREFIX=bsan-${{ needs.release-info.outputs.tag }}-${{needs.release-info.outputs.sha}}
125+
126+ - name : Export digest
127+ run : |
128+ mkdir -p ${{ runner.temp }}/digests
129+ digest="${{ steps.build.outputs.digest }}"
130+ touch "${{ runner.temp }}/digests/${digest#sha256:}"
131+
132+ - name : Upload digest
133+ uses : actions/upload-artifact@v4
134+ with :
135+ name : digests-${{ env.PLATFORM_PAIR }}
136+ path : ${{ runner.temp }}/digests/*
137+ if-no-files-found : error
138+ retention-days : 1
139+
140+ merge :
141+ runs-on : ubuntu-latest
142+ needs : [release-info, build]
143+ steps :
144+ - name : Download digests
145+ uses : actions/download-artifact@v4
146+ with :
147+ path : ${{ runner.temp }}/digests
148+ pattern : digests-*
149+ merge-multiple : true
150+
151+ - name : Login
152+ uses : docker/login-action@v3
153+ with :
154+ registry : ${{ env.REGISTRY }}
155+ username : ${{ github.actor }}
156+ password : ${{ secrets.GITHUB_TOKEN }}
157+
158+ - name : Set up Docker Buildx
159+ uses : docker/setup-buildx-action@v3
160+
161+ - name : Docker meta
162+ id : meta
163+ uses : docker/metadata-action@v5
164+ with :
165+ images : ${{ env.REGISTRY_IMAGE }}
166+ tags : |
167+ type=raw,value=${{ needs.release-info.outputs.tag }}
168+ - name : Create manifest list and push
169+ working-directory : ${{ runner.temp }}/digests
170+ run : |
171+ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
172+ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
173+
174+ - name : Inspect image
175+ run : |
176+ docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
177+
178+ cleanup :
179+ runs-on : ubuntu-latest
180+ needs : [merge]
181+ steps :
182+ - name : Delete untagged images
183+ env :
184+ GH_TOKEN : ${{ github.token }}
185+ run : |
186+ ENDPOINT=/orgs/${{ github.repository_owner }}/packages/container/rust/versions
187+ gh api "$ENDPOINT" --paginate \
188+ -q '.[] | select(.metadata.container.tags | length == 0) | .id' |
189+ while read -r id; do
190+ gh api -X DELETE "$ENDPOINT/$id"
191+ done
192+
0 commit comments