Skip to content

Commit a01e266

Browse files
committed
feat: added optional visual studio components option
1 parent 68f6906 commit a01e266

File tree

26 files changed

+798
-484
lines changed

26 files changed

+798
-484
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ jobs:
248248
uses: addnab/docker-run-action@v3
249249
with:
250250
image: swift:${{ fromJSON(steps.setup-swift.outputs.toolchain).docker }}
251-
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1
251+
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1
252252

253253
dependabot:
254254
name: Check dependabot PR raised updating swiftorg

__tests__/installer/windows.test.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as cache from '@actions/cache'
77
import * as toolCache from '@actions/tool-cache'
88
import {coerce as parseSemVer} from 'semver'
99
import {WindowsToolchainInstaller} from '../../src/installer/windows'
10-
import * as vs from '../../src/utils/visual_studio'
10+
import {VisualStudio} from '../../src/utils/visual_studio'
1111

1212
describe('windows toolchain installation verification', () => {
1313
const env = process.env
@@ -21,14 +21,14 @@ describe('windows toolchain installation verification', () => {
2121
branch: 'swift-5.8-release',
2222
windows: true
2323
}
24-
const visualStudio: vs.VisualStudio = {
24+
const visualStudio = VisualStudio.createFromJSON({
2525
installationPath: path.join('C:', 'Visual Studio'),
2626
installationVersion: '16',
2727
catalog: {productDisplayVersion: '16'},
2828
properties: {
2929
setupEngineFilePath: path.join('C:', 'Visual Studio', 'setup.exe')
3030
}
31-
}
31+
})
3232
const vsEnvs = [
3333
`UniversalCRTSdkDir=${path.join('C:', 'Windows Kits')}`,
3434
`UCRTVersion=10.0.17063`,
@@ -44,6 +44,20 @@ describe('windows toolchain installation verification', () => {
4444
process.env = env
4545
})
4646

47+
it('tests adding additional components', async () => {
48+
jest
49+
.spyOn(core, 'getInput')
50+
.mockReturnValue(
51+
'Microsoft.VisualStudio.Component.VC.ATL;Microsoft.VisualStudio.Component.VC.CMake.Project;Microsoft.VisualStudio.Component.Windows10SDK'
52+
)
53+
const installer = new WindowsToolchainInstaller(toolchain)
54+
expect(installer['vsRequirement'].components.slice(2)).toStrictEqual([
55+
'Microsoft.VisualStudio.Component.VC.ATL',
56+
'Microsoft.VisualStudio.Component.VC.CMake.Project',
57+
'Microsoft.VisualStudio.Component.Windows10SDK'
58+
])
59+
})
60+
4761
it('tests download with caching', async () => {
4862
const installer = new WindowsToolchainInstaller(toolchain)
4963
expect(installer['version']).toStrictEqual(parseSemVer('5.8'))
@@ -130,7 +144,7 @@ describe('windows toolchain installation verification', () => {
130144
it('tests add to PATH', async () => {
131145
const installer = new WindowsToolchainInstaller(toolchain)
132146
const installation = path.resolve('tool', 'installed', 'path')
133-
jest.spyOn(vs, 'setupVisualStudioTools').mockResolvedValue(visualStudio)
147+
jest.spyOn(VisualStudio, 'setup').mockResolvedValue(visualStudio)
134148
jest.spyOn(fs, 'access').mockRejectedValue(new Error())
135149
jest.spyOn(fs, 'copyFile').mockResolvedValue()
136150
jest.spyOn(exec, 'exec').mockResolvedValue(0)
@@ -186,7 +200,7 @@ describe('windows toolchain installation verification', () => {
186200
const swiftDev = path.join(cached, 'Swift-development', 'bin')
187201
const icu67 = path.join(cached, 'icu-67', 'usr', 'bin')
188202
const setupSpy = jest
189-
.spyOn(vs, 'setupVisualStudioTools')
203+
.spyOn(VisualStudio, 'setup')
190204
.mockResolvedValue(visualStudio)
191205
jest.spyOn(fs, 'access').mockRejectedValue(new Error())
192206
jest.spyOn(fs, 'copyFile').mockResolvedValue()

__tests__/main.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import * as core from '@actions/core'
2+
import * as main from '../src/main'
3+
import {Swiftorg} from '../src/swiftorg'
4+
import {Platform} from '../src/platform'
5+
import {LinuxToolchainInstaller} from '../src/installer'
6+
7+
describe('setup-swift run validation', () => {
8+
const swiftorgSpy = jest
9+
.spyOn(Swiftorg.prototype, 'update')
10+
.mockResolvedValue()
11+
const toolchainSpy = jest.spyOn(Platform, 'toolchain')
12+
const installSpy = jest.spyOn(Platform, 'install')
13+
const outputSpy = jest.spyOn(core, 'setOutput')
14+
const failedSpy = jest.spyOn(core, 'setFailed')
15+
const toolchain = {
16+
name: 'Ubuntu 22.04',
17+
date: new Date('2023-03-30 10:28:49.000000000 -05:00'),
18+
download: 'swift-5.8-RELEASE-ubuntu22.04.tar.gz',
19+
download_signature: 'swift-5.8-RELEASE-ubuntu22.04.tar.gz.sig',
20+
dir: 'swift-5.8-RELEASE',
21+
docker: '5.8-jammy',
22+
platform: 'ubuntu2204',
23+
branch: 'swift-5.8-release'
24+
}
25+
26+
it('tests dry run', async () => {
27+
toolchainSpy.mockResolvedValue(toolchain)
28+
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
29+
jest.spyOn(core, 'getInput').mockReturnValue('latest')
30+
await main.run()
31+
expect(failedSpy).not.toHaveBeenCalled()
32+
expect(installSpy).not.toHaveBeenCalled()
33+
for (const spy of [swiftorgSpy, toolchainSpy, outputSpy]) {
34+
expect(spy).toHaveBeenCalled()
35+
}
36+
for (const call of outputSpy.mock.calls) {
37+
switch (call[0]) {
38+
case 'swift-version':
39+
expect(call.slice(1)).toStrictEqual(['5.8'])
40+
break
41+
case 'toolchain': {
42+
const obj = JSON.parse(call[1])
43+
obj.date = new Date(obj.date)
44+
expect(obj).toStrictEqual(toolchain)
45+
break
46+
}
47+
}
48+
}
49+
})
50+
51+
it('tests install', async () => {
52+
const installer = new LinuxToolchainInstaller(toolchain)
53+
installSpy.mockResolvedValue(installer)
54+
jest.spyOn(core, 'getBooleanInput').mockReturnValue(false)
55+
jest.spyOn(core, 'getInput').mockReturnValue('latest')
56+
jest.spyOn(installer, 'installedSwiftVersion').mockResolvedValue('5.8')
57+
await main.run()
58+
expect(failedSpy).not.toHaveBeenCalled()
59+
expect(toolchainSpy).not.toHaveBeenCalled()
60+
for (const spy of [swiftorgSpy, installSpy, outputSpy]) {
61+
expect(spy).toHaveBeenCalled()
62+
}
63+
for (const call of outputSpy.mock.calls) {
64+
switch (call[0]) {
65+
case 'swift-version':
66+
expect(call.slice(1)).toStrictEqual(['5.8'])
67+
break
68+
case 'toolchain': {
69+
const obj = JSON.parse(call[1])
70+
obj.date = new Date(obj.date)
71+
expect(obj).toStrictEqual(toolchain)
72+
break
73+
}
74+
}
75+
}
76+
})
77+
78+
it('tests failure', async () => {
79+
toolchainSpy.mockResolvedValue(undefined)
80+
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
81+
jest.spyOn(core, 'getInput').mockReturnValue('latest')
82+
await main.run()
83+
expect(failedSpy).toHaveBeenCalled()
84+
for (const spy of [installSpy, outputSpy]) {
85+
expect(spy).not.toHaveBeenCalled()
86+
}
87+
})
88+
})

__tests__/swiftorg.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@ import {MODULE_DIR} from '../src/const'
55

66
describe('swiftorg sync validation', () => {
77
const accessSpy = jest.spyOn(fs, 'access')
8-
const rmdirSpy = jest.spyOn(fs, 'rmdir')
8+
const rmdirSpy = jest.spyOn(fs, 'rmdir').mockResolvedValue()
99
const execSpy = jest.spyOn(exec, 'exec')
1010

11-
beforeEach(() => {
12-
rmdirSpy.mockResolvedValue()
13-
})
14-
1511
it('tests latest sync', async () => {
1612
accessSpy.mockResolvedValue()
1713
execSpy.mockResolvedValue(0)

__tests__/utils/visual_studio.test.ts

Lines changed: 0 additions & 101 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as path from 'path'
2+
import {promises as fs} from 'fs'
3+
import * as exec from '@actions/exec'
4+
import {VisualStudio} from '../../../src/utils/visual_studio'
5+
6+
describe('visual studio setup validation', () => {
7+
const env = process.env
8+
const visualStudio = VisualStudio.createFromJSON({
9+
installationPath: path.join('C:', 'Visual Studio'),
10+
installationVersion: '16',
11+
catalog: {productDisplayVersion: '16'},
12+
properties: {
13+
setupEngineFilePath: path.join('C:', 'Visual Studio', 'setup.exe')
14+
}
15+
})
16+
17+
beforeEach(() => {
18+
process.env = {...env}
19+
})
20+
21+
afterEach(() => {
22+
jest.restoreAllMocks()
23+
process.env = env
24+
})
25+
26+
it('tests visual studio setup fails when invalid path', async () => {
27+
process.env.VSWHERE_PATH = path.join('C:', 'Visual Studio')
28+
jest.spyOn(fs, 'access').mockResolvedValue()
29+
jest.spyOn(exec, 'exec').mockResolvedValue(-1)
30+
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
31+
exitCode: 0,
32+
stdout: JSON.stringify([{...visualStudio, installationPath: ''}]),
33+
stderr: ''
34+
})
35+
await expect(
36+
VisualStudio.setup({version: '16', components: ['Component']})
37+
).rejects.toMatchObject(
38+
new Error(
39+
`Unable to find any Visual Studio installation for version: 16.`
40+
)
41+
)
42+
})
43+
44+
it('tests visual studio setup successfully', async () => {
45+
process.env.VSWHERE_PATH = path.join('C:', 'Visual Studio')
46+
jest.spyOn(fs, 'access').mockResolvedValue()
47+
jest.spyOn(exec, 'exec').mockResolvedValue(0)
48+
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
49+
exitCode: 0,
50+
stdout: JSON.stringify([visualStudio]),
51+
stderr: ''
52+
})
53+
await expect(
54+
VisualStudio.setup({version: '16', components: ['Component']})
55+
).resolves.toMatchObject(visualStudio)
56+
})
57+
})
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as path from 'path'
2+
import {promises as fs} from 'fs'
3+
import * as io from '@actions/io'
4+
import {VSWhere} from '../../../src/utils/visual_studio/vswhere'
5+
6+
describe('vswhere find validation', () => {
7+
const env = process.env
8+
9+
beforeEach(() => {
10+
process.env = {...env}
11+
})
12+
13+
afterEach(() => {
14+
jest.restoreAllMocks()
15+
process.env = env
16+
})
17+
18+
it('tests vswhere path from environment variable', async () => {
19+
jest.spyOn(fs, 'access').mockResolvedValue()
20+
const vswhereExe = path.join('C:', 'Visual Studio', 'vswhere.exe')
21+
process.env.VSWHERE_PATH = path.join('C:', 'Visual Studio')
22+
expect(await VSWhere.get()).toBe(vswhereExe)
23+
})
24+
25+
it('tests vswhere path from @actions/io', async () => {
26+
jest.spyOn(fs, 'access').mockResolvedValue()
27+
const vswhereExe = path.join('C:', 'Visual Studio', 'vswhere.exe')
28+
jest.spyOn(io, 'which').mockResolvedValue(vswhereExe)
29+
expect(await VSWhere.get()).toBe(vswhereExe)
30+
})
31+
32+
it('tests fallback vswhere path', async () => {
33+
jest.spyOn(fs, 'access').mockResolvedValue()
34+
const vswhereExe = path.join(
35+
'C:',
36+
'Program Files (x86)',
37+
'Microsoft Visual Studio',
38+
'Installer',
39+
'vswhere.exe'
40+
)
41+
process.env['ProgramFiles(x86)'] = path.join('C:', 'Program Files (x86)')
42+
expect(await VSWhere.get()).toBe(vswhereExe)
43+
})
44+
45+
it('tests vswhere missing', async () => {
46+
jest.spyOn(fs, 'access').mockRejectedValue(new Error())
47+
await expect(VSWhere.get()).rejects.toMatchObject(
48+
new Error('Missing vswhere.exe, needed Visual Studio installation')
49+
)
50+
})
51+
})

0 commit comments

Comments
 (0)