From 8d87ba5a72837d4f7722c4511bead926497cc7cf Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:09:36 +0100 Subject: [PATCH] docker(install): pin QEMU to 10.1.1 Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- .github/actions/macos-setup-qemu/action.yml | 17 ---- .github/workflows/test.yml | 33 ++++---- src/docker/install.ts | 89 +++++++++++++++++---- 3 files changed, 88 insertions(+), 51 deletions(-) delete mode 100644 .github/actions/macos-setup-qemu/action.yml diff --git a/.github/actions/macos-setup-qemu/action.yml b/.github/actions/macos-setup-qemu/action.yml deleted file mode 100644 index 4a6798d..0000000 --- a/.github/actions/macos-setup-qemu/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: 'Setup QEMU on macOS' -description: 'Set up QEMU on macOS runners' - -# FIXME: Remove this composite once QEMU issue is fixed on macOS runners -# https://github.com/docker/actions-toolkit/issues/455 - -runs: - using: composite - steps: - - run: | - set -ex - brew uninstall --ignore-dependencies qemu || true - brew autoremove || true - curl -o /tmp/qemu.rb https://raw.githubusercontent.com/Homebrew/homebrew-core/f1a9cf104a9a51779c7a532b658c490f69974839/Formula/q/qemu.rb - brew install /tmp/qemu.rb - continue-on-error: true - shell: bash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18904e1..625ba62 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -109,25 +109,22 @@ jobs: for (const os of ['ubuntu-latest', 'ubuntu-24.04-arm', 'macos-15-intel', 'windows-latest']) { for (const test of tests) { if (test === 'docker/install.test.itg.ts') { - // FIXME: docker install tests on macOS are currently broken: https://github.com/docker/actions-toolkit/issues/852 - if (!os.startsWith('macos')) { - if (os !== 'windows-latest') { - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: '27.3.1' }); - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'master' }); - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'latest' }); - } - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'v26.1.4' }); - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'latest' }); - includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'v29.0.0-rc.1', docker_install_channel: 'test' }); - if (os === 'ubuntu-latest') { - includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'image', docker_install_version: 'latest' }); - includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'archive', docker_install_version: 'latest' }); - } - if (os !== 'windows-latest') { - includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'image', docker_install_version: 'latest' }); - } - includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'archive', docker_install_version: 'latest' }); + if (os !== 'windows-latest') { + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: '27.3.1' }); + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'master' }); + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'image', docker_install_version: 'latest' }); } + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'v26.1.4' }); + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'latest' }); + includes.push({ os: os, test: test, test_name: 'root', docker_install_type: 'archive', docker_install_version: 'v29.0.0-rc.1', docker_install_channel: 'test' }); + if (os === 'ubuntu-latest') { + includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'image', docker_install_version: 'latest' }); + includes.push({ os: os, test: test, test_name: 'rootless', docker_install_type: 'archive', docker_install_version: 'latest' }); + } + if (os !== 'windows-latest') { + includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'image', docker_install_version: 'latest' }); + } + includes.push({ os: os, test: test, test_name: 'tcp', docker_install_type: 'archive', docker_install_version: 'latest' }); } else { includes.push({ os: os, test: test }); } diff --git a/src/docker/install.ts b/src/docker/install.ts index 84972fd..906019a 100644 --- a/src/docker/install.ts +++ b/src/docker/install.ts @@ -269,22 +269,12 @@ export class Install { await io.mkdirP(limaDir); const dockerHost = `unix://${limaDir}/docker.sock`; - // avoid brew to auto update and upgrade unrelated packages. - let envs = Object.assign({}, process.env, { - HOMEBREW_NO_AUTO_UPDATE: '1', - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: '1' - }) as { - [key: string]: string; - }; - if (!(await Install.limaInstalled())) { - await core.group('Installing lima', async () => { - await Exec.exec('brew', ['install', 'lima'], {env: envs}); - }); + await this.brewInstall('lima'); } await core.group('Lima version', async () => { - await Exec.exec('lima', ['--version'], {env: envs}); + await Exec.exec('lima', ['--version']); }); await core.group('Creating lima config', async () => { @@ -313,9 +303,8 @@ export class Install { }); if (!(await Install.qemuInstalled())) { - await core.group('Installing QEMU', async () => { - await Exec.exec('brew', ['install', 'qemu'], {env: envs}); - }); + // FIXME: QEMU 10.1.2 seems to break on GitHub runners. Pin to 10.1.1 in the meantime: https://github.com/docker/actions-toolkit/issues/852 + await this.brewInstall('qemu', '4a7a2dd5d44d068b3e89ebed5be7b4ada315d221'); } const qemuBin = await Install.qemuBin(); await core.group('QEMU version', async () => { @@ -324,7 +313,7 @@ export class Install { // lima might already be started on the runner so env var added in download // method is not expanded to the running process. - envs = Object.assign({}, envs, { + const envs = Object.assign({}, process.env, { PATH: `${this.toolDir}:${process.env.PATH}` }) as { [key: string]: string; @@ -751,4 +740,72 @@ EOF`, }); return JSON.parse(blob); } + + private async brewInstall(packageName: string, revision?: string): Promise { + // avoid brew to auto update and upgrade unrelated packages. + const envs = Object.assign({}, process.env, { + HOMEBREW_NO_AUTO_UPDATE: '1', + HOMEBREW_NO_INSTALL_UPGRADE: '1', + HOMEBREW_NO_INSTALL_CLEANUP: '1' + }) as { + [key: string]: string; + }; + + await core.group(`Installing ${packageName}`, async () => { + if (!revision) { + await Exec.exec('brew', ['install', packageName]); + } else { + const dockerTap = 'docker-actions-toolkit/tap'; + const hasDockerTap = await Exec.getExecOutput('brew', ['tap'], { + ignoreReturnCode: true, + silent: true, + env: envs + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr); + } + for (const line of res.stdout.trim().split('\n')) { + if (line.includes(dockerTap)) { + return true; + } + } + return false; + }); + if (!hasDockerTap) { + await Exec.exec('brew', ['tap-new', dockerTap], {env: envs}); + } + const brewRepoTapPath = await Exec.getExecOutput('brew', ['--repo', dockerTap], { + ignoreReturnCode: true, + silent: true, + env: envs + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr); + } + return res.stdout.trim(); + }); + const formulaURL = `https://raw.githubusercontent.com/Homebrew/homebrew-core/${revision}/Formula/${packageName.charAt(0)}/${packageName}.rb`; + await tc.downloadTool(formulaURL, path.join(brewRepoTapPath, 'Formula', `${packageName}.rb`)); + const hasFormulaInstalled = await Exec.getExecOutput('brew', ['ls', '-1'], { + ignoreReturnCode: true, + silent: true, + env: envs + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr); + } + for (const line of res.stdout.trim().split('\n')) { + if (line.trim() == packageName) { + return true; + } + } + return false; + }); + if (hasFormulaInstalled) { + await Exec.exec('brew', ['uninstall', packageName, '--ignore-dependencies'], {env: envs}); + } + await Exec.exec('brew', ['install', `${dockerTap}/${packageName}`], {env: envs}); + } + }); + } }