diff --git a/.github/workflows/ci-apple-container-mac.yml b/.github/workflows/ci-apple-container-mac.yml new file mode 100644 index 0000000000..971745d407 --- /dev/null +++ b/.github/workflows/ci-apple-container-mac.yml @@ -0,0 +1,1006 @@ +name: Apple Container CI macOs + +on: + workflow_call: + +env: + OTEL_SERVICE_NAME: ${{ vars.OTEL_SERVICE_NAME }} + OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.OTEL_EXPORTER_OTLP_ENDPOINT }} + OTEL_EXPORTER_OTLP_PROTOCOL: ${{ vars.OTEL_EXPORTER_OTLP_PROTOCOL }} + OTEL_RESOURCE_ATTRIBUTES: ${{ vars.OTEL_RESOURCE_ATTRIBUTES != '' && format('{0},', vars.OTEL_RESOURCE_ATTRIBUTES) || '' }}${{ github.event.pull_request != null && format('vcs.repository.change.id={0},vcs.repository.change.title={1},', github.event.pull_request.number, github.event.pull_request.title) || '' }}cicd.pipeline.run.id=${{ github.run_number }},cicd.pipeline.run.url.full=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }},cicd.pipeline.name=${{ github.workflow }},vcs.revision.id=${{ github.sha }},vcs.ref.name=${{ github.ref_name }},vcs.repository.name=${{ github.repository }},user.id=${{ github.actor }} + OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }} + +jobs: + build-earthly: + name: Build Earthly for macOS + runs-on: ubuntu-24.04 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Configure earth to use GCR mirror + run: |- + earth config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + - name: Bootstrap earth using released earth + run: earth bootstrap + - name: Build latest earth using released earth + run: earth -P --use-inline-cache +for-darwin-m1 + - name: Upload earthly binary artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: earthly-binary-mac + path: build/darwin/arm64/earthly + retention-days: 1 + - name: Buildkit logs (runs on failure) + run: docker logs earth-buildkitd + if: ${{ failure() }} + + apple-container-tests-quick: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Rebuild earth using latest earthly build + run: build/darwin/arm64/earthly -P --use-inline-cache +for-darwin-m1 + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-misc + run: build/darwin/arm64/earthly --ci -P +test-misc + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + # Add group jobs 1 to 12 + apple-container-tests-no-qemu-group1: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group1 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group1 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group2: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group2 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group2 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group3: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group3 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group3 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group4: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group4 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group4 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group5: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group5 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group5 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group6: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group6 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group6 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group7: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group7 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group7 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group8: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group8 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group8 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group9: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group9 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group9 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group10: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group10 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group10 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group11: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group11 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group11 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-group12: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-group12 + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-group12 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-no-qemu-slow: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-no-qemu-slow + run: build/darwin/arm64/earthly --ci -P +test-no-qemu-slow + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-tests-qemu: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Execute +test-qemu + run: build/darwin/arm64/earthly --ci -P +test-qemu + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Execute fail test + run: "! build/darwin/arm64/earthly --ci ./tests/fail+test-fail" + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-examples-1: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Link Earthly dir to Earthly dev dir + run: mkdir -p ~/.earthly && ln -s ~/.earthly ~/.earthly-dev + - name: Build +examples-1 (PR build) + run: build/darwin/arm64/earthly --ci -P +examples-1 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name != 'push' + - name: Build +examples-1 (main build) + run: build/darwin/arm64/earthly --ci --push -P +examples-1 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'push' + - name: Build and test multi-platform example + run: | + build/darwin/arm64/earthly ./examples/multiplatform+all + container run --rm earthbuild/examples:multiplatform_linux_arm64 | grep aarch64 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-examples-2: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Link Earthly dir to Earthly dev dir + run: mkdir -p ~/.earthly && ln -s ~/.earthly ~/.earthly-dev + - name: Build +examples-2 (PR build) + run: build/darwin/arm64/earthly --ci -P +examples-2 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name != 'push' + - name: Build +examples-2 (main build) + run: build/darwin/arm64/earthly --ci --push -P +examples-2 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'push' + - name: Build and test multi-platform example + run: | + build/darwin/arm64/earthly ./examples/multiplatform+all + container run --rm earthbuild/examples:multiplatform_linux_arm64 | grep aarch64 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-examples-3: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Link Earthly dir to Earthly dev dir + run: mkdir -p ~/.earthly && ln -s ~/.earthly ~/.earthly-dev + - name: Build +examples-3 (PR build) + run: build/darwin/arm64/earthly --ci -P +examples-3 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name != 'push' + - name: Build +examples-3 (main build) + run: build/darwin/arm64/earthly --ci --push -P +examples-3 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'push' + - name: Build and test multi-platform example + run: | + build/darwin/arm64/earthly ./examples/multiplatform+all + container run --rm earthbuild/examples:multiplatform_linux_arm64 | grep aarch64 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-examples-4: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Link Earthly dir to Earthly dev dir + run: mkdir -p ~/.earthly && ln -s ~/.earthly ~/.earthly-dev + - name: Build +examples-4 (PR build) + run: build/darwin/arm64/earthly --ci -P +examples-4 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name != 'push' + - name: Build +examples-4 (main build) + run: build/darwin/arm64/earthly --ci --push -P +examples-4 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'push' + - name: Build and test multi-platform example + run: | + build/darwin/arm64/earthly ./examples/multiplatform+all + container run --rm earthbuild/examples:multiplatform_linux_arm64 | grep aarch64 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} + + apple-container-examples-5: + needs: build-earthly + if: ${{ !failure() }} + runs-on: macos-26 + env: + FORCE_COLOR: 1 + EARTHLY_INSTALL_ID: "earthly-githubactions" + BUILT_EARTHLY_PATH: build/darwin/arm64/earthly + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: earthbuild/actions-setup@5d323543fa1d7b963384b46b2cbaef0bf6d88216 # v2.1.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Download built earthly binary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: earthly-binary-mac + path: build/darwin/arm64 + - name: Make earthly binary executable + run: chmod +x build/darwin/arm64/earthly + - name: Install apple/container + run: brew install container + - name: Start apple/container service + run: container system start --enable-kernel-install + - name: Configure earth to use GCR mirror and apple-container + run: |- + build/darwin/arm64/earthly config global.buildkit_additional_config "'[registry.\"docker.io\"] + mirrors = [\"mirror.gcr.io\", \"public.ecr.aws\"]'" + build/darwin/arm64/earthly config global.container_frontend apple-container-shell + - name: Bootstrap earth + run: build/darwin/arm64/earthly bootstrap + - name: Set EARTHLY_VERSION_FLAG_OVERRIDES env + run: |- + set -euo pipefail + EARTHLY_VERSION_FLAG_OVERRIDES="$(tr -d '\n' < .earthly_version_flag_overrides)" + echo "EARTHLY_VERSION_FLAG_OVERRIDES=$EARTHLY_VERSION_FLAG_OVERRIDES" >> "$GITHUB_ENV" + - name: Link Earthly dir to Earthly dev dir + run: mkdir -p ~/.earthly && ln -s ~/.earthly ~/.earthly-dev + - name: Build +examples-5 (PR build) + run: build/darwin/arm64/earthly --ci -P +examples-5 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name != 'push' + - name: Build +examples-5 (main build) + run: build/darwin/arm64/earthly --ci --push -P +examples-5 + if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) && github.event_name == 'push' + - name: Build and test multi-platform example + run: | + build/darwin/arm64/earthly ./examples/multiplatform+all + container run --rm earthbuild/examples:multiplatform_linux_arm64 | grep aarch64 + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository + - name: Buildkit logs (runs on failure) + run: container logs earthly-buildkitd + if: ${{ failure() }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 068f1ae5e9..e329a1dc50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,12 @@ jobs: uses: ./.github/workflows/ci-podman-ubuntu.yml secrets: inherit + apple-container: + name: Apple Container + needs: fast-check + uses: ./.github/workflows/ci-apple-container-mac.yml + secrets: inherit + next: name: Next needs: fast-check diff --git a/builder/builder.go b/builder/builder.go index 638d036a73..61fc623489 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -180,9 +180,11 @@ func (b *Builder) startRegistryProxy(ctx context.Context, caps apicaps.CapSet) ( return nil, false } - // Podman does not support the insecure localhost - if b.opt.ContainerFrontend.Scheme() == containerutil.SchemePodmanContainer { - cons.Printf("Registry proxy not supported on Podman. Falling back to tar-based outputs.") + // Podman and Apple Container do not support the insecure localhost registry proxy + scheme := b.opt.ContainerFrontend.Scheme() + if scheme == containerutil.SchemePodmanContainer || + scheme == containerutil.SchemeAppleContainer { + cons.Printf("Registry proxy not supported on Podman/Apple Container. Falling back to tar-based outputs.") return nil, false } diff --git a/buildkitd/buildkitd.go b/buildkitd/buildkitd.go index 0806e3cbea..f330451930 100644 --- a/buildkitd/buildkitd.go +++ b/buildkitd/buildkitd.go @@ -59,7 +59,10 @@ func NewClient( if errors.Is(retErr, os.ErrNotExist) { switch fe.Config().Setting { - case containerutil.FrontendPodman, containerutil.FrontendPodmanShell: + case containerutil.FrontendPodman, + containerutil.FrontendPodmanShell, + containerutil.FrontendAppleContainer, + containerutil.FrontendAppleContainerShell: tlsPaths := []string{ settings.TLSCA, settings.ServerTLSKey, @@ -127,8 +130,8 @@ func NewClient( bkCons := console.WithPrefix("buildkitd") if !isDockerAvailable(ctx, fe) { - bkCons.Printf("Is %[1]s installed and running? Are you part of any needed groups?\n", fe.Config().Binary) - return nil, fmt.Errorf("%s not available", fe.Config().Binary) + bkCons.Printf("Is %[1]s installed and running? Are you part of any needed groups?\n", frontendDisplayName(fe)) + return nil, fmt.Errorf("%s not available", frontendDisplayName(fe)) } info, workerInfo, err := maybeStart(ctx, console, image, containerName, installationName, fe, settings, opts...) @@ -267,7 +270,7 @@ func maybeStart( if isStarted { console. WithPrefix("buildkitd"). - Printf("Found buildkit daemon as %s container (%s)\n", fe.Config().Binary, containerName) + Printf("Found buildkit daemon as %s (%s)\n", frontendContainerDesc(fe), containerName) var ( info *client.Info @@ -284,7 +287,7 @@ func maybeStart( console. WithPrefix("buildkitd"). - Printf("Starting buildkit daemon as a %s container (%s)...\n", fe.Config().Binary, containerName) + Printf("Starting buildkit daemon as %s (%s)...\n", frontendContainerDescWithArticle(fe), containerName) err = Start(ctx, console, image, containerName, installationName, fe, settings, false) if err != nil { @@ -1317,3 +1320,43 @@ func humanizeBytes(v int64) string { return humanize.Bytes(bytes) } + +func frontendDisplayName(fe containerutil.ContainerFrontend) string { + switch fe.Config().Setting { + case containerutil.FrontendAppleContainer, containerutil.FrontendAppleContainerShell: + return "Apple Container" + case containerutil.FrontendPodman, containerutil.FrontendPodmanShell: + return "Podman" + case containerutil.FrontendDocker, containerutil.FrontendDockerShell: + return "Docker" + default: + return fe.Config().Binary + } +} + +func frontendContainerDesc(fe containerutil.ContainerFrontend) string { + switch fe.Config().Setting { + case containerutil.FrontendAppleContainer, containerutil.FrontendAppleContainerShell: + return "Apple Container" + case containerutil.FrontendPodman, containerutil.FrontendPodmanShell: + return "Podman container" + default: + return fe.Config().Binary + " container" + } +} + +func frontendContainerDescWithArticle(fe containerutil.ContainerFrontend) string { + switch fe.Config().Setting { + case containerutil.FrontendAppleContainer, containerutil.FrontendAppleContainerShell: + return "an Apple Container" + case containerutil.FrontendPodman, containerutil.FrontendPodmanShell: + return "a Podman container" + default: + desc := fe.Config().Binary + " container" + if len(desc) > 0 && (desc[0] == 'a' || desc[0] == 'e' || desc[0] == 'i' || desc[0] == 'o' || desc[0] == 'u') { + return "an " + desc + } + + return "a " + desc + } +} diff --git a/cmd/earthly/app/before.go b/cmd/earthly/app/before.go index 4091586432..2017220cd6 100644 --- a/cmd/earthly/app/before.go +++ b/cmd/earthly/app/before.go @@ -152,7 +152,7 @@ func (app *EarthApp) parseFrontend(ctx context.Context) error { app.BaseCLI.Flags().ContainerFrontend = stub if !app.BaseCLI.Flags().Verbose { - console.Printf("Unable to detect Docker or Podman. Use --verbose to see details (or errors)\n") + console.Printf("Unable to detect Docker, Podman, or Apple Container. Use --verbose to see details (or errors)\n") } console.VerbosePrintf("%s frontend initialization failed due to %s", diff --git a/config/config.go b/config/config.go index 4fc9f4efec..401925b0ae 100644 --- a/config/config.go +++ b/config/config.go @@ -71,33 +71,33 @@ var ( // GlobalConfig contains global config values. type GlobalConfig struct { - DarwinProxyImage string `help:"The container image & tag used for the Docker Desktop registry proxy." yaml:"darwin_proxy_image"` //nolint:lll - ServerTLSKey string `help:"The path to the server key for verification. Relative paths are interpreted as relative to the config path. Only used when earth manages buildkit." yaml:"buildkitd_tlskey"` //nolint:lll - BuildkitScheme string `help:" *Deprecated* Change how earth communicates with its buildkit daemon. Valid options are: docker-container, tcp. TCP is experimental." yaml:"buildkit_transport"` //nolint:lll - BuildkitImage string `help:"Choose a specific image for your buildkitd." yaml:"buildkit_image"` //nolint:lll - CachePath string `help:" *Deprecated* The path to keep earth's cache." yaml:"cache_path"` //nolint:lll - SecretProvider string `help:"Command to execute to retrieve secret." yaml:"secret_provider"` //nolint:lll - BuildkitAdditionalConfig string `help:"Additional config to use when starting the buildkit container; like using custom/self-signed certificates." yaml:"buildkit_additional_config"` //nolint:lll - IPTables string `help:"Which iptables binary to use. Valid values are iptables-legacy or iptables-nft. Bypasses any autodetection." yaml:"ip_tables"` //nolint:lll - ContainerFrontend string `help:"What program should be used to start and stop buildkitd, save images. Default is 'docker'. Valid options are 'docker' and 'podman' (experimental)." yaml:"container_frontend"` //nolint:lll - ServerTLSCert string `help:"The path to the server cert for verification. Relative paths are interpreted as relative to the config path. Only used when earth manages buildkit." yaml:"buildkitd_tlscert"` //nolint:lll - BuildkitHost string `help:"The URL of your buildkit, remote or local." yaml:"buildkit_host"` //nolint:lll - TLSCACert string `help:"The path to the CA cert for verification. Relative paths are interpreted as relative to the config path." yaml:"tlsca"` //nolint:lll - ClientTLSKey string `help:"The path to the client key for verification. Relative paths are interpreted as relative to the config path." yaml:"tlskey"` //nolint:lll - GitImage string `help:"Image used to resolve git repositories" yaml:"git_image"` //nolint:lll - LocalRegistryHost string `help:"The URL of the local registry used for image exports to Docker." yaml:"local_registry_host"` //nolint:lll - TLSCAKey string `help:"The path to the CA key for generating any missing certificates. Relative paths are interpreted as relative to the config path." yaml:"tlsca_key"` //nolint:lll - ClientTLSCert string `help:"The path to the client cert for verification. Relative paths are interpreted as relative to the config path." yaml:"tlscert"` //nolint:lll - BuildkitAdditionalArgs []string `help:"Additional args to pass to buildkit when it starts. Useful for custom/self-signed certs, or user namespace complications." yaml:"buildkit_additional_args"` //nolint:lll - BuildkitCacheSizePct int `help:"Size of the buildkit cache, as percentage (0-100)." yaml:"cache_size_pct"` //nolint:lll - BuildkitCacheSizeMb int `help:"Size of the buildkit cache in Megabytes." yaml:"cache_size_mb"` //nolint:lll - ConversionParallelism int `help:"Set the conversion parallelism for speeding up the use of IF, WITH, DOCKER --load, FROMDOCKERFILE and others. A value of 0 disables the feature" yaml:"conversion_parallelism"` //nolint:lll - BuildkitMaxParallelism int `help:"Max parallelism for buildkit workers" yaml:"buildkit_max_parallelism"` //nolint:lll - DarwinProxyWait time.Duration `help:"The maximum time to wait for the Darwin registry proxy support container to become available." yaml:"darwin_proxy_wait"` //nolint:lll - BuildkitRestartTimeoutS int `help:"How long to wait for buildkit to (re)start, in seconds." yaml:"buildkit_restart_timeout_s"` //nolint:lll - BuildkitCacheKeepDurationS int `help:"Max age of cache, in seconds. 0 disables age-based cache expiry." yaml:"buildkit_cache_keep_duration_s"` //nolint:lll - CniMtu uint16 `help:"Override auto-detection of the default interface MTU, for all containers within buildkit" yaml:"cni_mtu"` //nolint:lll - TLSEnabled bool `help:"If TLS should be used to communicate with Buildkit. Only honored when BuildkitScheme is 'tcp'." yaml:"tls_enabled"` //nolint:lll + DarwinProxyImage string `help:"The container image & tag used for the Docker Desktop registry proxy." yaml:"darwin_proxy_image"` //nolint:lll + ServerTLSKey string `help:"The path to the server key for verification. Relative paths are interpreted as relative to the config path. Only used when earth manages buildkit." yaml:"buildkitd_tlskey"` //nolint:lll + BuildkitScheme string `help:" *Deprecated* Change how earth communicates with its buildkit daemon. Valid options are: docker-container, tcp. TCP is experimental." yaml:"buildkit_transport"` //nolint:lll + BuildkitImage string `help:"Choose a specific image for your buildkitd." yaml:"buildkit_image"` //nolint:lll + CachePath string `help:" *Deprecated* The path to keep earth's cache." yaml:"cache_path"` //nolint:lll + SecretProvider string `help:"Command to execute to retrieve secret." yaml:"secret_provider"` //nolint:lll + BuildkitAdditionalConfig string `help:"Additional config to use when starting the buildkit container; like using custom/self-signed certificates." yaml:"buildkit_additional_config"` //nolint:lll + IPTables string `help:"Which iptables binary to use. Valid values are iptables-legacy or iptables-nft. Bypasses any autodetection." yaml:"ip_tables"` //nolint:lll + ContainerFrontend string `help:"What program should be used to start and stop buildkitd, save images. Default is 'docker'. Valid options are 'docker', 'podman' (experimental), and 'apple-container' (experimental)." yaml:"container_frontend"` //nolint:lll + ServerTLSCert string `help:"The path to the server cert for verification. Relative paths are interpreted as relative to the config path. Only used when earth manages buildkit." yaml:"buildkitd_tlscert"` //nolint:lll + BuildkitHost string `help:"The URL of your buildkit, remote or local." yaml:"buildkit_host"` //nolint:lll + TLSCACert string `help:"The path to the CA cert for verification. Relative paths are interpreted as relative to the config path." yaml:"tlsca"` //nolint:lll + ClientTLSKey string `help:"The path to the client key for verification. Relative paths are interpreted as relative to the config path." yaml:"tlskey"` //nolint:lll + GitImage string `help:"Image used to resolve git repositories" yaml:"git_image"` //nolint:lll + LocalRegistryHost string `help:"The URL of the local registry used for image exports to Docker." yaml:"local_registry_host"` //nolint:lll + TLSCAKey string `help:"The path to the CA key for generating any missing certificates. Relative paths are interpreted as relative to the config path." yaml:"tlsca_key"` //nolint:lll + ClientTLSCert string `help:"The path to the client cert for verification. Relative paths are interpreted as relative to the config path." yaml:"tlscert"` //nolint:lll + BuildkitAdditionalArgs []string `help:"Additional args to pass to buildkit when it starts. Useful for custom/self-signed certs, or user namespace complications." yaml:"buildkit_additional_args"` //nolint:lll + BuildkitCacheSizePct int `help:"Size of the buildkit cache, as percentage (0-100)." yaml:"cache_size_pct"` //nolint:lll + BuildkitCacheSizeMb int `help:"Size of the buildkit cache in Megabytes." yaml:"cache_size_mb"` //nolint:lll + ConversionParallelism int `help:"Set the conversion parallelism for speeding up the use of IF, WITH, DOCKER --load, FROMDOCKERFILE and others. A value of 0 disables the feature" yaml:"conversion_parallelism"` //nolint:lll + BuildkitMaxParallelism int `help:"Max parallelism for buildkit workers" yaml:"buildkit_max_parallelism"` //nolint:lll + DarwinProxyWait time.Duration `help:"The maximum time to wait for the Darwin registry proxy support container to become available." yaml:"darwin_proxy_wait"` //nolint:lll + BuildkitRestartTimeoutS int `help:"How long to wait for buildkit to (re)start, in seconds." yaml:"buildkit_restart_timeout_s"` //nolint:lll + BuildkitCacheKeepDurationS int `help:"Max age of cache, in seconds. 0 disables age-based cache expiry." yaml:"buildkit_cache_keep_duration_s"` //nolint:lll + CniMtu uint16 `help:"Override auto-detection of the default interface MTU, for all containers within buildkit" yaml:"cni_mtu"` //nolint:lll + TLSEnabled bool `help:"If TLS should be used to communicate with Buildkit. Only honored when BuildkitScheme is 'tcp'." yaml:"tls_enabled"` //nolint:lll } // GitConfig contains git-specific config values. diff --git a/util/containerutil/apple_container.go b/util/containerutil/apple_container.go new file mode 100644 index 0000000000..a5f1c6a7b4 --- /dev/null +++ b/util/containerutil/apple_container.go @@ -0,0 +1,474 @@ +package containerutil + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" +) + +type appleShellFrontend struct { + *shellFrontend +} + +type appleContainerInspect struct { + Configuration struct { + Labels map[string]string `json:"labels"` + ID string `json:"id"` + Image struct { + Reference string `json:"reference"` + } `json:"image"` + } `json:"configuration"` + Status struct { + State string `json:"state"` + Networks []struct { + Address string `json:"address"` + } `json:"networks"` + } `json:"status"` +} + +type appleImageInspect struct { + Configuration struct { + Name string `json:"name"` + Descriptor struct { + Digest string `json:"digest"` + } `json:"descriptor"` + } `json:"configuration"` + ID string `json:"id"` + Variants []struct { + Platform struct { + OS string `json:"os"` + Architecture string `json:"architecture"` + } `json:"platform"` + } `json:"variants"` +} + +type appleVolumeInspect struct { + ID string `json:"id"` + Configuration struct { + Name string `json:"name"` + Source string `json:"source"` + SizeInBytes uint64 `json:"sizeInBytes"` + } `json:"configuration"` +} + +// NewAppleContainerShellFrontend constructs a new Frontend using the apple container binary. +func NewAppleContainerShellFrontend(_ context.Context, cfg *FrontendConfig) (ContainerFrontend, error) { + fe := &appleShellFrontend{ + shellFrontend: &shellFrontend{ + binaryName: "container", + runCompatibilityArgs: make([]string, 0), + globalCompatibilityArgs: make([]string, 0), + Console: cfg.Console, + }, + } + + fe.rootless = false + + var err error + + fe.urls, err = fe.setupAndValidateAddresses(FrontendAppleContainerShell, cfg) + if err != nil { + return nil, errors.Wrap(err, "failed to calculate buildkit URLs") + } + + return fe, nil +} + +func (asf *appleShellFrontend) Scheme() string { + return SchemeAppleContainer +} + +func (asf *appleShellFrontend) Config() *CurrentFrontend { + return &CurrentFrontend{ + Setting: FrontendAppleContainerShell, + Binary: asf.binaryName, + Type: FrontendTypeShell, + FrontendURLs: asf.urls, + } +} + +func (asf *appleShellFrontend) IsAvailable(ctx context.Context) bool { + args := append(asf.globalCompatibilityArgs, "list") //nolint:gocritic + cmd := exec.CommandContext(ctx, asf.binaryName, args...) // #nosec G204 + err := cmd.Run() + + return err == nil +} + +func (asf *appleShellFrontend) Information(ctx context.Context) (*FrontendInfo, error) { + output, err := asf.commandContextOutput(ctx, "--version") + if err != nil { + return nil, err + } + + ver := strings.TrimSpace(output.string()) + + return &FrontendInfo{ + ClientVersion: ver, + ClientAPIVersion: "N/A", + ClientPlatform: "darwin/arm64", + ServerVersion: ver, + ServerAPIVersion: "N/A", + ServerPlatform: "darwin/arm64", + ServerAddress: "local", + }, nil +} + +func (asf *appleShellFrontend) ContainerList(ctx context.Context) ([]*ContainerInfo, error) { + output, err := asf.commandContextOutput(ctx, "list", "--format", "json", "--all") + if err != nil { + return nil, err + } + + var inspects []appleContainerInspect + + err = json.Unmarshal([]byte(output.stdout.String()), &inspects) + if err != nil { + return nil, errors.Wrapf(err, "failed to decode apple container list output: %s", output.stdout.String()) + } + + ret := make([]*ContainerInfo, len(inspects)) + for i, v := range inspects { + ipAddresses := map[string]string{} + + if len(v.Status.Networks) > 0 { + ip := v.Status.Networks[0].Address + if idx := strings.Index(ip, "/"); idx != -1 { + ip = ip[:idx] + } + + ipAddresses["bridge"] = ip + } + + ret[i] = &ContainerInfo{ + ID: v.Configuration.ID, + Name: v.Configuration.ID, + Status: v.Status.State, + Image: v.Configuration.Image.Reference, + IPs: ipAddresses, + } + } + + return ret, nil +} + +func (asf *appleShellFrontend) ContainerInfo( + ctx context.Context, namesOrIDs ...string, +) (map[string]*ContainerInfo, error) { + infos := map[string]*ContainerInfo{} + for _, nameOrID := range namesOrIDs { + infos[nameOrID] = &ContainerInfo{ + Name: nameOrID, + Status: StatusMissing, + } + } + + if len(namesOrIDs) == 0 { + return infos, nil + } + + args := append([]string{"inspect"}, namesOrIDs...) //nolint:goconst + output, _ := asf.commandContextOutput(ctx, args...) + + if strings.TrimSpace(output.stdout.String()) == "" { + return infos, nil + } + + var inspects []appleContainerInspect + + err := json.Unmarshal([]byte(output.stdout.String()), &inspects) + if err != nil { + var singleInspect appleContainerInspect + + singleErr := json.Unmarshal([]byte(output.stdout.String()), &singleInspect) + if singleErr == nil { + inspects = []appleContainerInspect{singleInspect} + } else { + return nil, errors.Wrapf(err, "failed to decode apple container inspect output: %s", output.stdout.String()) + } + } + + for _, v := range inspects { + ipAddresses := map[string]string{} + + if len(v.Status.Networks) > 0 { + ip := v.Status.Networks[0].Address + if idx := strings.Index(ip, "/"); idx != -1 { + ip = ip[:idx] + } + + ipAddresses["bridge"] = ip + } + + info := &ContainerInfo{ + ID: v.Configuration.ID, + Name: v.Configuration.ID, + Status: v.Status.State, + Image: v.Configuration.Image.Reference, + IPs: ipAddresses, + Labels: v.Configuration.Labels, + } + + for _, requested := range namesOrIDs { + if requested == v.Configuration.ID { + infos[requested] = info + } + } + } + + return infos, nil +} + +func (asf *appleShellFrontend) ContainerRemove(ctx context.Context, force bool, namesOrIDs ...string) error { + args := []string{"delete"} + if force { + args = append(args, "-f") + } + + args = append(args, namesOrIDs...) + _, err := asf.commandContextOutput(ctx, args...) + + return err +} + +func (asf *appleShellFrontend) ContainerRun(ctx context.Context, containers ...ContainerRun) error { + var err error + + for _, container := range containers { + args := make([]string, 0, 32) + args = append(args, "run") + + if container.Privileged { + args = append(args, "--cap-add", "ALL") + } + + for k, v := range container.Envs { + env := fmt.Sprintf("%s=%s", k, v) + args = append(args, "--env", env) + } + + for k, v := range container.Labels { + label := fmt.Sprintf("%s=%s", k, v) + args = append(args, "--label", label) + } + + for _, mnt := range container.Mounts { + mount := fmt.Sprintf("type=%s,source=%s,target=%s", mnt.Type, mnt.Source, mnt.Dest) + if mnt.ReadOnly { + mount += ",readonly" + } + + args = append(args, "--mount", mount) + } + + for _, prt := range container.Ports { + hostPort := strconv.FormatInt(int64(prt.HostPort), 10) + if prt.HostPort <= 0 { + hostPort = "" + } + + port := fmt.Sprintf("%s:%v:%v", prt.IP, hostPort, prt.ContainerPort) + + if prt.Protocol != "" { + port = fmt.Sprintf("%s/%s", port, prt.Protocol) + } + + args = append(args, "--publish", port) + } + + args = append(args, "-d") + args = append(args, "--name", container.NameOrID) + args = append(args, container.AdditionalArgs...) + args = append(args, asf.runCompatibilityArgs...) + args = append(args, container.ImageRef) + args = append(args, container.ContainerArgs...) + + _, cmdErr := asf.commandContextOutput(ctx, args...) + if cmdErr != nil { + err = multierror.Append(err, cmdErr) + } + } + + return err +} + +func (asf *appleShellFrontend) ImageInfo(ctx context.Context, refs ...string) (map[string]*ImageInfo, error) { + infos := map[string]*ImageInfo{} + for _, ref := range refs { + infos[ref] = &ImageInfo{} + } + + if len(refs) == 0 { + return infos, nil + } + + args := append([]string{"image", "inspect"}, refs...) //nolint:goconst + output, _ := asf.commandContextOutput(ctx, args...) + + if strings.TrimSpace(output.stdout.String()) == "" { + return infos, nil + } + + var inspects []appleImageInspect + + err := json.Unmarshal([]byte(output.stdout.String()), &inspects) + if err != nil { + var singleInspect appleImageInspect + + singleErr := json.Unmarshal([]byte(output.stdout.String()), &singleInspect) + if singleErr == nil { + inspects = []appleImageInspect{singleInspect} + } else { + return nil, errors.Wrapf(err, "failed to decode apple image inspect output: %s", output.stdout.String()) + } + } + + for i, v := range inspects { + if i >= len(refs) { + break + } + + var osStr, archStr string + if len(v.Variants) > 0 { + osStr = v.Variants[0].Platform.OS + archStr = v.Variants[0].Platform.Architecture + } + + infos[refs[i]] = &ImageInfo{ + ID: v.ID, + OS: osStr, + Architecture: archStr, + Tags: []string{v.Configuration.Name}, + } + } + + return infos, nil +} + +func (asf *appleShellFrontend) ImagePull(ctx context.Context, refs ...string) error { + var err error + + for _, ref := range refs { + args := []string{"image", "pull"} + if strings.HasPrefix(ref, asf.urls.LocalRegistryHost.Host+"/") { + args = append(args, "--scheme", "http") + } + + args = append(args, ref) + + _, cmdErr := asf.commandContextOutput(ctx, args...) + if cmdErr != nil { + err = multierror.Append(err, cmdErr) + } + } + + return err +} + +func (asf *appleShellFrontend) ImageTag(ctx context.Context, tags ...ImageTag) error { + var err error + + for _, tag := range tags { + _, cmdErr := asf.commandContextOutput(ctx, "image", "tag", tag.SourceRef, tag.TargetRef) + if cmdErr != nil { + err = multierror.Append(err, cmdErr) + } + } + + return err +} + +func (asf *appleShellFrontend) ImageLoadFromFileCommand(filename string) string { + binary, args := asf.commandContextStrings("image", "load", "--input", filename) + all := append([]string{binary}, args...) + + return strings.Join(all, " ") +} + +func (asf *appleShellFrontend) ImageLoad(ctx context.Context, images ...io.Reader) error { + var err error + + for _, image := range images { + file, tmpErr := os.CreateTemp("", "earthly-apple-load-*") + if tmpErr != nil { + err = multierror.Append(err, errors.Wrap(tmpErr, "failed to create temp tarball")) + continue + } + + _, copyErr := io.Copy(file, image) + if copyErr != nil { + err = multierror.Append(err, errors.Wrapf(tmpErr, "failed to write to %s", file.Name())) + continue + } + defer file.Close() + defer os.Remove(file.Name()) + + output, cmdErr := asf.commandContextOutput(ctx, "image", "load", "--input", file.Name()) + if cmdErr != nil { + err = multierror.Append(err, errors.Wrapf(cmdErr, "image load failed: %s", output.string())) + } + } + + return err +} + +func (asf *appleShellFrontend) VolumeInfo(ctx context.Context, volumeNames ...string) (map[string]*VolumeInfo, error) { + results := map[string]*VolumeInfo{} + for _, name := range volumeNames { + results[name] = &VolumeInfo{Name: name} + } + + if len(volumeNames) == 0 { + return results, nil + } + + args := append([]string{"volume", "inspect"}, volumeNames...) + + output, _ := asf.commandContextOutput(ctx, args...) + + if strings.TrimSpace(output.stdout.String()) == "" { + return results, nil + } + + var inspects []appleVolumeInspect + + err := json.Unmarshal([]byte(output.stdout.String()), &inspects) + if err != nil { + var singleInspect appleVolumeInspect + + singleErr := json.Unmarshal([]byte(output.stdout.String()), &singleInspect) + if singleErr == nil { + inspects = []appleVolumeInspect{singleInspect} + } else { + return results, errors.Wrapf(err, "failed to decode apple volume inspect output for %v", volumeNames) + } + } + + for _, v := range inspects { + name := v.Configuration.Name + if name == "" { + name = v.ID + } + + vi := &VolumeInfo{ + Name: name, + SizeBytes: v.Configuration.SizeInBytes, + Mountpoint: v.Configuration.Source, + } + for _, reqName := range volumeNames { + if reqName == name || reqName == v.ID { + results[reqName] = vi + } + } + } + + return results, nil +} diff --git a/util/containerutil/apple_container_test.go b/util/containerutil/apple_container_test.go new file mode 100644 index 0000000000..a100be9eff --- /dev/null +++ b/util/containerutil/apple_container_test.go @@ -0,0 +1,107 @@ +package containerutil + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAppleContainerInspectUnmarshal(t *testing.T) { + t.Parallel() + + data := `[ + { + "status": { + "state": "running", + "networks": [ + {"address": "192.168.64.2"} + ] + }, + "configuration": { + "id": "test-container-id", + "image": { + "reference": "docker.io/library/ubuntu:latest" + }, + "labels": { + "key": "value" + } + } + } + ]` + + var inspects []appleContainerInspect + + err := json.Unmarshal([]byte(data), &inspects) + require.NoError(t, err) + require.Len(t, inspects, 1) + + c := inspects[0] + assert.Equal(t, "running", c.Status.State) + require.Len(t, c.Status.Networks, 1) + assert.Equal(t, "192.168.64.2", c.Status.Networks[0].Address) + assert.Equal(t, "test-container-id", c.Configuration.ID) + assert.Equal(t, "docker.io/library/ubuntu:latest", c.Configuration.Image.Reference) + assert.Equal(t, "value", c.Configuration.Labels["key"]) +} + +func TestAppleImageInspectUnmarshal(t *testing.T) { + t.Parallel() + + data := `[ + { + "id": "sha256:abcd1234", + "configuration": { + "name": "docker.io/library/ubuntu:latest" + }, + "variants": [ + { + "platform": { + "os": "linux", + "architecture": "arm64" + } + } + ] + } + ]` + + var inspects []appleImageInspect + + err := json.Unmarshal([]byte(data), &inspects) + require.NoError(t, err) + require.Len(t, inspects, 1) + + img := inspects[0] + assert.Equal(t, "docker.io/library/ubuntu:latest", img.Configuration.Name) + assert.Equal(t, "sha256:abcd1234", img.ID) + require.Len(t, img.Variants, 1) + assert.Equal(t, "linux", img.Variants[0].Platform.OS) + assert.Equal(t, "arm64", img.Variants[0].Platform.Architecture) +} + +func TestAppleVolumeInspectUnmarshal(t *testing.T) { + t.Parallel() + + data := `[ + { + "id": "test-volume", + "configuration": { + "name": "test-volume", + "source": "/var/lib/container/volumes/test-volume", + "sizeInBytes": 1048576 + } + } + ]` + + var inspects []appleVolumeInspect + + err := json.Unmarshal([]byte(data), &inspects) + require.NoError(t, err) + require.Len(t, inspects, 1) + + v := inspects[0] + assert.Equal(t, "test-volume", v.Configuration.Name) + assert.Equal(t, "/var/lib/container/volumes/test-volume", v.Configuration.Source) + assert.Equal(t, uint64(1048576), v.Configuration.SizeInBytes) +} diff --git a/util/containerutil/frontend.go b/util/containerutil/frontend.go index ea24f44555..1d8b0f5a35 100644 --- a/util/containerutil/frontend.go +++ b/util/containerutil/frontend.go @@ -59,9 +59,10 @@ func FrontendForSetting(ctx context.Context, feType string, cfg *FrontendConfig) func autodetectFrontend(ctx context.Context, cfg *FrontendConfig) (ContainerFrontend, error) { var errs error - for _, feType := range []string{ + for _, feType := range [...]string{ FrontendDockerShell, FrontendPodmanShell, + FrontendAppleContainerShell, } { fe, err := frontendIfAvailable(ctx, feType, cfg) if err != nil { @@ -88,6 +89,8 @@ func frontendIfAvailable(ctx context.Context, feType string, cfg *FrontendConfig newFe = NewDockerShellFrontend case FrontendPodmanShell: newFe = NewPodmanShellFrontend + case FrontendAppleContainerShell: + newFe = NewAppleContainerShellFrontend default: return nil, fmt.Errorf("%s is not a supported container frontend", feType) } diff --git a/util/containerutil/shell_shared.go b/util/containerutil/shell_shared.go index dc18f76fc9..30767f439e 100644 --- a/util/containerutil/shell_shared.go +++ b/util/containerutil/shell_shared.go @@ -101,7 +101,7 @@ func parseContainerList(output string) ([]*ContainerInfo, error) { } func (sf *shellFrontend) ContainerInfo(ctx context.Context, namesOrIDs ...string) (map[string]*ContainerInfo, error) { - args := append([]string{"container", "inspect"}, namesOrIDs...) + args := append([]string{"container", "inspect"}, namesOrIDs...) //nolint:goconst // Ignore the error. This is because one or more of the provided names or IDs could be missing. // This allows for Info to report that the container itself is missing. @@ -276,7 +276,7 @@ func (sf *shellFrontend) ContainerRun(ctx context.Context, containers ...Contain } func (sf *shellFrontend) ImageInfo(ctx context.Context, refs ...string) (map[string]*ImageInfo, error) { - args := append([]string{"image", "inspect"}, refs...) + args := append([]string{"image", "inspect"}, refs...) //nolint:goconst // Ignore the error. This is because one or more of the provided refs could be missing. // This allows for Info to report that the image itself is missing. @@ -428,6 +428,10 @@ func DefaultAddressForSetting(setting string, localContainerName string, default // Podman only works over TCP. There are weird errors when trying to use the provided helper from buildkit. return fmt.Sprintf(TCPAddressFmt, defaultPort), nil + case FrontendAppleContainerShell: + // Apple container only works over TCP. + return fmt.Sprintf(TCPAddressFmt, defaultPort), nil + case FrontendStub: return DockerSchemePrefix + localContainerName, nil // Maintain old behavior } @@ -441,9 +445,16 @@ func parseAndValidateURL(addr string) (*url.URL, error) { return nil, fmt.Errorf("%s: %w", addr, errURLParseFailure) } - if parsed.Scheme != "tcp" && parsed.Scheme != SchemeDockerContainer && parsed.Scheme != SchemePodmanContainer { - format := "%s is not a valid scheme. Only tcp or docker-container is allowed at this time: %w" - return nil, fmt.Errorf(format, parsed.Scheme, errURLValidationFailure) + if parsed.Scheme != "tcp" && + parsed.Scheme != SchemeDockerContainer && + parsed.Scheme != SchemePodmanContainer && + parsed.Scheme != SchemeAppleContainer { + return nil, fmt.Errorf( + "%s is not a valid scheme. "+ + "Only tcp, docker-container, podman-container, or apple-container is allowed at this time: %w", + parsed.Scheme, + errURLValidationFailure, + ) } if parsed.Port() == "" && parsed.Scheme == "tcp" { @@ -467,5 +478,6 @@ func IsLocal(addr string) bool { hostname == net.IPv6loopback.String() || hostname == "localhost" || // Convention. Users hostname omitted; this is only really here for convenience. parsed.Scheme == SchemeDockerContainer || // Accommodate feature flagging during transition. Will have omitted TLS? - parsed.Scheme == SchemePodmanContainer + parsed.Scheme == SchemePodmanContainer || + parsed.Scheme == SchemeAppleContainer } diff --git a/util/containerutil/types.go b/util/containerutil/types.go index 91b21963be..bc0142d23e 100644 --- a/util/containerutil/types.go +++ b/util/containerutil/types.go @@ -173,6 +173,13 @@ const ( // FrontendPodmanShell forces usage of the podman binary for container operations. FrontendPodmanShell = "podman-shell" + // FrontendAppleContainer forces usage of the (future, currently unimplemented) apple-container API + // for container operations. + FrontendAppleContainer = "apple-container" + + // FrontendAppleContainerShell forces usage of the container binary for container operations. + FrontendAppleContainerShell = "apple-container-shell" + // FrontendStub is for when there is no valid provider but attempting to run anyways is desired; // like integration tests, or the earthbuild/earthbuild image when NO_DOCKER is set. FrontendStub = "stub" @@ -225,4 +232,7 @@ const ( // SchemePodmanContainer is the scheme used for podman-container addresses. SchemePodmanContainer = "podman-container" + + // SchemeAppleContainer is the scheme used for apple-container addresses. + SchemeAppleContainer = "apple-container" )