-
Notifications
You must be signed in to change notification settings - Fork 62
Optimize docker build #7898
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize docker build #7898
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| services: | ||
| frontend-image: | ||
| image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-frontend:${VERSION:-latest} | ||
| build: | ||
| context: ../ | ||
| dockerfile: .docker-hub/frontend/Dockerfile | ||
| # they have to be registered in GitHub under exactly this name in secrets or vars | ||
| args: | ||
| SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN:-} | ||
| SENTRY_ORG: ${SENTRY_ORG:-} | ||
| SENTRY_FRONTEND_PROJECT: ${SENTRY_FRONTEND_PROJECT:-} | ||
| SENTRY_RELEASE_NAME: ${RELEASE_NAME:-} | ||
| print-image: | ||
| image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-print:${VERSION:-latest} | ||
| build: | ||
| context: ../ | ||
| dockerfile: .docker-hub/print/Dockerfile | ||
| # they have to be registered in GitHub under exactly this name in secrets or vars | ||
| args: | ||
| SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN:-} | ||
| SENTRY_ORG: ${SENTRY_ORG:-} | ||
| SENTRY_PRINT_PROJECT: ${SENTRY_PRINT_PROJECT:-} | ||
| SENTRY_RELEASE_NAME: ${RELEASE_NAME:-} | ||
| varnish-image: | ||
| image: ${REGISTRY:-docker.io}/${REPO_OWNER:-ecamp}/ecamp3-varnish:${VERSION:-latest} | ||
| build: | ||
| context: ../ | ||
| dockerfile: .docker-hub/varnish/Dockerfile | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| name: '[reusable only] Build images and push to registry' | ||
| name: "[reusable only] Build images and push to registry" | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| workflow_call: | ||
| inputs: | ||
| tag: | ||
|
|
@@ -17,91 +18,246 @@ on: | |
| required: true | ||
| SENTRY_AUTH_TOKEN: | ||
|
|
||
| env: | ||
| DOCKER_BUILDKIT: 1 | ||
| COMPOSE_DOCKER_CLI_BUILD: 1 | ||
|
|
||
| jobs: | ||
| build-info: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| repo-owner: ${{ steps.repo-owner.outputs.result }} | ||
| tags: ${{ steps.image-tags.outputs.image-tags }} | ||
| build-config: ${{ steps.build-info.outputs.result }} | ||
| steps: | ||
| #github forces lower case for the image name | ||
| - name: Get lowercase repo owner name | ||
| uses: actions/github-script@v7 | ||
| id: repo-owner | ||
| with: | ||
| result-encoding: string | ||
| script: | | ||
| return context.repo.owner.toLowerCase() | ||
|
|
||
| - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 | ||
|
|
||
| - name: Set nightly tag if commit was on main | ||
| id: add-nightly-tag | ||
| if: startsWith(github.ref, 'refs/heads/devel') | ||
| run: | | ||
| echo "nightly-tag=nightly" | tr -d "\n" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Set latest tag if its a tag | ||
| id: add-latest-tag | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| run: | | ||
| echo "latest-tag=latest" | tr -d "\n" >> $GITHUB_OUTPUT | ||
|
|
||
| - uses: actions/github-script@v7 | ||
| id: get-tag | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| with: | ||
| result-encoding: string | ||
| script: | | ||
| return context.payload.ref.replace('refs/tags/', '') | ||
|
|
||
| - name: concat tags to list | ||
| id: image-tags | ||
| run: | | ||
| TAGS=$(cat <<-END | ||
| [ | ||
| "${{ inputs.sha || github.sha }}", | ||
| "${{ steps.add-nightly-tag.outputs.nightly-tag }}", | ||
| "${{ steps.add-latest-tag.outputs.latest-tag }}", | ||
| "${{ steps.get-tag.outputs.result }}" | ||
| ] | ||
| END | ||
| ) | ||
| TAGS=$(echo $TAGS | jq -c 'map(select(length > 0))') | ||
| echo "image-tags=$TAGS" | tr -d "\n" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Get build info | ||
| id: build-info | ||
| run: | | ||
| set -x | ||
| sudo snap install yq | ||
BacLuc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export REPO_NAME=$(basename $(pwd)) | ||
| echo "services:" > /tmp/docker-compose.yml | ||
| for i in $(find . -name docker-compose.yml); do | ||
| docker compose -f $i config | yq '.services | select(.[].build != null and .[].image != null)' | sed 's/^/ /' >> /tmp/docker-compose.yml | ||
| done | ||
|
|
||
| cat /tmp/docker-compose.yml | ||
|
|
||
| yq_pipe='.services' | ||
| yq_pipe=$yq_pipe'| to_entries[]' | ||
| yq_pipe=$yq_pipe'| select(.value.build != null and .value.image != null)' | ||
| yq_pipe=$yq_pipe'| .value.build.image=.value.image' | ||
| yq_pipe=$yq_pipe'| .value.build.service=.key' | ||
| yq_pipe=$yq_pipe'| .value.build' | ||
| yq_pipe=$yq_pipe'| .dockerfile=.context + "/" + .dockerfile' | ||
| yq_pipe=$yq_pipe'| [.]' | ||
| cat /tmp/docker-compose.yml | yq "$yq_pipe" | ||
| BUILD_INFO=$(cat /tmp/docker-compose.yml | yq "$yq_pipe" -o=json) | ||
| BUILD_INFO=$(echo $BUILD_INFO | jq -c -s add | sed "s|:local||g") | ||
| echo $BUILD_INFO | ||
| echo "result=$BUILD_INFO" | tr -d "\n" >> $GITHUB_OUTPUT | ||
| env: | ||
| REPO_OWNER: ${{ vars.DOCKER_HUB_USERNAME || steps.repo-owner.outputs.result }} | ||
| VERSION: local | ||
|
|
||
| build-and-push: | ||
| name: Build images and push | ||
| runs-on: ubuntu-latest | ||
| name: "Build and push image ${{ matrix.build-config.service }}" | ||
| needs: | ||
| - build-info | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| build-config: ${{ fromJSON(needs.build-info.outputs.build-config) }} | ||
| env: | ||
| tags: ${{ needs.build-info.outputs.tags }} | ||
| repo-owner: ${{ needs.build-info.outputs.repo-owner }} | ||
| steps: | ||
| - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 | ||
| with: | ||
| ref: ${{ inputs.sha }} | ||
| fetch-depth: 100 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fail |
||
|
|
||
| - run: | | ||
| echo "inputs:" | ||
| cat <<-HEREDOC | ||
| ${{ toJSON(inputs) }} | ||
| HEREDOC | ||
|
|
||
| echo "build config:" | ||
| cat <<-HEREDOC | ||
| ${{ toJSON(matrix.build-config) }} | ||
| HEREDOC | ||
|
|
||
| echo "build tags:" | ||
| cat <<-HEREDOC | ||
| ${{ env.tags }} | ||
| HEREDOC | ||
|
|
||
| echo "build repo owner:" | ||
| cat <<-HEREDOC | ||
| ${{ env.repo-owner }} | ||
| HEREDOC | ||
|
|
||
| if [ ! echo "${{ matrix.build-config.image }}" | grep -Eq '^[a-zA-Z0-9._/-]+$' ]; then | ||
| echo "Error: Invalid Docker image name '${{ matrix.build-config.image }}'." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Login to DockerHub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ vars.DOCKER_HUB_USERNAME }} | ||
| username: ${{ vars.DOCKER_HUB_USERNAME || env.repo-owner }} | ||
| password: ${{ secrets.DOCKER_HUB_PASSWORD }} | ||
|
|
||
| - name: Build and push frontend docker image | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| push: true | ||
| file: .docker-hub/frontend/Dockerfile | ||
| tags: | | ||
| ${{ ((inputs.tag != '') && format('{0}/ecamp3-frontend:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }} | ||
| ${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-frontend:${{ inputs.sha }} | ||
| context: . | ||
| build-args: | | ||
| SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} | ||
| SENTRY_ORG=${{ vars.SENTRY_ORG }} | ||
| SENTRY_FRONTEND_PROJECT=${{ vars.SENTRY_FRONTEND_PROJECT }} | ||
| SENTRY_RELEASE_NAME=${{ inputs.sha }} | ||
| cache-from: type=gha,scope=frontend | ||
| cache-to: type=gha,scope=frontend,mode=max | ||
|
|
||
| - name: Build and push api docker image | ||
| uses: docker/build-push-action@v6 | ||
| - name: find latest commit for image tag | ||
| id: get-latest-commit | ||
| run: | | ||
| set -x | ||
| context=$( echo '${{ toJSON(matrix.build-config) }}' | jq -r '.context' | sed "s|$PWD|.|g") | ||
| echo "context: $context" | ||
| dockerfile=$( echo '${{ toJSON(matrix.build-config) }}' | jq -r '.dockerfile' | sed "s|$PWD|.|g") | ||
| echo "dockerfile: $dockerfile" | ||
| git log --stat -n 1 $context $dockerfile | ||
| latest_commit=$(git log --pretty=format:"%H" -n 1 $context $dockerfile) | ||
| echo "latest_commit: $latest_commit" | ||
| echo "result=$latest_commit" | tr -d "\n" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: check if image already exists | ||
| id: check-image | ||
| run: | | ||
| set +e | ||
BacLuc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| docker pull ${{ matrix.build-config.image }}:${{ steps.get-latest-commit.outputs.result }} | ||
| image_exists=$? | ||
| set -e | ||
| if ([ $image_exists -eq 0 ]); then | ||
| echo "image already exists" | ||
| echo "result=true" | tr -d "\n" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "image does not exist" | ||
| echo "result=false" | tr -d "\n" >> $GITHUB_OUTPUT | ||
| fi | ||
|
|
||
| - name: Add latest commit to image tag if image does not exist | ||
| id: add-latest-commit-to-tags-if-image-does-not-exist | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| push: true | ||
| file: api/Dockerfile | ||
| tags: | | ||
| ${{ ((inputs.tag != '') && format('{0}/ecamp3-api:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }} | ||
| ${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-api:${{ inputs.sha }} | ||
| context: './api' | ||
| target: frankenphp_prod | ||
| cache-from: type=gha,scope=api | ||
| cache-to: type=gha,scope=api,mode=max | ||
|
|
||
| - name: Build and push print docker image | ||
| uses: docker/build-push-action@v6 | ||
| script: | | ||
| if (!${{ steps.check-image.outputs.result }}) { | ||
| return [...JSON.parse('${{ env.tags }}'), '${{ steps.get-latest-commit.outputs.result }}'] | ||
| } | ||
| return JSON.parse('${{ env.tags }}') | ||
|
|
||
| - uses: actions/github-script@v7 | ||
| id: expand-tags | ||
| with: | ||
| push: true | ||
| file: .docker-hub/print/Dockerfile | ||
| tags: | | ||
| ${{ ((inputs.tag != '') && format('{0}/ecamp3-print:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }} | ||
| ${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-print:${{ inputs.sha }} | ||
| context: . | ||
| build-args: | | ||
| SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} | ||
| SENTRY_ORG=${{ vars.SENTRY_ORG }} | ||
| SENTRY_PRINT_PROJECT=${{ vars.SENTRY_PRINT_PROJECT }} | ||
| SENTRY_RELEASE_NAME=${{ inputs.sha }} | ||
| cache-from: type=gha,scope=print | ||
| cache-to: type=gha,scope=print,mode=max | ||
|
|
||
| - name: Build and push varnish docker image | ||
| uses: docker/build-push-action@v6 | ||
| script: | | ||
| return JSON.parse('${{ steps.add-latest-commit-to-tags-if-image-does-not-exist.outputs.result }}').map(tag => `${{ matrix.build-config.image }}:${ tag }`) | ||
|
|
||
| - name: populate build args from secrets/vars | ||
| if: steps.check-image.outputs.result == 'false' | ||
| id: populate-build-args | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| push: true | ||
| file: .docker-hub/varnish/Dockerfile | ||
| tags: | | ||
| ${{ ((inputs.tag != '') && format('{0}/ecamp3-varnish:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }} | ||
| ${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-varnish:${{ inputs.sha }} | ||
| context: . | ||
| cache-from: type=gha,scope=varnish | ||
| cache-to: type=gha,scope=varnish,mode=max | ||
|
|
||
| - name: Build and push db-backup-restore docker image | ||
| script: | | ||
| const buildArgValues = { | ||
| "SENTRY_AUTH_TOKEN" : "${{ secrets.SENTRY_AUTH_TOKEN }}", | ||
| "SENTRY_FRONTEND_PROJECT" : "${{ vars.SENTRY_FRONTEND_PROJECT }}", | ||
| "SENTRY_ORG" : "${{ vars.SENTRY_ORG }}", | ||
| "SENTRY_PRINT_PROJECT" : "${{ vars.SENTRY_PRINT_PROJECT }}", | ||
| "SENTRY_RELEASE_NAME" : "${{ inputs.sha }}", | ||
|
Comment on lines
+213
to
+217
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to change in this PR, but can you explain why the sentry settings are build args instead of environment variables? This way they're baked into the images, right? Why is that beneficial for the sentry settings?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was too lazy until now to split sentry release bundle generation and docker build. |
||
| } | ||
| const args = JSON.parse(`${{ toJSON(matrix.build-config.args) }}`) | ||
| let result = "" | ||
| for (const arg in args) { | ||
| if (buildArgValues[arg]) { | ||
| args[arg] = buildArgValues[arg] | ||
| } | ||
| } | ||
|
Comment on lines
+221
to
+225
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we have an override mechanism for these? As far as I can tell, the variable values should be populated in the new docker-compose.yml already, no? Why overwrite them again here with the same values?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not really an override, its more filling in the build-args from the secret. |
||
| return args | ||
|
|
||
| - name: transform build args | ||
| if: steps.check-image.outputs.result == 'false' | ||
| id: transform-build-args | ||
| run: | | ||
| set -x | ||
| echo 'result<<EOF' >> $GITHUB_OUTPUT | ||
| echo '${{ steps.populate-build-args.outputs.result }}' | yq -p json >> $GITHUB_OUTPUT | ||
| echo 'EOF' >> $GITHUB_OUTPUT | ||
| cat $GITHUB_OUTPUT | ||
|
|
||
| - name: debug transform build args output | ||
| if: steps.check-image.outputs.result == 'false' | ||
| run: | | ||
| echo "result: ${{ steps.transform-build-args.outputs.result }}" | ||
| echo "result: ${{ toJSON(steps.transform-build-args.outputs) }}" | ||
|
|
||
| - name: Build and push image | ||
| uses: docker/build-push-action@v6 | ||
| if: steps.check-image.outputs.result == 'false' | ||
| with: | ||
| push: true | ||
| file: .helm/ecamp3/files/db-backup-restore-image/Dockerfile | ||
| tags: | | ||
| ${{ ((inputs.tag != '') && format('{0}/ecamp3-db-backup-restore:{1}', vars.DOCKER_HUB_USERNAME, inputs.tag) || '') }} | ||
| ${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-db-backup-restore:${{ inputs.sha }} | ||
| context: . | ||
| cache-from: type=gha,scope=db-backup-restore | ||
| cache-to: type=gha,scope=db-backup-restore,mode=max | ||
| file: ${{ matrix.build-config.dockerfile }} | ||
| tags: ${{ join(fromJSON(steps.expand-tags.outputs.result)) }} | ||
| context: ${{ matrix.build-config.context }} | ||
| build-args: | | ||
| ${{ steps.transform-build-args.outputs.result }} | ||
| cache-from: type=gha,scope=${{ matrix.build-config.image }} | ||
| cache-to: type=gha,scope=${{ matrix.build-config.image }},mode=max | ||
BacLuc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| - name: Retag and push images | ||
| if: steps.check-image.outputs.result == 'true' | ||
| run: | | ||
| for tag in $(echo '${{ env.tags }}' | jq -r '.[]'); do | ||
| docker tag ${{ matrix.build-config.image }}:${{ steps.get-latest-commit.outputs.result }} ${{ matrix.build-config.image }}:${tag} | ||
| docker push ${{ matrix.build-config.image }}:${tag} | ||
| done | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| services: | ||
| api: | ||
| image: '' | ||
| build: | ||
| target: frankenphp_dev | ||
| volumes: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened to the api and the db-backup-restore images which we were building before?