Skip to content

Commit cf0fd99

Browse files
committed
Add ability to resolve snapshot latest date
1 parent 3aed395 commit cf0fd99

File tree

7 files changed

+487
-72
lines changed

7 files changed

+487
-72
lines changed

__tests__/snapshot-package.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { OS } from "../src/os";
2+
import { SnapshotPackageResolver } from "../src/snapshot-package";
3+
4+
describe("build snapshot package info", () => {
5+
const githubToken = process.env.TEST_GITHUB_TOKEN;
6+
if (githubToken) {
7+
const getSnapshot = new SnapshotPackageResolver(githubToken);
8+
const expectedDate = new Date().toISOString().split("T")[0];
9+
10+
it("resolve main branch latest snapshot", async () => {
11+
const toolchain = await getSnapshot.getSnapshot("main-snapshot");
12+
13+
expect(toolchain).toEqual({ branch: "main", date: expectedDate });
14+
});
15+
16+
it("resolve simver branch snapshot", async () => {
17+
const toolchain = await getSnapshot.getSnapshot("6.0-snapshot");
18+
19+
expect(toolchain).toEqual({ branch: "6.0", date: expectedDate });
20+
});
21+
}
22+
23+
const resolver = new SnapshotPackageResolver(null);
24+
25+
it("resolve with explicit date to main", async () => {
26+
const toolchain = await resolver.getSnapshot("main-snapshot-2022-01-28");
27+
28+
expect(toolchain).toEqual({ branch: "main", date: "2022-01-28" });
29+
});
30+
31+
it("resolve with explicit date to semver", async () => {
32+
const toolchain = await resolver.getSnapshot("5.7-snapshot-2022-08-30");
33+
34+
expect(toolchain).toEqual({ branch: "5.7", date: "2022-08-30" });
35+
});
36+
37+
it("main branch for macOS", () => {
38+
const pkg = resolver.getPackage(
39+
{ branch: "main", date: "2024-08-01" },
40+
{ os: OS.MacOS, version: "latest", name: "macOS" }
41+
);
42+
43+
expect(pkg).toStrictEqual({
44+
url: "https://swift.org/builds/development/xcode/swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a/swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a-osx.pkg",
45+
name: "swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a",
46+
version: "6.0",
47+
isStableRelease: false,
48+
});
49+
});
50+
51+
it("main branch for Ubuntu", () => {
52+
const pkg = resolver.getPackage(
53+
{ branch: "main", date: "2024-08-01" },
54+
{ os: OS.Ubuntu, version: "22.04", name: "Ubuntu" }
55+
);
56+
57+
expect(pkg).toStrictEqual({
58+
url: "https://swift.org/builds/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a/swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a-ubuntu22.04.tar.gz",
59+
name: "swift-DEVELOPMENT-SNAPSHOT-2024-08-01-a",
60+
version: "6.0",
61+
isStableRelease: false,
62+
});
63+
});
64+
65+
it("simver branch for macOS", () => {
66+
const pkg = resolver.getPackage(
67+
{ branch: "5.10", date: "2024-08-02" },
68+
{ os: OS.MacOS, version: "latest", name: "macOS" }
69+
);
70+
71+
expect(pkg).toStrictEqual({
72+
url: "https://swift.org/builds/swift-5.10-branch/xcode/swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a/swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a-osx.pkg",
73+
name: "swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a",
74+
version: "5.10",
75+
isStableRelease: false,
76+
});
77+
});
78+
79+
it("simver branch for Ubuntu", () => {
80+
const pkg = resolver.getPackage(
81+
{ branch: "5.10", date: "2024-08-02" },
82+
{ os: OS.Ubuntu, version: "22.04", name: "Ubuntu" }
83+
);
84+
85+
expect(pkg).toStrictEqual({
86+
url: "https://swift.org/builds/swift-5.10-branch/ubuntu2204/swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a/swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a-ubuntu22.04.tar.gz",
87+
name: "swift-5.10-DEVELOPMENT-SNAPSHOT-2024-08-02-a",
88+
version: "5.10",
89+
isStableRelease: false,
90+
});
91+
});
92+
});

dist/index.js

+178-31
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ const os = __importStar(__nccwpck_require__(2037));
155155
const path = __importStar(__nccwpck_require__(1017));
156156
const core = __importStar(__nccwpck_require__(2186));
157157
const toolCache = __importStar(__nccwpck_require__(7784));
158-
const swift_versions_1 = __nccwpck_require__(8263);
158+
const fs = __importStar(__nccwpck_require__(7147));
159159
const gpg_1 = __nccwpck_require__(9060);
160-
async function install(version, system) {
160+
async function install(version, system, getPackage) {
161161
if (os.platform() !== "linux") {
162162
core.error("Trying to run linux installer on non-linux os");
163163
return;
@@ -166,16 +166,17 @@ async function install(version, system) {
166166
if (swiftPath === null || swiftPath.trim().length == 0) {
167167
core.debug(`No matching installation found`);
168168
await (0, gpg_1.setupKeys)();
169-
const swiftPkg = (0, swift_versions_1.swiftPackage)(version, system);
169+
const swiftPkg = await getPackage();
170170
let { pkg, signature } = await download(swiftPkg);
171171
await (0, gpg_1.verify)(signature, pkg);
172-
swiftPath = await unpack(pkg, swiftPkg.name, version, system);
172+
swiftPath = await unpack(swiftPkg, pkg, version, system);
173173
}
174174
else {
175175
core.debug("Matching installation found");
176176
}
177177
core.debug("Adding swift to path");
178178
let binPath = path.join(swiftPath, "/usr/bin");
179+
core.debug(`Swift binary path (exists=${fs.existsSync(binPath)}: ${binPath}`);
179180
core.addPath(binPath);
180181
core.debug("Swift installed");
181182
}
@@ -189,11 +190,16 @@ async function download({ url, name }) {
189190
core.debug("Swift download complete");
190191
return { pkg, signature, name };
191192
}
192-
async function unpack(packagePath, packageName, version, system) {
193-
core.debug("Extracting package");
193+
async function unpack({ name, isStableRelease }, packagePath, version, system) {
194+
core.debug(`Extracting package at ${packagePath}`);
194195
let extractPath = await toolCache.extractTar(packagePath);
195-
core.debug("Package extracted");
196-
let cachedPath = await toolCache.cacheDir(path.join(extractPath, packageName), `swift-${system.name}`, version);
196+
if (isStableRelease) {
197+
extractPath = path.join(extractPath, name);
198+
}
199+
else {
200+
extractPath = path.join(extractPath, `${name}-ubuntu${system.version}`);
201+
}
202+
let cachedPath = await toolCache.cacheDir(path.join(extractPath), `swift-${system.name}`, version);
197203
core.debug("Package cached");
198204
return cachedPath;
199205
}
@@ -234,16 +240,15 @@ exports.install = void 0;
234240
const core = __importStar(__nccwpck_require__(2186));
235241
const toolCache = __importStar(__nccwpck_require__(7784));
236242
const path = __importStar(__nccwpck_require__(1017));
237-
const swift_versions_1 = __nccwpck_require__(8263);
238243
const get_version_1 = __nccwpck_require__(951);
239-
async function install(version, system) {
244+
async function install(version, getPackage) {
240245
const toolchainName = `swift ${version}`;
241246
const toolchain = await toolchainVersion(toolchainName);
242247
if (toolchain !== version) {
243248
let swiftPath = toolCache.find("swift-macOS", version);
244249
if (swiftPath === null || swiftPath.trim().length == 0) {
245250
core.debug(`No matching installation found`);
246-
const pkg = (0, swift_versions_1.swiftPackage)(version, system);
251+
const pkg = await getPackage();
247252
const path = await download(pkg);
248253
const extracted = await unpack(pkg, path, version);
249254
swiftPath = extracted;
@@ -272,11 +277,14 @@ async function download({ url }) {
272277
core.debug("Downloading swift for macOS");
273278
return toolCache.downloadTool(url);
274279
}
275-
async function unpack({ name }, packagePath, version) {
276-
core.debug("Extracting package");
280+
async function unpack({ name, isStableRelease }, packagePath, version) {
281+
core.debug(`Extracting package at ${packagePath}`);
277282
const unpackedPath = await toolCache.extractXar(packagePath);
278-
const extractedPath = await toolCache.extractTar(path.join(unpackedPath, `${name}-package.pkg`, "Payload"));
279-
core.debug("Package extracted");
283+
let tarPath = path.join(unpackedPath, `${name}-package.pkg`, "Payload");
284+
let extractedPath = await toolCache.extractTar(tarPath);
285+
if (!isStableRelease) {
286+
extractedPath = path.join(extractedPath, `${name}-osx`);
287+
}
280288
const cachedPath = await toolCache.cacheDir(extractedPath, "swift-macOS", version);
281289
core.debug("Package cached");
282290
return cachedPath;
@@ -322,27 +330,22 @@ const macos = __importStar(__nccwpck_require__(4713));
322330
const linux = __importStar(__nccwpck_require__(7419));
323331
const windows = __importStar(__nccwpck_require__(6414));
324332
const get_version_1 = __nccwpck_require__(951);
333+
const snapshot_package_1 = __nccwpck_require__(3671);
325334
async function run() {
326335
try {
327336
const requestedVersion = core.getInput("swift-version", { required: true });
328337
let platform = await system.getSystem();
329-
let version = versions.verify(requestedVersion, platform);
330-
switch (platform.os) {
331-
case system.OS.MacOS:
332-
await macos.install(version, platform);
333-
break;
334-
case system.OS.Ubuntu:
335-
await linux.install(version, platform);
336-
break;
337-
case system.OS.Windows:
338-
await windows.install(version, platform);
339-
}
340-
const current = await (0, get_version_1.getVersion)();
341-
if (current === version) {
342-
core.setOutput("version", version);
338+
try {
339+
let version = versions.verify(requestedVersion, platform);
340+
await install(version, platform, async () => versions.swiftPackage(version, platform));
343341
}
344-
else {
345-
core.error(`Failed to setup requested swift version. requestd: ${version}, actual: ${current}`);
342+
catch {
343+
const resolver = new snapshot_package_1.SnapshotPackageResolver(null);
344+
const pkg = await resolver.execute(requestedVersion, platform);
345+
if (!pkg) {
346+
throw new Error(`Couldn't form a package for requested version ${requestedVersion} on ${platform}`);
347+
}
348+
await install(pkg.version, platform, async () => pkg);
346349
}
347350
}
348351
catch (error) {
@@ -356,6 +359,25 @@ async function run() {
356359
core.setFailed(`Unexpected error, unable to continue. Please report at https://github.com/swift-actions/setup-swift/issues${os_1.EOL}${dump}`);
357360
}
358361
}
362+
async function install(version, platform, getPackage) {
363+
switch (platform.os) {
364+
case system.OS.MacOS:
365+
await macos.install(version, getPackage);
366+
break;
367+
case system.OS.Ubuntu:
368+
await linux.install(version, platform, getPackage);
369+
break;
370+
case system.OS.Windows:
371+
await windows.install(version, platform);
372+
}
373+
const current = await (0, get_version_1.getVersion)();
374+
if (current === version) {
375+
core.setOutput("version", version);
376+
}
377+
else {
378+
core.error(`Failed to setup requested swift version. requestd: ${version}, actual: ${current}`);
379+
}
380+
}
359381
run();
360382

361383

@@ -424,6 +446,130 @@ async function getSystem() {
424446
exports.getSystem = getSystem;
425447

426448

449+
/***/ }),
450+
451+
/***/ 3671:
452+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
453+
454+
"use strict";
455+
456+
Object.defineProperty(exports, "__esModule", ({ value: true }));
457+
exports.SnapshotPackageResolver = void 0;
458+
const os_1 = __nccwpck_require__(1855);
459+
class SnapshotPackageResolver {
460+
githubToken;
461+
limit = 100;
462+
constructor(githubToken) {
463+
this.githubToken =
464+
githubToken || process.env.API_GITHUB_ACCESS_TOKEN || null;
465+
}
466+
async execute(version, platform) {
467+
const snapshot = await this.getSnapshot(version);
468+
if (!snapshot) {
469+
return null;
470+
}
471+
const pkg = this.getPackage(snapshot, platform);
472+
return pkg;
473+
}
474+
async getSnapshot(version) {
475+
let index = version.indexOf("-");
476+
if (index === -1) {
477+
return null;
478+
}
479+
const branch = version.split("-")[0];
480+
index = version.indexOf("-", index + 1);
481+
if (index === -1) {
482+
const snapshot = await this.fetchSnapshot(branch);
483+
return snapshot;
484+
}
485+
const date = version.slice(index + 1, version.length);
486+
return { branch, date };
487+
}
488+
async fetchSnapshot(targetBranch) {
489+
let page = 0;
490+
while (true) {
491+
const tags = await this.getTags(page);
492+
for (const tag of tags) {
493+
const snapshot = this.parseSnapshot(tag);
494+
if (snapshot && snapshot.branch == targetBranch) {
495+
return snapshot;
496+
}
497+
}
498+
if (tags.length < this.limit) {
499+
return null;
500+
}
501+
page += 1;
502+
}
503+
}
504+
parseSnapshot(tag) {
505+
const matches = tag.name.match(/swift(?:-(\d+)\\.(\d+))?-DEVELOPMENT-SNAPSHOT-(\d{4}-\d{2}-\d{2})/);
506+
if (!matches) {
507+
return null;
508+
}
509+
if (matches[1] && matches[2]) {
510+
const major = matches[1];
511+
const minor = matches[2];
512+
return { branch: `${major}.${minor}`, date: matches[3] };
513+
}
514+
return { branch: "main", date: matches[3] };
515+
}
516+
async getTags(page) {
517+
const url = `https://api.github.com/repos/apple/swift/tags?per_page=${this.limit}&page=${page}`;
518+
let headers = {};
519+
if (this.githubToken) {
520+
headers = {
521+
Authorization: `Bearer ${this.githubToken}`,
522+
};
523+
}
524+
const response = await fetch(url, {
525+
headers: headers,
526+
});
527+
const json = await response.json();
528+
const tags = json.map((e) => {
529+
return { name: e.name };
530+
});
531+
return tags;
532+
}
533+
getPackage(snapshot, system) {
534+
const identifier = snapshot.branch === "main"
535+
? `swift-DEVELOPMENT-SNAPSHOT-${snapshot.date}-a`
536+
: `swift-${snapshot.branch}-DEVELOPMENT-SNAPSHOT-${snapshot.date}-a`;
537+
let platform;
538+
let archiveFile;
539+
switch (system.os) {
540+
case os_1.OS.MacOS:
541+
platform = "xcode";
542+
archiveFile = `${identifier}-osx.pkg`;
543+
break;
544+
case os_1.OS.Ubuntu:
545+
platform = `ubuntu${system.version.replace(/\D/g, "")}`;
546+
archiveFile = `${identifier}-ubuntu${system.version}.tar.gz`;
547+
break;
548+
default:
549+
throw new Error("Cannot create download URL for an unsupported platform");
550+
}
551+
let url = "https://swift.org/builds/";
552+
if (snapshot.branch === "main") {
553+
url += "development/";
554+
}
555+
else {
556+
url += `swift-${snapshot.branch}-branch/`;
557+
}
558+
url += `${platform}/`;
559+
url += `${identifier}/`;
560+
url += `${archiveFile}`;
561+
return {
562+
url: url,
563+
name: identifier,
564+
// TODO: Remove hardcodede version for main!
565+
version: snapshot.branch === "main" ? "6.0" : snapshot.branch,
566+
isStableRelease: false,
567+
};
568+
}
569+
}
570+
exports.SnapshotPackageResolver = SnapshotPackageResolver;
571+
572+
427573
/***/ }),
428574

429575
/***/ 8263:
@@ -557,6 +703,7 @@ function swiftPackage(version, system) {
557703
url: `https://swift.org/builds/swift-${version}-release/${platform}/swift-${version}-RELEASE/${archiveFile}`,
558704
name: archiveName,
559705
version: version,
706+
isStableRelease: true,
560707
};
561708
}
562709
exports.swiftPackage = swiftPackage;

0 commit comments

Comments
 (0)