From cb0e023f052e3e88d548272d7d6b75bb453adc44 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Sat, 22 Jul 2023 17:19:53 +0200 Subject: [PATCH 1/6] feat(actions): Added support to run tags with conditions --- actions/plan/.eslintrc.json | 6 +- actions/plan/dist/index.js | 705 ++++++++++++++++- actions/plan/jest.config.ts | 12 + actions/plan/project.json | 8 + actions/plan/src/plan.ts | 10 +- .../has-one-of-required-tags.spec.ts | 37 + .../src/utils/has-one-of-required-tags.ts | 31 +- actions/plan/tsconfig.spec.json | 16 + actions/run-many/dist/index.js | 722 +++++++++++++++++- actions/run-many/src/run-many.ts | 13 +- .../src/utils/has-one-of-required-tags.ts | 36 + 11 files changed, 1514 insertions(+), 82 deletions(-) create mode 100644 actions/plan/jest.config.ts create mode 100644 actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts create mode 100644 actions/plan/tsconfig.spec.json create mode 100644 actions/run-many/src/utils/has-one-of-required-tags.ts diff --git a/actions/plan/.eslintrc.json b/actions/plan/.eslintrc.json index 3b549a51..f9768a57 100644 --- a/actions/plan/.eslintrc.json +++ b/actions/plan/.eslintrc.json @@ -1,5 +1,7 @@ { - "extends": "../../.eslintrc.json", + "extends": [ + "../../.eslintrc.json" + ], "ignorePatterns": [ "!**/*" ], @@ -14,7 +16,7 @@ ], "parserOptions": { "project": [ - "actions/plan/tsconfig.json" + "actions/plan/tsconfig.*?.json" ] }, "rules": {} diff --git a/actions/plan/dist/index.js b/actions/plan/dist/index.js index eabc9f77..a22108c2 100644 --- a/actions/plan/dist/index.js +++ b/actions/plan/dist/index.js @@ -4961,7 +4961,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.installSourceMapSupport = exports.SourcemapMap = void 0; -const source_map_support_1 = __importDefault(__nccwpck_require__(42059)); +const source_map_support_1 = __importDefault(__nccwpck_require__(95953)); exports.SourcemapMap = new Map(); function installSourceMapSupport() { source_map_support_1.default.install({ @@ -4981,6 +4981,639 @@ function installSourceMapSupport() { exports.installSourceMapSupport = installSourceMapSupport; //# sourceMappingURL=index.js.map +/***/ }), + +/***/ 95953: +/***/ ((module, exports, __nccwpck_require__) => { + +/* module decorator */ module = __nccwpck_require__.nmd(module); +var SourceMapConsumer = (__nccwpck_require__(36096).SourceMapConsumer); +var path = __nccwpck_require__(71017); + +var fs; +try { + fs = __nccwpck_require__(57147); + if (!fs.existsSync || !fs.readFileSync) { + // fs doesn't have all methods we need + fs = null; + } +} catch (err) { + /* nop */ +} + +var bufferFrom = __nccwpck_require__(82415); + +/** + * Requires a module which is protected against bundler minification. + * + * @param {NodeModule} mod + * @param {string} request + */ +function dynamicRequire(mod, request) { + return mod.require(request); +} + +// Only install once if called multiple times +var errorFormatterInstalled = false; +var uncaughtShimInstalled = false; + +// If true, the caches are reset before a stack trace formatting operation +var emptyCacheBetweenOperations = false; + +// Supports {browser, node, auto} +var environment = "auto"; + +// Maps a file path to a string containing the file contents +var fileContentsCache = {}; + +// Maps a file path to a source map for that file +var sourceMapCache = {}; + +// Regex for detecting source maps +var reSourceMap = /^data:application\/json[^,]+base64,/; + +// Priority list of retrieve handlers +var retrieveFileHandlers = []; +var retrieveMapHandlers = []; + +function isInBrowser() { + if (environment === "browser") + return true; + if (environment === "node") + return false; + return ((typeof window !== 'undefined') && (typeof XMLHttpRequest === 'function') && !(window.require && window.module && window.process && window.process.type === "renderer")); +} + +function hasGlobalProcessEventEmitter() { + return ((typeof process === 'object') && (process !== null) && (typeof process.on === 'function')); +} + +function globalProcessVersion() { + if ((typeof process === 'object') && (process !== null)) { + return process.version; + } else { + return ''; + } +} + +function globalProcessStderr() { + if ((typeof process === 'object') && (process !== null)) { + return process.stderr; + } +} + +function globalProcessExit(code) { + if ((typeof process === 'object') && (process !== null) && (typeof process.exit === 'function')) { + return process.exit(code); + } +} + +function handlerExec(list) { + return function(arg) { + for (var i = 0; i < list.length; i++) { + var ret = list[i](arg); + if (ret) { + return ret; + } + } + return null; + }; +} + +var retrieveFile = handlerExec(retrieveFileHandlers); + +retrieveFileHandlers.push(function(path) { + // Trim the path to make sure there is no extra whitespace. + path = path.trim(); + if (/^file:/.test(path)) { + // existsSync/readFileSync can't handle file protocol, but once stripped, it works + path = path.replace(/file:\/\/\/(\w:)?/, function(protocol, drive) { + return drive ? + '' : // file:///C:/dir/file -> C:/dir/file + '/'; // file:///root-dir/file -> /root-dir/file + }); + } + if (path in fileContentsCache) { + return fileContentsCache[path]; + } + + var contents = ''; + try { + if (!fs) { + // Use SJAX if we are in the browser + var xhr = new XMLHttpRequest(); + xhr.open('GET', path, /** async */ false); + xhr.send(null); + if (xhr.readyState === 4 && xhr.status === 200) { + contents = xhr.responseText; + } + } else if (fs.existsSync(path)) { + // Otherwise, use the filesystem + contents = fs.readFileSync(path, 'utf8'); + } + } catch (er) { + /* ignore any errors */ + } + + return fileContentsCache[path] = contents; +}); + +// Support URLs relative to a directory, but be careful about a protocol prefix +// in case we are in the browser (i.e. directories may start with "http://" or "file:///") +function supportRelativeURL(file, url) { + if (!file) return url; + var dir = path.dirname(file); + var match = /^\w+:\/\/[^\/]*/.exec(dir); + var protocol = match ? match[0] : ''; + var startPath = dir.slice(protocol.length); + if (protocol && /^\/\w\:/.test(startPath)) { + // handle file:///C:/ paths + protocol += '/'; + return protocol + path.resolve(dir.slice(protocol.length), url).replace(/\\/g, '/'); + } + return protocol + path.resolve(dir.slice(protocol.length), url); +} + +function retrieveSourceMapURL(source) { + var fileData; + + if (isInBrowser()) { + try { + var xhr = new XMLHttpRequest(); + xhr.open('GET', source, false); + xhr.send(null); + fileData = xhr.readyState === 4 ? xhr.responseText : null; + + // Support providing a sourceMappingURL via the SourceMap header + var sourceMapHeader = xhr.getResponseHeader("SourceMap") || + xhr.getResponseHeader("X-SourceMap"); + if (sourceMapHeader) { + return sourceMapHeader; + } + } catch (e) { + } + } + + // Get the URL of the source map + fileData = retrieveFile(source); + var re = /(?:\/\/[@#][\s]*sourceMappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*sourceMappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/mg; + // Keep executing the search to find the *last* sourceMappingURL to avoid + // picking up sourceMappingURLs from comments, strings, etc. + var lastMatch, match; + while (match = re.exec(fileData)) lastMatch = match; + if (!lastMatch) return null; + return lastMatch[1]; +}; + +// Can be overridden by the retrieveSourceMap option to install. Takes a +// generated source filename; returns a {map, optional url} object, or null if +// there is no source map. The map field may be either a string or the parsed +// JSON object (ie, it must be a valid argument to the SourceMapConsumer +// constructor). +var retrieveSourceMap = handlerExec(retrieveMapHandlers); +retrieveMapHandlers.push(function(source) { + var sourceMappingURL = retrieveSourceMapURL(source); + if (!sourceMappingURL) return null; + + // Read the contents of the source map + var sourceMapData; + if (reSourceMap.test(sourceMappingURL)) { + // Support source map URL as a data url + var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1); + sourceMapData = bufferFrom(rawData, "base64").toString(); + sourceMappingURL = source; + } else { + // Support source map URLs relative to the source URL + sourceMappingURL = supportRelativeURL(source, sourceMappingURL); + sourceMapData = retrieveFile(sourceMappingURL); + } + + if (!sourceMapData) { + return null; + } + + return { + url: sourceMappingURL, + map: sourceMapData + }; +}); + +function mapSourcePosition(position) { + var sourceMap = sourceMapCache[position.source]; + if (!sourceMap) { + // Call the (overrideable) retrieveSourceMap function to get the source map. + var urlAndMap = retrieveSourceMap(position.source); + if (urlAndMap) { + sourceMap = sourceMapCache[position.source] = { + url: urlAndMap.url, + map: new SourceMapConsumer(urlAndMap.map) + }; + + // Load all sources stored inline with the source map into the file cache + // to pretend like they are already loaded. They may not exist on disk. + if (sourceMap.map.sourcesContent) { + sourceMap.map.sources.forEach(function(source, i) { + var contents = sourceMap.map.sourcesContent[i]; + if (contents) { + var url = supportRelativeURL(sourceMap.url, source); + fileContentsCache[url] = contents; + } + }); + } + } else { + sourceMap = sourceMapCache[position.source] = { + url: null, + map: null + }; + } + } + + // Resolve the source URL relative to the URL of the source map + if (sourceMap && sourceMap.map && typeof sourceMap.map.originalPositionFor === 'function') { + var originalPosition = sourceMap.map.originalPositionFor(position); + + // Only return the original position if a matching line was found. If no + // matching line is found then we return position instead, which will cause + // the stack trace to print the path and line for the compiled file. It is + // better to give a precise location in the compiled file than a vague + // location in the original file. + if (originalPosition.source !== null) { + originalPosition.source = supportRelativeURL( + sourceMap.url, originalPosition.source); + return originalPosition; + } + } + + return position; +} + +// Parses code generated by FormatEvalOrigin(), a function inside V8: +// https://code.google.com/p/v8/source/browse/trunk/src/messages.js +function mapEvalOrigin(origin) { + // Most eval() calls are in this format + var match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin); + if (match) { + var position = mapSourcePosition({ + source: match[2], + line: +match[3], + column: match[4] - 1 + }); + return 'eval at ' + match[1] + ' (' + position.source + ':' + + position.line + ':' + (position.column + 1) + ')'; + } + + // Parse nested eval() calls using recursion + match = /^eval at ([^(]+) \((.+)\)$/.exec(origin); + if (match) { + return 'eval at ' + match[1] + ' (' + mapEvalOrigin(match[2]) + ')'; + } + + // Make sure we still return useful information if we didn't find anything + return origin; +} + +// This is copied almost verbatim from the V8 source code at +// https://code.google.com/p/v8/source/browse/trunk/src/messages.js. The +// implementation of wrapCallSite() used to just forward to the actual source +// code of CallSite.prototype.toString but unfortunately a new release of V8 +// did something to the prototype chain and broke the shim. The only fix I +// could find was copy/paste. +function CallSiteToString() { + var fileName; + var fileLocation = ""; + if (this.isNative()) { + fileLocation = "native"; + } else { + fileName = this.getScriptNameOrSourceURL(); + if (!fileName && this.isEval()) { + fileLocation = this.getEvalOrigin(); + fileLocation += ", "; // Expecting source position to follow. + } + + if (fileName) { + fileLocation += fileName; + } else { + // Source code does not originate from a file and is not native, but we + // can still get the source position inside the source string, e.g. in + // an eval string. + fileLocation += ""; + } + var lineNumber = this.getLineNumber(); + if (lineNumber != null) { + fileLocation += ":" + lineNumber; + var columnNumber = this.getColumnNumber(); + if (columnNumber) { + fileLocation += ":" + columnNumber; + } + } + } + + var line = ""; + var functionName = this.getFunctionName(); + var addSuffix = true; + var isConstructor = this.isConstructor(); + var isMethodCall = !(this.isToplevel() || isConstructor); + if (isMethodCall) { + var typeName = this.getTypeName(); + // Fixes shim to be backward compatable with Node v0 to v4 + if (typeName === "[object Object]") { + typeName = "null"; + } + var methodName = this.getMethodName(); + if (functionName) { + if (typeName && functionName.indexOf(typeName) != 0) { + line += typeName + "."; + } + line += functionName; + if (methodName && functionName.indexOf("." + methodName) != functionName.length - methodName.length - 1) { + line += " [as " + methodName + "]"; + } + } else { + line += typeName + "." + (methodName || ""); + } + } else if (isConstructor) { + line += "new " + (functionName || ""); + } else if (functionName) { + line += functionName; + } else { + line += fileLocation; + addSuffix = false; + } + if (addSuffix) { + line += " (" + fileLocation + ")"; + } + return line; +} + +function cloneCallSite(frame) { + var object = {}; + Object.getOwnPropertyNames(Object.getPrototypeOf(frame)).forEach(function(name) { + object[name] = /^(?:is|get)/.test(name) ? function() { return frame[name].call(frame); } : frame[name]; + }); + object.toString = CallSiteToString; + return object; +} + +function wrapCallSite(frame, state) { + // provides interface backward compatibility + if (state === undefined) { + state = { nextPosition: null, curPosition: null } + } + if(frame.isNative()) { + state.curPosition = null; + return frame; + } + + // Most call sites will return the source file from getFileName(), but code + // passed to eval() ending in "//# sourceURL=..." will return the source file + // from getScriptNameOrSourceURL() instead + var source = frame.getFileName() || frame.getScriptNameOrSourceURL(); + if (source) { + var line = frame.getLineNumber(); + var column = frame.getColumnNumber() - 1; + + // Fix position in Node where some (internal) code is prepended. + // See https://github.com/evanw/node-source-map-support/issues/36 + // Header removed in node at ^10.16 || >=11.11.0 + // v11 is not an LTS candidate, we can just test the one version with it. + // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 + var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/; + var headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62; + if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) { + column -= headerLength; + } + + var position = mapSourcePosition({ + source: source, + line: line, + column: column + }); + state.curPosition = position; + frame = cloneCallSite(frame); + var originalFunctionName = frame.getFunctionName; + frame.getFunctionName = function() { + if (state.nextPosition == null) { + return originalFunctionName(); + } + return state.nextPosition.name || originalFunctionName(); + }; + frame.getFileName = function() { return position.source; }; + frame.getLineNumber = function() { return position.line; }; + frame.getColumnNumber = function() { return position.column + 1; }; + frame.getScriptNameOrSourceURL = function() { return position.source; }; + return frame; + } + + // Code called using eval() needs special handling + var origin = frame.isEval() && frame.getEvalOrigin(); + if (origin) { + origin = mapEvalOrigin(origin); + frame = cloneCallSite(frame); + frame.getEvalOrigin = function() { return origin; }; + return frame; + } + + // If we get here then we were unable to change the source position + return frame; +} + +// This function is part of the V8 stack trace API, for more info see: +// https://v8.dev/docs/stack-trace-api +function prepareStackTrace(error, stack) { + if (emptyCacheBetweenOperations) { + fileContentsCache = {}; + sourceMapCache = {}; + } + + var name = error.name || 'Error'; + var message = error.message || ''; + var errorString = name + ": " + message; + + var state = { nextPosition: null, curPosition: null }; + var processedStack = []; + for (var i = stack.length - 1; i >= 0; i--) { + processedStack.push('\n at ' + wrapCallSite(stack[i], state)); + state.nextPosition = state.curPosition; + } + state.curPosition = state.nextPosition = null; + return errorString + processedStack.reverse().join(''); +} + +// Generate position and snippet of original source with pointer +function getErrorSource(error) { + var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack); + if (match) { + var source = match[1]; + var line = +match[2]; + var column = +match[3]; + + // Support the inline sourceContents inside the source map + var contents = fileContentsCache[source]; + + // Support files on disk + if (!contents && fs && fs.existsSync(source)) { + try { + contents = fs.readFileSync(source, 'utf8'); + } catch (er) { + contents = ''; + } + } + + // Format the line from the original source code like node does + if (contents) { + var code = contents.split(/(?:\r\n|\r|\n)/)[line - 1]; + if (code) { + return source + ':' + line + '\n' + code + '\n' + + new Array(column).join(' ') + '^'; + } + } + } + return null; +} + +function printErrorAndExit (error) { + var source = getErrorSource(error); + + // Ensure error is printed synchronously and not truncated + var stderr = globalProcessStderr(); + if (stderr && stderr._handle && stderr._handle.setBlocking) { + stderr._handle.setBlocking(true); + } + + if (source) { + console.error(); + console.error(source); + } + + console.error(error.stack); + globalProcessExit(1); +} + +function shimEmitUncaughtException () { + var origEmit = process.emit; + + process.emit = function (type) { + if (type === 'uncaughtException') { + var hasStack = (arguments[1] && arguments[1].stack); + var hasListeners = (this.listeners(type).length > 0); + + if (hasStack && !hasListeners) { + return printErrorAndExit(arguments[1]); + } + } + + return origEmit.apply(this, arguments); + }; +} + +var originalRetrieveFileHandlers = retrieveFileHandlers.slice(0); +var originalRetrieveMapHandlers = retrieveMapHandlers.slice(0); + +exports.wrapCallSite = wrapCallSite; +exports.getErrorSource = getErrorSource; +exports.mapSourcePosition = mapSourcePosition; +exports.retrieveSourceMap = retrieveSourceMap; + +exports.install = function(options) { + options = options || {}; + + if (options.environment) { + environment = options.environment; + if (["node", "browser", "auto"].indexOf(environment) === -1) { + throw new Error("environment " + environment + " was unknown. Available options are {auto, browser, node}") + } + } + + // Allow sources to be found by methods other than reading the files + // directly from disk. + if (options.retrieveFile) { + if (options.overrideRetrieveFile) { + retrieveFileHandlers.length = 0; + } + + retrieveFileHandlers.unshift(options.retrieveFile); + } + + // Allow source maps to be found by methods other than reading the files + // directly from disk. + if (options.retrieveSourceMap) { + if (options.overrideRetrieveSourceMap) { + retrieveMapHandlers.length = 0; + } + + retrieveMapHandlers.unshift(options.retrieveSourceMap); + } + + // Support runtime transpilers that include inline source maps + if (options.hookRequire && !isInBrowser()) { + // Use dynamicRequire to avoid including in browser bundles + var Module = dynamicRequire(module, 'module'); + var $compile = Module.prototype._compile; + + if (!$compile.__sourceMapSupport) { + Module.prototype._compile = function(content, filename) { + fileContentsCache[filename] = content; + sourceMapCache[filename] = undefined; + return $compile.call(this, content, filename); + }; + + Module.prototype._compile.__sourceMapSupport = true; + } + } + + // Configure options + if (!emptyCacheBetweenOperations) { + emptyCacheBetweenOperations = 'emptyCacheBetweenOperations' in options ? + options.emptyCacheBetweenOperations : false; + } + + // Install the error reformatter + if (!errorFormatterInstalled) { + errorFormatterInstalled = true; + Error.prepareStackTrace = prepareStackTrace; + } + + if (!uncaughtShimInstalled) { + var installHandler = 'handleUncaughtExceptions' in options ? + options.handleUncaughtExceptions : true; + + // Do not override 'uncaughtException' with our own handler in Node.js + // Worker threads. Workers pass the error to the main thread as an event, + // rather than printing something to stderr and exiting. + try { + // We need to use `dynamicRequire` because `require` on it's own will be optimized by WebPack/Browserify. + var worker_threads = dynamicRequire(module, 'worker_threads'); + if (worker_threads.isMainThread === false) { + installHandler = false; + } + } catch(e) {} + + // Provide the option to not install the uncaught exception handler. This is + // to support other uncaught exception handlers (in test frameworks, for + // example). If this handler is not installed and there are no other uncaught + // exception handlers, uncaught exceptions will be caught by node's built-in + // exception handler and the process will still be terminated. However, the + // generated JavaScript code will be shown above the stack trace instead of + // the original source code. + if (installHandler && hasGlobalProcessEventEmitter()) { + uncaughtShimInstalled = true; + shimEmitUncaughtException(); + } + } +}; + +exports.resetRetrieveHandlers = function() { + retrieveFileHandlers.length = 0; + retrieveMapHandlers.length = 0; + + retrieveFileHandlers = originalRetrieveFileHandlers.slice(0); + retrieveMapHandlers = originalRetrieveMapHandlers.slice(0); + + retrieveSourceMap = handlerExec(retrieveMapHandlers); + retrieveFile = handlerExec(retrieveFileHandlers); +} + + /***/ }), /***/ 89484: @@ -73804,7 +74437,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.serializeJson = exports.parseJson = exports.stripJsonComments = void 0; const jsonc_parser_1 = __nccwpck_require__(56416); Object.defineProperty(exports, "stripJsonComments", ({ enumerable: true, get: function () { return jsonc_parser_1.stripComments; } })); -const lines_and_columns_1 = __nccwpck_require__(74570); +const lines_and_columns_1 = __nccwpck_require__(34583); const code_frames_1 = __nccwpck_require__(73133); /** * Parses the given JSON string and returns the object the JSON content represents. @@ -85479,26 +86112,6 @@ function hasGlobalProcessEventEmitter() { return ((typeof process === 'object') && (process !== null) && (typeof process.on === 'function')); } -function globalProcessVersion() { - if ((typeof process === 'object') && (process !== null)) { - return process.version; - } else { - return ''; - } -} - -function globalProcessStderr() { - if ((typeof process === 'object') && (process !== null)) { - return process.stderr; - } -} - -function globalProcessExit(code) { - if ((typeof process === 'object') && (process !== null) && (typeof process.exit === 'function')) { - return process.exit(code); - } -} - function handlerExec(list) { return function(arg) { for (var i = 0; i < list.length; i++) { @@ -85809,7 +86422,7 @@ function wrapCallSite(frame, state) { // v11 is not an LTS candidate, we can just test the one version with it. // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/; - var headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62; + var headerLength = noHeader.test(process.version) ? 0 : 62; if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) { column -= headerLength; } @@ -85906,9 +86519,8 @@ function printErrorAndExit (error) { var source = getErrorSource(error); // Ensure error is printed synchronously and not truncated - var stderr = globalProcessStderr(); - if (stderr && stderr._handle && stderr._handle.setBlocking) { - stderr._handle.setBlocking(true); + if (process.stderr._handle && process.stderr._handle.setBlocking) { + process.stderr._handle.setBlocking(true); } if (source) { @@ -85917,7 +86529,7 @@ function printErrorAndExit (error) { } console.error(error.stack); - globalProcessExit(1); + process.exit(1); } function shimEmitUncaughtException () { @@ -271838,14 +272450,35 @@ exports.execCommand = execCommand; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.hasOneOfRequiredTags = void 0; -function hasOneOfRequiredTags(tags, requiresOneOfTheseTags) { - if (!requiresOneOfTheseTags || requiresOneOfTheseTags.length === 0) { +function doesTagMatchOneOfTheConditions(conditions, tag) { + return conditions.some((condition) => { + if (condition.includes('=') && tag.includes('=')) { + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); + let splitOn = '='; + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); + splitOn = '!='; + } + const [conditionKey, conditionValue] = condition.split(splitOn); + const [key, value] = tag.split('='); + if (conditionKey !== key) { + return false; + } + return matcher(conditionValue, value); + } + else { + return condition === tag; + } + }); +} +function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { + if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true; } if (!tags || tags.length === 0) { return false; } - return tags.some((tag) => requiresOneOfTheseTags.includes(tag)); + return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)); } exports.hasOneOfRequiredTags = hasOneOfRequiredTags; @@ -272747,7 +273380,7 @@ exports.yellowBright = yellowBright; /***/ }), -/***/ 74570: +/***/ 34583: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -272964,7 +273597,7 @@ function run() { const matrixInclude = []; for (const target of targets) { core.debug(`Getting info for target "${target}"`); - const requiresOnOfTheseTags = core.getMultilineInput(`${target}Tag`, { trimWhitespace: true }); + const tagConditions = core.getMultilineInput(`${target}Tag`, { trimWhitespace: true }); const maxJobs = parseInt(core.getInput(`${target}MaxJobs`), 10) || 1; const parallel = core.getInput(`${target}Parallel`); const preTargets = core.getMultilineInput(`${target}PreTargets`) || []; @@ -272973,15 +273606,15 @@ function run() { .map((projectName) => { const { targets, tags } = projects.get(projectName); if (Object.keys(targets).includes(target)) { - return (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(tags, requiresOnOfTheseTags); + return (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(tags, tagConditions); } return false; }) .filter(Boolean); if (amountOfProjectsWithTarget.length === 0) { let debugMessage = `No projects changed with target "${target}"`; - if (requiresOnOfTheseTags.length > 0) { - debugMessage += ` and one of the following tags "${requiresOnOfTheseTags.join(', ')}"`; + if (tagConditions.length > 0) { + debugMessage += ` and matching one of the following conditions "${tagConditions.join(', ')}"`; } core.debug(debugMessage); continue; @@ -272997,7 +273630,7 @@ function run() { for (let i = 0; i < maxJobCount; i++) { matrixInclude.push({ target, - tag: requiresOnOfTheseTags + tag: tagConditions .join('\n'), preTargets: preTargets.join('\n'), postTargets: postTargets.join('\n'), diff --git a/actions/plan/jest.config.ts b/actions/plan/jest.config.ts new file mode 100644 index 00000000..d4dd2621 --- /dev/null +++ b/actions/plan/jest.config.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ +export default { + displayName: 'actions-plan', + preset: '../../jest.preset.js', + globals: {}, + transform: { + '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/actions/plan', + testEnvironment: 'node' +} diff --git a/actions/plan/project.json b/actions/plan/project.json index beed7676..3195d2fc 100644 --- a/actions/plan/project.json +++ b/actions/plan/project.json @@ -17,6 +17,14 @@ "npx ncc build ./actions/plan/src/plan.ts --out ./actions/plan/dist" ] } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/actions/plan"], + "options": { + "jestConfig": "actions/plan/jest.config.ts", + "passWithNoTests": true + } } }, "tags": [] diff --git a/actions/plan/src/plan.ts b/actions/plan/src/plan.ts index e59e6c8b..c372c991 100644 --- a/actions/plan/src/plan.ts +++ b/actions/plan/src/plan.ts @@ -37,7 +37,7 @@ async function run() { for (const target of targets) { core.debug(`Getting info for target "${target}"`) - const requiresOnOfTheseTags = core.getMultilineInput(`${target}Tag`, { trimWhitespace: true }) + const tagConditions = core.getMultilineInput(`${target}Tag`, { trimWhitespace: true }) const maxJobs = parseInt(core.getInput(`${target}MaxJobs`), 10) || 1 const parallel = core.getInput(`${target}Parallel`) const preTargets = core.getMultilineInput(`${target}PreTargets`) || [] @@ -48,7 +48,7 @@ async function run() { const { targets, tags } = projects.get(projectName) if (Object.keys(targets).includes(target)) { - return hasOneOfRequiredTags(tags, requiresOnOfTheseTags) + return hasOneOfRequiredTags(tags, tagConditions) } return false @@ -57,8 +57,8 @@ async function run() { if (amountOfProjectsWithTarget.length === 0) { let debugMessage = `No projects changed with target "${target}"` - if (requiresOnOfTheseTags.length > 0) { - debugMessage += ` and one of the following tags "${requiresOnOfTheseTags.join(', ')}"` + if (tagConditions.length > 0) { + debugMessage += ` and matching one of the following conditions "${tagConditions.join(', ')}"` } core.debug(debugMessage) @@ -77,7 +77,7 @@ async function run() { for (let i = 0; i < maxJobCount; i++) { matrixInclude.push({ target, - tag: requiresOnOfTheseTags + tag: tagConditions .join('\n'), preTargets: preTargets.join('\n'), postTargets: postTargets.join('\n'), diff --git a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts new file mode 100644 index 00000000..7b05fdd2 --- /dev/null +++ b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts @@ -0,0 +1,37 @@ +import { hasOneOfRequiredTags } from '../has-one-of-required-tags' + +const projectTags = [ + 'resource=vercel', + 'resource=fake', + 'other-fake-tag' +] + +describe('hasOneOfRequiredTags', () => { + describe('matches', () => { + it('should have a match', () => { + expect(hasOneOfRequiredTags(projectTags, ['other-fake-tag'])).toEqual(true) + }) + + it('should match with "=" condition', () => { + expect(hasOneOfRequiredTags(projectTags, ['resource=vercel'])).toEqual(true) + }) + + it('should match with "!=" condition', () => { + expect(hasOneOfRequiredTags(projectTags, ['resource!=vercel'])).toEqual(true) + }) + }) + + describe('no matches', () => { + it('should have a match', () => { + expect(hasOneOfRequiredTags(projectTags, ['other-fake-tags'])).toEqual(false) + }) + + it('should match with "=" condition', () => { + expect(hasOneOfRequiredTags(projectTags, ['resource=firebase'])).toEqual(false) + }) + + it('should match with "!=" condition', () => { + expect(hasOneOfRequiredTags(projectTags, ['non-existing-key!=firebase'])).toEqual(false) + }) + }) +}) diff --git a/actions/plan/src/utils/has-one-of-required-tags.ts b/actions/plan/src/utils/has-one-of-required-tags.ts index 7ec15c92..c5c33ed6 100644 --- a/actions/plan/src/utils/has-one-of-required-tags.ts +++ b/actions/plan/src/utils/has-one-of-required-tags.ts @@ -1,5 +1,30 @@ -export function hasOneOfRequiredTags(tags?: string[], requiresOneOfTheseTags?: string[]): boolean { - if (!requiresOneOfTheseTags || requiresOneOfTheseTags.length === 0) { +function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { + return conditions.some((condition) => { + if (condition.includes('=') && tag.includes('=')) { + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim() + let splitOn = '=' + + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim() + splitOn = '!=' + } + + const [conditionKey, conditionValue] = condition.split(splitOn) + const [key, value] = tag.split('=') + + if (conditionKey !== key) { + return false + } + + return matcher(conditionValue, value) + } else { + return condition === tag + } + }) +} + +export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { + if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true } @@ -7,5 +32,5 @@ export function hasOneOfRequiredTags(tags?: string[], requiresOneOfTheseTags?: s return false } - return tags.some((tag) => requiresOneOfTheseTags.includes(tag)) + return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)) } diff --git a/actions/plan/tsconfig.spec.json b/actions/plan/tsconfig.spec.json new file mode 100644 index 00000000..fd04d434 --- /dev/null +++ b/actions/plan/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.spec.js", + "**/*.spec.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/actions/run-many/dist/index.js b/actions/run-many/dist/index.js index bf5d8a8d..a09c2d10 100644 --- a/actions/run-many/dist/index.js +++ b/actions/run-many/dist/index.js @@ -4961,7 +4961,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.installSourceMapSupport = exports.SourcemapMap = void 0; -const source_map_support_1 = __importDefault(__nccwpck_require__(42059)); +const source_map_support_1 = __importDefault(__nccwpck_require__(95953)); exports.SourcemapMap = new Map(); function installSourceMapSupport() { source_map_support_1.default.install({ @@ -4981,6 +4981,639 @@ function installSourceMapSupport() { exports.installSourceMapSupport = installSourceMapSupport; //# sourceMappingURL=index.js.map +/***/ }), + +/***/ 95953: +/***/ ((module, exports, __nccwpck_require__) => { + +/* module decorator */ module = __nccwpck_require__.nmd(module); +var SourceMapConsumer = (__nccwpck_require__(36096).SourceMapConsumer); +var path = __nccwpck_require__(71017); + +var fs; +try { + fs = __nccwpck_require__(57147); + if (!fs.existsSync || !fs.readFileSync) { + // fs doesn't have all methods we need + fs = null; + } +} catch (err) { + /* nop */ +} + +var bufferFrom = __nccwpck_require__(82415); + +/** + * Requires a module which is protected against bundler minification. + * + * @param {NodeModule} mod + * @param {string} request + */ +function dynamicRequire(mod, request) { + return mod.require(request); +} + +// Only install once if called multiple times +var errorFormatterInstalled = false; +var uncaughtShimInstalled = false; + +// If true, the caches are reset before a stack trace formatting operation +var emptyCacheBetweenOperations = false; + +// Supports {browser, node, auto} +var environment = "auto"; + +// Maps a file path to a string containing the file contents +var fileContentsCache = {}; + +// Maps a file path to a source map for that file +var sourceMapCache = {}; + +// Regex for detecting source maps +var reSourceMap = /^data:application\/json[^,]+base64,/; + +// Priority list of retrieve handlers +var retrieveFileHandlers = []; +var retrieveMapHandlers = []; + +function isInBrowser() { + if (environment === "browser") + return true; + if (environment === "node") + return false; + return ((typeof window !== 'undefined') && (typeof XMLHttpRequest === 'function') && !(window.require && window.module && window.process && window.process.type === "renderer")); +} + +function hasGlobalProcessEventEmitter() { + return ((typeof process === 'object') && (process !== null) && (typeof process.on === 'function')); +} + +function globalProcessVersion() { + if ((typeof process === 'object') && (process !== null)) { + return process.version; + } else { + return ''; + } +} + +function globalProcessStderr() { + if ((typeof process === 'object') && (process !== null)) { + return process.stderr; + } +} + +function globalProcessExit(code) { + if ((typeof process === 'object') && (process !== null) && (typeof process.exit === 'function')) { + return process.exit(code); + } +} + +function handlerExec(list) { + return function(arg) { + for (var i = 0; i < list.length; i++) { + var ret = list[i](arg); + if (ret) { + return ret; + } + } + return null; + }; +} + +var retrieveFile = handlerExec(retrieveFileHandlers); + +retrieveFileHandlers.push(function(path) { + // Trim the path to make sure there is no extra whitespace. + path = path.trim(); + if (/^file:/.test(path)) { + // existsSync/readFileSync can't handle file protocol, but once stripped, it works + path = path.replace(/file:\/\/\/(\w:)?/, function(protocol, drive) { + return drive ? + '' : // file:///C:/dir/file -> C:/dir/file + '/'; // file:///root-dir/file -> /root-dir/file + }); + } + if (path in fileContentsCache) { + return fileContentsCache[path]; + } + + var contents = ''; + try { + if (!fs) { + // Use SJAX if we are in the browser + var xhr = new XMLHttpRequest(); + xhr.open('GET', path, /** async */ false); + xhr.send(null); + if (xhr.readyState === 4 && xhr.status === 200) { + contents = xhr.responseText; + } + } else if (fs.existsSync(path)) { + // Otherwise, use the filesystem + contents = fs.readFileSync(path, 'utf8'); + } + } catch (er) { + /* ignore any errors */ + } + + return fileContentsCache[path] = contents; +}); + +// Support URLs relative to a directory, but be careful about a protocol prefix +// in case we are in the browser (i.e. directories may start with "http://" or "file:///") +function supportRelativeURL(file, url) { + if (!file) return url; + var dir = path.dirname(file); + var match = /^\w+:\/\/[^\/]*/.exec(dir); + var protocol = match ? match[0] : ''; + var startPath = dir.slice(protocol.length); + if (protocol && /^\/\w\:/.test(startPath)) { + // handle file:///C:/ paths + protocol += '/'; + return protocol + path.resolve(dir.slice(protocol.length), url).replace(/\\/g, '/'); + } + return protocol + path.resolve(dir.slice(protocol.length), url); +} + +function retrieveSourceMapURL(source) { + var fileData; + + if (isInBrowser()) { + try { + var xhr = new XMLHttpRequest(); + xhr.open('GET', source, false); + xhr.send(null); + fileData = xhr.readyState === 4 ? xhr.responseText : null; + + // Support providing a sourceMappingURL via the SourceMap header + var sourceMapHeader = xhr.getResponseHeader("SourceMap") || + xhr.getResponseHeader("X-SourceMap"); + if (sourceMapHeader) { + return sourceMapHeader; + } + } catch (e) { + } + } + + // Get the URL of the source map + fileData = retrieveFile(source); + var re = /(?:\/\/[@#][\s]*sourceMappingURL=([^\s'"]+)[\s]*$)|(?:\/\*[@#][\s]*sourceMappingURL=([^\s*'"]+)[\s]*(?:\*\/)[\s]*$)/mg; + // Keep executing the search to find the *last* sourceMappingURL to avoid + // picking up sourceMappingURLs from comments, strings, etc. + var lastMatch, match; + while (match = re.exec(fileData)) lastMatch = match; + if (!lastMatch) return null; + return lastMatch[1]; +}; + +// Can be overridden by the retrieveSourceMap option to install. Takes a +// generated source filename; returns a {map, optional url} object, or null if +// there is no source map. The map field may be either a string or the parsed +// JSON object (ie, it must be a valid argument to the SourceMapConsumer +// constructor). +var retrieveSourceMap = handlerExec(retrieveMapHandlers); +retrieveMapHandlers.push(function(source) { + var sourceMappingURL = retrieveSourceMapURL(source); + if (!sourceMappingURL) return null; + + // Read the contents of the source map + var sourceMapData; + if (reSourceMap.test(sourceMappingURL)) { + // Support source map URL as a data url + var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1); + sourceMapData = bufferFrom(rawData, "base64").toString(); + sourceMappingURL = source; + } else { + // Support source map URLs relative to the source URL + sourceMappingURL = supportRelativeURL(source, sourceMappingURL); + sourceMapData = retrieveFile(sourceMappingURL); + } + + if (!sourceMapData) { + return null; + } + + return { + url: sourceMappingURL, + map: sourceMapData + }; +}); + +function mapSourcePosition(position) { + var sourceMap = sourceMapCache[position.source]; + if (!sourceMap) { + // Call the (overrideable) retrieveSourceMap function to get the source map. + var urlAndMap = retrieveSourceMap(position.source); + if (urlAndMap) { + sourceMap = sourceMapCache[position.source] = { + url: urlAndMap.url, + map: new SourceMapConsumer(urlAndMap.map) + }; + + // Load all sources stored inline with the source map into the file cache + // to pretend like they are already loaded. They may not exist on disk. + if (sourceMap.map.sourcesContent) { + sourceMap.map.sources.forEach(function(source, i) { + var contents = sourceMap.map.sourcesContent[i]; + if (contents) { + var url = supportRelativeURL(sourceMap.url, source); + fileContentsCache[url] = contents; + } + }); + } + } else { + sourceMap = sourceMapCache[position.source] = { + url: null, + map: null + }; + } + } + + // Resolve the source URL relative to the URL of the source map + if (sourceMap && sourceMap.map && typeof sourceMap.map.originalPositionFor === 'function') { + var originalPosition = sourceMap.map.originalPositionFor(position); + + // Only return the original position if a matching line was found. If no + // matching line is found then we return position instead, which will cause + // the stack trace to print the path and line for the compiled file. It is + // better to give a precise location in the compiled file than a vague + // location in the original file. + if (originalPosition.source !== null) { + originalPosition.source = supportRelativeURL( + sourceMap.url, originalPosition.source); + return originalPosition; + } + } + + return position; +} + +// Parses code generated by FormatEvalOrigin(), a function inside V8: +// https://code.google.com/p/v8/source/browse/trunk/src/messages.js +function mapEvalOrigin(origin) { + // Most eval() calls are in this format + var match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin); + if (match) { + var position = mapSourcePosition({ + source: match[2], + line: +match[3], + column: match[4] - 1 + }); + return 'eval at ' + match[1] + ' (' + position.source + ':' + + position.line + ':' + (position.column + 1) + ')'; + } + + // Parse nested eval() calls using recursion + match = /^eval at ([^(]+) \((.+)\)$/.exec(origin); + if (match) { + return 'eval at ' + match[1] + ' (' + mapEvalOrigin(match[2]) + ')'; + } + + // Make sure we still return useful information if we didn't find anything + return origin; +} + +// This is copied almost verbatim from the V8 source code at +// https://code.google.com/p/v8/source/browse/trunk/src/messages.js. The +// implementation of wrapCallSite() used to just forward to the actual source +// code of CallSite.prototype.toString but unfortunately a new release of V8 +// did something to the prototype chain and broke the shim. The only fix I +// could find was copy/paste. +function CallSiteToString() { + var fileName; + var fileLocation = ""; + if (this.isNative()) { + fileLocation = "native"; + } else { + fileName = this.getScriptNameOrSourceURL(); + if (!fileName && this.isEval()) { + fileLocation = this.getEvalOrigin(); + fileLocation += ", "; // Expecting source position to follow. + } + + if (fileName) { + fileLocation += fileName; + } else { + // Source code does not originate from a file and is not native, but we + // can still get the source position inside the source string, e.g. in + // an eval string. + fileLocation += ""; + } + var lineNumber = this.getLineNumber(); + if (lineNumber != null) { + fileLocation += ":" + lineNumber; + var columnNumber = this.getColumnNumber(); + if (columnNumber) { + fileLocation += ":" + columnNumber; + } + } + } + + var line = ""; + var functionName = this.getFunctionName(); + var addSuffix = true; + var isConstructor = this.isConstructor(); + var isMethodCall = !(this.isToplevel() || isConstructor); + if (isMethodCall) { + var typeName = this.getTypeName(); + // Fixes shim to be backward compatable with Node v0 to v4 + if (typeName === "[object Object]") { + typeName = "null"; + } + var methodName = this.getMethodName(); + if (functionName) { + if (typeName && functionName.indexOf(typeName) != 0) { + line += typeName + "."; + } + line += functionName; + if (methodName && functionName.indexOf("." + methodName) != functionName.length - methodName.length - 1) { + line += " [as " + methodName + "]"; + } + } else { + line += typeName + "." + (methodName || ""); + } + } else if (isConstructor) { + line += "new " + (functionName || ""); + } else if (functionName) { + line += functionName; + } else { + line += fileLocation; + addSuffix = false; + } + if (addSuffix) { + line += " (" + fileLocation + ")"; + } + return line; +} + +function cloneCallSite(frame) { + var object = {}; + Object.getOwnPropertyNames(Object.getPrototypeOf(frame)).forEach(function(name) { + object[name] = /^(?:is|get)/.test(name) ? function() { return frame[name].call(frame); } : frame[name]; + }); + object.toString = CallSiteToString; + return object; +} + +function wrapCallSite(frame, state) { + // provides interface backward compatibility + if (state === undefined) { + state = { nextPosition: null, curPosition: null } + } + if(frame.isNative()) { + state.curPosition = null; + return frame; + } + + // Most call sites will return the source file from getFileName(), but code + // passed to eval() ending in "//# sourceURL=..." will return the source file + // from getScriptNameOrSourceURL() instead + var source = frame.getFileName() || frame.getScriptNameOrSourceURL(); + if (source) { + var line = frame.getLineNumber(); + var column = frame.getColumnNumber() - 1; + + // Fix position in Node where some (internal) code is prepended. + // See https://github.com/evanw/node-source-map-support/issues/36 + // Header removed in node at ^10.16 || >=11.11.0 + // v11 is not an LTS candidate, we can just test the one version with it. + // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 + var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/; + var headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62; + if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) { + column -= headerLength; + } + + var position = mapSourcePosition({ + source: source, + line: line, + column: column + }); + state.curPosition = position; + frame = cloneCallSite(frame); + var originalFunctionName = frame.getFunctionName; + frame.getFunctionName = function() { + if (state.nextPosition == null) { + return originalFunctionName(); + } + return state.nextPosition.name || originalFunctionName(); + }; + frame.getFileName = function() { return position.source; }; + frame.getLineNumber = function() { return position.line; }; + frame.getColumnNumber = function() { return position.column + 1; }; + frame.getScriptNameOrSourceURL = function() { return position.source; }; + return frame; + } + + // Code called using eval() needs special handling + var origin = frame.isEval() && frame.getEvalOrigin(); + if (origin) { + origin = mapEvalOrigin(origin); + frame = cloneCallSite(frame); + frame.getEvalOrigin = function() { return origin; }; + return frame; + } + + // If we get here then we were unable to change the source position + return frame; +} + +// This function is part of the V8 stack trace API, for more info see: +// https://v8.dev/docs/stack-trace-api +function prepareStackTrace(error, stack) { + if (emptyCacheBetweenOperations) { + fileContentsCache = {}; + sourceMapCache = {}; + } + + var name = error.name || 'Error'; + var message = error.message || ''; + var errorString = name + ": " + message; + + var state = { nextPosition: null, curPosition: null }; + var processedStack = []; + for (var i = stack.length - 1; i >= 0; i--) { + processedStack.push('\n at ' + wrapCallSite(stack[i], state)); + state.nextPosition = state.curPosition; + } + state.curPosition = state.nextPosition = null; + return errorString + processedStack.reverse().join(''); +} + +// Generate position and snippet of original source with pointer +function getErrorSource(error) { + var match = /\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(error.stack); + if (match) { + var source = match[1]; + var line = +match[2]; + var column = +match[3]; + + // Support the inline sourceContents inside the source map + var contents = fileContentsCache[source]; + + // Support files on disk + if (!contents && fs && fs.existsSync(source)) { + try { + contents = fs.readFileSync(source, 'utf8'); + } catch (er) { + contents = ''; + } + } + + // Format the line from the original source code like node does + if (contents) { + var code = contents.split(/(?:\r\n|\r|\n)/)[line - 1]; + if (code) { + return source + ':' + line + '\n' + code + '\n' + + new Array(column).join(' ') + '^'; + } + } + } + return null; +} + +function printErrorAndExit (error) { + var source = getErrorSource(error); + + // Ensure error is printed synchronously and not truncated + var stderr = globalProcessStderr(); + if (stderr && stderr._handle && stderr._handle.setBlocking) { + stderr._handle.setBlocking(true); + } + + if (source) { + console.error(); + console.error(source); + } + + console.error(error.stack); + globalProcessExit(1); +} + +function shimEmitUncaughtException () { + var origEmit = process.emit; + + process.emit = function (type) { + if (type === 'uncaughtException') { + var hasStack = (arguments[1] && arguments[1].stack); + var hasListeners = (this.listeners(type).length > 0); + + if (hasStack && !hasListeners) { + return printErrorAndExit(arguments[1]); + } + } + + return origEmit.apply(this, arguments); + }; +} + +var originalRetrieveFileHandlers = retrieveFileHandlers.slice(0); +var originalRetrieveMapHandlers = retrieveMapHandlers.slice(0); + +exports.wrapCallSite = wrapCallSite; +exports.getErrorSource = getErrorSource; +exports.mapSourcePosition = mapSourcePosition; +exports.retrieveSourceMap = retrieveSourceMap; + +exports.install = function(options) { + options = options || {}; + + if (options.environment) { + environment = options.environment; + if (["node", "browser", "auto"].indexOf(environment) === -1) { + throw new Error("environment " + environment + " was unknown. Available options are {auto, browser, node}") + } + } + + // Allow sources to be found by methods other than reading the files + // directly from disk. + if (options.retrieveFile) { + if (options.overrideRetrieveFile) { + retrieveFileHandlers.length = 0; + } + + retrieveFileHandlers.unshift(options.retrieveFile); + } + + // Allow source maps to be found by methods other than reading the files + // directly from disk. + if (options.retrieveSourceMap) { + if (options.overrideRetrieveSourceMap) { + retrieveMapHandlers.length = 0; + } + + retrieveMapHandlers.unshift(options.retrieveSourceMap); + } + + // Support runtime transpilers that include inline source maps + if (options.hookRequire && !isInBrowser()) { + // Use dynamicRequire to avoid including in browser bundles + var Module = dynamicRequire(module, 'module'); + var $compile = Module.prototype._compile; + + if (!$compile.__sourceMapSupport) { + Module.prototype._compile = function(content, filename) { + fileContentsCache[filename] = content; + sourceMapCache[filename] = undefined; + return $compile.call(this, content, filename); + }; + + Module.prototype._compile.__sourceMapSupport = true; + } + } + + // Configure options + if (!emptyCacheBetweenOperations) { + emptyCacheBetweenOperations = 'emptyCacheBetweenOperations' in options ? + options.emptyCacheBetweenOperations : false; + } + + // Install the error reformatter + if (!errorFormatterInstalled) { + errorFormatterInstalled = true; + Error.prepareStackTrace = prepareStackTrace; + } + + if (!uncaughtShimInstalled) { + var installHandler = 'handleUncaughtExceptions' in options ? + options.handleUncaughtExceptions : true; + + // Do not override 'uncaughtException' with our own handler in Node.js + // Worker threads. Workers pass the error to the main thread as an event, + // rather than printing something to stderr and exiting. + try { + // We need to use `dynamicRequire` because `require` on it's own will be optimized by WebPack/Browserify. + var worker_threads = dynamicRequire(module, 'worker_threads'); + if (worker_threads.isMainThread === false) { + installHandler = false; + } + } catch(e) {} + + // Provide the option to not install the uncaught exception handler. This is + // to support other uncaught exception handlers (in test frameworks, for + // example). If this handler is not installed and there are no other uncaught + // exception handlers, uncaught exceptions will be caught by node's built-in + // exception handler and the process will still be terminated. However, the + // generated JavaScript code will be shown above the stack trace instead of + // the original source code. + if (installHandler && hasGlobalProcessEventEmitter()) { + uncaughtShimInstalled = true; + shimEmitUncaughtException(); + } + } +}; + +exports.resetRetrieveHandlers = function() { + retrieveFileHandlers.length = 0; + retrieveMapHandlers.length = 0; + + retrieveFileHandlers = originalRetrieveFileHandlers.slice(0); + retrieveMapHandlers = originalRetrieveMapHandlers.slice(0); + + retrieveSourceMap = handlerExec(retrieveMapHandlers); + retrieveFile = handlerExec(retrieveFileHandlers); +} + + /***/ }), /***/ 89484: @@ -73930,7 +74563,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.serializeJson = exports.parseJson = exports.stripJsonComments = void 0; const jsonc_parser_1 = __nccwpck_require__(56416); Object.defineProperty(exports, "stripJsonComments", ({ enumerable: true, get: function () { return jsonc_parser_1.stripComments; } })); -const lines_and_columns_1 = __nccwpck_require__(74570); +const lines_and_columns_1 = __nccwpck_require__(34583); const code_frames_1 = __nccwpck_require__(73133); /** * Parses the given JSON string and returns the object the JSON content represents. @@ -85699,26 +86332,6 @@ function hasGlobalProcessEventEmitter() { return ((typeof process === 'object') && (process !== null) && (typeof process.on === 'function')); } -function globalProcessVersion() { - if ((typeof process === 'object') && (process !== null)) { - return process.version; - } else { - return ''; - } -} - -function globalProcessStderr() { - if ((typeof process === 'object') && (process !== null)) { - return process.stderr; - } -} - -function globalProcessExit(code) { - if ((typeof process === 'object') && (process !== null) && (typeof process.exit === 'function')) { - return process.exit(code); - } -} - function handlerExec(list) { return function(arg) { for (var i = 0; i < list.length; i++) { @@ -86029,7 +86642,7 @@ function wrapCallSite(frame, state) { // v11 is not an LTS candidate, we can just test the one version with it. // Test node versions for: 10.16-19, 10.20+, 12-19, 20-99, 100+, or 11.11 var noHeader = /^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/; - var headerLength = noHeader.test(globalProcessVersion()) ? 0 : 62; + var headerLength = noHeader.test(process.version) ? 0 : 62; if (line === 1 && column > headerLength && !isInBrowser() && !frame.isEval()) { column -= headerLength; } @@ -86126,9 +86739,8 @@ function printErrorAndExit (error) { var source = getErrorSource(error); // Ensure error is printed synchronously and not truncated - var stderr = globalProcessStderr(); - if (stderr && stderr._handle && stderr._handle.setBlocking) { - stderr._handle.setBlocking(true); + if (process.stderr._handle && process.stderr._handle.setBlocking) { + process.stderr._handle.setBlocking(true); } if (source) { @@ -86137,7 +86749,7 @@ function printErrorAndExit (error) { } console.error(error.stack); - globalProcessExit(1); + process.exit(1); } function shimEmitUncaughtException () { @@ -272337,6 +272949,7 @@ const helpers_1 = __nccwpck_require__(30329); const yargs_1 = tslib_1.__importDefault(__nccwpck_require__(68654)); const build_command_1 = __nccwpck_require__(70384); const exec_1 = __nccwpck_require__(59902); +const has_one_of_required_tags_1 = __nccwpck_require__(51922); const run_target_1 = __nccwpck_require__(81067); exports.argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)) .options({ @@ -272353,7 +272966,7 @@ function run() { const nxTree = new tree_1.FsTree(process.cwd(), false); const projects = (0, project_configuration_1.getProjects)(nxTree); // Get all options - const withTag = core.getMultilineInput('tag', { trimWhitespace: true }) || (exports.argv.tag ? [exports.argv.tag] : []); + const tagConditions = core.getMultilineInput('tag', { trimWhitespace: true }) || (exports.argv.tag ? [exports.argv.tag] : []); const target = core.getInput('target', { required: !exports.argv.target }) || exports.argv.target; const config = core.getInput('config') || exports.argv.config; const jobIndex = parseInt(core.getInput('index') || '1', 10); @@ -272370,8 +272983,8 @@ function run() { core.debug(`Job count ${jobCount}`); core.debug(`Pre targets ${JSON.stringify(preTargets, null, 2)}`); core.debug(`Post targets ${JSON.stringify(postTargets, null, 2)}`); - if (withTag.length > 0) { - core.info(`Running all projects with one of the following tags "${withTag.join(', ')}"`); + if (tagConditions.length > 0) { + core.info(`Running all projects with one of the following tags "${tagConditions.join(', ')}"`); } const cwd = (0, path_1.resolve)(process.cwd(), workingDirectory); // Get all affected projects @@ -272395,8 +273008,11 @@ function run() { core.debug(`[${projectName}]: Has the "ci=off" tag, skipping it.`); return false; } + if (!tagConditions || tagConditions.length === 0) { + return true; + } // If a tag is provided the project should have it - return (!withTag || withTag.length === 0) || tags.some((tag) => withTag.includes(tag)); + return (!tagConditions || tagConditions.length === 0) || (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(tags, tagConditions); }).sort((projectNameA, projectNameB) => (projectNameA.localeCompare(projectNameB))); const sliceSize = Math.max(Math.floor(projectsToRun.length / jobCount), 1); const runProjects = jobIndex < jobCount @@ -272572,6 +273188,48 @@ function getRestArgs() { exports.getRestArgs = getRestArgs; +/***/ }), + +/***/ 51922: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.hasOneOfRequiredTags = void 0; +function doesTagMatchOneOfTheConditions(conditions, tag) { + return conditions.some((condition) => { + if (condition.includes('=') && tag.includes('=')) { + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); + let splitOn = '='; + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); + splitOn = '!='; + } + const [conditionKey, conditionValue] = condition.split(splitOn); + const [key, value] = tag.split('='); + if (conditionKey !== key) { + return false; + } + return matcher(conditionValue, value); + } + else { + return condition === tag; + } + }); +} +function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { + if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { + return true; + } + if (!tags || tags.length === 0) { + return false; + } + return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)); +} +exports.hasOneOfRequiredTags = hasOneOfRequiredTags; + + /***/ }), /***/ 81067: @@ -273607,7 +274265,7 @@ exports.yellowBright = yellowBright; /***/ }), -/***/ 74570: +/***/ 34583: /***/ ((__unused_webpack_module, exports) => { "use strict"; diff --git a/actions/run-many/src/run-many.ts b/actions/run-many/src/run-many.ts index cd97a7c2..0e988c1a 100644 --- a/actions/run-many/src/run-many.ts +++ b/actions/run-many/src/run-many.ts @@ -7,6 +7,7 @@ import yargs from 'yargs/yargs' import { buildCommand } from './utils/build-command' import { execCommand } from './utils/exec' +import { hasOneOfRequiredTags } from './utils/has-one-of-required-tags' import { runTarget } from './utils/run-target' export const argv = yargs(hideBin(process.argv)) @@ -25,7 +26,7 @@ async function run() { const projects = getProjects(nxTree) // Get all options - const withTag = core.getMultilineInput('tag', { trimWhitespace: true }) || (argv.tag ? [argv.tag] : []) + const tagConditions = core.getMultilineInput('tag', { trimWhitespace: true }) || (argv.tag ? [argv.tag] : []) const target = core.getInput('target', { required: !argv.target }) || argv.target const config = core.getInput('config') || argv.config const jobIndex = parseInt(core.getInput('index') || '1', 10) @@ -45,8 +46,8 @@ async function run() { core.debug(`Pre targets ${JSON.stringify(preTargets, null, 2)}`) core.debug(`Post targets ${JSON.stringify(postTargets, null, 2)}`) - if (withTag.length > 0) { - core.info(`Running all projects with one of the following tags "${withTag.join(', ')}"`) + if (tagConditions.length > 0) { + core.info(`Running all projects with one of the following tags "${tagConditions.join(', ')}"`) } const cwd = resolve(process.cwd(), workingDirectory) @@ -79,8 +80,12 @@ async function run() { return false } + if (!tagConditions || tagConditions.length === 0) { + return true + } + // If a tag is provided the project should have it - return (!withTag || withTag.length === 0) || tags.some((tag) => withTag.includes(tag)) + return (!tagConditions || tagConditions.length === 0) || hasOneOfRequiredTags(tags, tagConditions) }).sort((projectNameA, projectNameB) => ( projectNameA.localeCompare(projectNameB) )) diff --git a/actions/run-many/src/utils/has-one-of-required-tags.ts b/actions/run-many/src/utils/has-one-of-required-tags.ts new file mode 100644 index 00000000..c5c33ed6 --- /dev/null +++ b/actions/run-many/src/utils/has-one-of-required-tags.ts @@ -0,0 +1,36 @@ +function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { + return conditions.some((condition) => { + if (condition.includes('=') && tag.includes('=')) { + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim() + let splitOn = '=' + + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim() + splitOn = '!=' + } + + const [conditionKey, conditionValue] = condition.split(splitOn) + const [key, value] = tag.split('=') + + if (conditionKey !== key) { + return false + } + + return matcher(conditionValue, value) + } else { + return condition === tag + } + }) +} + +export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { + if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { + return true + } + + if (!tags || tags.length === 0) { + return false + } + + return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)) +} From 346766ea0fe4c33d2ecae4406dda0233c9b39866 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Sat, 22 Jul 2023 17:20:25 +0200 Subject: [PATCH 2/6] refactor(vercel): Linting / lining --- .../vercel/src/executors/build/build.impl.ts | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/vercel/src/executors/build/build.impl.ts b/packages/vercel/src/executors/build/build.impl.ts index 6233988e..bafe1e4a 100644 --- a/packages/vercel/src/executors/build/build.impl.ts +++ b/packages/vercel/src/executors/build/build.impl.ts @@ -28,8 +28,7 @@ export function buildExecutor( ): Promise<{ success: boolean }> { const { targets } = context.workspace.projects[context.projectName] const framework = options.framework || 'nextjs' - const buildTarget = - options.buildTarget || (framework === 'nextjs' ? 'build-next' : 'build') + const buildTarget = options.buildTarget || (framework === 'nextjs' ? 'build-next' : 'build') if (!options.orgId) { throw new Error(`"orgId" option is required!`) @@ -49,10 +48,7 @@ export function buildExecutor( throw new Error(`"${buildTarget}" target has no "outputPath" configured!`) } - if ( - options.buildConfig && - !targets[buildTarget]?.configurations[options.buildConfig] - ) { + if (options.buildConfig && !targets[buildTarget]?.configurations[options.buildConfig]) { throw new Error( `"${buildTarget}" target has no configuration "${options.buildConfig}"!` ) @@ -74,19 +70,16 @@ export function buildExecutor( settings: {} }) - const vercelEnironment = - context.configurationName === 'production' ? 'production' : 'preview' + const vercelEnironment = context.configurationName === 'production' ? 'production' : 'preview' // Pull latest - const { success: pullSuccess } = execCommand( - buildCommand([ - 'npx vercel pull --yes', - `--environment=${vercelEnironment}`, - vercelToken && `--token=${vercelToken}`, + const { success: pullSuccess } = execCommand(buildCommand([ + 'npx vercel pull --yes', + `--environment=${vercelEnironment}`, + vercelToken && `--token=${vercelToken}`, - options.debug && '--debug' - ]) - ) + options.debug && '--debug' + ])) if (!pullSuccess) { throw new Error(`Was unable to pull!`) @@ -124,16 +117,14 @@ export function buildExecutor( } }) - const { success } = execCommand( - buildCommand([ - 'npx vercel build', - `--output ${targets[buildTarget].options.outputPath}/.vercel/output`, - context.configurationName === 'production' && '--prod', - vercelToken && `--token=${vercelToken}`, + const { success } = execCommand(buildCommand([ + 'npx vercel build', + `--output ${targets[buildTarget].options.outputPath}/.vercel/output`, + context.configurationName === 'production' && '--prod', + vercelToken && `--token=${vercelToken}`, - options.debug && '--debug' - ]) - ) + options.debug && '--debug' + ])) if (success) { // Write the project.json to the .vercel directory From 7c3c445635053747b3d8cce36c465273437c1a1d Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Sat, 22 Jul 2023 17:31:02 +0200 Subject: [PATCH 3/6] feat(actions-run-many): Always log with `--output-style=stream` for better readability --- actions/run-many/dist/index.js | 1 + actions/run-many/src/utils/run-target.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/actions/run-many/dist/index.js b/actions/run-many/dist/index.js index a09c2d10..3922c2d5 100644 --- a/actions/run-many/dist/index.js +++ b/actions/run-many/dist/index.js @@ -273258,6 +273258,7 @@ function runTarget(cwd, projects, runProjects, target, config, parallel, withSum 'npx nx run-many', `--target=${target}`, `--projects=${projectsWithTarget.join(',')}`, + `--output-style=stream`, config && `--configuration=${config}`, (core.isDebug() || run_many_1.argv.verbose) && '--verbose', `${(0, get_rest_args_1.getRestArgs)()}` diff --git a/actions/run-many/src/utils/run-target.ts b/actions/run-many/src/utils/run-target.ts index c569a699..37f34b2b 100644 --- a/actions/run-many/src/utils/run-target.ts +++ b/actions/run-many/src/utils/run-target.ts @@ -33,6 +33,7 @@ export async function runTarget( 'npx nx run-many', `--target=${target}`, `--projects=${projectsWithTarget.join(',')}`, + `--output-style=stream`, config && `--configuration=${config}`, (core.isDebug() || argv.verbose) && '--verbose', `${getRestArgs()}` From 3fdf7c645c378ba3398335d696fd225545f08444 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Thu, 27 Jul 2023 14:15:07 +0200 Subject: [PATCH 4/6] feat(actions): Added support for `AND` condition in tag --- actions/plan/dist/index.js | 38 ++++++++++------- .../has-one-of-required-tags.spec.ts | 11 +++++ .../src/utils/has-one-of-required-tags.ts | 42 ++++++++++++------- actions/run-many/dist/index.js | 38 ++++++++++------- .../src/utils/has-one-of-required-tags.ts | 42 ++++++++++++------- 5 files changed, 107 insertions(+), 64 deletions(-) diff --git a/actions/plan/dist/index.js b/actions/plan/dist/index.js index a22108c2..9c315e50 100644 --- a/actions/plan/dist/index.js +++ b/actions/plan/dist/index.js @@ -272450,26 +272450,32 @@ exports.execCommand = execCommand; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.hasOneOfRequiredTags = void 0; -function doesTagMatchOneOfTheConditions(conditions, tag) { - return conditions.some((condition) => { - if (condition.includes('=') && tag.includes('=')) { - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); - let splitOn = '='; - if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); - splitOn = '!='; - } - const [conditionKey, conditionValue] = condition.split(splitOn); +function hasTagMatchingCondition(condition, tags) { + if (condition.includes('=')) { + // If it includes a "," it's a AND condition + if (condition.includes(',')) { + const subConditions = condition.split(',').map((subCondition) => subCondition.trim()); + // It should match all the conditions + return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)); + } + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); + let splitOn = '='; + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); + splitOn = '!='; + } + const [conditionKey, conditionValue] = condition.split(splitOn); + return tags.some((tag) => { const [key, value] = tag.split('='); if (conditionKey !== key) { return false; } return matcher(conditionValue, value); - } - else { - return condition === tag; - } - }); + }); + } + else { + return tags.some((tag) => tag === condition); + } } function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { @@ -272478,7 +272484,7 @@ function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { if (!tags || tags.length === 0) { return false; } - return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)); + return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); } exports.hasOneOfRequiredTags = hasOneOfRequiredTags; diff --git a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts index 7b05fdd2..3904def5 100644 --- a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts +++ b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts @@ -34,4 +34,15 @@ describe('hasOneOfRequiredTags', () => { expect(hasOneOfRequiredTags(projectTags, ['non-existing-key!=firebase'])).toEqual(false) }) }) + + describe('and condition', () => { + it('should match', () => { + expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource=fake'])).toEqual(true) + expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource!=not-exist'])).toEqual(true) + }) + + it('should not match', () => { + expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource=not-exist'])).toEqual(false) + }) + }) }) diff --git a/actions/plan/src/utils/has-one-of-required-tags.ts b/actions/plan/src/utils/has-one-of-required-tags.ts index c5c33ed6..40e2dc73 100644 --- a/actions/plan/src/utils/has-one-of-required-tags.ts +++ b/actions/plan/src/utils/has-one-of-required-tags.ts @@ -1,15 +1,24 @@ -function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { - return conditions.some((condition) => { - if (condition.includes('=') && tag.includes('=')) { - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim() - let splitOn = '=' - - if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim() - splitOn = '!=' - } +function hasTagMatchingCondition(condition: string, tags: string[]): boolean { + if (condition.includes('=')) { + // If it includes a "," it's a AND condition + if (condition.includes(',')) { + const subConditions = condition.split(',').map((subCondition) => subCondition.trim()) + + // It should match all the conditions + return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)) + } + + let matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() === tagValue.trim() + let splitOn = '=' + + if (condition.includes('!=')) { + matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() !== tagValue.trim() + splitOn = '!=' + } + + const [conditionKey, conditionValue] = condition.split(splitOn) - const [conditionKey, conditionValue] = condition.split(splitOn) + return tags.some((tag) => { const [key, value] = tag.split('=') if (conditionKey !== key) { @@ -17,10 +26,11 @@ function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { } return matcher(conditionValue, value) - } else { - return condition === tag - } - }) + }) + + } else { + return tags.some((tag) => tag === condition) + } } export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { @@ -32,5 +42,5 @@ export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagCondit return false } - return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)) + return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) } diff --git a/actions/run-many/dist/index.js b/actions/run-many/dist/index.js index 3922c2d5..3e41d225 100644 --- a/actions/run-many/dist/index.js +++ b/actions/run-many/dist/index.js @@ -273197,26 +273197,32 @@ exports.getRestArgs = getRestArgs; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.hasOneOfRequiredTags = void 0; -function doesTagMatchOneOfTheConditions(conditions, tag) { - return conditions.some((condition) => { - if (condition.includes('=') && tag.includes('=')) { - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); - let splitOn = '='; - if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); - splitOn = '!='; - } - const [conditionKey, conditionValue] = condition.split(splitOn); +function hasTagMatchingCondition(condition, tags) { + if (condition.includes('=')) { + // If it includes a "," it's a AND condition + if (condition.includes(',')) { + const subConditions = condition.split(',').map((subCondition) => subCondition.trim()); + // It should match all the conditions + return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)); + } + let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); + let splitOn = '='; + if (condition.includes('!=')) { + matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); + splitOn = '!='; + } + const [conditionKey, conditionValue] = condition.split(splitOn); + return tags.some((tag) => { const [key, value] = tag.split('='); if (conditionKey !== key) { return false; } return matcher(conditionValue, value); - } - else { - return condition === tag; - } - }); + }); + } + else { + return tags.some((tag) => tag === condition); + } } function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { @@ -273225,7 +273231,7 @@ function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { if (!tags || tags.length === 0) { return false; } - return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)); + return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); } exports.hasOneOfRequiredTags = hasOneOfRequiredTags; diff --git a/actions/run-many/src/utils/has-one-of-required-tags.ts b/actions/run-many/src/utils/has-one-of-required-tags.ts index c5c33ed6..40e2dc73 100644 --- a/actions/run-many/src/utils/has-one-of-required-tags.ts +++ b/actions/run-many/src/utils/has-one-of-required-tags.ts @@ -1,15 +1,24 @@ -function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { - return conditions.some((condition) => { - if (condition.includes('=') && tag.includes('=')) { - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim() - let splitOn = '=' - - if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim() - splitOn = '!=' - } +function hasTagMatchingCondition(condition: string, tags: string[]): boolean { + if (condition.includes('=')) { + // If it includes a "," it's a AND condition + if (condition.includes(',')) { + const subConditions = condition.split(',').map((subCondition) => subCondition.trim()) + + // It should match all the conditions + return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)) + } + + let matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() === tagValue.trim() + let splitOn = '=' + + if (condition.includes('!=')) { + matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() !== tagValue.trim() + splitOn = '!=' + } + + const [conditionKey, conditionValue] = condition.split(splitOn) - const [conditionKey, conditionValue] = condition.split(splitOn) + return tags.some((tag) => { const [key, value] = tag.split('=') if (conditionKey !== key) { @@ -17,10 +26,11 @@ function doesTagMatchOneOfTheConditions(conditions: string[], tag: string) { } return matcher(conditionValue, value) - } else { - return condition === tag - } - }) + }) + + } else { + return tags.some((tag) => tag === condition) + } } export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { @@ -32,5 +42,5 @@ export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagCondit return false } - return tags.some((tag) => doesTagMatchOneOfTheConditions(requiresOnOfTheseTagConditions, tag)) + return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) } From 8794a57441a84f7542d2e525644622b70cba56a6 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Thu, 27 Jul 2023 14:26:30 +0200 Subject: [PATCH 5/6] refactor(actions): Improve logging when conditions fail --- actions/plan/dist/index.js | 23 +++++++++++++++---- actions/plan/src/plan.ts | 2 +- .../has-one-of-required-tags.spec.ts | 18 +++++++-------- .../src/utils/has-one-of-required-tags.ts | 23 +++++++++++++++++-- actions/run-many/dist/index.js | 23 +++++++++++++++---- actions/run-many/src/run-many.ts | 2 +- .../src/utils/has-one-of-required-tags.ts | 23 +++++++++++++++++-- 7 files changed, 91 insertions(+), 23 deletions(-) diff --git a/actions/plan/dist/index.js b/actions/plan/dist/index.js index 9c315e50..8c4b38ba 100644 --- a/actions/plan/dist/index.js +++ b/actions/plan/dist/index.js @@ -272444,12 +272444,14 @@ exports.execCommand = execCommand; /***/ }), /***/ 47777: -/***/ ((__unused_webpack_module, exports) => { +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.hasOneOfRequiredTags = void 0; +const tslib_1 = __nccwpck_require__(65315); +const core = tslib_1.__importStar(__nccwpck_require__(57896)); function hasTagMatchingCondition(condition, tags) { if (condition.includes('=')) { // If it includes a "," it's a AND condition @@ -272477,14 +272479,27 @@ function hasTagMatchingCondition(condition, tags) { return tags.some((tag) => tag === condition); } } -function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { +function cleanLogConditions(conditions) { + return conditions.map((condition) => { + if (condition.includes(',')) { + return condition.split(',').map((subCondition) => subCondition.trim()) + .join(' AND '); + } + return condition; + }).join(' OR '); +} +function hasOneOfRequiredTags(projectName, tags, requiresOnOfTheseTagConditions) { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true; } if (!tags || tags.length === 0) { return false; } - return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); + const hasMatch = requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); + if (!hasMatch) { + core.debug(`[${projectName}]: Does not match any of the provided condition "${cleanLogConditions(requiresOnOfTheseTagConditions)}"`); + } + return hasMatch; } exports.hasOneOfRequiredTags = hasOneOfRequiredTags; @@ -273612,7 +273627,7 @@ function run() { .map((projectName) => { const { targets, tags } = projects.get(projectName); if (Object.keys(targets).includes(target)) { - return (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(tags, tagConditions); + return (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(projectName, tags, tagConditions); } return false; }) diff --git a/actions/plan/src/plan.ts b/actions/plan/src/plan.ts index c372c991..72287228 100644 --- a/actions/plan/src/plan.ts +++ b/actions/plan/src/plan.ts @@ -48,7 +48,7 @@ async function run() { const { targets, tags } = projects.get(projectName) if (Object.keys(targets).includes(target)) { - return hasOneOfRequiredTags(tags, tagConditions) + return hasOneOfRequiredTags(projectName,tags, tagConditions) } return false diff --git a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts index 3904def5..96a4398d 100644 --- a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts +++ b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts @@ -9,40 +9,40 @@ const projectTags = [ describe('hasOneOfRequiredTags', () => { describe('matches', () => { it('should have a match', () => { - expect(hasOneOfRequiredTags(projectTags, ['other-fake-tag'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['other-fake-tag'])).toEqual(true) }) it('should match with "=" condition', () => { - expect(hasOneOfRequiredTags(projectTags, ['resource=vercel'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=vercel'])).toEqual(true) }) it('should match with "!=" condition', () => { - expect(hasOneOfRequiredTags(projectTags, ['resource!=vercel'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource!=vercel'])).toEqual(true) }) }) describe('no matches', () => { it('should have a match', () => { - expect(hasOneOfRequiredTags(projectTags, ['other-fake-tags'])).toEqual(false) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['other-fake-tags'])).toEqual(false) }) it('should match with "=" condition', () => { - expect(hasOneOfRequiredTags(projectTags, ['resource=firebase'])).toEqual(false) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=firebase'])).toEqual(false) }) it('should match with "!=" condition', () => { - expect(hasOneOfRequiredTags(projectTags, ['non-existing-key!=firebase'])).toEqual(false) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['non-existing-key!=firebase'])).toEqual(false) }) }) describe('and condition', () => { it('should match', () => { - expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource=fake'])).toEqual(true) - expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource!=not-exist'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=vercel,resource=fake'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=vercel,resource!=not-exist'])).toEqual(true) }) it('should not match', () => { - expect(hasOneOfRequiredTags(projectTags, ['resource=vercel,resource=not-exist'])).toEqual(false) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=vercel,resource=not-exist'])).toEqual(false) }) }) }) diff --git a/actions/plan/src/utils/has-one-of-required-tags.ts b/actions/plan/src/utils/has-one-of-required-tags.ts index 40e2dc73..fb1f0827 100644 --- a/actions/plan/src/utils/has-one-of-required-tags.ts +++ b/actions/plan/src/utils/has-one-of-required-tags.ts @@ -1,3 +1,5 @@ +import * as core from '@actions/core' + function hasTagMatchingCondition(condition: string, tags: string[]): boolean { if (condition.includes('=')) { // If it includes a "," it's a AND condition @@ -33,7 +35,18 @@ function hasTagMatchingCondition(condition: string, tags: string[]): boolean { } } -export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { +function cleanLogConditions(conditions: string[]) { + return conditions.map((condition) => { + if (condition.includes(',')) { + return condition.split(',').map((subCondition) => subCondition.trim()) + .join(' AND ') + } + + return condition + }).join(' OR ') +} + +export function hasOneOfRequiredTags(projectName: string, tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true } @@ -42,5 +55,11 @@ export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagCondit return false } - return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) + const hasMatch = requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) + + if (!hasMatch) { + core.debug(`[${projectName}]: Does not match any of the provided condition "${cleanLogConditions(requiresOnOfTheseTagConditions)}"`) + } + + return hasMatch } diff --git a/actions/run-many/dist/index.js b/actions/run-many/dist/index.js index 3e41d225..5b2e87f5 100644 --- a/actions/run-many/dist/index.js +++ b/actions/run-many/dist/index.js @@ -273012,7 +273012,7 @@ function run() { return true; } // If a tag is provided the project should have it - return (!tagConditions || tagConditions.length === 0) || (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(tags, tagConditions); + return (!tagConditions || tagConditions.length === 0) || (0, has_one_of_required_tags_1.hasOneOfRequiredTags)(projectName, tags, tagConditions); }).sort((projectNameA, projectNameB) => (projectNameA.localeCompare(projectNameB))); const sliceSize = Math.max(Math.floor(projectsToRun.length / jobCount), 1); const runProjects = jobIndex < jobCount @@ -273191,12 +273191,14 @@ exports.getRestArgs = getRestArgs; /***/ }), /***/ 51922: -/***/ ((__unused_webpack_module, exports) => { +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.hasOneOfRequiredTags = void 0; +const tslib_1 = __nccwpck_require__(65315); +const core = tslib_1.__importStar(__nccwpck_require__(57896)); function hasTagMatchingCondition(condition, tags) { if (condition.includes('=')) { // If it includes a "," it's a AND condition @@ -273224,14 +273226,27 @@ function hasTagMatchingCondition(condition, tags) { return tags.some((tag) => tag === condition); } } -function hasOneOfRequiredTags(tags, requiresOnOfTheseTagConditions) { +function cleanLogConditions(conditions) { + return conditions.map((condition) => { + if (condition.includes(',')) { + return condition.split(',').map((subCondition) => subCondition.trim()) + .join(' AND '); + } + return condition; + }).join(' OR '); +} +function hasOneOfRequiredTags(projectName, tags, requiresOnOfTheseTagConditions) { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true; } if (!tags || tags.length === 0) { return false; } - return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); + const hasMatch = requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)); + if (!hasMatch) { + core.debug(`[${projectName}]: Does not match any of the provided condition "${cleanLogConditions(requiresOnOfTheseTagConditions)}"`); + } + return hasMatch; } exports.hasOneOfRequiredTags = hasOneOfRequiredTags; diff --git a/actions/run-many/src/run-many.ts b/actions/run-many/src/run-many.ts index 0e988c1a..aa5af4fa 100644 --- a/actions/run-many/src/run-many.ts +++ b/actions/run-many/src/run-many.ts @@ -85,7 +85,7 @@ async function run() { } // If a tag is provided the project should have it - return (!tagConditions || tagConditions.length === 0) || hasOneOfRequiredTags(tags, tagConditions) + return (!tagConditions || tagConditions.length === 0) || hasOneOfRequiredTags(projectName, tags, tagConditions) }).sort((projectNameA, projectNameB) => ( projectNameA.localeCompare(projectNameB) )) diff --git a/actions/run-many/src/utils/has-one-of-required-tags.ts b/actions/run-many/src/utils/has-one-of-required-tags.ts index 40e2dc73..fb1f0827 100644 --- a/actions/run-many/src/utils/has-one-of-required-tags.ts +++ b/actions/run-many/src/utils/has-one-of-required-tags.ts @@ -1,3 +1,5 @@ +import * as core from '@actions/core' + function hasTagMatchingCondition(condition: string, tags: string[]): boolean { if (condition.includes('=')) { // If it includes a "," it's a AND condition @@ -33,7 +35,18 @@ function hasTagMatchingCondition(condition: string, tags: string[]): boolean { } } -export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { +function cleanLogConditions(conditions: string[]) { + return conditions.map((condition) => { + if (condition.includes(',')) { + return condition.split(',').map((subCondition) => subCondition.trim()) + .join(' AND ') + } + + return condition + }).join(' OR ') +} + +export function hasOneOfRequiredTags(projectName: string, tags?: string[], requiresOnOfTheseTagConditions?: string[]): boolean { if (!requiresOnOfTheseTagConditions || requiresOnOfTheseTagConditions.length === 0) { return true } @@ -42,5 +55,11 @@ export function hasOneOfRequiredTags(tags?: string[], requiresOnOfTheseTagCondit return false } - return requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) + const hasMatch = requiresOnOfTheseTagConditions.some((condition) => hasTagMatchingCondition(condition, tags)) + + if (!hasMatch) { + core.debug(`[${projectName}]: Does not match any of the provided condition "${cleanLogConditions(requiresOnOfTheseTagConditions)}"`) + } + + return hasMatch } From 0ca42f68de3588fba0a680bf86aaf5ac316781a3 Mon Sep 17 00:00:00 2001 From: Tycho Bokdam Date: Thu, 27 Jul 2023 15:03:58 +0200 Subject: [PATCH 6/6] refactor(actions): Improve `!=` logic for tags --- actions/plan/dist/index.js | 35 ++++++++++------ .../has-one-of-required-tags.spec.ts | 12 ++++-- .../src/utils/has-one-of-required-tags.ts | 40 ++++++++++++------- actions/run-many/dist/index.js | 35 ++++++++++------ .../src/utils/has-one-of-required-tags.ts | 40 ++++++++++++------- 5 files changed, 102 insertions(+), 60 deletions(-) diff --git a/actions/plan/dist/index.js b/actions/plan/dist/index.js index 8c4b38ba..e431663f 100644 --- a/actions/plan/dist/index.js +++ b/actions/plan/dist/index.js @@ -272458,22 +272458,31 @@ function hasTagMatchingCondition(condition, tags) { if (condition.includes(',')) { const subConditions = condition.split(',').map((subCondition) => subCondition.trim()); // It should match all the conditions - return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)); + return subConditions.every((subCondition) => { + return hasTagMatchingCondition(subCondition, tags); + }); } - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); - let splitOn = '='; if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); - splitOn = '!='; - } - const [conditionKey, conditionValue] = condition.split(splitOn); - return tags.some((tag) => { - const [key, value] = tag.split('='); - if (conditionKey !== key) { - return false; + const [conditionKey, conditionValue] = condition.split('!='); + const useTags = tags.filter((tag) => tag.startsWith(`${conditionKey}=`)); + // If the project does not have any tags with the condition key it's allowed + if (useTags.length === 0) { + return true; } - return matcher(conditionValue, value); - }); + return useTags.some((tag) => { + return tag.split('=').pop().trim() !== conditionValue.trim(); + }, []); + } + else { + const [conditionKey, conditionValue] = condition.split('='); + return tags.some((tag) => { + const [key, value] = tag.split('='); + if (conditionKey !== key) { + return false; + } + return conditionValue.trim() === value.trim(); + }); + } } else { return tags.some((tag) => tag === condition); diff --git a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts index 96a4398d..4426e492 100644 --- a/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts +++ b/actions/plan/src/utils/__tests__/has-one-of-required-tags.spec.ts @@ -3,6 +3,7 @@ import { hasOneOfRequiredTags } from '../has-one-of-required-tags' const projectTags = [ 'resource=vercel', 'resource=fake', + 'size=big', 'other-fake-tag' ] @@ -18,20 +19,23 @@ describe('hasOneOfRequiredTags', () => { it('should match with "!=" condition', () => { expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource!=vercel'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['project!=non-existing'])).toEqual(true) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['non-existing-key!=firebase,resource!=vercel'])).toEqual(true) }) }) describe('no matches', () => { - it('should have a match', () => { + it('should not have a match', () => { expect(hasOneOfRequiredTags('fake-project', projectTags, ['other-fake-tags'])).toEqual(false) }) - it('should match with "=" condition', () => { + it('should not match with "=" condition', () => { expect(hasOneOfRequiredTags('fake-project', projectTags, ['resource=firebase'])).toEqual(false) + expect(hasOneOfRequiredTags('fake-project', projectTags, ['non-existing-key=firebase'])).toEqual(false) }) - it('should match with "!=" condition', () => { - expect(hasOneOfRequiredTags('fake-project', projectTags, ['non-existing-key!=firebase'])).toEqual(false) + it('should not match with "!=" condition', () => { + expect(hasOneOfRequiredTags('fake-project', projectTags, ['size!=big'])).toEqual(false) }) }) diff --git a/actions/plan/src/utils/has-one-of-required-tags.ts b/actions/plan/src/utils/has-one-of-required-tags.ts index fb1f0827..63bf7bb1 100644 --- a/actions/plan/src/utils/has-one-of-required-tags.ts +++ b/actions/plan/src/utils/has-one-of-required-tags.ts @@ -7,28 +7,38 @@ function hasTagMatchingCondition(condition: string, tags: string[]): boolean { const subConditions = condition.split(',').map((subCondition) => subCondition.trim()) // It should match all the conditions - return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)) + return subConditions.every((subCondition) => { + return hasTagMatchingCondition(subCondition, tags) + }) } - let matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() === tagValue.trim() - let splitOn = '=' - if (condition.includes('!=')) { - matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() !== tagValue.trim() - splitOn = '!=' - } + const [conditionKey, conditionValue] = condition.split('!=') - const [conditionKey, conditionValue] = condition.split(splitOn) + const useTags = tags.filter((tag) => tag.startsWith(`${conditionKey}=`)) - return tags.some((tag) => { - const [key, value] = tag.split('=') - - if (conditionKey !== key) { - return false + // If the project does not have any tags with the condition key it's allowed + if (useTags.length === 0) { + return true } - return matcher(conditionValue, value) - }) + return useTags.some((tag) => { + return tag.split('=').pop().trim() !== conditionValue.trim() + }, []) + + } else { + const [conditionKey, conditionValue] = condition.split('=') + + return tags.some((tag) => { + const [key, value] = tag.split('=') + + if (conditionKey !== key) { + return false + } + + return conditionValue.trim() === value.trim() + }) + } } else { return tags.some((tag) => tag === condition) diff --git a/actions/run-many/dist/index.js b/actions/run-many/dist/index.js index 5b2e87f5..1490b662 100644 --- a/actions/run-many/dist/index.js +++ b/actions/run-many/dist/index.js @@ -273205,22 +273205,31 @@ function hasTagMatchingCondition(condition, tags) { if (condition.includes(',')) { const subConditions = condition.split(',').map((subCondition) => subCondition.trim()); // It should match all the conditions - return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)); + return subConditions.every((subCondition) => { + return hasTagMatchingCondition(subCondition, tags); + }); } - let matcher = (conditionValue, tagValue) => conditionValue.trim() === tagValue.trim(); - let splitOn = '='; if (condition.includes('!=')) { - matcher = (conditionValue, tagValue) => conditionValue.trim() !== tagValue.trim(); - splitOn = '!='; - } - const [conditionKey, conditionValue] = condition.split(splitOn); - return tags.some((tag) => { - const [key, value] = tag.split('='); - if (conditionKey !== key) { - return false; + const [conditionKey, conditionValue] = condition.split('!='); + const useTags = tags.filter((tag) => tag.startsWith(`${conditionKey}=`)); + // If the project does not have any tags with the condition key it's allowed + if (useTags.length === 0) { + return true; } - return matcher(conditionValue, value); - }); + return useTags.some((tag) => { + return tag.split('=').pop().trim() !== conditionValue.trim(); + }, []); + } + else { + const [conditionKey, conditionValue] = condition.split('='); + return tags.some((tag) => { + const [key, value] = tag.split('='); + if (conditionKey !== key) { + return false; + } + return conditionValue.trim() === value.trim(); + }); + } } else { return tags.some((tag) => tag === condition); diff --git a/actions/run-many/src/utils/has-one-of-required-tags.ts b/actions/run-many/src/utils/has-one-of-required-tags.ts index fb1f0827..63bf7bb1 100644 --- a/actions/run-many/src/utils/has-one-of-required-tags.ts +++ b/actions/run-many/src/utils/has-one-of-required-tags.ts @@ -7,28 +7,38 @@ function hasTagMatchingCondition(condition: string, tags: string[]): boolean { const subConditions = condition.split(',').map((subCondition) => subCondition.trim()) // It should match all the conditions - return subConditions.every((subCondition) => hasTagMatchingCondition(subCondition, tags)) + return subConditions.every((subCondition) => { + return hasTagMatchingCondition(subCondition, tags) + }) } - let matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() === tagValue.trim() - let splitOn = '=' - if (condition.includes('!=')) { - matcher = (conditionValue: string, tagValue: string): boolean => conditionValue.trim() !== tagValue.trim() - splitOn = '!=' - } + const [conditionKey, conditionValue] = condition.split('!=') - const [conditionKey, conditionValue] = condition.split(splitOn) + const useTags = tags.filter((tag) => tag.startsWith(`${conditionKey}=`)) - return tags.some((tag) => { - const [key, value] = tag.split('=') - - if (conditionKey !== key) { - return false + // If the project does not have any tags with the condition key it's allowed + if (useTags.length === 0) { + return true } - return matcher(conditionValue, value) - }) + return useTags.some((tag) => { + return tag.split('=').pop().trim() !== conditionValue.trim() + }, []) + + } else { + const [conditionKey, conditionValue] = condition.split('=') + + return tags.some((tag) => { + const [key, value] = tag.split('=') + + if (conditionKey !== key) { + return false + } + + return conditionValue.trim() === value.trim() + }) + } } else { return tags.some((tag) => tag === condition)