|
| 1 | +# Copyright Red Hat, Inc. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +name: Docker Build (Hermetic) |
| 16 | +description: Build operator image hermetically using Hermeto (offline/reproducible build) |
| 17 | +inputs: |
| 18 | + imageName: |
| 19 | + description: The full image name including registry (e.g., quay.io/rhdh-community/operator) |
| 20 | + required: true |
| 21 | + imageTags: |
| 22 | + description: The tags to apply to the image |
| 23 | + required: true |
| 24 | + imageLabels: |
| 25 | + description: The labels for the Docker image |
| 26 | + required: false |
| 27 | + platform: |
| 28 | + description: "Target given CPU platform architecture (default: linux/amd64)" |
| 29 | + required: false |
| 30 | + default: linux/amd64 |
| 31 | + containerfilePath: |
| 32 | + description: Path to the Dockerfile to use |
| 33 | + required: false |
| 34 | + default: 'Dockerfile' |
| 35 | + skipArtifactUpload: |
| 36 | + description: Skip uploading the built image as a GitHub artifact |
| 37 | + required: false |
| 38 | + default: 'false' |
| 39 | + |
| 40 | +runs: |
| 41 | + using: composite |
| 42 | + steps: |
| 43 | + - name: Extract metadata (tags, labels) for Docker |
| 44 | + id: meta |
| 45 | + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 |
| 46 | + with: |
| 47 | + images: ${{ inputs.imageName }} |
| 48 | + tags: | |
| 49 | + ${{ inputs.imageTags }} |
| 50 | + labels: | |
| 51 | + ${{ inputs.imageLabels }} |
| 52 | +
|
| 53 | + - name: Ensure podman is available |
| 54 | + shell: bash |
| 55 | + run: | |
| 56 | + if ! command -v podman &>/dev/null; then |
| 57 | + echo "podman not found, installing..." |
| 58 | + sudo apt-get -y update && sudo apt-get -y install podman |
| 59 | + fi |
| 60 | + podman --version |
| 61 | +
|
| 62 | + - name: Set up hermetic build variables |
| 63 | + shell: bash |
| 64 | + run: | |
| 65 | + # renovate: datasource=docker depName=quay.io/konflux-ci/hermeto |
| 66 | + echo "HERMETO_IMAGE=quay.io/konflux-ci/hermeto:0.60.1" >> "$GITHUB_ENV" |
| 67 | + echo "LOCAL_CACHE_DIR=./hermeto-cache/operator" >> "$GITHUB_ENV" |
| 68 | +
|
| 69 | + - name: Restore hermeto dependency cache |
| 70 | + id: cache-deps |
| 71 | + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 |
| 72 | + with: |
| 73 | + path: ./hermeto-cache/operator |
| 74 | + key: hermeto-deps-${{ inputs.platform }}-${{ hashFiles('go.mod', 'go.sum', 'rpms.lock.yaml') }} |
| 75 | + |
| 76 | + - name: Fetch dependencies with hermeto |
| 77 | + if: steps.cache-deps.outputs.cache-hit != 'true' |
| 78 | + shell: bash |
| 79 | + run: | |
| 80 | + set -ex |
| 81 | +
|
| 82 | + mkdir -p "$LOCAL_CACHE_DIR" |
| 83 | +
|
| 84 | + echo "::group::Fetching dependencies with hermeto" |
| 85 | + podman run --rm -v "$PWD:/source:z" -v "$LOCAL_CACHE_DIR:/cachi2:z" -w /source "$HERMETO_IMAGE" \ |
| 86 | + --log-level DEBUG \ |
| 87 | + fetch-deps \ |
| 88 | + --source . \ |
| 89 | + --output /cachi2/output \ |
| 90 | + '[{"type": "rpm", "path": "."}, {"type": "gomod", "path": "."}]' |
| 91 | + echo "::endgroup::" |
| 92 | +
|
| 93 | + if [ ! -d "$LOCAL_CACHE_DIR/output" ]; then |
| 94 | + echo "No output directory found after fetch-deps" |
| 95 | + exit 1 |
| 96 | + fi |
| 97 | +
|
| 98 | + echo "::group::Generating environment file" |
| 99 | + podman run --rm -v "$PWD:/source:z" -v "$LOCAL_CACHE_DIR:/cachi2:z" -w /source "$HERMETO_IMAGE" \ |
| 100 | + --log-level DEBUG \ |
| 101 | + generate-env --format env \ |
| 102 | + --output /cachi2/cachi2.env /cachi2/output |
| 103 | + echo "::endgroup::" |
| 104 | +
|
| 105 | + echo "::group::Injecting files" |
| 106 | + podman run --rm -v "$PWD:/source:z" -v "$LOCAL_CACHE_DIR:/cachi2:z" -w /source "$HERMETO_IMAGE" \ |
| 107 | + --log-level DEBUG \ |
| 108 | + inject-files /cachi2/output |
| 109 | + echo "::endgroup::" |
| 110 | +
|
| 111 | + - name: Generate env and inject files (cache hit) |
| 112 | + if: steps.cache-deps.outputs.cache-hit == 'true' |
| 113 | + shell: bash |
| 114 | + run: | |
| 115 | + set -ex |
| 116 | + echo "::group::Generating environment file from cached deps" |
| 117 | + podman run --rm -v "$PWD:/source:z" -v "$LOCAL_CACHE_DIR:/cachi2:z" -w /source "$HERMETO_IMAGE" \ |
| 118 | + --log-level DEBUG \ |
| 119 | + generate-env --format env \ |
| 120 | + --output /cachi2/cachi2.env /cachi2/output |
| 121 | + echo "::endgroup::" |
| 122 | +
|
| 123 | + echo "::group::Injecting files from cached deps" |
| 124 | + podman run --rm -v "$PWD:/source:z" -v "$LOCAL_CACHE_DIR:/cachi2:z" -w /source "$HERMETO_IMAGE" \ |
| 125 | + --log-level DEBUG \ |
| 126 | + inject-files /cachi2/output |
| 127 | + echo "::endgroup::" |
| 128 | +
|
| 129 | + - name: Fix cache ownership for non-root buildah |
| 130 | + shell: bash |
| 131 | + run: | |
| 132 | + set -ex |
| 133 | + echo LOCAL_CACHE_DIR_REALPATH=$(realpath "$LOCAL_CACHE_DIR") >> "$GITHUB_ENV" |
| 134 | + sudo chown -R runner "$(realpath "$LOCAL_CACHE_DIR")" |
| 135 | +
|
| 136 | + - name: Transform Dockerfile for hermetic build |
| 137 | + shell: bash |
| 138 | + id: transform-containerfile |
| 139 | + env: |
| 140 | + CONTAINERFILE_PATH: ${{ inputs.containerfilePath }} |
| 141 | + TRANSFORMED_CONTAINERFILE: ${{ inputs.containerfilePath }}.hermeto |
| 142 | + run: | |
| 143 | + set -x |
| 144 | +
|
| 145 | + cp "$CONTAINERFILE_PATH" "$TRANSFORMED_CONTAINERFILE" |
| 146 | +
|
| 147 | + # Insert RPM repo replacement before every dnf/microdnf install |
| 148 | + sed -i '/RUN *\(dnf\|microdnf\) install/i RUN rm -r /etc/yum.repos.d/* && cp /cachi2/output/deps/rpm/$(uname -m)/repos.d/hermeto.repo /etc/yum.repos.d/' \ |
| 149 | + "$TRANSFORMED_CONTAINERFILE" |
| 150 | +
|
| 151 | + # Prepend cachi2 env sourcing to every RUN command |
| 152 | + sed -i 's/^\s*RUN /RUN . \/cachi2\/cachi2.env \&\& /' "$TRANSFORMED_CONTAINERFILE" |
| 153 | +
|
| 154 | + echo "transformed_containerfile=$TRANSFORMED_CONTAINERFILE" >> "$GITHUB_OUTPUT" |
| 155 | +
|
| 156 | + - name: Build Docker Image |
| 157 | + id: build |
| 158 | + uses: redhat-actions/buildah-build@7a95fa7ee0f02d552a32753e7414641a04307056 # v2.13 |
| 159 | + with: |
| 160 | + containerfiles: ${{ steps.transform-containerfile.outputs.transformed_containerfile }} |
| 161 | + context: . |
| 162 | + platform: ${{ inputs.platform }} |
| 163 | + tags: ${{ steps.meta.outputs.tags }} |
| 164 | + labels: ${{ steps.meta.outputs.labels }} |
| 165 | + extra-args: | |
| 166 | + --network=none |
| 167 | + --volume ${{ env.LOCAL_CACHE_DIR_REALPATH }}:/cachi2:z |
| 168 | +
|
| 169 | + - name: Save image as artifact |
| 170 | + if: ${{ inputs.skipArtifactUpload != 'true' }} |
| 171 | + shell: bash |
| 172 | + env: |
| 173 | + TAGS_LIST: ${{ steps.meta.outputs.tags }} |
| 174 | + run: | |
| 175 | + mkdir -p ./operator-podman-artifacts |
| 176 | + echo "Saving images with tags:" |
| 177 | + echo "$TAGS_LIST" |
| 178 | + readarray -t tags <<< "$TAGS_LIST" |
| 179 | + podman save "${tags[@]}" -o ./operator-podman-artifacts/image.tar |
| 180 | + echo "$TAGS_LIST" > ./operator-podman-artifacts/tags.txt |
| 181 | +
|
| 182 | + - name: Upload image artifact |
| 183 | + if: ${{ inputs.skipArtifactUpload != 'true' }} |
| 184 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 185 | + with: |
| 186 | + name: podman-image-${{ github.event.number || github.ref_name }}-${{ env.SHORT_SHA }} |
| 187 | + path: ./operator-podman-artifacts/ |
| 188 | + retention-days: 1 |
| 189 | + if-no-files-found: error |
0 commit comments