-
Notifications
You must be signed in to change notification settings - Fork 756
233 lines (213 loc) · 11.8 KB
/
push-mimir-build-image.yml
File metadata and controls
233 lines (213 loc) · 11.8 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
name: Build and Push mimir-build-image
# configure trigger by pull request
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
build_and_push:
runs-on: ubuntu-latest
permissions:
# Allow pushing to the GitHub repo for collaborators, forks should remain read-only
contents: write
# Allow PR modification for collaborators, forks should remain read-only
pull-requests: write
# Necessary to authenticate with Vault which happens in dockerhub-login
id-token: write
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
# We only want for this workflow to do anything when mimir-build-image/Dockerfile is modified.
# However, we want for it to be able to run for all PRs, just so it can be made a required check
# (i.e. so PRs can't be merged until it's done).
- name: Check if mimir-build-image/Dockerfile was modified
id: check_dockerfile
run: |
if gh pr diff ${{ github.event.pull_request.number }} --name-only | grep -q '^mimir-build-image/Dockerfile$'; then
echo "modified=true" >> $GITHUB_OUTPUT
echo "Dockerfile was modified"
else
echo "modified=false" >> $GITHUB_OUTPUT
echo "Dockerfile was not modified"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Retrieve GitHub App credentials to get a token with org member read permissions.
# GITHUB_TOKEN doesn't have org-level permissions, so we use the mimir-github-bot GitHub App.
- name: Retrieve GitHub App Credentials from Vault
if: steps.check_dockerfile.outputs.modified == 'true'
id: get-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # v1.1.0
with:
repo_secrets: |
APP_ID=mimir-github-bot:app_id
PRIVATE_KEY=mimir-github-bot:private_key
- name: Generate GitHub App Token for Membership
if: steps.check_dockerfile.outputs.modified == 'true'
id: app-token-for-membership
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
with:
app-id: ${{ env.APP_ID }}
private-key: ${{ env.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
# Check if PR author is a Grafana organization member using the GitHub API.
# This is more reliable than author_association which depends on public membership visibility.
# Uses the GitHub App token because GITHUB_TOKEN doesn't have org-level permissions.
- name: Check if PR author is org member
if: steps.check_dockerfile.outputs.modified == 'true'
id: check_membership
run: |
# Check membership, capturing the HTTP status code
status_code=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/orgs/grafana/members/${PR_AUTHOR}" \
--include 2>&1 | head -1 | awk '{print $2}')
if [ "$status_code" = "204" ]; then
echo "is_member=true" >> $GITHUB_OUTPUT
echo "✓ User ${PR_AUTHOR} is a Grafana organization member"
else
echo "is_member=false" >> $GITHUB_OUTPUT
echo "✗ User ${PR_AUTHOR} is not a Grafana organization member (HTTP $status_code)"
fi
env:
GITHUB_TOKEN: ${{ steps.app-token-for-membership.outputs.token }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
# We want to allow running github actions for all contributors, but don't want all contributors to be able to
# publish new build images just by sending the PR. Only Grafana org members and Renovate can proceed.
# This step sets an 'authorized' output that all subsequent privileged steps depend on.
- name: Check authorization
if: steps.check_dockerfile.outputs.modified == 'true'
id: authorize
run: |
if [ "${{ steps.check_membership.outputs.is_member }}" = "true" ] || [ "${PR_AUTHOR}" = "renovate-sh-app[bot]" ]; then
echo "authorized=true" >> $GITHUB_OUTPUT
echo "✓ User ${PR_AUTHOR} is authorized to build images"
else
echo "authorized=false" >> $GITHUB_OUTPUT
# We want to fail the workflow here because we only get here if the build image Dockerfile
# has changed, and such a PR shouldn't pass without a successful build and push.
echo "::error::Only Grafana organization members and Renovate can trigger build image updates"
exit 1
fi
env:
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
- name: Checkout Pull Request Branch
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
run: gh pr checkout ${{ github.event.pull_request.number }}
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Setup QEMU
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Set up Docker Buildx
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Login to Docker Hub
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
uses: grafana/shared-workflows/actions/dockerhub-login@13fb504e3bfe323c1188bf244970d94b2d336e86 # v1.0.1
- name: Prepare Variables
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
id: prepare
run: |
echo "path=mimir-build-image/Dockerfile" >> $GITHUB_OUTPUT
main_build_image=$(make print-build-image)
main_image_tag=$(echo $main_build_image | cut -d ':' -f 2)
image_name=$(echo $main_build_image | cut -d ':' -f 1)
echo "image=$image_name" >> $GITHUB_OUTPUT
echo "main_image_tag=$main_image_tag" >> $GITHUB_OUTPUT
- name: Compute Image Tag
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
id: compute_hash
run: |
current_hash=$(md5sum ${{ steps.prepare.outputs.path }} | awk '{print substr($1, 0, 10)}')
echo "the file path is ${{ steps.prepare.outputs.path }}"
echo "build tag is $current_hash"
tag="pr${{ github.event.pull_request.number }}-$current_hash"
echo "tag=$tag" >> $GITHUB_OUTPUT
- name: Check Should Build Image
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
id: check_build
run: |
echo "Checking if image should be built"
if skopeo inspect --raw "docker://${{ steps.prepare.outputs.image }}:${{ steps.compute_hash.outputs.tag }}" >/dev/null 2>&1; then
echo "build=false" >> $GITHUB_OUTPUT
echo "Tag ${{ steps.compute_hash.outputs.tag }} exists"
else
echo "Tag ${{ steps.compute_hash.outputs.tag }} does not exist"
echo "build=true" >> $GITHUB_OUTPUT
fi
- name: Add Comment to the PR
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
id: notification
run: |
if [ ${{ steps.check_build.outputs.build }} == 'true' ]; then
gh pr comment $PR_NUMBER --body "**Building new version of mimir-build-image**. After image is built and pushed to Docker Hub, \
a new commit will automatically be added to this PR with new image version \`$IMAGE:$TAG\`. This can take up to 1 hour."
else
echo "This PR will not trigger a build of mimir-build-image"
gh pr comment $PR_NUMBER --body "**Not building new version of mimir-build-image**. This PR modifies \`mimir-build-image/Dockerfile\`, but the image \`$IMAGE:$TAG\` already exists."
fi
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
TAG: ${{ steps.compute_hash.outputs.tag }}
IMAGE: ${{ steps.prepare.outputs.image }}
- name: Build and Push Docker Image
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true' && steps.check_build.outputs.build == 'true'
run: |
echo "Building and Pushing Docker Image"
make push-multiarch-build-image IMAGE_TAG=${{ steps.compute_hash.outputs.tag }}
- name: Compare built tag with Makefile
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true'
id: compare_tag
run: |
echo "Comparing built tag with Makefile"
if [ ${{ steps.compute_hash.outputs.tag }} == "$(make print-build-image)" ]; then
echo "Built tag is the same as the one in Makefile"
echo "isDifferent=false" >> $GITHUB_OUTPUT
else
echo "Built tag is different from the one in Makefile"
echo "isDifferent=true" >> $GITHUB_OUTPUT
fi
# Tokens expire after 1h. Since building the image can take a long time, we create a fresh App Token for the last
# step of the build. (https://github.com/actions/create-github-app-token/issues/121#issuecomment-2027574184)
- name: Generate GitHub App Token
if: steps.check_dockerfile.outputs.modified == 'true'
id: app-token-for-commit
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
with:
app-id: ${{ env.APP_ID }}
private-key: ${{ env.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
# This step uses "Mimir bot" token (generated at the start of the workflow) instead of
# "github-actions bot" (secrets.GITHUB_TOKEN) because any events triggered by the latter
# don't spawn GitHub actions.
# Refer to https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow
- name: Add commit to PR in order to update Build Image version
if: steps.check_dockerfile.outputs.modified == 'true' && steps.authorize.outputs.authorized == 'true' && steps.compare_tag.outputs.isDifferent == 'true'
run: |
echo "Get current Build Image Version"
echo "Current Build Image Version is $MAIN_TAG"
echo "Built Image Version is $TAG"
if [ "$MAIN_TAG" = "$TAG" ]; then
echo "Build Image Version is already up to date"
else
echo "Build Image Version is not up to date"
sed -i "s/$MAIN_TAG/${{ steps.compute_hash.outputs.tag }}/g" Makefile
git config --global user.email "${GITHUB_EMAIL}"
git config --global user.name "${GITHUB_LOGIN}"
git config --global url.https://x-access-token:${GITHUB_TOKEN}@github.com/.insteadOf https://github.com/
git add Makefile
git commit -m "Update build image version to ${{ steps.compute_hash.outputs.tag }}"
git push origin HEAD
fi
env:
GITHUB_TOKEN: ${{ steps.app-token-for-commit.outputs.token }}
GITHUB_LOGIN: mimir-github-bot[bot]
GITHUB_EMAIL: 199097951+mimir-github-bot[bot]@users.noreply.github.com
TAG: ${{ steps.compute_hash.outputs.tag }}
MAIN_TAG: ${{ steps.prepare.outputs.main_image_tag }}