Skip to content

Commit f2a737f

Browse files
authored
fix: fixed Visual Studio setup with fallback windows installation (#314)
* chore: check link.exe * fix: fixed Visual Studio setup with fallback windows installation * ci: added Visual Studio validation to action
1 parent 32c3196 commit f2a737f

File tree

7 files changed

+125
-62
lines changed

7 files changed

+125
-62
lines changed

.github/workflows/main.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ jobs:
212212
- name: Verify Swift version
213213
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1
214214

215+
- name: Check link
216+
if: runner.os == 'Windows'
217+
run: which link | grep "Microsoft Visual Studio" || exit 1
218+
215219
dry-run:
216220
name: Check action with dry run
217221
if: needs.ci.outputs.run == 'true'
@@ -332,6 +336,10 @@ jobs:
332336
- name: Verify Swift version
333337
run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1
334338

339+
- name: Check link
340+
if: runner.os == 'Windows'
341+
run: which link | grep "Microsoft Visual Studio" || exit 1
342+
335343
- name: Test Swift package
336344
run: |
337345
swift package init --type library --name SetupLib

__tests__/installer/windows.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ describe('windows toolchain installation verification', () => {
154154
expect(exportVariableSpy).toHaveBeenCalledTimes(1)
155155
expect(addPathSpy.mock.calls).toStrictEqual([['b'], ['c']])
156156
expect(exportVariableSpy.mock.calls).toStrictEqual([['SDKROOT', 'root']])
157+
158+
const setupSpy = jest
159+
.spyOn(VisualStudio, 'setup')
160+
.mockResolvedValue(visualStudio)
161+
const updateSpy = jest
162+
.spyOn(VisualStudio.prototype, 'update')
163+
.mockResolvedValue()
164+
await installer['add']('')
165+
expect(setupSpy).toHaveBeenCalled()
166+
expect(updateSpy).toHaveBeenCalledWith('root')
157167
})
158168

159169
it('tests add to PATH', async () => {

dist/index.js

Lines changed: 49 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/installer/windows/index.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as semver from 'semver'
66
import {VerifyingToolchainInstaller} from '../verify'
77
import {WindowsToolchainSnapshot} from '../../snapshot'
88
import {VisualStudio} from '../../utils'
9-
import {Installation} from './installation'
9+
import {Installation, CustomInstallation} from './installation'
1010

1111
export class WindowsToolchainInstaller extends VerifyingToolchainInstaller<WindowsToolchainSnapshot> {
1212
private get vsRequirement() {
@@ -40,37 +40,47 @@ export class WindowsToolchainInstaller extends VerifyingToolchainInstaller<Windo
4040

4141
protected async unpack(exe: string) {
4242
const installation = await Installation.install(exe)
43-
return installation?.location ?? ''
43+
return installation instanceof Installation ? installation.location : ''
4444
}
4545

4646
protected async add(installLocation: string) {
4747
const installation = await Installation.get(installLocation)
48-
if (!installation) {
49-
return
50-
}
51-
const sdkroot = installation.sdkroot
52-
core.exportVariable('SDKROOT', sdkroot)
53-
if (installation.devdir) {
54-
core.exportVariable('DEVELOPER_DIR', installation.devdir)
55-
}
56-
const location = installation.location
57-
const swiftPath = path.join(installation.toolchain, 'usr', 'bin')
58-
const swiftDev = path.join(location, 'Swift-development', 'bin')
59-
const icu67 = path.join(location, 'icu-67', 'usr', 'bin')
60-
const tools = path.join(location, 'Tools')
61-
const runtimePath = path.join(installation.runtime, 'usr', 'bin')
62-
const requirePaths = [swiftPath, swiftDev, icu67, tools, runtimePath]
48+
const sdkrootKey = 'SDKROOT'
49+
let sdkroot: string | undefined
50+
if (installation instanceof Installation) {
51+
sdkroot = installation?.sdkroot ?? core
52+
core.exportVariable(sdkrootKey, sdkroot)
53+
if (installation.devdir) {
54+
core.exportVariable('DEVELOPER_DIR', installation.devdir)
55+
}
56+
57+
const location = installation.location
58+
const swiftPath = path.join(installation.toolchain, 'usr', 'bin')
59+
const swiftDev = path.join(location, 'Swift-development', 'bin')
60+
const icu67 = path.join(location, 'icu-67', 'usr', 'bin')
61+
const tools = path.join(location, 'Tools')
62+
const runtimePath = path.join(installation.runtime, 'usr', 'bin')
63+
const requirePaths = [swiftPath, swiftDev, icu67, tools, runtimePath]
6364

64-
for (const envPath of requirePaths) {
65-
try {
66-
await fs.access(envPath)
67-
core.debug(`Adding "${envPath}" to PATH`)
68-
core.addPath(envPath)
69-
} catch {
70-
core.debug(`"${envPath}" doesn't exist. Skip adding to PATH`)
65+
for (const envPath of requirePaths) {
66+
try {
67+
await fs.access(envPath)
68+
core.debug(`Adding "${envPath}" to PATH`)
69+
core.addPath(envPath)
70+
} catch {
71+
core.debug(`"${envPath}" doesn't exist. Skip adding to PATH`)
72+
}
7173
}
74+
core.debug(`Swift installed at "${swiftPath}"`)
75+
} else if (installation instanceof CustomInstallation) {
76+
sdkroot = installation.variables[sdkrootKey]
7277
}
73-
core.debug(`Swift installed at "${swiftPath}"`)
78+
79+
if (!sdkroot) {
80+
core.warning(`Failed VS enviroment after installation ${installLocation}`)
81+
return
82+
}
83+
7484
const visualStudio = await VisualStudio.setup(this.vsRequirement)
7585
await visualStudio.update(sdkroot)
7686
const swiftFlags = [

src/installer/windows/installation/base.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export class Installation {
77
readonly devdir?: string
88
) {}
99
}
10+
11+
export class CustomInstallation {
12+
constructor(
13+
readonly newPaths: string[],
14+
readonly variables: Record<string, string>
15+
) {}
16+
}

src/installer/windows/installation/fallback.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as path from 'path'
22
import * as core from '@actions/core'
33
import {getExecOutput} from '@actions/exec'
4+
import {CustomInstallation} from './base'
45

56
function comapareEnvironment(oldJSON: string, newJSON: string) {
67
const difference: Record<string, string> = {}
@@ -65,4 +66,5 @@ export async function fallback(
6566
for (const pair of Object.entries(data.variables)) {
6667
core.exportVariable(pair[0], pair[1])
6768
}
69+
return new CustomInstallation(data.newPaths, data.variables)
6870
}

src/installer/windows/installation/index.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
import * as core from '@actions/core'
22
import {exec} from '@actions/exec'
3-
import {Installation} from './base'
3+
import {Installation, CustomInstallation} from './base'
44
import {firstDirectoryLayout, secondDirectoryLayout} from './approach'
55
import {env, fallback} from './fallback'
66

77
declare module './base' {
88
// eslint-disable-next-line no-shadow, @typescript-eslint/no-namespace
99
export namespace Installation {
10-
export function get(install?: string): Promise<Installation | undefined>
11-
export function install(exe: string): Promise<Installation | undefined>
10+
export function get(
11+
install?: string
12+
): Promise<Installation | CustomInstallation | undefined>
13+
export function install(
14+
exe: string
15+
): Promise<Installation | CustomInstallation>
1216
export function detect(
1317
oldEnv: Record<string, string>,
1418
newEnv: Record<string, string>
15-
): Promise<Installation | undefined>
19+
): Promise<Installation | CustomInstallation>
1620
}
1721
}
1822

1923
Installation.get = async (install?: string) => {
2024
if (!(install?.length ?? 1)) {
21-
return undefined
25+
return lastInstallation
2226
}
27+
2328
const approaches = [
2429
async () => secondDirectoryLayout(install),
2530
async () => firstDirectoryLayout(install)
@@ -42,12 +47,14 @@ Installation.get = async (install?: string) => {
4247
return undefined
4348
}
4449

50+
let lastInstallation: Installation | CustomInstallation
4551
Installation.install = async (exe: string) => {
4652
core.debug(`Installing toolchain from "${exe}"`)
4753
const oldEnv = await env()
4854
await exec(`"${exe}"`, ['-q'])
4955
const newEnv = await env()
50-
return Installation.detect(oldEnv, newEnv)
56+
lastInstallation = await Installation.detect(oldEnv, newEnv)
57+
return lastInstallation
5158
}
5259

5360
Installation.detect = async (
@@ -56,7 +63,7 @@ Installation.detect = async (
5663
) => {
5764
const installation = await Installation.get()
5865
if (!installation) {
59-
fallback(oldEnv, newEnv)
66+
return fallback(oldEnv, newEnv)
6067
}
6168
return installation
6269
}

0 commit comments

Comments
 (0)