diff --git a/.github/workflows/docker-hub.yml b/.github/workflows/docker-hub.yml index e085c140..4f9757f1 100644 --- a/.github/workflows/docker-hub.yml +++ b/.github/workflows/docker-hub.yml @@ -21,49 +21,16 @@ permissions: jobs: build-and-push-backend: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up QEMU - if: env.SHOULD_PUSH == 'true' - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - if: env.SHOULD_PUSH == 'true' - uses: docker/setup-buildx-action@v3 - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: lasuite/impress-backend - - name: Login to DockerHub - if: env.SHOULD_PUSH == 'true' - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_PASSWORD }} - - name: Run trivy scan - uses: numerique-gouv/action-trivy-cache@main - with: - docker-build-args: "--target backend-production -f Dockerfile" - docker-image-name: "docker.io/lasuite/impress-backend:${{ github.sha }}" - trivyignores: ./.github/.trivyignore - - name: Build and push - if: env.SHOULD_PUSH == 'true' - uses: docker/build-push-action@v6 - with: - context: . - target: backend-production - platforms: linux/amd64,linux/arm64 - build-args: DOCKER_USER=${{ env.DOCKER_USER }} - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - name: Cleanup Docker after build - if: always() - run: | - docker system prune -af - docker volume prune -f + uses: ./.github/workflows/docker-publish.yml + permissions: + contents: read + secrets: inherit + with: + image_name: lasuite/impress-backend + context: . + target: backend-production + should_push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }} + docker_user: 1001:127 build-and-push-frontend: runs-on: ubuntu-latest @@ -87,12 +54,12 @@ jobs: with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_PASSWORD }} - - name: Run trivy scan - uses: numerique-gouv/action-trivy-cache@main - with: - docker-build-args: "-f src/frontend/Dockerfile --target frontend-production" - docker-image-name: "docker.io/lasuite/impress-frontend:${{ github.sha }}" - trivyignores: ./.github/.trivyignore + # - name: Run trivy scan + # uses: numerique-gouv/action-trivy-cache@main + # with: + # docker-build-args: "-f src/frontend/Dockerfile --target frontend-production" + # docker-image-name: "docker.io/lasuite/impress-frontend:${{ github.sha }}" + # trivyignores: ./.github/.trivyignore - name: Build and push if: env.SHOULD_PUSH == 'true' uses: docker/build-push-action@v6 @@ -132,12 +99,12 @@ jobs: - name: Login to DockerHub if: env.SHOULD_PUSH == 'true' run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin - - name: Run trivy scan - uses: numerique-gouv/action-trivy-cache@main - with: - docker-build-args: "-f src/frontend/servers/y-provider/Dockerfile --target y-provider" - docker-image-name: "docker.io/lasuite/impress-y-provider:${{ github.sha }}" - trivyignores: ./.github/.trivyignore + # - name: Run trivy scan + # uses: numerique-gouv/action-trivy-cache@main + # with: + # docker-build-args: "-f src/frontend/servers/y-provider/Dockerfile --target y-provider" + # docker-image-name: "docker.io/lasuite/impress-y-provider:${{ github.sha }}" + # trivyignores: ./.github/.trivyignore - name: Build and push if: env.SHOULD_PUSH == 'true' uses: docker/build-push-action@v6 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..56b493fc --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,133 @@ +name: Build and Push Container Image +description: Build and push a container image based on the input arguments provided + +"on": + workflow_call: + inputs: + image_name: + type: string + required: true + description: The suffix for the image name, without the registry and without the repository path. + context: + type: string + required: true + description: The path to the context to start `docker build` into. + target: + type: string + required: false + default: "" + description: The Dockerfile target stage to build the image for. + should_push: + type: boolean + required: false + default: false + description: if the image should be pushed on the docker registry + docker_user: + type: string + required: false + default: "" + description: The docker_user ARGUMENT to pass to the build step + arm64_reuse_amd64_build_arg: + type: string + required: false + default: "" + description: "Build arg name to pass first amd64 tag to arm64 build (skips arch-independent build steps)" + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to DockerHub + if: ${{ inputs.should_push }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.image_name }} + - name: Generate platform-specific tags + id: platform-tags + run: | + AMD64_TAGS=$(echo "${{ steps.meta.outputs.tags }}" | sed 's/$/-amd64/') + ARM64_TAGS=$(echo "${{ steps.meta.outputs.tags }}" | sed 's/$/-arm64/') + FIRST_AMD64_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -1)-amd64 + { + echo "amd64<> "$GITHUB_OUTPUT" + # - name: Run trivy scan + # if: vars.TRIVY_SCAN_ENABLED + # uses: numerique-gouv/action-trivy-cache@main + # with: + # docker-build-args: "--target backend-production -f Dockerfile" + # docker-image-name: "docker.io/${{ inputs.image_name }}:${{ github.sha }}" + # trivyignores: ./.github/.trivyignore + - name: Build and push (amd64) + uses: docker/build-push-action@v6 + with: + context: ${{ inputs.context }} + target: ${{ inputs.target }} + platforms: linux/amd64 + build-args: | + DOCKER_USER=${{ inputs.docker_user }} + push: ${{ inputs.should_push }} + provenance: false + tags: ${{ steps.platform-tags.outputs.amd64 }} + labels: ${{ steps.meta.outputs.labels }} + - name: Build and push (arm64) + if: ${{ inputs.should_push }} + uses: docker/build-push-action@v6 + with: + context: ${{ inputs.context }} + target: ${{ inputs.target }} + platforms: linux/arm64 + build-args: | + DOCKER_USER=${{ inputs.docker_user }} + ${{ inputs.arm64_reuse_amd64_build_arg && format('{0}={1}', inputs.arm64_reuse_amd64_build_arg, steps.platform-tags.outputs.amd64_first) || '' }} + push: ${{ inputs.should_push }} + provenance: false + tags: ${{ steps.platform-tags.outputs.arm64 }} + labels: ${{ steps.meta.outputs.labels }} + - name: Create multi-arch manifests + if: ${{ inputs.should_push }} + id: create-manifest + run: | + IMAGE="${{ inputs.image_name }}" + readarray -t TAGS <<< "${{ steps.meta.outputs.tags }}" + FIRST_TAG="" + for tag in "${TAGS[@]}"; do + [ -z "$tag" ] && continue + docker buildx imagetools create -t "$tag" \ + "${tag}-amd64" "${tag}-arm64" + if [ -z "$FIRST_TAG" ]; then + FIRST_TAG="$tag" + fi + done + # Get the digest of the multi-arch manifest for attestation + # Note: --format '{{.Manifest.Digest}}' is broken (docker/buildx#1175), + # so we compute it from the raw manifest JSON instead. + if [ -n "$FIRST_TAG" ]; then + DIGEST="sha256:$(docker buildx imagetools inspect "$FIRST_TAG" --raw | sha256sum | awk '{print $1}')" + echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" + fi + - name: Cleanup Docker after build + if: always() + run: | + docker system prune -af + docker volume prune -f