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