From d9f96ff5d0638a9bce611929af649510110d1c57 Mon Sep 17 00:00:00 2001 From: Mark Cafaro <34887852+mcafaro@users.noreply.github.com> Date: Tue, 19 Mar 2024 09:38:13 -0400 Subject: [PATCH] Install Amazon Corretto 8 on Apple Silicon (#101) * Install JDK as sys dep on Apple Silicon * Try a later version of tool-cache dep * Revert "Try a later version of tool-cache dep" This reverts commit 4c139578263892d673793c7c11b1bc51214b9dc7. * Try JDK in tar.gz package * Try direct pkg URL * Set JDK download path * Change Simulink to Symbolic Math Toolbox * Remove JDK pkg if it exists before downloading --- .github/workflows/bat.yml | 24 ++++++------- src/install.ts | 13 +++---- src/install.unit.test.ts | 25 +++---------- src/matlab.ts | 18 ++++++++++ src/matlab.unit.test.ts | 75 ++++++++++++++++++++++++++++++++++++++- src/properties.json | 3 +- 6 files changed, 115 insertions(+), 43 deletions(-) diff --git a/.github/workflows/bat.yml b/.github/workflows/bat.yml index a70f176..c24e610 100644 --- a/.github/workflows/bat.yml +++ b/.github/workflows/bat.yml @@ -28,31 +28,31 @@ jobs: include: - os: ubuntu-latest release: latest - products: Simulink Simulink_Test + products: Symbolic_Math_Toolbox check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer)); - check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer)); + check-toolbox: symbolicVer = ver('symbolic'); assert(~isempty(symbolicVer)); - os: ubuntu-20.04 release: R2021bU2 products: | MATLAB - Simulink + Symbolic_Math_Toolbox check-matlab: matlabVer = ver('matlab'); assert(strcmp(matlabVer.Release,'(R2021b)')); - check-simulink: simulinkVer = ver('simulink'); assert(strcmp(simulinkVer.Release,'(R2021b)')); + check-toolbox: symbolicVer = ver('symbolic'); assert(strcmp(symbolicVer.Release,'(R2021b)')); - os: windows-latest release: latest - products: Simulink + products: Symbolic_Math_Toolbox check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer)); - check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer)); + check-toolbox: symbolicVer = ver('symbolic'); assert(~isempty(symbolicVer)); - os: macos-latest release: latest - products: Simulink + products: Symbolic_Math_Toolbox check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer)); - check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer)); + check-toolbox: symbolicVer = ver('symbolic'); assert(~isempty(symbolicVer)); - os: macos-14 release: latest - products: Simulink + products: Symbolic_Math_Toolbox check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer)); - check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer)); + check-toolbox: symbolicVer = ver('symbolic'); assert(~isempty(symbolicVer)); steps: - uses: actions/download-artifact@v4 with: @@ -70,10 +70,10 @@ jobs: uses: matlab-actions/run-command@v2 with: command: "${{ matrix.check-matlab }}" - - name: Check Simulink version + - name: Check toolbox version uses: matlab-actions/run-command@v2 with: - command: "${{ matrix.check-simulink }}" + command: "${{ matrix.check-toolbox }}" - name: Check NoOp on 2nd install uses: ./ with: diff --git a/src/install.ts b/src/install.ts index af45c0a..384abe5 100644 --- a/src/install.ts +++ b/src/install.ts @@ -3,10 +3,8 @@ import * as core from "@actions/core"; import * as matlab from "./matlab"; import * as mpm from "./mpm"; -import * as script from "./script"; import * as path from "path"; -import * as cache from './cache-restore'; -import properties from "./properties.json"; +import * as cache from "./cache-restore"; /** * Set up an instance of MATLAB on the runner. @@ -26,12 +24,9 @@ export async function install(platform: string, architecture: string, release: s return Promise.reject(Error(`Release '${releaseInfo.name}' is not supported. Use 'R2020b' or a later release.`)); } - // Install runtime system dependencies for MATLAB on Linux - if (platform === "linux") { - await core.group("Preparing system for MATLAB", () => - script.downloadAndRunScript(platform, properties.matlabDepsUrl, [releaseInfo.name]) - ); - } + await core.group("Preparing system for MATLAB", async () => + matlab.installSystemDependencies(platform, architecture, releaseInfo.name) + ); await core.group("Setting up MATLAB", async () => { let [destination, alreadyExists]: [string, boolean] = await matlab.makeToolcacheDir(releaseInfo, platform); diff --git a/src/install.unit.test.ts b/src/install.unit.test.ts index b38ec22..cd01fdf 100644 --- a/src/install.unit.test.ts +++ b/src/install.unit.test.ts @@ -1,16 +1,14 @@ // Copyright 2020-2024 The MathWorks, Inc. import * as core from "@actions/core"; -import * as cache from './cache-restore'; +import * as cache from "./cache-restore"; import * as install from "./install"; import * as matlab from "./matlab"; import * as mpm from "./mpm"; -import * as script from "./script"; jest.mock("@actions/core"); jest.mock("./matlab"); jest.mock("./mpm"); -jest.mock("./script"); jest.mock("./cache-restore"); afterEach(() => { @@ -18,7 +16,7 @@ afterEach(() => { }); describe("install procedure", () => { - let downloadAndRunScriptMock: jest.Mock; + let matlabInstallSystemDependenciesMock: jest.Mock; let matlabGetReleaseInfoMock: jest.Mock; let matlabMakeToolcacheDirMock: jest.Mock; let matlabSetupBatchMock: jest.Mock; @@ -42,7 +40,7 @@ describe("install procedure", () => { const doInstall = () => install.install(platform, arch, release, products, useCache); beforeEach(() => { - downloadAndRunScriptMock = script.downloadAndRunScript as jest.Mock; + matlabInstallSystemDependenciesMock = matlab.installSystemDependencies as jest.Mock; matlabGetReleaseInfoMock = matlab.getReleaseInfo as jest.Mock; matlabMakeToolcacheDirMock = matlab.makeToolcacheDir as jest.Mock; matlabSetupBatchMock = matlab.setupBatch as jest.Mock; @@ -63,7 +61,7 @@ describe("install procedure", () => { it("ideally works", async () => { await expect(doInstall()).resolves.toBeUndefined(); - expect(downloadAndRunScriptMock).toHaveBeenCalledTimes(1); + expect(matlabInstallSystemDependenciesMock).toHaveBeenCalledTimes(1); expect(matlabSetupBatchMock).toHaveBeenCalledTimes(1); expect(mpmSetupMock).toHaveBeenCalledTimes(1); expect(mpmInstallMock).toHaveBeenCalledTimes(1); @@ -71,19 +69,6 @@ describe("install procedure", () => { expect(setOutputMock).toHaveBeenCalledTimes(1); }); - ["darwin", "win32"].forEach((os) => { - it(`does not run deps script on ${os}`, async () => { - await expect(install.install(os, arch, release, products, useCache)).resolves.toBeUndefined(); - expect(downloadAndRunScriptMock).toHaveBeenCalledTimes(0); - expect(core.group).toHaveBeenCalledTimes(1); - expect(matlabSetupBatchMock).toHaveBeenCalledTimes(1); - expect(mpmSetupMock).toHaveBeenCalledTimes(1); - expect(mpmInstallMock).toHaveBeenCalledTimes(1); - expect(addPathMock).toHaveBeenCalledTimes(1); - expect(setOutputMock).toHaveBeenCalledTimes(1); - }); - }); - it("NoOp on existing install", async () => { matlabMakeToolcacheDirMock.mockResolvedValue(["/opt/hostedtoolcache/MATLAB/9.13.0/x64", true]); await expect(doInstall()).resolves.toBeUndefined(); @@ -107,7 +92,7 @@ describe("install procedure", () => { }); it("rejects when the setup deps fails", async () => { - downloadAndRunScriptMock.mockRejectedValueOnce(Error("oof")); + matlabInstallSystemDependenciesMock.mockRejectedValueOnce(Error("oof")); await expect(doInstall()).rejects.toBeDefined(); }); diff --git a/src/matlab.ts b/src/matlab.ts index 1004d7f..97ba593 100644 --- a/src/matlab.ts +++ b/src/matlab.ts @@ -9,6 +9,7 @@ import * as fs from "fs"; import { homedir } from "os"; import * as path from "path"; import properties from "./properties.json"; +import * as script from "./script"; export interface Release { name: string; @@ -180,3 +181,20 @@ export function getSupportPackagesPath(platform: string, release: string): strin return supportPackagesDir; } +export async function installSystemDependencies(platform: string, architecture: string, release: string) { + if (platform === "linux") { + return script.downloadAndRunScript(platform, properties.matlabDepsUrl, [release]); + } else if (platform === "darwin" && architecture === "arm64") { + return installAppleSiliconJdk(); + } +} + +async function installAppleSiliconJdk() { + const jdkPath = path.join(process.env["RUNNER_TEMP"] ?? "", "jdk.pkg"); + io.rmRF(jdkPath); + const jdk = await tc.downloadTool(properties.appleSiliconJdkUrl, jdkPath); + const exitCode = await exec.exec(`sudo installer -pkg "${jdk}" -target /`); + if (exitCode !== 0) { + return Promise.reject(Error("Unable to install Java runtime.")); + } +} diff --git a/src/matlab.unit.test.ts b/src/matlab.unit.test.ts index 25d96be..e445543 100644 --- a/src/matlab.unit.test.ts +++ b/src/matlab.unit.test.ts @@ -4,11 +4,13 @@ import * as core from "@actions/core"; import * as exec from "@actions/exec"; import * as http from "@actions/http-client"; import * as httpjs from "http"; -import * as net from 'net'; +import * as net from "net"; import * as path from "path"; import * as tc from "@actions/tool-cache"; import * as matlab from "./matlab"; +import * as script from "./script"; import fs from "fs"; +import properties from "./properties.json"; jest.mock("http"); jest.mock("net"); @@ -16,6 +18,7 @@ jest.mock("@actions/core"); jest.mock("@actions/exec"); jest.mock("@actions/http-client"); jest.mock("@actions/tool-cache"); +jest.mock("./script"); afterEach(() => { jest.resetAllMocks(); @@ -256,4 +259,74 @@ describe("matlab tests", () => { expect(matlab.getReleaseInfo("latest")).rejects.toBeDefined(); }); }); + + describe("installSystemDependencies", () => { + let downloadAndRunScriptMock: jest.Mock; + let tcDownloadToolMock: jest.Mock; + let execMock: jest.Mock; + const arch = "x64"; + const release = "R2023b"; + + beforeEach(() => { + downloadAndRunScriptMock = script.downloadAndRunScript as jest.Mock; + tcDownloadToolMock = tc.downloadTool as jest.Mock; + execMock = exec.exec as jest.Mock; + }); + + describe("test on all supported platforms", () => { + it(`works on linux`, async () => { + const platform = "linux"; + await expect( + matlab.installSystemDependencies(platform, arch, release) + ).resolves.toBeUndefined(); + expect(downloadAndRunScriptMock).toHaveBeenCalledWith( + platform, + properties.matlabDepsUrl, + [release] + ); + }); + + it(`works on windows`, async () => { + const platform = "win32"; + await expect( + matlab.installSystemDependencies(platform, arch, release) + ).resolves.toBeUndefined(); + }); + + it(`works on mac`, async () => { + const platform = "darwin"; + await expect( + matlab.installSystemDependencies(platform, arch, release) + ).resolves.toBeUndefined(); + }); + + it(`works on mac with apple silicon`, async () => { + const platform = "darwin"; + tcDownloadToolMock.mockResolvedValue("java.jdk"); + execMock.mockResolvedValue(0); + await expect( + matlab.installSystemDependencies(platform, "arm64", release) + ).resolves.toBeUndefined(); + expect(tcDownloadToolMock).toHaveBeenCalledWith(properties.appleSiliconJdkUrl, expect.anything()); + expect(execMock).toHaveBeenCalledWith(`sudo installer -pkg "java.jdk" -target /`); + }); + }); + + it("rejects when the apple silicon JDK download fails", async () => { + const platform = "darwin"; + tcDownloadToolMock.mockRejectedValue(Error("oof")); + await expect( + matlab.installSystemDependencies(platform, "arm64", release) + ).rejects.toBeDefined(); + }); + + it("rejects when the apple silicon JDK fails to install", async () => { + const platform = "darwin"; + tcDownloadToolMock.mockResolvedValue("java.jdk"); + execMock.mockResolvedValue(1); + await expect( + matlab.installSystemDependencies(platform, "arm64", release) + ).rejects.toBeDefined(); + }); + }); }); diff --git a/src/properties.json b/src/properties.json index 2a688bb..ab3507b 100644 --- a/src/properties.json +++ b/src/properties.json @@ -2,5 +2,6 @@ "matlabDepsUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-deps/v0/install.sh", "matlabLatestReleaseUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest", "matlabBatchRootUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-batch/v1/", - "mpmRootUrl": "https://www.mathworks.com/mpm/" + "mpmRootUrl": "https://www.mathworks.com/mpm/", + "appleSiliconJdkUrl": "https://corretto.aws/downloads/resources/8.402.08.1/amazon-corretto-8.402.08.1-macosx-aarch64.pkg" }