Skip to content

Commit 180c8e5

Browse files
Update release info (#57)
1 parent 4b8f191 commit 180c8e5

File tree

9 files changed

+112
-54
lines changed

9 files changed

+112
-54
lines changed

.github/workflows/bat.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ jobs:
4747
products: Simulink
4848
check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer));
4949
check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer));
50+
- os: macos-latest
51+
release: latest
52+
products: Simulink
53+
check-matlab: matlabVer = ver('matlab'); assert(~isempty(matlabVer));
54+
check-simulink: simulinkVer = ver('simulink'); assert(~isempty(simulinkVer));
5055
steps:
5156
- uses: actions/download-artifact@v3
5257
with:

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ jobs:
2222
uses: actions/checkout@v3
2323
- name: Set up MATLAB
2424
uses: matlab-actions/setup-matlab@v2-beta
25-
with:
26-
products: Simulink Simulink_Test
25+
with:
26+
products: Simulink Simulink_Test
2727
- name: Run build
2828
uses: matlab-actions/run-build@v1
2929
with:

src/install.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ export async function install(platform: string, architecture: string, release: s
3030

3131
await core.group("Setting up MATLAB", async () => {
3232
const mpmPath: string = await mpm.setup(platform, architecture);
33-
const [destination, alreadyExists]: [string, boolean] = await matlab.makeToolcacheDir(releaseInfo);
33+
let [destination, alreadyExists]: [string, boolean] = await matlab.makeToolcacheDir(releaseInfo, platform);
34+
if (platform === "darwin") {
35+
destination = destination + "/MATLAB.app";
36+
}
3437
if (!alreadyExists) {
3538
await mpm.install(mpmPath, releaseInfo, products, destination);
3639
}

src/install.unit.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ describe("install procedure", () => {
6666
expect(setOutputMock).toHaveBeenCalledTimes(1);
6767
});
6868

69+
it("installs to MATLAB.app on mac", async () => {
70+
await expect(install.install("darwin", arch, release, products)).resolves.toBeUndefined();
71+
expect(mpmInstallMock.mock.calls[0][3]).toMatch("MATLAB.app");
72+
});
73+
6974
["darwin", "win32"].forEach((os) => {
7075
it(`does not run deps script on ${os}`, async () => {
7176
await expect(install.install(os, arch, release, products)).resolves.toBeUndefined();

src/matlab.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,7 @@ export interface Release {
1414
update: string;
1515
}
1616

17-
interface MATLABReleaseInfo {
18-
latest: string;
19-
version: {
20-
[release: string]: string | undefined
21-
}
22-
}
23-
24-
export async function makeToolcacheDir(release: Release): Promise<[string, boolean]> {
17+
export async function makeToolcacheDir(release: Release, platform: string): Promise<[string, boolean]> {
2518
let toolpath: string = tc.find("MATLAB", release.version);
2619
let alreadyExists = false;
2720
if (toolpath) {
@@ -46,27 +39,45 @@ export async function setupBatch(platform: string) {
4639
}
4740

4841
export async function getReleaseInfo(release: string): Promise<Release> {
49-
const client: http.HttpClient = new http.HttpClient();
50-
const releaseInfo = await client.getJson<MATLABReleaseInfo>(properties.matlabReleaseInfoUrl);
51-
52-
if (!releaseInfo.result) {
53-
return Promise.reject(Error(`Unable to retrieve the MATLAB release information. Contact MathWorks at continuous-integration@mathworks.com if the problem persists.`));
42+
// Get release name from input parameter
43+
let name: string;
44+
let trimmedRelease = release.toLowerCase().trim()
45+
if (trimmedRelease === "latest") {
46+
try {
47+
const client: http.HttpClient = new http.HttpClient();
48+
const latestResp = await client.get(properties.matlabLatestReleaseUrl);
49+
name = await latestResp.readBody();
50+
}
51+
catch {
52+
return Promise.reject(Error(`Unable to retrieve the MATLAB release information. Contact MathWorks at continuous-integration@mathworks.com if the problem persists.`));
53+
}
54+
} else {
55+
let nameMatch = trimmedRelease.match(/r[0-9]{4}[a-b]/);
56+
if (!nameMatch) {
57+
return Promise.reject(Error(`${release} is invalid or unsupported. Specify the value as R2020a or a later release.`));
58+
}
59+
name = nameMatch[0];
5460
}
5561

56-
let name: string = release.toLowerCase().trim().substring(0,6);
57-
if (name === "latest") {
58-
name = releaseInfo.result.latest;
62+
// create semantic version of format year.semiannual.update from release
63+
let year = name.slice(1,5);
64+
let semiannual = name[5] === "a"? "1": "2";
65+
let updateMatch = release.toLowerCase().match(/u[0-9]+/);
66+
let version = `${year}.${semiannual}`;
67+
let update: string;
68+
if (updateMatch) {
69+
update = updateMatch[0]
70+
version += `.${update[1]}`;
71+
} else {
72+
// Notify user if Update version format is invalid
73+
if (trimmedRelease !== name && trimmedRelease !== "latest") {
74+
const invalidUpdate = trimmedRelease.replace(name, "");
75+
return Promise.reject(Error(`${invalidUpdate} is not a valid update release name.`));
76+
}
77+
update = "";
78+
version += ".999"
5979
}
6080

61-
// Remove update version
62-
let version = releaseInfo.result.version[name];
63-
let update = release.toLowerCase().trim().substring(6,release.length);
64-
if (!update) {
65-
update = "latest"
66-
}
67-
if (!version) {
68-
return Promise.reject(Error(`${release} is invalid or unsupported. Specify the value as R2020a or a later release.`));
69-
}
7081
return {
7182
name: name,
7283
version: version,

src/matlab.unit.test.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
import * as core from "@actions/core";
44
import * as http from "@actions/http-client";
5+
import * as httpjs from "http";
6+
import * as net from 'net';
57
import * as tc from "@actions/tool-cache";
68
import * as matlab from "./matlab";
79
import * as script from "./script";
810

9-
jest.mock("./script")
11+
jest.mock("./script");
12+
jest.mock("http");
13+
jest.mock("net");
1014
jest.mock("@actions/core");
1115
jest.mock("@actions/http-client");
1216
jest.mock("@actions/tool-cache");
@@ -18,9 +22,10 @@ afterEach(() => {
1822
describe("matlab tests", () => {
1923
const release = {
2024
name: "r2022b",
21-
version: "9.13.0",
22-
update: "latest",
25+
version: "2022.2.999",
26+
update: "",
2327
}
28+
const platform = "linux";
2429
describe("toolcacheLocation", () => {
2530
let findMock: jest.Mock<any, any>;
2631
let cacheFileMock: jest.Mock<any, any>;
@@ -34,14 +39,14 @@ describe("matlab tests", () => {
3439

3540
it("returns toolpath if in toolcache", async () => {
3641
findMock.mockReturnValue("/opt/hostedtoolcache/matlab/r2022b");
37-
await expect(matlab.makeToolcacheDir(release)).resolves.toMatchObject(["/opt/hostedtoolcache/matlab/r2022b", true]);
42+
await expect(matlab.makeToolcacheDir(release, platform)).resolves.toMatchObject(["/opt/hostedtoolcache/matlab/r2022b", true]);
3843
expect(infoMock).toHaveBeenCalledTimes(1);
3944
});
4045

4146
it("creates cache and returns new path if not in toolcache", async () => {
4247
findMock.mockReturnValue("");
4348
cacheFileMock.mockReturnValue("/opt/hostedtoolcache/matlab/r2022b");
44-
await expect(matlab.makeToolcacheDir(release)).resolves.toMatchObject(["/opt/hostedtoolcache/matlab/r2022b", false]);
49+
await expect(matlab.makeToolcacheDir(release, platform)).resolves.toMatchObject(["/opt/hostedtoolcache/matlab/r2022b", false]);
4550
})
4651
});
4752

@@ -78,18 +83,14 @@ describe("matlab tests", () => {
7883
});
7984

8085
describe("getReleaseInfo", () => {
86+
let infoMock: jest.Mock<any, any>;
8187
beforeEach(() => {
88+
infoMock = core.info as jest.Mock;
8289
// Mock MATLABReleaseInfo response from http client
83-
jest.spyOn(http.HttpClient.prototype, 'getJson').mockImplementation(async () => {
90+
jest.spyOn(http.HttpClient.prototype, 'get').mockImplementation(async () => {
8491
return {
85-
statusCode: 200,
86-
result: {
87-
latest: 'r2022b',
88-
version: {
89-
r2022b: '9.13.0',
90-
}
91-
},
92-
headers: {}
92+
message: new httpjs.IncomingMessage(new net.Socket()),
93+
readBody: () => {return Promise.resolve("r2022b")}
9394
};
9495
})
9596
});
@@ -102,28 +103,47 @@ describe("matlab tests", () => {
102103
expect(matlab.getReleaseInfo("R2022b")).resolves.toMatchObject(release);
103104
});
104105

106+
it("Sets minor version according to a or b release", () => {
107+
const R2022aRelease = {
108+
name: "r2022a",
109+
update: "",
110+
version: "2022.1.999",
111+
}
112+
expect(matlab.getReleaseInfo("R2022a")).resolves.toMatchObject(R2022aRelease);
113+
114+
const R2022bRelease = {
115+
name: "r2022b",
116+
update: "",
117+
version: "2022.2.999",
118+
}
119+
expect(matlab.getReleaseInfo("R2022b")).resolves.toMatchObject(R2022bRelease);
120+
});
121+
105122
it("allows specifying update number", () => {
106123
const releaseWithUpdate = {
107124
name: "r2022b",
108125
update: "u2",
109-
version: "9.13.0",
126+
version: "2022.2.2",
110127
}
111128
expect(matlab.getReleaseInfo("R2022bU2")).resolves.toMatchObject(releaseWithUpdate);
112129
});
113130

131+
it("displays message for invalid update level input format and uses latest", () => {
132+
expect(matlab.getReleaseInfo("r2022bUpdate1")).rejects.toBeDefined();
133+
});
134+
114135
it("rejects for unsupported release", () => {
115136
expect(matlab.getReleaseInfo("R2022c")).rejects.toBeDefined();
116137
});
117138

118139
it("rejects if for bad http response", () => {
119-
jest.spyOn(http.HttpClient.prototype, 'getJson').mockImplementation(async () => {
140+
jest.spyOn(http.HttpClient.prototype, 'get').mockImplementation(async () => {
120141
return {
121-
statusCode: 400,
122-
result: undefined,
123-
headers: {}
142+
message: new httpjs.IncomingMessage(new net.Socket()),
143+
readBody: () => {return Promise.reject("Bam!")}
124144
};
125145
})
126-
expect(matlab.getReleaseInfo("R2022b")).rejects.toBeDefined();
146+
expect(matlab.getReleaseInfo("latest")).rejects.toBeDefined();
127147
});
128148
});
129149
});

src/mpm.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export async function setup(platform: string, architecture: string): Promise<str
1818
case "linux":
1919
mpmUrl = properties.mpmRootUrl + "glnxa64/mpm";
2020
break;
21+
case "darwin":
22+
mpmUrl = properties.mpmRootUrl + "maci64/mpm";
23+
break;
2124
default:
2225
return Promise.reject(Error(`This action is not supported on ${platform} runners using the ${architecture} architecture.`));
2326
}
@@ -26,7 +29,10 @@ export async function setup(platform: string, architecture: string): Promise<str
2629
if (platform === "win32") {
2730
let mpmExtractedPath: string = await tc.extractZip(mpm);
2831
mpm = path.join(mpmExtractedPath, "bin", "win64", "mpm.exe");
29-
}
32+
} else if (platform === "darwin") {
33+
let mpmExtractedPath: string = await tc.extractZip(mpm);
34+
mpm = path.join(mpmExtractedPath, "bin", "maci64", "mpm");
35+
}
3036

3137
const exitCode = await exec.exec(`chmod +x ${mpm}`);
3238
if (exitCode !== 0) {

src/mpm.unit.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ describe("setup mpm", () => {
5151
expect(tcExtractZipMock).toHaveBeenCalledTimes(1);
5252
expect(tcDownloadToolMock.mock.calls[0][0]).toContain("win64");
5353
});
54+
55+
it(`works on mac`, async () => {
56+
const platform = "darwin";
57+
tcDownloadToolMock.mockResolvedValue(zipMockPath);
58+
tcExtractZipMock.mockResolvedValue(mpmMockPath);
59+
execMock.mockResolvedValue(0);
60+
await expect(mpm.setup(platform, arch)).resolves.toBe(path.join(mpmMockPath, "bin", "maci64", "mpm"));
61+
expect(tcExtractZipMock).toHaveBeenCalledTimes(1);
62+
expect(tcDownloadToolMock.mock.calls[0][0]).toContain("maci64");
63+
});
5464
});
5565

5666
it("errors on unsupported platform", async () => {
@@ -89,11 +99,9 @@ describe("setup mpm", () => {
8999

90100
describe("mpm install", () => {
91101
let execMock: jest.Mock<any, any>;
92-
let addPathMock: jest.Mock<any, any>;
93-
let setOutputMock: jest.Mock<any, any>;
94102
const mpmPath = "mpm";
95-
const releaseInfo = {name: "r2022b", version: "9.13.0", update: "latest"};
96-
const mpmRelease = "r2022blatest"
103+
const releaseInfo = {name: "r2022b", version: "9.13.0", update: ""};
104+
const mpmRelease = "r2022b"
97105
beforeEach(() => {
98106
execMock = exec.exec as jest.Mock;
99107
});

src/properties.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"matlabBatchUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-batch/v0/install.sh",
33
"matlabDepsUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-deps/v0/install.sh",
4-
"matlabReleaseInfoUrl": "https://mw-ci-static-dev.s3.amazonaws.com/matlab-deps/v0/versions.json",
4+
"matlabLatestReleaseUrl": "https://ssd.mathworks.com/supportfiles/ci/matlab-release/v0/latest",
55
"mpmRootUrl": "https://www.mathworks.com/mpm/"
66
}

0 commit comments

Comments
 (0)