Skip to content

Commit b04a6dc

Browse files
committed
CI: parallelize docker build
And simplify adding new image. Now you just have to add a docker compose with an image name and a build section. Also unset the image of api for dev, some people had problems when there was an image defined which cannot be downloaded. Populate the buildArgValues in the build-and-push step because passing secrets between jobs seems to be discouraged by github. https://github.com/orgs/community/discussions/37942
1 parent 96ddba4 commit b04a6dc

File tree

5 files changed

+204
-71
lines changed

5 files changed

+204
-71
lines changed

.docker-hub/docker-compose.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
services:
2+
frontend-image:
3+
image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-frontend:${VERSION:-latest}
4+
build:
5+
context: ../
6+
dockerfile: .docker-hub/frontend/Dockerfile
7+
# they have to be registered in GitHub under exactly this name in secrets or vars
8+
args:
9+
SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN:-}
10+
SENTRY_ORG: ${SENTRY_ORG:-}
11+
SENTRY_FRONTEND_PROJECT: ${SENTRY_FRONTEND_PROJECT:-}
12+
SENTRY_RELEASE_NAME: ${RELEASE_NAME:-}
13+
print-image:
14+
image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-print:${VERSION:-latest}
15+
build:
16+
context: ../
17+
dockerfile: .docker-hub/print/Dockerfile
18+
# they have to be registered in GitHub under exactly this name in secrets or vars
19+
args:
20+
SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN:-}
21+
SENTRY_ORG: ${SENTRY_ORG:-}
22+
SENTRY_PRINT_PROJECT: ${SENTRY_PRINT_PROJECT:-}
23+
SENTRY_RELEASE_NAME: ${RELEASE_NAME:-}
24+
varnish-image:
25+
image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-varnish:${VERSION:-latest}
26+
build:
27+
context: ../
28+
dockerfile: .docker-hub/varnish/Dockerfile
Lines changed: 173 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
name: '[reusable only] Build images and push to registry'
1+
name: "[reusable only] Build images and push to registry"
22

33
on:
4+
workflow_dispatch:
45
workflow_call:
56
inputs:
67
tag:
@@ -17,91 +18,194 @@ on:
1718
required: true
1819
SENTRY_AUTH_TOKEN:
1920

21+
env:
22+
DOCKER_BUILDKIT: 1
23+
COMPOSE_DOCKER_CLI_BUILD: 1
24+
2025
jobs:
21-
build-and-push:
22-
name: Build images and push
26+
build-info:
2327
runs-on: ubuntu-latest
28+
outputs:
29+
repo-owner: ${{ steps.repo-owner.outputs.result }}
30+
tags: ${{ steps.image-tags.outputs.image-tags }}
31+
build-config: ${{ steps.build-info.outputs.result }}
2432
steps:
33+
#github forces lower case for the image name
34+
- name: Get lowercase repo owner name
35+
uses: actions/github-script@v7
36+
id: repo-owner
37+
with:
38+
result-encoding: string
39+
script: |
40+
return context.repo.owner.toLowerCase()
41+
2542
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
43+
44+
- name: Set nightly tag if commit was on main
45+
id: add-nightly-tag
46+
if: startsWith(github.ref, 'refs/heads/devel')
47+
run: |
48+
echo "nightly-tag=nightly" | tr -d "\n" >> $GITHUB_OUTPUT
49+
50+
- name: Set latest tag if its a tag
51+
id: add-latest-tag
52+
if: startsWith(github.ref, 'refs/tags/')
53+
run: |
54+
echo "latest-tag=latest" | tr -d "\n" >> $GITHUB_OUTPUT
55+
56+
- uses: actions/github-script@v7
57+
id: get-tag
58+
if: startsWith(github.ref, 'refs/tags/')
2659
with:
27-
ref: ${{ inputs.sha }}
60+
result-encoding: string
61+
script: |
62+
return context.payload.ref.replace('refs/tags/', '')
63+
64+
- name: concat tags to list
65+
id: image-tags
66+
run: |
67+
TAGS=$(cat <<-END
68+
[
69+
"${{ inputs.sha || github.sha }}",
70+
"${{ steps.add-nightly-tag.outputs.nightly-tag }}",
71+
"${{ steps.add-latest-tag.outputs.latest-tag }}",
72+
"${{ steps.get-tag.outputs.result }}"
73+
]
74+
END
75+
)
76+
TAGS=$(echo $TAGS | jq -c 'map(select(length > 0))')
77+
echo "image-tags=$TAGS" | tr -d "\n" >> $GITHUB_OUTPUT
78+
79+
- name: Get build info
80+
id: build-info
81+
run: |
82+
set -x
83+
sudo snap install yq
84+
85+
export REPO_NAME=$(basename $(pwd))
86+
echo "services:" > /tmp/docker-compose.yml
87+
for i in $(find . -name docker-compose.yml); do
88+
docker compose -f $i config | yq '.services | select(.[].build != null and .[].image != null)' | sed 's/^/ /' >> /tmp/docker-compose.yml
89+
done
90+
91+
cat /tmp/docker-compose.yml
92+
93+
yq_pipe='.services'
94+
yq_pipe=$yq_pipe'| to_entries[]'
95+
yq_pipe=$yq_pipe'| select(.value.build != null and .value.image != null)'
96+
yq_pipe=$yq_pipe'| .value.build.image=.value.image'
97+
yq_pipe=$yq_pipe'| .value.build.service=.key'
98+
yq_pipe=$yq_pipe'| .value.build'
99+
yq_pipe=$yq_pipe'| .dockerfile=.context + "/" + .dockerfile'
100+
yq_pipe=$yq_pipe'| [.]'
101+
cat /tmp/docker-compose.yml | yq "$yq_pipe"
102+
BUILD_INFO=$(cat /tmp/docker-compose.yml | yq "$yq_pipe" -o=json)
103+
BUILD_INFO=$(echo $BUILD_INFO | jq -c -s add | sed "s|:local||g")
104+
echo $BUILD_INFO
105+
echo "result=$BUILD_INFO" | tr -d "\n" >> $GITHUB_OUTPUT
106+
env:
107+
REPO_OWNER: ${{ vars.DOCKER_HUB_USERNAME || steps.repo-owner.outputs.result }}
108+
VERSION: local
109+
110+
build-and-push:
111+
runs-on: ubuntu-latest
112+
name: "Build and push image ${{ matrix.build-config.service }}"
113+
needs:
114+
- build-info
115+
strategy:
116+
fail-fast: false
117+
matrix:
118+
build-config: ${{ fromJSON(needs.build-info.outputs.build-config) }}
119+
env:
120+
tags: ${{ needs.build-info.outputs.tags }}
121+
repo-owner: ${{ needs.build-info.outputs.repo-owner }}
122+
steps:
123+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
124+
125+
- run: |
126+
echo "inputs:"
127+
cat <<-HEREDOC
128+
${{ toJSON(inputs) }}
129+
HEREDOC
130+
131+
echo "build config:"
132+
cat <<-HEREDOC
133+
${{ toJSON(matrix.build-config) }}
134+
HEREDOC
135+
136+
echo "build tags:"
137+
cat <<-HEREDOC
138+
${{ env.tags }}
139+
HEREDOC
140+
141+
echo "build repo owner:"
142+
cat <<-HEREDOC
143+
${{ env.repo-owner }}
144+
HEREDOC
145+
146+
if [ ! echo "${{ matrix.build-config.image }}" | grep -Eq '^[a-zA-Z0-9._/-]+$' ]; then
147+
echo "Error: Invalid Docker image name '${{ matrix.build-config.image }}'."
148+
exit 1
149+
fi
28150
29151
- name: Set up Docker Buildx
30152
uses: docker/setup-buildx-action@v3
31153

32154
- name: Login to DockerHub
33155
uses: docker/login-action@v3
34156
with:
35-
username: ${{ vars.DOCKER_HUB_USERNAME }}
157+
username: ${{ vars.DOCKER_HUB_USERNAME || env.repo-owner }}
36158
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
37159

38-
- name: Build and push frontend docker image
39-
uses: docker/build-push-action@v6
160+
- uses: actions/github-script@v7
161+
id: expand-tags
40162
with:
41-
push: true
42-
file: .docker-hub/frontend/Dockerfile
43-
tags: |
44-
${{ ((inputs.tag != '') && format('{0}/ecamp3-frontend:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }}
45-
${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-frontend:${{ inputs.sha }}
46-
context: .
47-
build-args: |
48-
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
49-
SENTRY_ORG=${{ vars.SENTRY_ORG }}
50-
SENTRY_FRONTEND_PROJECT=${{ vars.SENTRY_FRONTEND_PROJECT }}
51-
SENTRY_RELEASE_NAME=${{ inputs.sha }}
52-
cache-from: type=gha,scope=frontend
53-
cache-to: type=gha,scope=frontend,mode=max
54-
55-
- name: Build and push api docker image
56-
uses: docker/build-push-action@v6
163+
script: |
164+
return JSON.parse('${{ env.tags }}').map(tag => `${{ matrix.build-config.image }}:${ tag }`)
165+
166+
- name: populate build args from secrets/vars
167+
id: populate-build-args
168+
uses: actions/github-script@v7
57169
with:
58-
push: true
59-
file: api/Dockerfile
60-
tags: |
61-
${{ ((inputs.tag != '') && format('{0}/ecamp3-api:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }}
62-
${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-api:${{ inputs.sha }}
63-
context: './api'
64-
target: frankenphp_prod
65-
cache-from: type=gha,scope=api
66-
cache-to: type=gha,scope=api,mode=max
67-
68-
- name: Build and push print docker image
170+
script: |
171+
const buildArgValues = {
172+
"SENTRY_AUTH_TOKEN" : "${{ secrets.SENTRY_AUTH_TOKEN }}",
173+
"SENTRY_FRONTEND_PROJECT" : "${{ vars.SENTRY_FRONTEND_PROJECT }}",
174+
"SENTRY_ORG" : "${{ vars.SENTRY_ORG }}",
175+
"SENTRY_PRINT_PROJECT" : "${{ vars.SENTRY_PRINT_PROJECT }}",
176+
"SENTRY_RELEASE_NAME" : "${{ inputs.sha }}",
177+
}
178+
const args = JSON.parse(`${{ toJSON(matrix.build-config.args) }}`)
179+
let result = ""
180+
for (const arg in args) {
181+
if (buildArgValues[arg]) {
182+
args[arg] = buildArgValues[arg]
183+
}
184+
}
185+
return args
186+
187+
- name: transform build args
188+
id: transform-build-args
189+
run: |
190+
set -x
191+
echo 'result<<EOF' >> $GITHUB_OUTPUT
192+
echo '${{ steps.populate-build-args.outputs.result }}' | yq -p json >> $GITHUB_OUTPUT
193+
echo 'EOF' >> $GITHUB_OUTPUT
194+
cat $GITHUB_OUTPUT
195+
196+
- name: debug output
197+
run: |
198+
echo "result: ${{ steps.transform-build-args.outputs.result }}"
199+
echo "result: ${{ toJSON(steps.transform-build-args.outputs) }}"
200+
201+
- name: Build and push image
69202
uses: docker/build-push-action@v6
70203
with:
71204
push: true
72-
file: .docker-hub/print/Dockerfile
73-
tags: |
74-
${{ ((inputs.tag != '') && format('{0}/ecamp3-print:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }}
75-
${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-print:${{ inputs.sha }}
76-
context: .
205+
file: ${{ matrix.build-config.dockerfile }}
206+
tags: ${{ join(fromJSON(steps.expand-tags.outputs.result)) }}
207+
context: ${{ matrix.build-config.context }}
77208
build-args: |
78-
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
79-
SENTRY_ORG=${{ vars.SENTRY_ORG }}
80-
SENTRY_PRINT_PROJECT=${{ vars.SENTRY_PRINT_PROJECT }}
81-
SENTRY_RELEASE_NAME=${{ inputs.sha }}
82-
cache-from: type=gha,scope=print
83-
cache-to: type=gha,scope=print,mode=max
84-
85-
- name: Build and push varnish docker image
86-
uses: docker/build-push-action@v6
87-
with:
88-
push: true
89-
file: .docker-hub/varnish/Dockerfile
90-
tags: |
91-
${{ ((inputs.tag != '') && format('{0}/ecamp3-varnish:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }}
92-
${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-varnish:${{ inputs.sha }}
93-
context: .
94-
cache-from: type=gha,scope=varnish
95-
cache-to: type=gha,scope=varnish,mode=max
96-
97-
- name: Build and push db-backup-restore docker image
98-
uses: docker/build-push-action@v6
99-
with:
100-
push: true
101-
file: .helm/ecamp3/files/db-backup-restore-image/Dockerfile
102-
tags: |
103-
${{ ((inputs.tag != '') && format('{0}/ecamp3-db-backup-restore:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }}
104-
${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-db-backup-restore:${{ inputs.sha }}
105-
context: .
106-
cache-from: type=gha,scope=db-backup-restore
107-
cache-to: type=gha,scope=db-backup-restore,mode=max
209+
${{ steps.transform-build-args.outputs.result }}
210+
cache-from: type=gha,scope=${{ matrix.build-config.image }}
211+
cache-to: type=gha,scope=${{ matrix.build-config.image }},mode=max

.github/workflows/reusable-e2e-tests-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
load: true
2626
target: frankenphp_prod
2727
builder: ${{ steps.buildx.outputs.name }}
28-
tags: ecamp/ecamp3-dev-api
28+
tags: docker.io/ecamp/ecamp3-api:latest
2929
cache-from: type=gha,scope=api
3030
cache-to: type=gha,scope=api,mode=max
3131
outputs: type=docker,dest=/tmp/ecamp3-dev-api.tar

docker-compose.override.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
services:
22
api:
3+
image: ''
34
build:
45
target: frankenphp_dev
56
volumes:

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ services:
2525
- pdf
2626

2727
api:
28-
image: ecamp/ecamp3-dev-api
28+
image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-api:${VERSION:-latest}
2929
build:
3030
context: ./api
3131
target: frankenphp_prod

0 commit comments

Comments
 (0)