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
11 changes: 5 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
- name: Setup npm pacakges
run: npm install --legacy-peer-deps
env:
SETUPSWIFT_SWIFTORG_METADATA: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.swiftorg != '' && format('{"commit":"{0}}"}', github.event.inputs.swiftorg) || github.event_name == 'workflow_dispatch' && github.event.inputs.swiftorg == 'true' || github.event_name == 'schedule' && '{"commit":"HEAD"}' }}
SETUPSWIFT_SWIFTORG_METADATA: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.swiftorg != '' && format('{{"commit":"{0}"}}', github.event.inputs.swiftorg) || github.event_name == 'workflow_dispatch' && github.event.inputs.swiftorg == 'true' || github.event_name == 'schedule' && '{"commit":"HEAD"}' }}

- name: Check latest swift.org
id: swift-org
Expand Down Expand Up @@ -159,6 +159,7 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
swift: ['latest']
development: [false, true]
check-link: [false]
include:
- os: macos-latest
swift: '5.0.0' # oldest
Expand All @@ -172,12 +173,14 @@ jobs:
- os: windows-latest
swift: '5.9' # 2nd installation approach
development: false
check-link: false
- os: ubuntu-latest
swift: '5.3.0' # oldest
development: false
- os: windows-latest
swift: '5.3' # 1st installation approach
development: false
check-link: true
- os: ubuntu-22.04
swift: ${{ fromJSON(vars.SETUPSWIFT_CUSTOM_TOOLCHAINS).ubuntu2204 }} # custom toolchain
development: true
Expand Down Expand Up @@ -227,7 +230,7 @@ jobs:
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1

- name: Check link
if: runner.os == 'Windows'
if: runner.os == 'Windows' && matrix.check-link == 'true'
run: which link | grep "Microsoft Visual Studio" || exit 1

- name: Install SDK
Expand Down Expand Up @@ -412,10 +415,6 @@ jobs:
- name: Verify Swift version
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1

- name: Check link
if: runner.os == 'Windows'
run: which link | grep "Microsoft Visual Studio" || exit 1

- name: Verify Swift SDKs
if: runner.os != 'Windows'
run: swift sdk list | grep ${{ steps.setup-swift.outputs.swift-version }}-RELEASE_static-linux || exit 1
Expand Down
266 changes: 244 additions & 22 deletions __tests__/installer/windows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,37 @@ describe('windows toolchain installation verification', () => {
jest.spyOn(fs, 'rename').mockResolvedValue()
jest.spyOn(core, 'getBooleanInput').mockReturnValue(false)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
const execSpy = 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']('x86_64')).resolves.toBe(
`${download}.exe`
)
expect(execSpy).not.toHaveBeenCalled()
expect(cacheSpy).not.toHaveBeenCalled()
})

it('tests download without caching with custom Visual Studio components', async () => {
const installer = new WindowsToolchainInstaller(toolchain)
expect(installer['version']).toStrictEqual(parseSemVer('5.8'))
expect(installer['baseUrl'].href).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, 'getInput').mockReturnValue(' ')
jest.spyOn(core, 'getBooleanInput').mockReturnValue(false)
jest.spyOn(exec, 'exec').mockResolvedValue(0)
const execSpy = jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: JSON.stringify([visualStudio]),
stderr: ''
Expand All @@ -192,6 +222,7 @@ describe('windows toolchain installation verification', () => {
await expect(installer['download']('x86_64')).resolves.toBe(
`${download}.exe`
)
expect(execSpy).toHaveBeenCalled()
expect(cacheSpy).not.toHaveBeenCalled()
})

Expand Down Expand Up @@ -265,6 +296,11 @@ describe('windows toolchain installation verification', () => {
expect(addPathSpy.mock.calls).toStrictEqual([['b'], ['c']])
expect(exportVariableSpy.mock.calls).toStrictEqual([['SDKROOT', 'root']])

jest.spyOn(exec, 'getExecOutput').mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
const setupSpy = jest
.spyOn(VisualStudio, 'setup')
.mockResolvedValue(visualStudio)
Expand Down Expand Up @@ -315,6 +351,11 @@ describe('windows toolchain installation verification', () => {
expect(addPathSpy.mock.calls).toStrictEqual([['b'], ['c']])
expect(exportVariableSpy.mock.calls).toStrictEqual([['SDKROOT', 'root']])

jest.spyOn(exec, 'getExecOutput').mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
const setupSpy = jest
.spyOn(VisualStudio, 'setup')
.mockResolvedValue(visualStudio)
Expand All @@ -336,11 +377,18 @@ describe('windows toolchain installation verification', () => {
.mockResolvedValue()
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
const toolPath = path.join(
installation,
'Developer',
Expand Down Expand Up @@ -387,11 +435,18 @@ describe('windows toolchain installation verification', () => {
})
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
const toolPath = path.join(
installation,
'Developer',
Expand Down Expand Up @@ -426,7 +481,9 @@ describe('windows toolchain installation verification', () => {
it('tests add to PATH without SDK copying', async () => {
const installer = new WindowsToolchainInstaller(toolchain)
const installation = path.resolve('tool', 'installed', 'path')
jest.spyOn(VisualStudio, 'setup').mockResolvedValue(visualStudio)
const vsSetupSpy = jest
.spyOn(VisualStudio, 'setup')
.mockResolvedValue(visualStudio)
jest
.spyOn(fs, 'access')
.mockRejectedValueOnce(new Error())
Expand All @@ -441,11 +498,168 @@ describe('windows toolchain installation verification', () => {
})
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest.spyOn(exec, 'getExecOutput').mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
const toolPath = path.join(
installation,
'Developer',
'Toolchains',
'unknown-Asserts-development.xctoolchain'
)
const sdkroot = path.join(
installation,
'Developer',
'Platforms',
'Windows.platform',
'Developer',
'SDKs',
'Windows.sdk'
)
const swiftLibs = path.join(sdkroot, 'usr', 'lib', 'swift')
const swiftPath = path.join(toolPath, 'usr', 'bin')
const swiftDev = path.join(installation, 'Swift-development', 'bin')
const icu67 = path.join(installation, 'icu-67', 'usr', 'bin')
await installer['add'](installation, 'x86_64')
expect(vsSetupSpy).toHaveBeenCalled()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
expect(process.env.PATH?.includes(swiftDev)).toBeTruthy()
expect(process.env.PATH?.includes(icu67)).toBeTruthy()
expect(process.env.SDKROOT).toBe(sdkroot)
expect(process.env.SWIFTFLAGS).toContain(`-sdk ${sdkroot}`)
expect(process.env.SWIFTFLAGS).toContain(`-I ${swiftLibs}`)
expect(process.env.SWIFTFLAGS).toContain(
`-L ${path.join(swiftLibs, 'windows')}`
)
})

it('tests add to PATH without SDK copying Swift 5.9.1', async () => {
const toolchain = {
name: 'Windows 10',
date: new Date('2023-10-19'),
download: 'swift-5.9-RELEASE-windows10.exe',
download_signature: 'swift-5.9.1-RELEASE-windows10.exe.sig',
dir: 'swift-5.9.1-RELEASE',
platform: 'windows10',
branch: 'swift-5.9.1-release',
windows: true,
preventCaching: false
}
const installer = new WindowsToolchainInstaller(toolchain)
const installation = path.resolve('tool', 'installed', 'path')
const vsSetupSpy = jest.spyOn(VisualStudio, 'setup')
jest
.spyOn(fs, 'access')
.mockRejectedValueOnce(new Error())
.mockImplementation(async p => {
if (
typeof p === 'string' &&
(p.endsWith('ucrt.modulemap') || p.endsWith('winsdk.modulemap'))
) {
return Promise.reject(new Error())
}
return Promise.resolve()
})
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.9.1',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest.spyOn(core, 'getBooleanInput').mockReturnValue(false)
const toolPath = path.join(
installation,
'Developer',
'Toolchains',
'unknown-Asserts-development.xctoolchain'
)
const sdkroot = path.join(
installation,
'Developer',
'Platforms',
'Windows.platform',
'Developer',
'SDKs',
'Windows.sdk'
)
const swiftLibs = path.join(sdkroot, 'usr', 'lib', 'swift')
const swiftPath = path.join(toolPath, 'usr', 'bin')
const swiftDev = path.join(installation, 'Swift-development', 'bin')
const icu67 = path.join(installation, 'icu-67', 'usr', 'bin')
await installer['add'](installation, 'x86_64')
expect(vsSetupSpy).not.toHaveBeenCalled()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
expect(process.env.PATH?.includes(swiftDev)).toBeTruthy()
expect(process.env.PATH?.includes(icu67)).toBeTruthy()
expect(process.env.SDKROOT).toBe(sdkroot)
expect(process.env.SWIFTFLAGS).toContain(`-sdk ${sdkroot}`)
expect(process.env.SWIFTFLAGS).toContain(`-I ${swiftLibs}`)
expect(process.env.SWIFTFLAGS).toContain(
`-L ${path.join(swiftLibs, 'windows')}`
)
})

it('tests add to PATH without SDK copying Swift 5.9.1 with Visual Studio linker', async () => {
const toolchain = {
name: 'Windows 10',
date: new Date('2023-10-19'),
download: 'swift-5.9-RELEASE-windows10.exe',
download_signature: 'swift-5.9.1-RELEASE-windows10.exe.sig',
dir: 'swift-5.9.1-RELEASE',
platform: 'windows10',
branch: 'swift-5.9.1-release',
windows: true,
preventCaching: false
}
const installer = new WindowsToolchainInstaller(toolchain)
const installation = path.resolve('tool', 'installed', 'path')
const vsSetupSpy = jest
.spyOn(VisualStudio, 'setup')
.mockResolvedValue(visualStudio)
jest
.spyOn(fs, 'access')
.mockRejectedValueOnce(new Error())
.mockImplementation(async p => {
if (
typeof p === 'string' &&
(p.endsWith('ucrt.modulemap') || p.endsWith('winsdk.modulemap'))
) {
return Promise.reject(new Error())
}
return Promise.resolve()
})
jest.spyOn(fs, 'copyFile').mockResolvedValue()
jest.spyOn(exec, 'exec').mockResolvedValue(0)
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.9.1',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest.spyOn(core, 'getBooleanInput').mockReturnValue(true)
const toolPath = path.join(
installation,
'Developer',
Expand All @@ -466,6 +680,7 @@ describe('windows toolchain installation verification', () => {
const swiftDev = path.join(installation, 'Swift-development', 'bin')
const icu67 = path.join(installation, 'icu-67', 'usr', 'bin')
await installer['add'](installation, 'x86_64')
expect(vsSetupSpy).toHaveBeenCalled()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
expect(process.env.PATH?.includes(swiftDev)).toBeTruthy()
expect(process.env.PATH?.includes(icu67)).toBeTruthy()
Expand Down Expand Up @@ -517,11 +732,18 @@ describe('windows toolchain installation verification', () => {
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,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
jest
.spyOn(exec, 'getExecOutput')
.mockResolvedValueOnce({
exitCode: 0,
stdout: 'Apple Swift version 5.8',
stderr: ''
})
.mockResolvedValue({
exitCode: 0,
stdout: vsEnvs.join(os.EOL),
stderr: ''
})
await installer.install('x86_64')
expect(setupSpy).toHaveBeenCalled()
expect(process.env.PATH?.includes(swiftPath)).toBeTruthy()
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ inputs:
i.e. Enable this option for installing static SDK: https://www.swift.org/documentation/articles/static-linux-getting-started.html
required: false
default: 'false'
prefer-visual-studio-linker:
description: >-
Whether to prefer using the Visual Studio linker over the default linker. This is unsafe and not recommended to set.
required: false
default: 'false'
sdks:
description: >-
Semi-colon separated list of Swift SDKs to install along with the main toolchain.
Expand Down
Loading
Loading