-
Notifications
You must be signed in to change notification settings - Fork 12
273 lines (263 loc) · 11.7 KB
/
build-test-publish.yml
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
name: Build, Test, Publish
on:
pull_request:
push:
branches: ["main"]
schedule:
- cron: "0 0 * * 1-5"
workflow_dispatch:
permissions:
contents: read
id-token: write
defaults:
run:
# Setting an explicit bash shell ensures GitHub Actions enables pipefail mode too, rather
# than only error on exit. This is important for UX since this workflow uses pipes. See:
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell
shell: bash
jobs:
create:
strategy:
fail-fast: false
matrix:
builder: ["builder-20", "builder-22", "builder-24", "salesforce-functions"]
arch: ["amd64", "arm64"]
exclude:
# Builders prior to Heroku-24 don't support ARM. We're using an exclude rather than
# an include so that the ARM jobs don't appear out of sequence in the GitHub UI.
- builder: builder-20
arch: arm64
- builder: builder-22
arch: arm64
- builder: salesforce-functions
arch: arm64
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-24.04-arm-medium' || 'ubuntu-24.04' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Pack CLI
uses: buildpacks/github-actions/[email protected]
- name: Create builder image
run: pack builder create ${{ matrix.builder }} --config ${{ matrix.builder }}/builder.toml --pull-policy always
# We export the run image too (and not just the generated builder image), since it adds virtually
# no size to the archive (since the layers are mostly duplicates), and it ends up being quicker
# than docker pulling the run image in the jobs that perform the pack build.
# We manually compress the archive rather than relying upon actions/cache's compression, since
# it ends up being faster both in this job and also later when the images are consumed.
- name: Export Docker images from the Docker daemon
# Using sed rather than yq until this yq bug is fixed:
# https://github.com/mikefarah/yq/issues/1758
run: |
RUN_IMAGE_TAG=$(sed --quiet --regexp-extended --expression 's/run-image\s*=\s*"(.+)"/\1/p' ${{ matrix.builder }}/builder.toml)
docker save ${{ matrix.builder }} ${RUN_IMAGE_TAG} | zstd -T0 --long=31 -o images.tar.zst
# We use a cache here rather than artifacts because it's 4x faster and we
# don't need the builder archive outside of this workflow run anyway.
- name: Save Docker images to the cache
uses: actions/cache/save@v4
with:
key: ${{ github.run_id}}-${{ matrix.builder }}-${{ matrix.arch }}
path: images.tar.zst
test:
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-24.04-arm-medium' || 'ubuntu-24.04' }}
needs: create
strategy:
fail-fast: false
matrix:
language: ["dotnet", "go", "gradle", "java", "node-js", "php", "python", "ruby", "scala"]
builder: ["builder-20", "builder-22", "builder-24"]
arch: ["amd64", "arm64"]
exclude:
- builder: builder-20
arch: arm64
- builder: builder-22
arch: arm64
- builder: builder-20
language: dotnet
steps:
- name: Checkout getting started guide
uses: actions/checkout@v4
with:
ref: main
repository: heroku/${{ matrix.language }}-getting-started.git
- name: Install Pack CLI
uses: buildpacks/github-actions/[email protected]
- name: Restore Docker images from the cache
uses: actions/cache/restore@v4
with:
fail-on-cache-miss: true
key: ${{ github.run_id}}-${{ matrix.builder }}-${{ matrix.arch }}
path: images.tar.zst
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
- name: Load Docker images into the Docker daemon
run: zstd -dc --long=31 images.tar.zst | docker load
- name: Build getting started guide image
run: pack build getting-started --force-color --builder ${{ matrix.builder }} --trust-builder --pull-policy never
- name: Start getting started guide image
# The `DYNO` env var is set to more accurately reflect the Heroku environment, since some of the getting
# started guides use the presence of `DYNO` to determine whether to enable production mode or not.
run: docker run --name getting-started --detach -p 8080:8080 --env PORT=8080 --env DYNO=web.1 getting-started
- name: Test getting started web server response
run: |
if curl -sSf --retry 10 --retry-delay 1 --retry-all-errors --connect-timeout 3 http://localhost:8080 -o response.txt; then
echo "Successful response from server"
else
echo "Server did not respond successfully"
docker logs getting-started
[[ -f response.txt ]] && cat response.txt
exit 1
fi
test-functions:
runs-on: ubuntu-24.04
needs: create
strategy:
fail-fast: false
matrix:
language: ["java", "javascript", "typescript"]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Pack CLI
uses: buildpacks/github-actions/[email protected]
with:
# Using an older version of Pack CLI here (that only supports Platform API <= 0.9),
# for testing parity with the Platform API version used by Kodon for Functions:
# https://github.com/buildpacks/pack/blob/v0.27.0/internal/build/lifecycle_executor.go#L30
# https://github.com/heroku/kodon/blob/functions_eol/internal/constants/constants.go#L75
pack-version: "0.27.0"
- name: Restore Docker images from the cache
uses: actions/cache/restore@v4
with:
fail-on-cache-miss: true
key: ${{ github.run_id}}-salesforce-functions-amd64
path: images.tar.zst
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
- name: Load Docker images into the Docker daemon
run: zstd -dc --long=31 images.tar.zst | docker load
- name: Build example function image
run: pack build example-function --path salesforce-functions/examples/${{ matrix.language }} --builder salesforce-functions --trust-builder --pull-policy never
- name: Start example function image
run: docker run --name example-function --detach -p 8080:8080 --env PORT=8080 --env DYNO=web.1 example-function
- name: Test example function web server response
run: |
if curl -sSf --retry 10 --retry-delay 1 --retry-all-errors --connect-timeout 3 -X POST -H 'x-health-check: true' http://localhost:8080 -o response.txt; then
echo "Successful response from function"
else
echo "Function did not respond successfully"
docker logs example-function
[[ -f response.txt ]] && cat response.txt
exit 1
fi
publish-image:
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-24.04-arm-medium' || 'ubuntu-24.04' }}
if: success() && github.ref == 'refs/heads/main'
needs: ["test", "test-functions"]
strategy:
fail-fast: false
matrix:
include:
- builder: builder-20
arch: amd64
tag_docker_hub: heroku/builder:20
- builder: builder-22
arch: amd64
tag_docker_hub: heroku/builder:22
tag_ecr_public: heroku/builder:22
- builder: salesforce-functions
arch: amd64
tag_private: heroku-22:builder-functions
- builder: builder-24
arch: amd64
tag_docker_hub: heroku/builder:24_linux-amd64
tag_ecr_public: heroku/builder:24_linux-amd64
- builder: builder-24
arch: arm64
tag_docker_hub: heroku/builder:24_linux-arm64
tag_ecr_public: heroku/builder:24_linux-arm64
steps:
- name: Restore Docker images from the cache
uses: actions/cache/restore@v4
with:
fail-on-cache-miss: true
key: ${{ github.run_id}}-${{ matrix.builder }}-${{ matrix.arch }}
path: images.tar.zst
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
- name: Load Docker images into the Docker daemon
run: zstd -dc --long=31 images.tar.zst | docker load
- name: Log into Docker Hub
if: matrix.tag_docker_hub != ''
run: echo '${{ secrets.DOCKER_HUB_TOKEN }}' | docker login -u '${{ secrets.DOCKER_HUB_USER }}' --password-stdin
- name: Log into internal registry
if: matrix.tag_private != ''
run: |
REGISTRY_TOKEN=$(
curl -sSf --retry 3 --retry-delay 1 --retry-all-errors --connect-timeout 3 \
-X POST -d '{"username":"${{ secrets.SERVICE_TOKEN_USER_NAME }}", "password":"${{ secrets.SERVICE_TOKEN_PASSWORD }}"}' \
'${{ secrets.SERVICE_TOKEN_ENDPOINT }}' \
| jq --exit-status -r '.raw_id_token'
)
echo "${REGISTRY_TOKEN}" | docker login '${{ secrets.REGISTRY_HOST }}' -u '${{ secrets.REGISTRY_USER }}' --password-stdin
- name: Configure AWS credentials
if: matrix.tag_ecr_public != ''
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ECR_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Log in to Amazon ECR Public
if: matrix.tag_ecr_public != ''
id: login-ecr-public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Tag builder and push to Docker Hub
if: matrix.tag_docker_hub != ''
run: |
DOCKER_HUB_IMAGE_URI='${{ matrix.tag_docker_hub }}'
set -x
docker tag '${{ matrix.builder }}' "${DOCKER_HUB_IMAGE_URI}"
docker push "${DOCKER_HUB_IMAGE_URI}"
- name: Tag builder and push to internal registry
if: matrix.tag_private != ''
run: |
PRIVATE_IMAGE_URI='${{ secrets.REGISTRY_HOST }}/s/${{ secrets.SERVICE_TOKEN_USER_NAME }}/${{ matrix.tag_private }}'
set -x
docker tag '${{ matrix.builder }}' "${PRIVATE_IMAGE_URI}"
docker push "${PRIVATE_IMAGE_URI}"
- name: Tag builder and push to public ECR
if: matrix.tag_ecr_public != ''
run: |
ECR_PUBLIC_IMAGE_URI='public.ecr.aws/${{ matrix.tag_ecr_public }}'
set -x
docker tag '${{ matrix.builder }}' "${ECR_PUBLIC_IMAGE_URI}"
docker push "${ECR_PUBLIC_IMAGE_URI}"
publish-manifest:
runs-on: ubuntu-24.04
needs: publish-image
strategy:
fail-fast: false
matrix:
include:
- tag_uri: "docker.io/heroku/builder:24"
manifest_uris: "docker.io/heroku/builder:24_linux-amd64 docker.io/heroku/builder:24_linux-arm64"
- tag_uri: "public.ecr.aws/heroku/builder:24"
manifest_uris: "public.ecr.aws/heroku/builder:24_linux-amd64 public.ecr.aws/heroku/builder:24_linux-arm64"
steps:
- name: Log in to Docker Hub
run: echo '${{ secrets.DOCKER_HUB_TOKEN }}' | docker login -u '${{ secrets.DOCKER_HUB_USER }}' --password-stdin
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ECR_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Log in to Amazon ECR Public
id: login-ecr-public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Create and push manifest lists
run: |
set -x
docker manifest create "${{ matrix.tag_uri }}" ${{ matrix.manifest_uris }}
docker manifest push "${{ matrix.tag_uri }}"