From be90dff3dbdc59cf5cdb364308029077eb71ef1c Mon Sep 17 00:00:00 2001 From: Soumya Ranjan Mahunt Date: Mon, 25 Aug 2025 09:23:13 +0000 Subject: [PATCH] fix: used installed Windows SDKs instead of hardcoded version --- .github/workflows/main.yml | 2 +- __tests__/installer/windows.test.ts | 57 ++++++++-- dist/index.js | 104 +++++++++++++++--- package.json | 2 +- src/installer/windows/index.ts | 38 ++++++- .../windows/installation/approach.ts | 11 +- src/utils/visual_studio/vswhere.ts | 4 +- src/utils/windows/index.ts | 11 ++ 8 files changed, 194 insertions(+), 35 deletions(-) create mode 100644 src/utils/windows/index.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ec56d338..83d73257 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -381,7 +381,7 @@ jobs: with: { 'swift-version': 'latest', 'check-latest': '${{ needs.ci.outputs.check_latest }}', - 'visual-studio-components': 'Microsoft.VisualStudio.Component.Windows11SDK.22000' + 'visual-studio-components': '${{ matrix.os }}'.includes('arm') ? 'Microsoft.VisualStudio.Component.Windows11SDK.22000' : '' } } ] diff --git a/__tests__/installer/windows.test.ts b/__tests__/installer/windows.test.ts index c2730a8d..c8bfe05f 100644 --- a/__tests__/installer/windows.test.ts +++ b/__tests__/installer/windows.test.ts @@ -1,5 +1,5 @@ import * as path from 'path' -import {promises as fs} from 'fs' +import {Dirent, promises as fs} from 'fs' import * as core from '@actions/core' import * as exec from '@actions/exec' import * as cache from '@actions/cache' @@ -57,7 +57,9 @@ describe('windows toolchain installation verification', () => { 'Microsoft.VisualStudio.Component.VC.ATL;Microsoft.VisualStudio.Component.VC.CMake.Project;Microsoft.VisualStudio.Component.Windows10SDK' ) const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('x86_64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('x86_64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.VC.ATL', 'Microsoft.VisualStudio.Component.VC.CMake.Project', @@ -69,7 +71,9 @@ describe('windows toolchain installation verification', () => { it('tests setting up on Windows 10', async () => { jest.spyOn(os, 'release').mockReturnValue('10.0.17063') const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('x86_64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('x86_64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.Windows10SDK.17763' ]) @@ -78,7 +82,9 @@ describe('windows toolchain installation verification', () => { it('tests setting up on ARM64 Windows 10', async () => { jest.spyOn(os, 'release').mockReturnValue('10.0.17063') const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('arm64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('arm64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', 'Microsoft.VisualStudio.Component.Windows10SDK.17763' ]) @@ -87,7 +93,9 @@ describe('windows toolchain installation verification', () => { it('tests setting up on Windows 11', async () => { jest.spyOn(os, 'release').mockReturnValue('10.0.22621') const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('x86_64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('x86_64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.Windows11SDK.22621' ]) @@ -99,7 +107,9 @@ describe('windows toolchain installation verification', () => { .spyOn(core, 'getInput') .mockReturnValue('Microsoft.VisualStudio.Component.Windows11SDK.22621') const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('x86_64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('x86_64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.Windows11SDK.22621' ]) @@ -118,12 +128,45 @@ describe('windows toolchain installation verification', () => { preventCaching: false } const installer = new WindowsToolchainInstaller(toolchain) - expect(installer['vsRequirement']('arm64').components).toStrictEqual([ + expect( + (await installer['vsRequirement']('arm64')).components + ).toStrictEqual([ 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', 'Microsoft.VisualStudio.Component.Windows11SDK.22000' ]) }) + it('tests setting up on Windows 10 with Windows 11 SDK with unavailable recommended SDK', async () => { + jest.spyOn(os, 'release').mockReturnValue('10.0.17063') + jest.spyOn(fs, 'readdir').mockResolvedValue([ + { + name: 'wdf', + isDirectory: () => true + } as unknown as Dirent, + { + name: '10.0.22621.0', + isDirectory: () => true + } as unknown as Dirent + ]) + const toolchain = { + name: 'Windows 10 Swift Development Snapshot', + date: new Date('2025-04-03 10:10:00-06:00'), + download: 'swift-DEVELOPMENT-SNAPSHOT-2025-04-03-a-windows10.exe', + dir: 'swift-DEVELOPMENT-SNAPSHOT-2025-04-03-a', + platform: 'windows10', + branch: 'development', + windows: true, + preventCaching: false + } + const installer = new WindowsToolchainInstaller(toolchain) + expect( + (await installer['vsRequirement']('arm64')).components + ).toStrictEqual([ + 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', + 'Microsoft.VisualStudio.Component.Windows11SDK.22621' + ]) + }) + it('tests download without caching', async () => { const installer = new WindowsToolchainInstaller(toolchain) expect(installer['version']).toStrictEqual(parseSemVer('5.8')) diff --git a/dist/index.js b/dist/index.js index 805208a2..50d35007 100644 --- a/dist/index.js +++ b/dist/index.js @@ -496,20 +496,43 @@ const core = __importStar(__nccwpck_require__(7484)); const semver = __importStar(__nccwpck_require__(2088)); const verify_1 = __nccwpck_require__(6298); const utils_1 = __nccwpck_require__(1750); +const windows_1 = __nccwpck_require__(4468); const installation_1 = __nccwpck_require__(4694); class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { - get winsdk() { + async winsdk() { const win11Semver = '10.0.22000'; const recommended = semver.gte(this.version ?? '6.2.0', '6.2.0') ? win11Semver : '10.0.17763'; const current = os.release(); - const version = semver.gte(current, recommended) ? current : recommended; + let version = semver.gte(current, recommended) ? current : recommended; + const insatlled = await this.insatlledSdks(); + if (insatlled.length && !insatlled.includes(version)) { + version = insatlled[0]; + } const major = semver.lt(version, win11Semver) ? semver.major(version) : 11; const minor = semver.patch(version); return `Microsoft.VisualStudio.Component.Windows${major}SDK.${minor}`; } - vsRequirement(arch) { + async insatlledSdks() { + const sdksPath = path.join((0, windows_1.program86)(), 'Windows Kits', '10', 'Include'); + try { + const dirs = await fs_1.promises.readdir(sdksPath, { withFileTypes: true }); + return dirs + .filter(dirent => dirent.isDirectory()) + .map(dirent => { + const parts = dirent.name.split('.'); + return parts.length >= 3 ? parts.slice(0, 3).join('.') : dirent.name; + }) + .filter(version => semver.valid(version)) + .sort(semver.rcompare); + } + catch (error) { + core.warning(`Unable to get installed SDKs due to: "${error}"`); + return []; + } + } + async vsRequirement(arch) { const componentsStr = core.getInput('visual-studio-components'); const providedComponents = componentsStr ? componentsStr.split(';') : []; const winsdkComponent = providedComponents.find(component => { @@ -531,7 +554,7 @@ class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { ...providedComponents ]; if (!winsdkComponent) { - vsComponents.push(this.winsdk); + vsComponents.push(await this.winsdk()); } return { version: '16', @@ -542,7 +565,7 @@ class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { async download(arch) { core.debug(`Using VS requirement ${JSON.stringify(this.vsRequirement(arch))}`); const [, toolchain] = await Promise.all([ - utils_1.VisualStudio.setup(this.vsRequirement(arch)), + utils_1.VisualStudio.setup(await this.vsRequirement(arch)), super.download(arch) ]); const exeFile = `${toolchain}.exe`; @@ -591,7 +614,7 @@ class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { core.warning(`Failed VS enviroment after installation ${installLocation}`); return; } - const visualStudio = await utils_1.VisualStudio.setup(this.vsRequirement(arch)); + const visualStudio = await utils_1.VisualStudio.setup(await this.vsRequirement(arch)); await visualStudio.update(sdkroot); const swiftFlags = [ '-sdk', @@ -654,10 +677,11 @@ const path = __importStar(__nccwpck_require__(6928)); const fs_1 = __nccwpck_require__(9896); const core = __importStar(__nccwpck_require__(7484)); const base_1 = __nccwpck_require__(2349); +const windows_1 = __nccwpck_require__(4468); async function firstDirectoryLayout(root) { core.debug('Trying first installation approach'); - const systemDrive = process.env.SystemDrive ?? 'C:'; - const location = root ?? path.join(systemDrive, 'Library'); + const drive = (0, windows_1.systemDrive)(); + const location = root ?? path.join(drive, 'Library'); const devdir = path.join(location, 'Developer'); const toolchainName = 'unknown-Asserts-development.xctoolchain'; const toolchain = path.join(devdir, 'Toolchains', toolchainName); @@ -666,7 +690,7 @@ async function firstDirectoryLayout(root) { const sdkroot = path.join(devdir, 'Platforms', 'Windows.platform', winsdk); const runtimeRoot = path.join(location, 'Swift'); const runtime = path.join(runtimeRoot, 'runtime-development'); - const devPath = path.join(systemDrive, 'Program Files', 'Swift'); + const devPath = path.join(drive, 'Program Files', 'Swift'); try { await fs_1.promises.access(devPath); await fs_1.promises.cp(devPath, runtimeRoot, { recursive: true }); @@ -679,8 +703,8 @@ async function firstDirectoryLayout(root) { } async function secondDirectoryLayout(root) { core.debug('Trying second installation approach'); - const systemDrive = root ?? process.env.SystemDrive ?? 'C:'; - const location = path.join(systemDrive, 'Program Files', 'Swift'); + const drive = root ?? (0, windows_1.systemDrive)(); + const location = path.join(drive, 'Program Files', 'Swift'); const toolchainName = '0.0.0+Asserts'; const toolchain = path.join(location, 'Toolchains', toolchainName); await fs_1.promises.access(toolchain); @@ -2510,6 +2534,7 @@ const path = __importStar(__nccwpck_require__(6928)); const fs_1 = __nccwpck_require__(9896); const io = __importStar(__nccwpck_require__(4994)); const core = __importStar(__nccwpck_require__(7484)); +const windows_1 = __nccwpck_require__(4468); // eslint-disable-next-line @typescript-eslint/no-namespace var VSWhere; (function (VSWhere) { @@ -2535,8 +2560,7 @@ var VSWhere; } catch { // fall back to VS-installed path - const program86 = 'ProgramFiles(x86)'; - vswhereToolExe = path.join(process.env[program86] ?? path.join('C:', program86), 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'); + vswhereToolExe = path.join((0, windows_1.program86)(), 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'); core.debug(`Trying Visual Studio-installed path: ${vswhereToolExe}`); } } @@ -2552,6 +2576,60 @@ var VSWhere; })(VSWhere || (exports.VSWhere = VSWhere = {})); +/***/ }), + +/***/ 4468: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.systemDrive = systemDrive; +exports.program86 = program86; +const path = __importStar(__nccwpck_require__(6928)); +function systemDrive() { + return process.env.SystemDrive ?? 'C:'; +} +function program86() { + const drive = systemDrive(); + const program86 = 'ProgramFiles(x86)'; + return process.env[program86] ?? path.join(drive, program86); +} + + /***/ }), /***/ 1634: diff --git a/package.json b/package.json index 5a929fb4..43dc96fe 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "ci" ], "scripts": { - "prepare": "npm run build && node -e \"require('./.github/utils/update_metadata').fetch()\"", + "prepare": "node -e \"require('./.github/utils/update_metadata').fetch()\" && npm run build", "build": "tsc", "format": "prettier --write \"**/*.ts\"", "lint": "depcheck --ignores ts-node,ts-jest,globals,@eslint/js,@eslint/eslintrc && eslint src/**/*.ts && prettier --check \"**/*.ts\"", diff --git a/src/installer/windows/index.ts b/src/installer/windows/index.ts index 8022d713..34ab57a3 100644 --- a/src/installer/windows/index.ts +++ b/src/installer/windows/index.ts @@ -6,22 +6,46 @@ import * as semver from 'semver' import {VerifyingToolchainInstaller} from '../verify' import {WindowsToolchainSnapshot} from '../../snapshot' import {VisualStudio, VISUAL_STUDIO_WINSDK_COMPONENT_REGEX} from '../../utils' +import {program86} from '../../utils/windows' import {Installation, CustomInstallation} from './installation' export class WindowsToolchainInstaller extends VerifyingToolchainInstaller { - private get winsdk() { + private async winsdk() { const win11Semver = '10.0.22000' const recommended = semver.gte(this.version ?? '6.2.0', '6.2.0') ? win11Semver : '10.0.17763' const current = os.release() - const version = semver.gte(current, recommended) ? current : recommended + let version = semver.gte(current, recommended) ? current : recommended + const insatlled = await this.insatlledSdks() + if (insatlled.length && !insatlled.includes(version)) { + version = insatlled[0] + } + const major = semver.lt(version, win11Semver) ? semver.major(version) : 11 const minor = semver.patch(version) return `Microsoft.VisualStudio.Component.Windows${major}SDK.${minor}` } - private vsRequirement(arch: string) { + private async insatlledSdks() { + const sdksPath = path.join(program86(), 'Windows Kits', '10', 'Include') + try { + const dirs = await fs.readdir(sdksPath, {withFileTypes: true}) + return dirs + .filter(dirent => dirent.isDirectory()) + .map(dirent => { + const parts = dirent.name.split('.') + return parts.length >= 3 ? parts.slice(0, 3).join('.') : dirent.name + }) + .filter(version => semver.valid(version)) + .sort(semver.rcompare) + } catch (error) { + core.warning(`Unable to get installed SDKs due to: "${error}"`) + return [] + } + } + + private async vsRequirement(arch: string) { const componentsStr = core.getInput('visual-studio-components') const providedComponents = componentsStr ? componentsStr.split(';') : [] const winsdkComponent = providedComponents.find(component => { @@ -47,7 +71,7 @@ export class WindowsToolchainInstaller extends VerifyingToolchainInstaller