Skip to content

Commit 57d797e

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

File tree

7 files changed

+450
-56
lines changed

7 files changed

+450
-56
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

+162-23
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,8 @@ 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);
159158
const gpg_1 = __nccwpck_require__(9060);
160-
async function install(version, system) {
159+
async function install(version, system, getPackage) {
161160
if (os.platform() !== "linux") {
162161
core.error("Trying to run linux installer on non-linux os");
163162
return;
@@ -166,7 +165,7 @@ async function install(version, system) {
166165
if (swiftPath === null || swiftPath.trim().length == 0) {
167166
core.debug(`No matching installation found`);
168167
await (0, gpg_1.setupKeys)();
169-
const swiftPkg = (0, swift_versions_1.swiftPackage)(version, system);
168+
const swiftPkg = await getPackage();
170169
let { pkg, signature } = await download(swiftPkg);
171170
await (0, gpg_1.verify)(signature, pkg);
172171
swiftPath = await unpack(pkg, swiftPkg.name, version, system);
@@ -190,6 +189,7 @@ async function download({ url, name }) {
190189
return { pkg, signature, name };
191190
}
192191
async function unpack(packagePath, packageName, version, system) {
192+
console.log(process.cwd());
193193
core.debug("Extracting package");
194194
let extractPath = await toolCache.extractTar(packagePath);
195195
core.debug("Package extracted");
@@ -234,16 +234,15 @@ exports.install = void 0;
234234
const core = __importStar(__nccwpck_require__(2186));
235235
const toolCache = __importStar(__nccwpck_require__(7784));
236236
const path = __importStar(__nccwpck_require__(1017));
237-
const swift_versions_1 = __nccwpck_require__(8263);
238237
const get_version_1 = __nccwpck_require__(951);
239-
async function install(version, system) {
238+
async function install(version, getPackage) {
240239
const toolchainName = `swift ${version}`;
241240
const toolchain = await toolchainVersion(toolchainName);
242241
if (toolchain !== version) {
243242
let swiftPath = toolCache.find("swift-macOS", version);
244243
if (swiftPath === null || swiftPath.trim().length == 0) {
245244
core.debug(`No matching installation found`);
246-
const pkg = (0, swift_versions_1.swiftPackage)(version, system);
245+
const pkg = await getPackage();
247246
const path = await download(pkg);
248247
const extracted = await unpack(pkg, path, version);
249248
swiftPath = extracted;
@@ -275,7 +274,8 @@ async function download({ url }) {
275274
async function unpack({ name }, packagePath, version) {
276275
core.debug("Extracting package");
277276
const unpackedPath = await toolCache.extractXar(packagePath);
278-
const extractedPath = await toolCache.extractTar(path.join(unpackedPath, `${name}-package.pkg`, "Payload"));
277+
let tarPath = path.join(unpackedPath, `${name}-package.pkg`, "Payload");
278+
const extractedPath = await toolCache.extractTar(tarPath);
279279
core.debug("Package extracted");
280280
const cachedPath = await toolCache.cacheDir(extractedPath, "swift-macOS", version);
281281
core.debug("Package cached");
@@ -322,27 +322,22 @@ const macos = __importStar(__nccwpck_require__(4713));
322322
const linux = __importStar(__nccwpck_require__(7419));
323323
const windows = __importStar(__nccwpck_require__(6414));
324324
const get_version_1 = __nccwpck_require__(951);
325+
const snapshot_package_1 = __nccwpck_require__(3671);
325326
async function run() {
326327
try {
327328
const requestedVersion = core.getInput("swift-version", { required: true });
328329
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);
330+
try {
331+
let version = versions.verify(requestedVersion, platform);
332+
await install(version, platform, async () => versions.swiftPackage(version, platform));
343333
}
344-
else {
345-
core.error(`Failed to setup requested swift version. requestd: ${version}, actual: ${current}`);
334+
catch {
335+
const resolver = new snapshot_package_1.SnapshotPackageResolver(null);
336+
const pkg = await resolver.execute(requestedVersion, platform);
337+
if (!pkg) {
338+
throw new Error(`Couldn't form a package for requested version ${requestedVersion} on ${platform}`);
339+
}
340+
await install(pkg.version, platform, async () => pkg);
346341
}
347342
}
348343
catch (error) {
@@ -356,6 +351,25 @@ async function run() {
356351
core.setFailed(`Unexpected error, unable to continue. Please report at https://github.com/swift-actions/setup-swift/issues${os_1.EOL}${dump}`);
357352
}
358353
}
354+
async function install(version, platform, getPackage) {
355+
switch (platform.os) {
356+
case system.OS.MacOS:
357+
await macos.install(version, getPackage);
358+
break;
359+
case system.OS.Ubuntu:
360+
await linux.install(version, platform, getPackage);
361+
break;
362+
case system.OS.Windows:
363+
await windows.install(version, platform);
364+
}
365+
const current = await (0, get_version_1.getVersion)();
366+
if (current === version) {
367+
core.setOutput("version", version);
368+
}
369+
else {
370+
core.error(`Failed to setup requested swift version. requestd: ${version}, actual: ${current}`);
371+
}
372+
}
359373
run();
360374

361375

@@ -424,6 +438,130 @@ async function getSystem() {
424438
exports.getSystem = getSystem;
425439

426440

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

429567
/***/ 8263:
@@ -557,6 +695,7 @@ function swiftPackage(version, system) {
557695
url: `https://swift.org/builds/swift-${version}-release/${platform}/swift-${version}-RELEASE/${archiveFile}`,
558696
name: archiveName,
559697
version: version,
698+
isStableRelease: true,
560699
};
561700
}
562701
exports.swiftPackage = swiftPackage;

src/linux-install.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import * as os from "os";
22
import * as path from "path";
3-
import { exec } from "@actions/exec";
43
import * as core from "@actions/core";
54
import * as toolCache from "@actions/tool-cache";
65
import { System } from "./os";
7-
import { swiftPackage, Package } from "./swift-versions";
6+
import { Package } from "./swift-versions";
87
import { setupKeys, verify } from "./gpg";
98

10-
export async function install(version: string, system: System) {
9+
export async function install(
10+
version: string,
11+
system: System,
12+
getPackage: () => Promise<Package>
13+
) {
1114
if (os.platform() !== "linux") {
1215
core.error("Trying to run linux installer on non-linux os");
1316
return;
@@ -20,7 +23,7 @@ export async function install(version: string, system: System) {
2023

2124
await setupKeys();
2225

23-
const swiftPkg = swiftPackage(version, system);
26+
const swiftPkg = await getPackage();
2427
let { pkg, signature } = await download(swiftPkg);
2528

2629
await verify(signature, pkg);
@@ -56,6 +59,7 @@ async function unpack(
5659
version: string,
5760
system: System
5861
) {
62+
console.log(process.cwd());
5963
core.debug("Extracting package");
6064
let extractPath = await toolCache.extractTar(packagePath);
6165
core.debug("Package extracted");

0 commit comments

Comments
 (0)