diff --git a/.github/workflows/docker-build-images.md b/.github/workflows/docker-build-images.md index 7b2b1101..d0925c04 100644 --- a/.github/workflows/docker-build-images.md +++ b/.github/workflows/docker-build-images.md @@ -64,7 +64,7 @@ jobs: # Default: "${{ github.repository_owner }}" oci-registry-username: "" - # Images to build parameters. + # Images to build parameters. Json array of objects. # Example: [{ # "name": "application", # "dockerfile": "./docker/application/Dockerfile", @@ -117,6 +117,20 @@ jobs: | **target** | Sets the target stage to build. See [Docker build-image action](../../actions/docker/build-image/README.md) | | **true** | | **platforms** | List of platforms to build for. It is used as `platform` in [Docker build-image action](../../actions/docker/build-image/README.md). Can be a string (Example: `linux/amd64`) or an object (Example: `{"name": "darwin/amd64","runs-on": "macos-latest"}`) | | **true** | +#### Platforms entry parameters + +| **Parameter** | **Description** | **Default** | **Required** | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------- | ------------ | +| **name** | Platform name. Example: `linux/amd64` | | **true** | +| **runs-on** | Json array of runner(s) to use. See | | **false** | + +##### Default `runs-on` strategy + +If a platform entry omits the runs-on field, the following default strategy applies: + +- When the main docker-build-images job uses a standard hosted runner, that runner is automatically matched to each platform. +- If the main docker-build-images job uses a custom or self-hosted runner, all platforms use the same runner. + ## Outputs diff --git a/.github/workflows/docker-build-images.yml b/.github/workflows/docker-build-images.yml index 4a9b7cb1..4d844d65 100644 --- a/.github/workflows/docker-build-images.yml +++ b/.github/workflows/docker-build-images.yml @@ -54,7 +54,7 @@ on: # yamllint disable-line rule:truthy required: false images: description: | - Images to build parameters. + Images to build parameters. Json array of objects. Example: [ { "name": "application", @@ -99,8 +99,8 @@ permissions: jobs: prepare-variables: outputs: - images: ${{ steps.validate-inputs.outputs.images }} artifact-name: ${{ steps.define-artifact-name.outputs.artifact-name }} + images: ${{ steps.define-images-by-platform.outputs.images }} runs-on: ${{ fromJson(inputs.runs-on) }} steps: - id: validate-inputs @@ -138,7 +138,6 @@ jobs: // jscpd:ignore-end // Check each item - const imagesByPlatform = []; for (const key in images) { const image = images[key]; if (typeof image !== 'object') { @@ -172,12 +171,56 @@ jobs: if (!image['repository']) { image['repository'] = `${{ github.repository }}`; } + } + + core.setOutput('images', JSON.stringify(images)); + + - id: define-images-by-platform + uses: actions/github-script@v7.0.1 + with: + script: | + const images = JSON.parse(`${{ steps.validate-inputs.outputs.images }}`); + + const runsOnInput = `${{ inputs.runs-on }}`; + const isDefaultRunsOn = runsOnInput === '["ubuntu-latest"]'; + runsOn = JSON.parse(runsOnInput); + + // See https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners + const standardHostedRunnerByPlatform = [ + { runner: "ubuntu-24.04-arm", platformPattern: /^linux\/arm/ }, // FIXME: should use latest when available + { runner: "macos-latest", platformPattern: /^darwin\// }, + { runner: "windows-latest", platformPattern: /^windows\// } + ]; + + function getPlatformRunsOn(platform) { + if (platform.hasOwnProperty('runs-on')) { + return platform['runs-on']; + } + + if (!isDefaultRunsOn) { + return runsOn; + } + + for (const { runner, platformPattern } of standardHostedRunnerByPlatform) { + if (platformPattern.test(platform.name)) { + return [runner]; + } + } + + return runsOn; + } + + // Check each item + const imagesByPlatform = []; + for (const key in images) { + const image = images[key]; // Add image for each platform const platforms = image['platforms']; + for (const platform of platforms) { - let platformName = platform; - let platformRunsOn = runsOn; + let platformName; + let platformRunsOn; // Platform can be a string or an object if (typeof platform === 'object') { @@ -185,11 +228,11 @@ jobs: throw new Error(`"images[${key}].platforms[${platform}].name" input is missing`); } platformName = platform['name']; - - if (platform.hasOwnProperty('runs-on')) { - platformRunsOn = platform['runs-on']; - } - } else if (typeof platform !== 'string') { + platformRunsOn = getPlatformRunsOn(platform); + } else if (typeof platform === 'string') { + platformName = platform; + platformRunsOn = getPlatformRunsOn({ name: platform }); + } else { throw new Error(`"images[${key}].platforms[${platform}]" input is not a string or an object`); } diff --git a/actions/docker/build-image/action.yml b/actions/docker/build-image/action.yml index a8a4f4bc..e2c5c4e3 100644 --- a/actions/docker/build-image/action.yml +++ b/actions/docker/build-image/action.yml @@ -126,7 +126,7 @@ runs: with: value: ${{ inputs.platform }} - - id: get-platform-config + - id: get-docker-config shell: bash run: | TAG_SUFFIX="-${{ steps.slugify-platform.outputs.result }}" @@ -136,11 +136,55 @@ runs: echo "cache-flavor=$CACHE_FLAVOR" >> "$GITHUB_OUTPUT" + # Check if docker exists + if command -v docker &> /dev/null; then + echo "docker-exists=true" >> "$GITHUB_OUTPUT" + fi + + PLATFORM="${{ inputs.platform }}" + if [ -z "$PLATFORM" ]; then + echo "platform-exists=true" >> "$GITHUB_OUTPUT" + else + PLATFORM_OS=$(echo "$PLATFORM" | cut -d'/' -f1) + RUNNER_OS=$(uname -s | tr '[:upper:]' '[:lower:]') + + echo "::debug::PLATFORM_OS=$PLATFORM_OS" + echo "::debug::RUNNER_OS=$RUNNER_OS" + + if [ "$PLATFORM_OS" = "$RUNNER_OS" ]; then + PLATFORM_ARCH=$(echo "$PLATFORM" | cut -d'/' -f2-) + RUNNER_ARCH=$(uname -m) + + echo "::debug::PLATFORM_ARCH=$PLATFORM_ARCH" + echo "::debug::RUNNER_ARCH=$RUNNER_ARCH" + + # Convert runner arch to docker arch + case $RUNNER_ARCH in + x86_64) + RUNNER_ARCH="amd64" + ;; + aarch64) + RUNNER_ARCH="arm64" + ;; + armv7l) + RUNNER_ARCH="arm/v7" + ;; + *) + echo "::warning::Unsupported architecture: $RUNNER_ARCH" + ;; + esac + + if [ "$PLATFORM_ARCH" = "$RUNNER_ARCH" ]; then + echo "platform-exists=true" >> "$GITHUB_OUTPUT" + fi + fi + fi + - id: cache uses: int128/docker-build-cache-config-action@v1.37.0 with: image: ${{ steps.metadata.outputs.image }}/cache - flavor: ${{ steps.get-platform-config.outputs.cache-flavor }} + flavor: ${{ steps.get-docker-config.outputs.cache-flavor }} pull-request-cache: true cache-type: gha @@ -151,17 +195,21 @@ runs: - shell: bash run: git lfs pull - - uses: docker/setup-qemu-action@v3 + - if: steps.get-docker-config.outputs.docker-exists != 'true' + uses: docker/setup-docker-action@v4 + + - if: steps.get-docker-config.outputs.platform-exists != 'true' + uses: docker/setup-qemu-action@v3 with: platforms: ${{ inputs.platform }} - uses: docker/setup-buildx-action@v3 with: # FIXME: upgrade version when available (https://github.com/docker/buildx/releases) - version: v0.19.2 + version: v0.20.1 # FIXME: upgrade version when available (https://hub.docker.com/r/moby/buildkit) driver-opts: | - image=moby/buildkit:v0.18.1 + image=moby/buildkit:v0.19.0 - uses: docker/login-action@v3 with: diff --git a/tests/charts/umbrella-application/ci/ingress-nopath-values.yaml b/tests/charts/umbrella-application/ci/ingress-nopath-values.yaml index 9e37faa1..33240ce4 100644 --- a/tests/charts/umbrella-application/ci/ingress-nopath-values.yaml +++ b/tests/charts/umbrella-application/ci/ingress-nopath-values.yaml @@ -5,6 +5,6 @@ ingress: hosts: - host: chart-example.local paths: - - path: '' + - path: "" pathType: ImplementationSpecific tls: []