Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions __tests__/installer/linux.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,15 @@ describe('linux toolchain installation verification', () => {
const cached = path.resolve('tool', 'cached', 'path')
const swiftPath = path.join(cached, 'usr', 'bin')
jest.spyOn(toolCache, 'find').mockReturnValue(cached)
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue(cached)
jest.spyOn(cache, 'saveCache').mockResolvedValue(1)
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
const downloadSpy = jest.spyOn(toolCache, 'downloadTool')
const extractSpy = jest.spyOn(toolCache, 'extractTar')
const cacheSpy = jest.spyOn(toolCache, 'cacheDir')
jest.spyOn(exec, 'exec').mockResolvedValue(0)
await installer.install()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
for (const spy of [downloadSpy, extractSpy, cacheSpy]) {
for (const spy of [downloadSpy, extractSpy]) {
expect(spy).not.toHaveBeenCalled()
}
})
Expand Down
29 changes: 3 additions & 26 deletions __tests__/installer/windows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,32 +58,6 @@ describe('windows toolchain installation verification', () => {
])
})

it('tests download with caching', async () => {
const installer = new WindowsToolchainInstaller(toolchain)
expect(installer['version']).toStrictEqual(parseSemVer('5.8'))
expect(installer['baseUrl']).toBe(
'https://download.swift.org/swift-5.8-release/windows10/swift-5.8-RELEASE'
)

const download = path.resolve('tool', 'download', 'path')
process.env.VSWHERE_PATH = path.join('C:', 'Visual Studio')
jest.spyOn(fs, 'access').mockResolvedValue()
jest.spyOn(fs, 'rename').mockResolvedValue()
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: JSON.stringify([visualStudio]),
stderr: ''
})
jest.spyOn(cache, 'restoreCache').mockResolvedValue(undefined)
const cacheSpy = jest.spyOn(cache, 'saveCache').mockResolvedValue(1)
jest.spyOn(toolCache, 'downloadTool').mockResolvedValue(download)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
await expect(installer['download']()).resolves.toBe(`${download}.exe`)
expect(cacheSpy).toHaveBeenCalled()
})

it('tests download without caching', async () => {
const installer = new WindowsToolchainInstaller(toolchain)
expect(installer['version']).toStrictEqual(parseSemVer('5.8'))
Expand Down Expand Up @@ -204,7 +178,10 @@ describe('windows toolchain installation verification', () => {
.mockResolvedValue(visualStudio)
jest.spyOn(fs, 'access').mockRejectedValue(new Error())
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
jest.spyOn(toolCache, 'find').mockReturnValue(cached)
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue(cached)
jest.spyOn(cache, 'saveCache').mockResolvedValue(1)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
Expand Down
23 changes: 17 additions & 6 deletions __tests__/installer/xcode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as toolCache from '@actions/tool-cache'
import {coerce as parseSemVer} from 'semver'
import * as plist from 'plist'
import {XcodeToolchainInstaller} from '../../src/installer/xcode'
import {NoInstallationNeededError} from '../../src/installer/base'

jest.mock('plist')

Expand Down Expand Up @@ -43,9 +42,14 @@ describe('macOS toolchain installation verification', () => {
stdout: `swift-driver version: 1.75.2 Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)\nTarget: arm64-apple-macosx13.0`,
stderr: ''
})
await expect(installer['download']()).rejects.toMatchObject(
new NoInstallationNeededError('Bundled with xcode')
)
const installationNeededSpy = jest.spyOn(installer, 'isInstallationNeeded')
const downloadSpy = jest.spyOn(toolCache, 'downloadTool')
const extractSpy = jest.spyOn(toolCache, 'extractXar')
await installer.install()
for (const spy of [downloadSpy, extractSpy]) {
expect(spy).not.toHaveBeenCalled()
}
expect(installationNeededSpy).toHaveBeenCalled()
expect(process.env.DEVELOPER_DIR).toBe(toolchain.xcodePath)
})

Expand Down Expand Up @@ -131,18 +135,25 @@ describe('macOS toolchain installation verification', () => {
const identifier = 'org.swift.581202305171a'
jest.spyOn(toolCache, 'find').mockReturnValue(cached)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(cache, 'saveCache').mockResolvedValue(1)
const downloadSpy = jest.spyOn(toolCache, 'downloadTool')
const extractSpy = jest.spyOn(toolCache, 'extractXar')
const deploySpy = jest.spyOn(toolCache, 'extractTar')
const cacheSpy = jest.spyOn(toolCache, 'cacheDir')
jest.spyOn(toolCache, 'cacheDir').mockResolvedValue(cached)
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: `Apple Swift version 5.9-dev (LLVM fd38736063c15cd, Swift a533c63d783f5b8)\nTarget: arm64-apple-macosx13.0`,
stderr: ''
})
jest.spyOn(fs, 'access').mockResolvedValue()
jest.spyOn(fs, 'readFile').mockResolvedValue('')
jest.spyOn(plist, 'parse').mockReturnValue({CFBundleIdentifier: identifier})
await installer.install()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
expect(process.env.TOOLCHAINS).toBe(identifier)
for (const spy of [downloadSpy, extractSpy, deploySpy, cacheSpy]) {
for (const spy of [downloadSpy, extractSpy, deploySpy]) {
expect(spy).not.toHaveBeenCalled()
}
})
Expand Down
79 changes: 39 additions & 40 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 28 additions & 34 deletions src/installer/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export type SnapshotForInstaller<Installer> =
? Snapshot
: never

export class NoInstallationNeededError extends Error {}

export abstract class ToolchainInstaller<Snapshot extends ToolchainSnapshot> {
constructor(readonly data: Snapshot) {}

Expand All @@ -34,45 +32,41 @@ export abstract class ToolchainInstaller<Snapshot extends ToolchainSnapshot> {
}

async install(arch?: string) {
try {
const key = `${this.data.branch}-${this.data.platform}`
const version = this.version?.raw
let tool: string | undefined
if (version) {
tool = toolCache.find(key, version, arch).trim()
}
if (!tool?.length) {
const key = `${this.data.dir}-${this.data.platform}`
const version = this.version?.raw
let tool: string | undefined
if (version) {
tool = toolCache.find(key, version, arch).trim()
core.debug(`Found tool at "${tool}" in tool cache`)
}
if (!tool?.length) {
const tmpDir = process.env.RUNNER_TEMP || os.tmpdir()
const restore = path.join(tmpDir, 'setup-swift', key)
if (await cache.restoreCache([restore], key)) {
core.debug(`Restored snapshot at "${restore}" from key "${key}"`)
tool = restore
} else {
const resource = await this.download()
const installation = await this.unpack(resource)
if (version) {
tool = await toolCache.cacheDir(installation, key, version, arch)
} else {
core.debug('Proceeding without caching non-versioned snapshot')
tool = installation
}
}
await this.add(tool)
} catch (error) {
if (!(error instanceof NoInstallationNeededError)) {
throw error
core.debug(`Downloaded and installed snapshot at "${installation}"`)
tool = installation
}
}
if (version) {
tool = await toolCache.cacheDir(tool, key, version, arch)
core.debug(`Added to tool cache at "${tool}"`)
}
if (core.getBooleanInput('cache-snapshot')) {
await cache.saveCache([tool], key)
core.debug(`Saved to cache with key "${key}"`)
}
await this.add(tool)
}

protected async download() {
const tmpDir = process.env.RUNNER_TEMP || os.tmpdir()
let resourcePath = path.join(tmpDir, 'setup-swift', this.data.download)
if (!(await cache.restoreCache([resourcePath], this.data.download))) {
const url = `${this.baseUrl}/${this.data.download}`
core.debug(`Downloading snapshot from "${url}"`)
resourcePath = await toolCache.downloadTool(url)
if (core.getBooleanInput('cache-snapshot')) {
await cache.saveCache([resourcePath], this.data.download)
}
} else {
core.debug(`Picked snapshot from cache key "${this.data.download}"`)
}
return resourcePath
const url = `${this.baseUrl}/${this.data.download}`
core.debug(`Downloading snapshot from "${url}"`)
return await toolCache.downloadTool(url)
}

protected abstract unpack(resource: string): Promise<string>
Expand Down
10 changes: 7 additions & 3 deletions src/installer/xcode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as core from '@actions/core'
import {exec} from '@actions/exec'
import * as toolCache from '@actions/tool-cache'
import * as plist from 'plist'
import {ToolchainInstaller, NoInstallationNeededError} from './base'
import {ToolchainInstaller} from './base'
import {XcodeToolchainSnapshot} from '../snapshot'

export class XcodeToolchainInstaller extends ToolchainInstaller<XcodeToolchainSnapshot> {
Expand Down Expand Up @@ -41,10 +41,14 @@ export class XcodeToolchainInstaller extends ToolchainInstaller<XcodeToolchainSn
return this.data.dir !== `swift-${version}-RELEASE`
}

protected async download() {
async install(arch?: string | undefined) {
if (!(await this.isInstallationNeeded())) {
throw new NoInstallationNeededError('Bundled with xcode')
return
}
await super.install(arch)
}

protected async download() {
const toolchain = await super.download()
core.debug(`Checking package signature for "${toolchain}"`)
await exec('pkgutil', ['--check-signature', toolchain])
Expand Down