diff --git a/dist/index.js b/dist/index.js index 134764c..0449621 100644 --- a/dist/index.js +++ b/dist/index.js @@ -48,6 +48,25 @@ module.exports = "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (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 (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -58,11 +77,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -const childProcess = __webpack_require__(129); -const path = __webpack_require__(622); -const util_1 = __webpack_require__(669); -const ioUtil = __webpack_require__(672); -const exec = util_1.promisify(childProcess.exec); +exports.findInPath = exports.which = exports.mkdirP = exports.rmRF = exports.mv = exports.cp = void 0; +const assert_1 = __webpack_require__(357); +const path = __importStar(__webpack_require__(622)); +const ioUtil = __importStar(__webpack_require__(672)); /** * Copies a file or folder. * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js @@ -73,14 +91,14 @@ const exec = util_1.promisify(childProcess.exec); */ function cp(source, dest, options = {}) { return __awaiter(this, void 0, void 0, function* () { - const { force, recursive } = readCopyOptions(options); + const { force, recursive, copySourceDirectory } = readCopyOptions(options); const destStat = (yield ioUtil.exists(dest)) ? yield ioUtil.stat(dest) : null; // Dest is an existing file, but not forcing if (destStat && destStat.isFile() && !force) { return; } // If dest is an existing directory, should copy inside. - const newDest = destStat && destStat.isDirectory() + const newDest = destStat && destStat.isDirectory() && copySourceDirectory ? path.join(dest, path.basename(source)) : dest; if (!(yield ioUtil.exists(source))) { @@ -143,51 +161,23 @@ exports.mv = mv; function rmRF(inputPath) { return __awaiter(this, void 0, void 0, function* () { if (ioUtil.IS_WINDOWS) { - // Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another - // program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del. - try { - if (yield ioUtil.isDirectory(inputPath, true)) { - yield exec(`rd /s /q "${inputPath}"`); - } - else { - yield exec(`del /f /a "${inputPath}"`); - } - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; - } - // Shelling out fails to remove a symlink folder with missing source, this unlink catches that - try { - yield ioUtil.unlink(inputPath); - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; + // Check for invalid characters + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + if (/[*"<>|]/.test(inputPath)) { + throw new Error('File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'); } } - else { - let isDir = false; - try { - isDir = yield ioUtil.isDirectory(inputPath); - } - catch (err) { - // if you try to delete a file that doesn't exist, desired result is achieved - // other errors are valid - if (err.code !== 'ENOENT') - throw err; - return; - } - if (isDir) { - yield exec(`rm -rf "${inputPath}"`); - } - else { - yield ioUtil.unlink(inputPath); - } + try { + // note if path does not exist, error is silent + yield ioUtil.rm(inputPath, { + force: true, + maxRetries: 3, + recursive: true, + retryDelay: 300 + }); + } + catch (err) { + throw new Error(`File was unable to be removed ${err}`); } }); } @@ -201,7 +191,8 @@ exports.rmRF = rmRF; */ function mkdirP(fsPath) { return __awaiter(this, void 0, void 0, function* () { - yield ioUtil.mkdirP(fsPath); + assert_1.ok(fsPath, 'a path argument must be provided'); + yield ioUtil.mkdir(fsPath, { recursive: true }); }); } exports.mkdirP = mkdirP; @@ -229,62 +220,80 @@ function which(tool, check) { throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.`); } } + return result; } - try { - // build the list of extensions to try - const extensions = []; - if (ioUtil.IS_WINDOWS && process.env.PATHEXT) { - for (const extension of process.env.PATHEXT.split(path.delimiter)) { - if (extension) { - extensions.push(extension); - } + const matches = yield findInPath(tool); + if (matches && matches.length > 0) { + return matches[0]; + } + return ''; + }); +} +exports.which = which; +/** + * Returns a list of all occurrences of the given tool on the system path. + * + * @returns Promise the paths of the tool + */ +function findInPath(tool) { + return __awaiter(this, void 0, void 0, function* () { + if (!tool) { + throw new Error("parameter 'tool' is required"); + } + // build the list of extensions to try + const extensions = []; + if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) { + for (const extension of process.env['PATHEXT'].split(path.delimiter)) { + if (extension) { + extensions.push(extension); } } - // if it's rooted, return it if exists. otherwise return empty. - if (ioUtil.isRooted(tool)) { - const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions); - if (filePath) { - return filePath; - } - return ''; - } - // if any path separators, return empty - if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) { - return ''; - } - // build the list of directories - // - // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, - // it feels like we should not do this. Checking the current directory seems like more of a use - // case of a shell, and the which() function exposed by the toolkit should strive for consistency - // across platforms. - const directories = []; - if (process.env.PATH) { - for (const p of process.env.PATH.split(path.delimiter)) { - if (p) { - directories.push(p); - } - } + } + // if it's rooted, return it if exists. otherwise return empty. + if (ioUtil.isRooted(tool)) { + const filePath = yield ioUtil.tryGetExecutablePath(tool, extensions); + if (filePath) { + return [filePath]; } - // return the first match - for (const directory of directories) { - const filePath = yield ioUtil.tryGetExecutablePath(directory + path.sep + tool, extensions); - if (filePath) { - return filePath; + return []; + } + // if any path separators, return empty + if (tool.includes(path.sep)) { + return []; + } + // build the list of directories + // + // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, + // it feels like we should not do this. Checking the current directory seems like more of a use + // case of a shell, and the which() function exposed by the toolkit should strive for consistency + // across platforms. + const directories = []; + if (process.env.PATH) { + for (const p of process.env.PATH.split(path.delimiter)) { + if (p) { + directories.push(p); } } - return ''; } - catch (err) { - throw new Error(`which failed with message ${err.message}`); + // find all matches + const matches = []; + for (const directory of directories) { + const filePath = yield ioUtil.tryGetExecutablePath(path.join(directory, tool), extensions); + if (filePath) { + matches.push(filePath); + } } + return matches; }); } -exports.which = which; +exports.findInPath = findInPath; function readCopyOptions(options) { const force = options.force == null ? true : options.force; const recursive = Boolean(options.recursive); - return { force, recursive }; + const copySourceDirectory = options.copySourceDirectory == null + ? true + : Boolean(options.copySourceDirectory); + return { force, recursive, copySourceDirectory }; } function cpDirRecursive(sourceDir, destDir, currentDepth, force) { return __awaiter(this, void 0, void 0, function* () { @@ -360,6 +369,25 @@ exports.default = _default; "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (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 (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -370,12 +398,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -const os = __webpack_require__(87); -const events = __webpack_require__(614); -const child = __webpack_require__(129); -const path = __webpack_require__(622); -const io = __webpack_require__(1); -const ioUtil = __webpack_require__(672); +exports.argStringToArray = exports.ToolRunner = void 0; +const os = __importStar(__webpack_require__(87)); +const events = __importStar(__webpack_require__(614)); +const child = __importStar(__webpack_require__(129)); +const path = __importStar(__webpack_require__(622)); +const io = __importStar(__webpack_require__(1)); +const ioUtil = __importStar(__webpack_require__(672)); +const timers_1 = __webpack_require__(213); /* eslint-disable @typescript-eslint/unbound-method */ const IS_WINDOWS = process.platform === 'win32'; /* @@ -445,11 +475,12 @@ class ToolRunner extends events.EventEmitter { s = s.substring(n + os.EOL.length); n = s.indexOf(os.EOL); } - strBuffer = s; + return s; } catch (err) { // streaming lines to console is best effort. Don't fail a build. this._debug(`error processing line. Failed with error ${err}`); + return ''; } } _getSpawnFileName() { @@ -731,7 +762,7 @@ class ToolRunner extends events.EventEmitter { // if the tool is only a file name, then resolve it from the PATH // otherwise verify it exists (add extension on Windows if necessary) this.toolPath = yield io.which(this.toolPath, true); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { this._debug(`exec tool: ${this.toolPath}`); this._debug('arguments:'); for (const arg of this.args) { @@ -745,9 +776,12 @@ class ToolRunner extends events.EventEmitter { state.on('debug', (message) => { this._debug(message); }); + if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) { + return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`)); + } const fileName = this._getSpawnFileName(); const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName)); - const stdbuffer = ''; + let stdbuffer = ''; if (cp.stdout) { cp.stdout.on('data', (data) => { if (this.options.listeners && this.options.listeners.stdout) { @@ -756,14 +790,14 @@ class ToolRunner extends events.EventEmitter { if (!optionsNonNull.silent && optionsNonNull.outStream) { optionsNonNull.outStream.write(data); } - this._processLineBuffer(data, stdbuffer, (line) => { + stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => { if (this.options.listeners && this.options.listeners.stdline) { this.options.listeners.stdline(line); } }); }); } - const errbuffer = ''; + let errbuffer = ''; if (cp.stderr) { cp.stderr.on('data', (data) => { state.processStderr = true; @@ -778,7 +812,7 @@ class ToolRunner extends events.EventEmitter { : optionsNonNull.outStream; s.write(data); } - this._processLineBuffer(data, errbuffer, (line) => { + errbuffer = this._processLineBuffer(data, errbuffer, (line) => { if (this.options.listeners && this.options.listeners.errline) { this.options.listeners.errline(line); } @@ -819,7 +853,13 @@ class ToolRunner extends events.EventEmitter { resolve(exitCode); } }); - }); + if (this.options.input) { + if (!cp.stdin) { + throw new Error('child process missing stdin'); + } + cp.stdin.end(this.options.input); + } + })); }); } } @@ -905,7 +945,7 @@ class ExecState extends events.EventEmitter { this._setResult(); } else if (this.processExited) { - this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this); + this.timeout = timers_1.setTimeout(ExecState.HandleTimeout, this.delay, this); } } _debug(message) { @@ -1648,6 +1688,7 @@ const exec = __importStar(__webpack_require__(986)); const fs = __importStar(__webpack_require__(747)); const path = __importStar(__webpack_require__(622)); const io = __importStar(__webpack_require__(1)); +const vsVer = __importStar(__webpack_require__(251)); const IS_WINDOWS = process.platform === 'win32'; const VS_VERSION = core.getInput('vs-version') || 'latest'; const VSWHERE_PATH = core.getInput('vswhere-path'); @@ -1662,12 +1703,91 @@ if (VS_VERSION !== 'latest') { VSWHERE_EXEC += `-version "${VS_VERSION}" `; } core.debug(`Execution arguments: ${VSWHERE_EXEC}`); +function checkVersionInPath() { + return __awaiter(this, void 0, void 0, function* () { + const tool = yield io.which('msbuild', false); + const execOutput = yield exec.getExecOutput(`"${tool}"`, ['--ver'], { + silent: true + }); + // Exit if path is wrong or version does not match regex + if (execOutput.exitCode !== 0) { + return false; + } + const versionMatch = execOutput.stdout.match(/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/); + const versionString = versionMatch ? versionMatch[0] : ''; + if (!versionString) { + return false; + } + // If "latest" every found version goes + if (VS_VERSION === 'latest') { + return true; + } + // Prepare arrays for tool version and min-max versions + const splitVersion = versionString.split('.'); + const version = [ + parseInt(splitVersion[0]), + parseInt(splitVersion[1]), + parseInt(splitVersion[2]), + parseInt(splitVersion[3]) + ]; + const constraints = VS_VERSION.split(','); + const minInclusive = !constraints[0].startsWith('('); + const maxInclusive = constraints.length === 2 ? constraints[1].endsWith(']') : false; + const minVersionString = (constraints[0].replace('[', '').replace('(', '') || '0.0').split('.'); + while (minVersionString.length !== 4) { + minVersionString.push('0'); + } + const minVersion = [ + parseInt(minVersionString[0]), + parseInt(minVersionString[1]), + parseInt(minVersionString[2]), + parseInt(minVersionString[3]) + ]; + const maxVersionString = ((constraints[1] ? constraints[1].replace(')', '').replace(']', '') : '') || + '65535.65535.65535.65535').split('.'); + while (maxVersionString.length !== 4) { + maxVersionString.push('0'); + } + const maxVersion = [ + parseInt(maxVersionString[0]), + parseInt(maxVersionString[1]), + parseInt(maxVersionString[2]), + parseInt(maxVersionString[3]) + ]; + // Check version + if (minInclusive) { + if (vsVer.lt(version, minVersion)) { + return false; + } + } + else { + if (vsVer.lte(version, minVersion)) { + return false; + } + } + if (maxInclusive) { + if (vsVer.gt(version, maxVersion)) { + return false; + } + } + else { + if (vsVer.gte(version, maxVersion)) { + return false; + } + } + return true; + }); +} function run() { return __awaiter(this, void 0, void 0, function* () { try { - // exit if non Windows runner + // exit if non Windows runner and msbuild not already in PATH if (IS_WINDOWS === false) { - core.setFailed('setup-msbuild can only be run on Windows runners'); + if (yield checkVersionInPath()) { + core.info('Correct msbuild version is already in PATH'); + return; + } + core.setFailed('setup-msbuild can only run vswhere on Windows runners'); return; } // check to see if we are using a specific path for vswhere @@ -1707,7 +1827,7 @@ function run() { if (MSBUILD_ARCH === 'x64') { MSBUILD_ARCH = 'amd64'; } - let toolPath = path.join(installationPath, `MSBuild\\Current\\Bin\\${MSBUILD_ARCH}\\MSBuild.exe`); + const toolPath = path.join(installationPath, `MSBuild\\Current\\Bin\\${MSBUILD_ARCH}\\MSBuild.exe`); core.debug(`Checking for path: ${toolPath}`); if (!fs.existsSync(toolPath)) { return; @@ -1743,7 +1863,12 @@ function run() { core.debug(`Tool path added to PATH: ${toolFolderPath}`); } catch (error) { - core.setFailed(error.message); + if (error instanceof Error) { + core.setFailed(error.message); + } + else { + core.setFailed('Unknown error'); + } } }); } @@ -1809,6 +1934,59 @@ exports.default = _default; module.exports = require("https"); +/***/ }), + +/***/ 213: +/***/ (function(module) { + +module.exports = require("timers"); + +/***/ }), + +/***/ 251: +/***/ (function(__unusedmodule, exports) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function lt(a, b) { + for (let index = 0; index < 4; index++) { + if (a[index] < b[index]) { + return true; + } + else if (a[index] > b[index]) { + return false; + } + } + return false; +} +exports.lt = lt; +function lte(a, b) { + return !gt(a, b); +} +exports.lte = lte; +function gt(a, b) { + for (let index = 0; index < 4; index++) { + if (a[index] > b[index]) { + return true; + } + else if (a[index] < b[index]) { + return false; + } + } + return false; +} +exports.gt = gt; +function gte(a, b) { + return !lt(a, b); +} +exports.gte = gte; +function eq(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; +} +exports.eq = eq; + + /***/ }), /***/ 293: @@ -1855,6 +2033,13 @@ exports.default = _default; /***/ }), +/***/ 304: +/***/ (function(module) { + +module.exports = require("string_decoder"); + +/***/ }), + /***/ 329: /***/ (function(__unusedmodule, exports, __webpack_require__) { @@ -2978,6 +3163,25 @@ module.exports = require("util"); "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (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 (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -2989,11 +3193,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); -const assert_1 = __webpack_require__(357); -const fs = __webpack_require__(747); -const path = __webpack_require__(622); -_a = fs.promises, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink; +exports.getCmdPath = exports.tryGetExecutablePath = exports.isRooted = exports.isDirectory = exports.exists = exports.READONLY = exports.UV_FS_O_EXLOCK = exports.IS_WINDOWS = exports.unlink = exports.symlink = exports.stat = exports.rmdir = exports.rm = exports.rename = exports.readlink = exports.readdir = exports.open = exports.mkdir = exports.lstat = exports.copyFile = exports.chmod = void 0; +const fs = __importStar(__webpack_require__(747)); +const path = __importStar(__webpack_require__(622)); +_a = fs.promises +// export const {open} = 'fs' +, exports.chmod = _a.chmod, exports.copyFile = _a.copyFile, exports.lstat = _a.lstat, exports.mkdir = _a.mkdir, exports.open = _a.open, exports.readdir = _a.readdir, exports.readlink = _a.readlink, exports.rename = _a.rename, exports.rm = _a.rm, exports.rmdir = _a.rmdir, exports.stat = _a.stat, exports.symlink = _a.symlink, exports.unlink = _a.unlink; +// export const {open} = 'fs' exports.IS_WINDOWS = process.platform === 'win32'; +// See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691 +exports.UV_FS_O_EXLOCK = 0x10000000; +exports.READONLY = fs.constants.O_RDONLY; function exists(fsPath) { return __awaiter(this, void 0, void 0, function* () { try { @@ -3032,49 +3242,6 @@ function isRooted(p) { return p.startsWith('/'); } exports.isRooted = isRooted; -/** - * Recursively create a directory at `fsPath`. - * - * This implementation is optimistic, meaning it attempts to create the full - * path first, and backs up the path stack from there. - * - * @param fsPath The path to create - * @param maxDepth The maximum recursion depth - * @param depth The current recursion depth - */ -function mkdirP(fsPath, maxDepth = 1000, depth = 1) { - return __awaiter(this, void 0, void 0, function* () { - assert_1.ok(fsPath, 'a path argument must be provided'); - fsPath = path.resolve(fsPath); - if (depth >= maxDepth) - return exports.mkdir(fsPath); - try { - yield exports.mkdir(fsPath); - return; - } - catch (err) { - switch (err.code) { - case 'ENOENT': { - yield mkdirP(path.dirname(fsPath), maxDepth, depth + 1); - yield exports.mkdir(fsPath); - return; - } - default: { - let stats; - try { - stats = yield exports.stat(fsPath); - } - catch (err2) { - throw err; - } - if (!stats.isDirectory()) - throw err; - } - } - } - }); -} -exports.mkdirP = mkdirP; /** * Best effort attempt to determine whether a file exists and is executable. * @param filePath file path to check @@ -3171,6 +3338,12 @@ function isUnixExecutable(stats) { ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid())); } +// Get the path of cmd.exe in windows +function getCmdPath() { + var _a; + return (_a = process.env['COMSPEC']) !== null && _a !== void 0 ? _a : `cmd.exe`; +} +exports.getCmdPath = getCmdPath; //# sourceMappingURL=io-util.js.map /***/ }), @@ -3431,6 +3604,25 @@ exports.default = _default; "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (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 (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -3441,7 +3633,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -const tr = __webpack_require__(9); +exports.getExecOutput = exports.exec = void 0; +const string_decoder_1 = __webpack_require__(304); +const tr = __importStar(__webpack_require__(9)); /** * Exec a command. * Output will be streamed to the live console. @@ -3466,6 +3660,51 @@ function exec(commandLine, args, options) { }); } exports.exec = exec; +/** + * Exec a command and get the output. + * Output will be streamed to the live console. + * Returns promise with the exit code and collected stdout and stderr + * + * @param commandLine command to execute (can include additional args). Must be correctly escaped. + * @param args optional arguments for tool. Escaping is handled by the lib. + * @param options optional exec options. See ExecOptions + * @returns Promise exit code, stdout, and stderr + */ +function getExecOutput(commandLine, args, options) { + var _a, _b; + return __awaiter(this, void 0, void 0, function* () { + let stdout = ''; + let stderr = ''; + //Using string decoder covers the case where a mult-byte character is split + const stdoutDecoder = new string_decoder_1.StringDecoder('utf8'); + const stderrDecoder = new string_decoder_1.StringDecoder('utf8'); + const originalStdoutListener = (_a = options === null || options === void 0 ? void 0 : options.listeners) === null || _a === void 0 ? void 0 : _a.stdout; + const originalStdErrListener = (_b = options === null || options === void 0 ? void 0 : options.listeners) === null || _b === void 0 ? void 0 : _b.stderr; + const stdErrListener = (data) => { + stderr += stderrDecoder.write(data); + if (originalStdErrListener) { + originalStdErrListener(data); + } + }; + const stdOutListener = (data) => { + stdout += stdoutDecoder.write(data); + if (originalStdoutListener) { + originalStdoutListener(data); + } + }; + const listeners = Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.listeners), { stdout: stdOutListener, stderr: stdErrListener }); + const exitCode = yield exec(commandLine, args, Object.assign(Object.assign({}, options), { listeners })); + //flush any remaining characters + stdout += stdoutDecoder.end(); + stderr += stderrDecoder.end(); + return { + exitCode, + stdout, + stderr + }; + }); +} +exports.getExecOutput = getExecOutput; //# sourceMappingURL=exec.js.map /***/ }), diff --git a/package-lock.json b/package-lock.json index 00a3193..23688d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "setup-msbuild", - "version": "1.3.0", + "version": "1.3.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "setup-msbuild", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "dependencies": { "@actions/core": "^1.10.0", - "@actions/exec": "^1.0.3", - "@actions/tool-cache": "^1.3.0" + "@actions/exec": "^1.1.1", + "@actions/io": "^1.1.3" }, "devDependencies": { "@types/jest": "^24.0.23", @@ -55,38 +55,17 @@ } }, "node_modules/@actions/exec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz", - "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", "dependencies": { "@actions/io": "^1.0.1" } }, - "node_modules/@actions/http-client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", - "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", - "dependencies": { - "tunnel": "0.0.6" - } - }, "node_modules/@actions/io": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", - "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" - }, - "node_modules/@actions/tool-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.3.0.tgz", - "integrity": "sha512-pbv32I89niDShw1YTDo0OFQmWPkZPElE7e3So1jfEzjIyzGRfYIzshwOVhemJZLcDtzo3kxO3GFDAmuVvub/6w==", - "dependencies": { - "@actions/core": "^1.2.0", - "@actions/exec": "^1.0.0", - "@actions/http-client": "^1.0.1", - "@actions/io": "^1.0.1", - "semver": "^6.1.0", - "uuid": "^3.3.2" - } + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" }, "node_modules/@babel/code-frame": { "version": "7.5.5", @@ -6901,6 +6880,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -7866,6 +7846,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, "bin": { "uuid": "bin/uuid" } @@ -8206,38 +8187,17 @@ } }, "@actions/exec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz", - "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", "requires": { "@actions/io": "^1.0.1" } }, - "@actions/http-client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", - "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", - "requires": { - "tunnel": "0.0.6" - } - }, "@actions/io": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", - "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" - }, - "@actions/tool-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.3.0.tgz", - "integrity": "sha512-pbv32I89niDShw1YTDo0OFQmWPkZPElE7e3So1jfEzjIyzGRfYIzshwOVhemJZLcDtzo3kxO3GFDAmuVvub/6w==", - "requires": { - "@actions/core": "^1.2.0", - "@actions/exec": "^1.0.0", - "@actions/http-client": "^1.0.1", - "@actions/io": "^1.0.1", - "semver": "^6.1.0", - "uuid": "^3.3.2" - } + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" }, "@babel/code-frame": { "version": "7.5.5", @@ -13770,7 +13730,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "set-blocking": { "version": "2.0.0", @@ -14552,7 +14513,8 @@ "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true }, "validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index 2ab4da2..de906a0 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "license": "MIT", "dependencies": { "@actions/core": "^1.10.0", - "@actions/exec": "^1.0.3", - "@actions/tool-cache": "^1.3.0" + "@actions/exec": "^1.1.1", + "@actions/io": "^1.1.3" }, "devDependencies": { "@types/jest": "^24.0.23", diff --git a/src/main.ts b/src/main.ts index b449dc3..9be9f5b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import * as exec from '@actions/exec' import * as fs from 'fs' import * as path from 'path' import * as io from '@actions/io' +import * as vsVer from './vs-ver' import {ExecOptions} from '@actions/exec/lib/interfaces' const IS_WINDOWS = process.platform === 'win32' @@ -12,10 +13,11 @@ const ALLOW_PRERELEASE = core.getInput('vs-prerelease') || 'false' let MSBUILD_ARCH = core.getInput('msbuild-architecture') || 'x86' // if a specific version of VS is requested -let VSWHERE_EXEC = '-products * -requires Microsoft.Component.MSBuild -property installationPath -latest ' +let VSWHERE_EXEC = + '-products * -requires Microsoft.Component.MSBuild -property installationPath -latest ' if (ALLOW_PRERELEASE === 'true') { - VSWHERE_EXEC += ' -prerelease ' - } + VSWHERE_EXEC += ' -prerelease ' +} if (VS_VERSION !== 'latest') { VSWHERE_EXEC += `-version "${VS_VERSION}" ` @@ -23,11 +25,96 @@ if (VS_VERSION !== 'latest') { core.debug(`Execution arguments: ${VSWHERE_EXEC}`) +async function checkVersionInPath(): Promise { + const tool = await io.which('msbuild', false) + const execOutput = await exec.getExecOutput(`"${tool}"`, ['--ver'], { + silent: true + }) + + // Exit if path is wrong or version does not match regex + if (execOutput.exitCode !== 0) { + return false + } + const versionMatch = execOutput.stdout.match(/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) + const versionString = versionMatch ? versionMatch[0] : '' + if (!versionString) { + return false + } + + // If "latest" every found version goes + if (VS_VERSION === 'latest') { + return true + } + + // Prepare arrays for tool version and min-max versions + const splitVersion = versionString.split('.') + const version: [number, number, number, number] = [ + parseInt(splitVersion[0]), + parseInt(splitVersion[1]), + parseInt(splitVersion[2]), + parseInt(splitVersion[3]) + ] + const constraints = VS_VERSION.split(',') + const minInclusive = !constraints[0].startsWith('(') + const maxInclusive = + constraints.length === 2 ? constraints[1].endsWith(']') : false + const minVersionString = ( + constraints[0].replace('[', '').replace('(', '') || '0.0' + ).split('.') + while (minVersionString.length !== 4) { + minVersionString.push('0') + } + const minVersion: [number, number, number, number] = [ + parseInt(minVersionString[0]), + parseInt(minVersionString[1]), + parseInt(minVersionString[2]), + parseInt(minVersionString[3]) + ] + const maxVersionString = ( + (constraints[1] ? constraints[1].replace(')', '').replace(']', '') : '') || + '65535.65535.65535.65535' + ).split('.') + while (maxVersionString.length !== 4) { + maxVersionString.push('0') + } + const maxVersion: [number, number, number, number] = [ + parseInt(maxVersionString[0]), + parseInt(maxVersionString[1]), + parseInt(maxVersionString[2]), + parseInt(maxVersionString[3]) + ] + + // Check version + if (minInclusive) { + if (vsVer.lt(version, minVersion)) { + return false + } + } else { + if (vsVer.lte(version, minVersion)) { + return false + } + } + if (maxInclusive) { + if (vsVer.gt(version, maxVersion)) { + return false + } + } else { + if (vsVer.gte(version, maxVersion)) { + return false + } + } + return true +} + async function run(): Promise { try { - // exit if non Windows runner + // exit if non Windows runner and msbuild not already in PATH if (IS_WINDOWS === false) { - core.setFailed('setup-msbuild can only be run on Windows runners') + if (await checkVersionInPath()) { + core.info('Correct msbuild version is already in PATH') + return + } + core.setFailed('setup-msbuild can only run vswhere on Windows runners') return } @@ -77,7 +164,7 @@ async function run(): Promise { if (MSBUILD_ARCH === 'x64') { MSBUILD_ARCH = 'amd64' } - let toolPath = path.join( + const toolPath = path.join( installationPath, `MSBuild\\Current\\Bin\\${MSBUILD_ARCH}\\MSBuild.exe` ) @@ -128,7 +215,11 @@ async function run(): Promise { core.addPath(toolFolderPath) core.debug(`Tool path added to PATH: ${toolFolderPath}`) } catch (error) { - core.setFailed(error.message) + if (error instanceof Error) { + core.setFailed(error.message) + } else { + core.setFailed('Unknown error') + } } } diff --git a/src/vs-ver.ts b/src/vs-ver.ts new file mode 100644 index 0000000..d0247ff --- /dev/null +++ b/src/vs-ver.ts @@ -0,0 +1,48 @@ +export function lt( + a: [number, number, number, number], + b: [number, number, number, number] +): boolean { + for (let index = 0; index < 4; index++) { + if (a[index] < b[index]) { + return true + } else if (a[index] > b[index]) { + return false + } + } + return false +} + +export function lte( + a: [number, number, number, number], + b: [number, number, number, number] +): boolean { + return !gt(a, b) +} + +export function gt( + a: [number, number, number, number], + b: [number, number, number, number] +): boolean { + for (let index = 0; index < 4; index++) { + if (a[index] > b[index]) { + return true + } else if (a[index] < b[index]) { + return false + } + } + return false +} + +export function gte( + a: [number, number, number, number], + b: [number, number, number, number] +): boolean { + return !lt(a, b) +} + +export function eq( + a: [number, number, number, number], + b: [number, number, number, number] +): boolean { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] +}