From 9c6893830b53bb4b561293a427c3c20c9ae96f55 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:03:49 +0100 Subject: [PATCH 1/2] #35462: Added support for multiple itemTypes --- dist/index.js | 813 ++++++++++--------- package-lock.json | 6 +- src/action/decorate/summary.ts | 76 +- test/unit/action/decorate/objects/summary.ts | 21 +- test/unit/action/decorate/summary.test.ts | 2 + 5 files changed, 473 insertions(+), 445 deletions(-) diff --git a/dist/index.js b/dist/index.js index e9f7393c..1c5d48d2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -201,6 +201,7 @@ const logger_1 = __nccwpck_require__(26440); const url_1 = __nccwpck_require__(76259); const markdown_1 = __nccwpck_require__(60517); const config_1 = __nccwpck_require__(86444); +const capitalize = (s) => s && String(s[0]).toUpperCase() + String(s).slice(1); function createSummaryBody(analysisResult) { logger_1.logger.header('Creating summary.'); core_1.summary.addHeading('TICS Quality Gate'); @@ -215,7 +216,7 @@ function createSummaryBody(analysisResult) { if (condition.details && condition.details.items.length > 0) { core_1.summary.addRaw(`${os_1.EOL}
${statusMarkdown}${condition.message}${os_1.EOL}`); core_1.summary.addBreak(); - core_1.summary.addTable(createConditionTable(condition.details)); + createConditionTables(condition.details).forEach(table => core_1.summary.addTable(table)); core_1.summary.addRaw('
', true); } else { @@ -330,41 +331,43 @@ function createFilesSummary(fileList) { * @param conditions Conditions of the quality gate * @returns Table containing a summary for all conditions */ -function createConditionTable(details) { - const rows = []; - const titleRow = [ - { - data: 'File', - header: true - }, - { - data: details.dataKeys.actualValue.title, - header: true - } - ]; - if (details.dataKeys.blockingAfter) { - titleRow.push({ - data: details.dataKeys.blockingAfter.title, - header: true - }); - } - rows.push(titleRow); - details.items - .filter(item => item.itemType === 'file') - .forEach(item => { - const dataRow = [ - `${os_1.EOL}${os_1.EOL}[${item.name}](${(0, url_1.joinUrl)(config_1.ticsConfig.displayUrl, item.link)})${os_1.EOL}${os_1.EOL}`, - item.data.actualValue.formattedValue +function createConditionTables(details) { + return details.itemTypes.map(itemType => { + const rows = []; + const titleRow = [ + { + data: capitalize(itemType), + header: true + }, + { + data: details.dataKeys.actualValue.title, + header: true + } ]; - if (item.data.blockingAfter) { - dataRow.push(item.data.blockingAfter.formattedValue); - } - else if (details.dataKeys.blockingAfter) { - dataRow.push('0'); + if (details.dataKeys.blockingAfter) { + titleRow.push({ + data: details.dataKeys.blockingAfter.title, + header: true + }); } - rows.push(dataRow); + rows.push(titleRow); + details.items + .filter(item => item.itemType === itemType) + .forEach(item => { + const dataRow = [ + `${os_1.EOL}${os_1.EOL}[${item.name}](${(0, url_1.joinUrl)(config_1.ticsConfig.displayUrl, item.link)})${os_1.EOL}${os_1.EOL}`, + item.data.actualValue.formattedValue + ]; + if (item.data.blockingAfter) { + dataRow.push(item.data.blockingAfter.formattedValue); + } + else if (details.dataKeys.blockingAfter) { + dataRow.push('0'); + } + rows.push(dataRow); + }); + return rows; }); - return rows; } /** * Groups the annotations and creates review comments for them. @@ -41423,6 +41426,378 @@ function cleanEscapedString(input) { } +/***/ }), + +/***/ 93822: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +exports.parseISO = parseISO; +var _index = __nccwpck_require__(17818); + +/** + * The {@link parseISO} function options. + */ + +/** + * @name parseISO + * @category Common Helpers + * @summary Parse ISO string + * + * @description + * Parse the given string in ISO 8601 format and return an instance of Date. + * + * Function accepts complete ISO 8601 formats as well as partial implementations. + * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 + * + * If the argument isn't a string, the function cannot parse the string or + * the values are invalid, it returns Invalid Date. + * + * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). + * + * @param argument - The value to convert + * @param options - An object with options + * + * @returns The parsed date in the local time zone + * + * @example + * // Convert string '2014-02-11T11:30:30' to date: + * const result = parseISO('2014-02-11T11:30:30') + * //=> Tue Feb 11 2014 11:30:30 + * + * @example + * // Convert string '+02014101' to date, + * // if the additional number of digits in the extended year format is 1: + * const result = parseISO('+02014101', { additionalDigits: 1 }) + * //=> Fri Apr 11 2014 00:00:00 + */ +function parseISO(argument, options) { + const additionalDigits = options?.additionalDigits ?? 2; + const dateStrings = splitDateString(argument); + + let date; + if (dateStrings.date) { + const parseYearResult = parseYear(dateStrings.date, additionalDigits); + date = parseDate(parseYearResult.restDateString, parseYearResult.year); + } + + if (!date || isNaN(date.getTime())) { + return new Date(NaN); + } + + const timestamp = date.getTime(); + let time = 0; + let offset; + + if (dateStrings.time) { + time = parseTime(dateStrings.time); + if (isNaN(time)) { + return new Date(NaN); + } + } + + if (dateStrings.timezone) { + offset = parseTimezone(dateStrings.timezone); + if (isNaN(offset)) { + return new Date(NaN); + } + } else { + const dirtyDate = new Date(timestamp + time); + // JS parsed string assuming it's in UTC timezone + // but we need it to be parsed in our timezone + // so we use utc values to build date in our timezone. + // Year values from 0 to 99 map to the years 1900 to 1999 + // so set year explicitly with setFullYear. + const result = new Date(0); + result.setFullYear( + dirtyDate.getUTCFullYear(), + dirtyDate.getUTCMonth(), + dirtyDate.getUTCDate(), + ); + result.setHours( + dirtyDate.getUTCHours(), + dirtyDate.getUTCMinutes(), + dirtyDate.getUTCSeconds(), + dirtyDate.getUTCMilliseconds(), + ); + return result; + } + + return new Date(timestamp + time + offset); +} + +const patterns = { + dateTimeDelimiter: /[T ]/, + timeZoneDelimiter: /[Z ]/i, + timezone: /([Z+-].*)$/, +}; + +const dateRegex = + /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/; +const timeRegex = + /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/; +const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/; + +function splitDateString(dateString) { + const dateStrings = {}; + const array = dateString.split(patterns.dateTimeDelimiter); + let timeString; + + // The regex match should only return at maximum two array elements. + // [date], [time], or [date, time]. + if (array.length > 2) { + return dateStrings; + } + + if (/:/.test(array[0])) { + timeString = array[0]; + } else { + dateStrings.date = array[0]; + timeString = array[1]; + if (patterns.timeZoneDelimiter.test(dateStrings.date)) { + dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0]; + timeString = dateString.substr( + dateStrings.date.length, + dateString.length, + ); + } + } + + if (timeString) { + const token = patterns.timezone.exec(timeString); + if (token) { + dateStrings.time = timeString.replace(token[1], ""); + dateStrings.timezone = token[1]; + } else { + dateStrings.time = timeString; + } + } + + return dateStrings; +} + +function parseYear(dateString, additionalDigits) { + const regex = new RegExp( + "^(?:(\\d{4}|[+-]\\d{" + + (4 + additionalDigits) + + "})|(\\d{2}|[+-]\\d{" + + (2 + additionalDigits) + + "})$)", + ); + + const captures = dateString.match(regex); + // Invalid ISO-formatted year + if (!captures) return { year: NaN, restDateString: "" }; + + const year = captures[1] ? parseInt(captures[1]) : null; + const century = captures[2] ? parseInt(captures[2]) : null; + + // either year or century is null, not both + return { + year: century === null ? year : century * 100, + restDateString: dateString.slice((captures[1] || captures[2]).length), + }; +} + +function parseDate(dateString, year) { + // Invalid ISO-formatted year + if (year === null) return new Date(NaN); + + const captures = dateString.match(dateRegex); + // Invalid ISO-formatted string + if (!captures) return new Date(NaN); + + const isWeekDate = !!captures[4]; + const dayOfYear = parseDateUnit(captures[1]); + const month = parseDateUnit(captures[2]) - 1; + const day = parseDateUnit(captures[3]); + const week = parseDateUnit(captures[4]); + const dayOfWeek = parseDateUnit(captures[5]) - 1; + + if (isWeekDate) { + if (!validateWeekDate(year, week, dayOfWeek)) { + return new Date(NaN); + } + return dayOfISOWeekYear(year, week, dayOfWeek); + } else { + const date = new Date(0); + if ( + !validateDate(year, month, day) || + !validateDayOfYearDate(year, dayOfYear) + ) { + return new Date(NaN); + } + date.setUTCFullYear(year, month, Math.max(dayOfYear, day)); + return date; + } +} + +function parseDateUnit(value) { + return value ? parseInt(value) : 1; +} + +function parseTime(timeString) { + const captures = timeString.match(timeRegex); + if (!captures) return NaN; // Invalid ISO-formatted time + + const hours = parseTimeUnit(captures[1]); + const minutes = parseTimeUnit(captures[2]); + const seconds = parseTimeUnit(captures[3]); + + if (!validateTime(hours, minutes, seconds)) { + return NaN; + } + + return ( + hours * _index.millisecondsInHour + + minutes * _index.millisecondsInMinute + + seconds * 1000 + ); +} + +function parseTimeUnit(value) { + return (value && parseFloat(value.replace(",", "."))) || 0; +} + +function parseTimezone(timezoneString) { + if (timezoneString === "Z") return 0; + + const captures = timezoneString.match(timezoneRegex); + if (!captures) return 0; + + const sign = captures[1] === "+" ? -1 : 1; + const hours = parseInt(captures[2]); + const minutes = (captures[3] && parseInt(captures[3])) || 0; + + if (!validateTimezone(hours, minutes)) { + return NaN; + } + + return ( + sign * + (hours * _index.millisecondsInHour + minutes * _index.millisecondsInMinute) + ); +} + +function dayOfISOWeekYear(isoWeekYear, week, day) { + const date = new Date(0); + date.setUTCFullYear(isoWeekYear, 0, 4); + const fourthOfJanuaryDay = date.getUTCDay() || 7; + const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay; + date.setUTCDate(date.getUTCDate() + diff); + return date; +} + +// Validation functions + +// February is null to handle the leap year (using ||) +const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + +function isLeapYearIndex(year) { + return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0); +} + +function validateDate(year, month, date) { + return ( + month >= 0 && + month <= 11 && + date >= 1 && + date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28)) + ); +} + +function validateDayOfYearDate(year, dayOfYear) { + return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365); +} + +function validateWeekDate(_year, week, day) { + return week >= 1 && week <= 53 && day >= 0 && day <= 6; +} + +function validateTime(hours, minutes, seconds) { + if (hours === 24) { + return minutes === 0 && seconds === 0; + } + + return ( + seconds >= 0 && + seconds < 60 && + minutes >= 0 && + minutes < 60 && + hours >= 0 && + hours < 25 + ); +} + +function validateTimezone(_hours, minutes) { + return minutes >= 0 && minutes <= 59; +} + + +/***/ }), + +/***/ 50135: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +exports.parseJSON = parseJSON; /** + * @name parseJSON + * @category Common Helpers + * @summary Parse a JSON date string + * + * @description + * Converts a complete ISO date string in UTC time, the typical format for transmitting + * a date in JSON, to a JavaScript `Date` instance. + * + * This is a minimal implementation for converting dates retrieved from a JSON API to + * a `Date` instance which can be used with other functions in the `date-fns` library. + * The following formats are supported: + * + * - `2000-03-15T05:20:10.123Z`: The output of `.toISOString()` and `JSON.stringify(new Date())` + * - `2000-03-15T05:20:10Z`: Without milliseconds + * - `2000-03-15T05:20:10+00:00`: With a zero offset, the default JSON encoded format in some other languages + * - `2000-03-15T05:20:10+05:45`: With a positive or negative offset, the default JSON encoded format in some other languages + * - `2000-03-15T05:20:10+0000`: With a zero offset without a colon + * - `2000-03-15T05:20:10`: Without a trailing 'Z' symbol + * - `2000-03-15T05:20:10.1234567`: Up to 7 digits in milliseconds field. Only first 3 are taken into account since JS does not allow fractional milliseconds + * - `2000-03-15 05:20:10`: With a space instead of a 'T' separator for APIs returning a SQL date without reformatting + * + * For convenience and ease of use these other input types are also supported + * via [toDate](https://date-fns.org/docs/toDate): + * + * - A `Date` instance will be cloned + * - A `number` will be treated as a timestamp + * + * Any other input type or invalid date strings will return an `Invalid Date`. + * + * @param dateStr - A fully formed ISO8601 date string to convert + * + * @returns The parsed date in the local time zone + */ +function parseJSON(dateStr) { + const parts = dateStr.match( + /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{0,7}))?(?:Z|(.)(\d{2}):?(\d{2})?)?/, + ); + if (parts) { + // Group 8 matches the sign + return new Date( + Date.UTC( + +parts[1], + +parts[2] - 1, + +parts[3], + +parts[4] - (+parts[9] || 0) * (parts[8] == "-" ? -1 : 1), + +parts[5] - (+parts[10] || 0) * (parts[8] == "-" ? -1 : 1), + +parts[6], + +((parts[7] || "0") + "00").substring(0, 3), + ), + ); + } + return new Date(NaN); +} + + /***/ }), /***/ 5619: @@ -43949,378 +44324,6 @@ function isLeapYearIndex(year) { } -/***/ }), - -/***/ 93822: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.parseISO = parseISO; -var _index = __nccwpck_require__(17818); - -/** - * The {@link parseISO} function options. - */ - -/** - * @name parseISO - * @category Common Helpers - * @summary Parse ISO string - * - * @description - * Parse the given string in ISO 8601 format and return an instance of Date. - * - * Function accepts complete ISO 8601 formats as well as partial implementations. - * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 - * - * If the argument isn't a string, the function cannot parse the string or - * the values are invalid, it returns Invalid Date. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * - * @param argument - The value to convert - * @param options - An object with options - * - * @returns The parsed date in the local time zone - * - * @example - * // Convert string '2014-02-11T11:30:30' to date: - * const result = parseISO('2014-02-11T11:30:30') - * //=> Tue Feb 11 2014 11:30:30 - * - * @example - * // Convert string '+02014101' to date, - * // if the additional number of digits in the extended year format is 1: - * const result = parseISO('+02014101', { additionalDigits: 1 }) - * //=> Fri Apr 11 2014 00:00:00 - */ -function parseISO(argument, options) { - const additionalDigits = options?.additionalDigits ?? 2; - const dateStrings = splitDateString(argument); - - let date; - if (dateStrings.date) { - const parseYearResult = parseYear(dateStrings.date, additionalDigits); - date = parseDate(parseYearResult.restDateString, parseYearResult.year); - } - - if (!date || isNaN(date.getTime())) { - return new Date(NaN); - } - - const timestamp = date.getTime(); - let time = 0; - let offset; - - if (dateStrings.time) { - time = parseTime(dateStrings.time); - if (isNaN(time)) { - return new Date(NaN); - } - } - - if (dateStrings.timezone) { - offset = parseTimezone(dateStrings.timezone); - if (isNaN(offset)) { - return new Date(NaN); - } - } else { - const dirtyDate = new Date(timestamp + time); - // JS parsed string assuming it's in UTC timezone - // but we need it to be parsed in our timezone - // so we use utc values to build date in our timezone. - // Year values from 0 to 99 map to the years 1900 to 1999 - // so set year explicitly with setFullYear. - const result = new Date(0); - result.setFullYear( - dirtyDate.getUTCFullYear(), - dirtyDate.getUTCMonth(), - dirtyDate.getUTCDate(), - ); - result.setHours( - dirtyDate.getUTCHours(), - dirtyDate.getUTCMinutes(), - dirtyDate.getUTCSeconds(), - dirtyDate.getUTCMilliseconds(), - ); - return result; - } - - return new Date(timestamp + time + offset); -} - -const patterns = { - dateTimeDelimiter: /[T ]/, - timeZoneDelimiter: /[Z ]/i, - timezone: /([Z+-].*)$/, -}; - -const dateRegex = - /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/; -const timeRegex = - /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/; -const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/; - -function splitDateString(dateString) { - const dateStrings = {}; - const array = dateString.split(patterns.dateTimeDelimiter); - let timeString; - - // The regex match should only return at maximum two array elements. - // [date], [time], or [date, time]. - if (array.length > 2) { - return dateStrings; - } - - if (/:/.test(array[0])) { - timeString = array[0]; - } else { - dateStrings.date = array[0]; - timeString = array[1]; - if (patterns.timeZoneDelimiter.test(dateStrings.date)) { - dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0]; - timeString = dateString.substr( - dateStrings.date.length, - dateString.length, - ); - } - } - - if (timeString) { - const token = patterns.timezone.exec(timeString); - if (token) { - dateStrings.time = timeString.replace(token[1], ""); - dateStrings.timezone = token[1]; - } else { - dateStrings.time = timeString; - } - } - - return dateStrings; -} - -function parseYear(dateString, additionalDigits) { - const regex = new RegExp( - "^(?:(\\d{4}|[+-]\\d{" + - (4 + additionalDigits) + - "})|(\\d{2}|[+-]\\d{" + - (2 + additionalDigits) + - "})$)", - ); - - const captures = dateString.match(regex); - // Invalid ISO-formatted year - if (!captures) return { year: NaN, restDateString: "" }; - - const year = captures[1] ? parseInt(captures[1]) : null; - const century = captures[2] ? parseInt(captures[2]) : null; - - // either year or century is null, not both - return { - year: century === null ? year : century * 100, - restDateString: dateString.slice((captures[1] || captures[2]).length), - }; -} - -function parseDate(dateString, year) { - // Invalid ISO-formatted year - if (year === null) return new Date(NaN); - - const captures = dateString.match(dateRegex); - // Invalid ISO-formatted string - if (!captures) return new Date(NaN); - - const isWeekDate = !!captures[4]; - const dayOfYear = parseDateUnit(captures[1]); - const month = parseDateUnit(captures[2]) - 1; - const day = parseDateUnit(captures[3]); - const week = parseDateUnit(captures[4]); - const dayOfWeek = parseDateUnit(captures[5]) - 1; - - if (isWeekDate) { - if (!validateWeekDate(year, week, dayOfWeek)) { - return new Date(NaN); - } - return dayOfISOWeekYear(year, week, dayOfWeek); - } else { - const date = new Date(0); - if ( - !validateDate(year, month, day) || - !validateDayOfYearDate(year, dayOfYear) - ) { - return new Date(NaN); - } - date.setUTCFullYear(year, month, Math.max(dayOfYear, day)); - return date; - } -} - -function parseDateUnit(value) { - return value ? parseInt(value) : 1; -} - -function parseTime(timeString) { - const captures = timeString.match(timeRegex); - if (!captures) return NaN; // Invalid ISO-formatted time - - const hours = parseTimeUnit(captures[1]); - const minutes = parseTimeUnit(captures[2]); - const seconds = parseTimeUnit(captures[3]); - - if (!validateTime(hours, minutes, seconds)) { - return NaN; - } - - return ( - hours * _index.millisecondsInHour + - minutes * _index.millisecondsInMinute + - seconds * 1000 - ); -} - -function parseTimeUnit(value) { - return (value && parseFloat(value.replace(",", "."))) || 0; -} - -function parseTimezone(timezoneString) { - if (timezoneString === "Z") return 0; - - const captures = timezoneString.match(timezoneRegex); - if (!captures) return 0; - - const sign = captures[1] === "+" ? -1 : 1; - const hours = parseInt(captures[2]); - const minutes = (captures[3] && parseInt(captures[3])) || 0; - - if (!validateTimezone(hours, minutes)) { - return NaN; - } - - return ( - sign * - (hours * _index.millisecondsInHour + minutes * _index.millisecondsInMinute) - ); -} - -function dayOfISOWeekYear(isoWeekYear, week, day) { - const date = new Date(0); - date.setUTCFullYear(isoWeekYear, 0, 4); - const fourthOfJanuaryDay = date.getUTCDay() || 7; - const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay; - date.setUTCDate(date.getUTCDate() + diff); - return date; -} - -// Validation functions - -// February is null to handle the leap year (using ||) -const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - -function isLeapYearIndex(year) { - return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0); -} - -function validateDate(year, month, date) { - return ( - month >= 0 && - month <= 11 && - date >= 1 && - date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28)) - ); -} - -function validateDayOfYearDate(year, dayOfYear) { - return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365); -} - -function validateWeekDate(_year, week, day) { - return week >= 1 && week <= 53 && day >= 0 && day <= 6; -} - -function validateTime(hours, minutes, seconds) { - if (hours === 24) { - return minutes === 0 && seconds === 0; - } - - return ( - seconds >= 0 && - seconds < 60 && - minutes >= 0 && - minutes < 60 && - hours >= 0 && - hours < 25 - ); -} - -function validateTimezone(_hours, minutes) { - return minutes >= 0 && minutes <= 59; -} - - -/***/ }), - -/***/ 50135: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.parseJSON = parseJSON; /** - * @name parseJSON - * @category Common Helpers - * @summary Parse a JSON date string - * - * @description - * Converts a complete ISO date string in UTC time, the typical format for transmitting - * a date in JSON, to a JavaScript `Date` instance. - * - * This is a minimal implementation for converting dates retrieved from a JSON API to - * a `Date` instance which can be used with other functions in the `date-fns` library. - * The following formats are supported: - * - * - `2000-03-15T05:20:10.123Z`: The output of `.toISOString()` and `JSON.stringify(new Date())` - * - `2000-03-15T05:20:10Z`: Without milliseconds - * - `2000-03-15T05:20:10+00:00`: With a zero offset, the default JSON encoded format in some other languages - * - `2000-03-15T05:20:10+05:45`: With a positive or negative offset, the default JSON encoded format in some other languages - * - `2000-03-15T05:20:10+0000`: With a zero offset without a colon - * - `2000-03-15T05:20:10`: Without a trailing 'Z' symbol - * - `2000-03-15T05:20:10.1234567`: Up to 7 digits in milliseconds field. Only first 3 are taken into account since JS does not allow fractional milliseconds - * - `2000-03-15 05:20:10`: With a space instead of a 'T' separator for APIs returning a SQL date without reformatting - * - * For convenience and ease of use these other input types are also supported - * via [toDate](https://date-fns.org/docs/toDate): - * - * - A `Date` instance will be cloned - * - A `number` will be treated as a timestamp - * - * Any other input type or invalid date strings will return an `Invalid Date`. - * - * @param dateStr - A fully formed ISO8601 date string to convert - * - * @returns The parsed date in the local time zone - */ -function parseJSON(dateStr) { - const parts = dateStr.match( - /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{0,7}))?(?:Z|(.)(\d{2}):?(\d{2})?)?/, - ); - if (parts) { - // Group 8 matches the sign - return new Date( - Date.UTC( - +parts[1], - +parts[2] - 1, - +parts[3], - +parts[4] - (+parts[9] || 0) * (parts[8] == "-" ? -1 : 1), - +parts[5] - (+parts[10] || 0) * (parts[8] == "-" ? -1 : 1), - +parts[6], - +((parts[7] || "0") + "00").substring(0, 3), - ), - ); - } - return new Date(NaN); -} - - /***/ }), /***/ 456: diff --git a/package-lock.json b/package-lock.json index e84f57c6..995345e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3122,9 +3122,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts index 95ba7181..523e875b 100644 --- a/src/action/decorate/summary.ts +++ b/src/action/decorate/summary.ts @@ -20,6 +20,8 @@ import { import { generateExpandableAreaMarkdown, generateStatusMarkdown } from './markdown'; import { githubConfig, ticsConfig } from '../../configuration/config'; +const capitalize = (s: string): string => s && String(s[0]).toUpperCase() + String(s).slice(1); + export function createSummaryBody(analysisResult: AnalysisResult): string { logger.header('Creating summary.'); summary.addHeading('TICS Quality Gate'); @@ -37,7 +39,7 @@ export function createSummaryBody(analysisResult: AnalysisResult): string { if (condition.details && condition.details.items.length > 0) { summary.addRaw(`${EOL}
${statusMarkdown}${condition.message}${EOL}`); summary.addBreak(); - summary.addTable(createConditionTable(condition.details)); + createConditionTables(condition.details).forEach(table => summary.addTable(table)); summary.addRaw('
', true); } else { summary.addRaw(`${EOL}   ${statusMarkdown}${condition.message}`, true); @@ -174,43 +176,45 @@ export function createFilesSummary(fileList: string[]): string { * @param conditions Conditions of the quality gate * @returns Table containing a summary for all conditions */ -function createConditionTable(details: ConditionDetails): SummaryTableRow[] { - const rows: SummaryTableRow[] = []; - const titleRow: SummaryTableRow = [ - { - data: 'File', - header: true - }, - { - data: details.dataKeys.actualValue.title, - header: true - } - ]; - if (details.dataKeys.blockingAfter) { - titleRow.push({ - data: details.dataKeys.blockingAfter.title, - header: true - }); - } - rows.push(titleRow); - - details.items - .filter(item => item.itemType === 'file') - .forEach(item => { - const dataRow: SummaryTableRow = [ - `${EOL}${EOL}[${item.name}](${joinUrl(ticsConfig.displayUrl, item.link)})${EOL}${EOL}`, - item.data.actualValue.formattedValue - ]; - - if (item.data.blockingAfter) { - dataRow.push(item.data.blockingAfter.formattedValue); - } else if (details.dataKeys.blockingAfter) { - dataRow.push('0'); +function createConditionTables(details: ConditionDetails): SummaryTableRow[][] { + return details.itemTypes.map(itemType => { + const rows: SummaryTableRow[] = []; + const titleRow: SummaryTableRow = [ + { + data: capitalize(itemType), + header: true + }, + { + data: details.dataKeys.actualValue.title, + header: true } + ]; + if (details.dataKeys.blockingAfter) { + titleRow.push({ + data: details.dataKeys.blockingAfter.title, + header: true + }); + } + rows.push(titleRow); + + details.items + .filter(item => item.itemType === itemType) + .forEach(item => { + const dataRow: SummaryTableRow = [ + `${EOL}${EOL}[${item.name}](${joinUrl(ticsConfig.displayUrl, item.link)})${EOL}${EOL}`, + item.data.actualValue.formattedValue + ]; + + if (item.data.blockingAfter) { + dataRow.push(item.data.blockingAfter.formattedValue); + } else if (details.dataKeys.blockingAfter) { + dataRow.push('0'); + } - rows.push(dataRow); - }); - return rows; + rows.push(dataRow); + }); + return rows; + }); } /** diff --git a/test/unit/action/decorate/objects/summary.ts b/test/unit/action/decorate/objects/summary.ts index d9299b02..bbd1dd30 100644 --- a/test/unit/action/decorate/objects/summary.ts +++ b/test/unit/action/decorate/objects/summary.ts @@ -47,7 +47,7 @@ export const analysisResultsSoaked: AnalysisResult = { message: 'No new Coding Standard Violations for levels 1, 2, 3 with respect to first analysis; failed for 145 files. There will be blocking issues in 138 files after the grace period ends.', details: { - itemTypes: ['file'], + itemTypes: ['file', 'function'], dataKeys: { actualValue: { title: 'Blocking now', @@ -119,6 +119,25 @@ export const analysisResultsSoaked: AnalysisResult = { link: 'AnnotatedSource.html#axes\u003dSuppressions(no),Date(1516070506),Project(20065),Level(Set(1,2,3)),DeltaDate(Run(0)),DiffType(new),File(Path(HIE,20065,main,PADAnalysis,ApplicationNative,src,views,AboutBox.cpp)),Blocking(deferred)\u0026diff\u003dtrue\u0026metrics\u003dG(Diff(CS,DiffType(new)),Level(Set(1,2,3)))' } } + }, + { + itemType: 'function', + name: 'PADAnalysis/ApplicationNative/src/views/AboutBoxs.cpp', + link: 'AnnotatedSource.html#axes\u003dSuppressions(no),Date(1516070506),Project(20065),Level(Set(1,2,3)),DeltaDate(Run(0)),DiffType(new),File(Path(HIE,20065,main,PADAnalysis,ApplicationNative,src,views,AboutBox.cpp))\u0026diff\u003dtrue\u0026metrics\u003dG(Diff(CS,DiffType(new)),Level(Set(1,2,3)))', + data: { + actualValue: { + formattedValue: '+25', + value: 24.0, + classes: ['delta-worse'], + link: 'AnnotatedSource.html#axes\u003dSuppressions(no),Date(1516070506),Project(20065),Level(Set(1,2,3)),DeltaDate(Run(0)),DiffType(new),File(Path(HIE,20065,main,PADAnalysis,ApplicationNative,src,views,AboutBox.cpp)),Blocking(Set(yes,no))\u0026diff\u003dtrue\u0026metrics\u003dG(Diff(CS,DiffType(new)),Level(Set(1,2,3)))' + }, + blockingAfter: { + formattedValue: '0', + value: 0.0, + classes: [], + link: 'AnnotatedSource.html#axes\u003dSuppressions(no),Date(1516070506),Project(20065),Level(Set(1,2,3)),DeltaDate(Run(0)),DiffType(new),File(Path(HIE,20065,main,PADAnalysis,ApplicationNative,src,views,AboutBox.cpp)),Blocking(deferred)\u0026diff\u003dtrue\u0026metrics\u003dG(Diff(CS,DiffType(new)),Level(Set(1,2,3)))' + } + } } ] } diff --git a/test/unit/action/decorate/summary.test.ts b/test/unit/action/decorate/summary.test.ts index d5440bc8..cae9212a 100644 --- a/test/unit/action/decorate/summary.test.ts +++ b/test/unit/action/decorate/summary.test.ts @@ -36,6 +36,8 @@ describe('createSummaryBody', () => { expect(string).toContain('+39+3'); expect(string).toContain('+300'); expect(string).toContain('+240'); + expect(string).toContain('FunctionBlocking nowBlocking after 2018-03-23'); + expect(string).toContain('+250'); summary.clear(); }); From d5538c295a67d6c759a145e1a0daaece18afe107 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:28:03 +0100 Subject: [PATCH 2/2] #35462: Do not post undefined items in unpostable annotations --- dist/index.js | 5 ++++- src/action/decorate/summary.ts | 5 ++++- test/unit/action/decorate/summary.test.ts | 12 +++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dist/index.js b/dist/index.js index 1c5d48d2..24c35c61 100644 --- a/dist/index.js +++ b/dist/index.js @@ -536,7 +536,10 @@ function createUnpostableAnnotationsDetails(unpostableReviewComments) { else if (previousPath !== path) { body += ``; } - body += ``; + body += ``; previousPath = reviewComment.path ? reviewComment.path : ''; }); body += '
${path}
${icon}${blocking}Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''}
Category: ${reviewComment.category ?? ''}
${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount}
${reviewComment.msg}
${icon}${blocking}Line: ${reviewComment.line.toString()}`; + body += reviewComment.level ? `
Level: ${reviewComment.level.toString()}` : ''; + body += reviewComment.category ? `
Category: ${reviewComment.category}` : ''; + body += `
${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount}
${reviewComment.msg}
'; diff --git a/src/action/decorate/summary.ts b/src/action/decorate/summary.ts index 523e875b..1d62f523 100644 --- a/src/action/decorate/summary.ts +++ b/src/action/decorate/summary.ts @@ -397,7 +397,10 @@ export function createUnpostableAnnotationsDetails(unpostableReviewComments: Ext } else if (previousPath !== path) { body += ``; } - body += ``; + body += ``; previousPath = reviewComment.path ? reviewComment.path : ''; }); body += '
${path}
${icon}${blocking}Line: ${reviewComment.line.toString()} Level: ${reviewComment.level?.toString() ?? ''}
Category: ${reviewComment.category ?? ''}
${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount}
${reviewComment.msg}
${icon}${blocking}Line: ${reviewComment.line.toString()}`; + body += reviewComment.level ? `
Level: ${reviewComment.level.toString()}` : ''; + body += reviewComment.category ? `
Category: ${reviewComment.category}` : ''; + body += `
${reviewComment.type} violation: ${reviewComment.rule ?? ''} ${displayCount}
${reviewComment.msg}
'; diff --git a/test/unit/action/decorate/summary.test.ts b/test/unit/action/decorate/summary.test.ts index cae9212a..b94ebf08 100644 --- a/test/unit/action/decorate/summary.test.ts +++ b/test/unit/action/decorate/summary.test.ts @@ -583,7 +583,7 @@ describe('createUnpostableReviewCommentsSummary', () => { const response = createUnpostableAnnotationsDetails(unpostable); expect(response).toContain(``); expect(response).toContain( - `` + `` ); }); @@ -622,7 +622,7 @@ describe('createUnpostableReviewCommentsSummary', () => { const response = createUnpostableAnnotationsDetails(unpostable); expect(response).toContainTimes(`
${unpostable[0].path}
:x:BlockingLine: ${unpostable[0].line} Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:x:BlockingLine: ${unpostable[0].line}
Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
`, 1); expect(response).toContainTimes( - ``, + ``, 2 ); }); @@ -662,7 +662,7 @@ describe('createUnpostableReviewCommentsSummary', () => { const response = createUnpostableAnnotationsDetails(unpostable); expect(response).toContainTimes(`
${unpostable[0].path}
:x:BlockingLine: ${unpostable[0].line} Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:x:BlockingLine: ${unpostable[0].line}
Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
`, 1); expect(response).toContainTimes( - ``, + ``, 2 ); }); @@ -687,8 +687,6 @@ describe('createUnpostableReviewCommentsSummary', () => { fullPath: '/home/src/test.js', path: 'src/test.js', line: 0, - level: 1, - category: 'test', type: 'test', rule: 'test', displayCount: '', @@ -707,11 +705,11 @@ describe('createUnpostableReviewCommentsSummary', () => { expect(response).toContainTimes(`
${unpostable[0].path}
:x:BlockingLine: ${unpostable[0].line} Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:x:BlockingLine: ${unpostable[0].line}
Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
`, 1); expect(response).toContainTimes(`
${unpostable[0].path}
`, 1); expect(response).toContainTimes( - ``, + ``, 1 ); expect(response).toContainTimes( - ``, + ``, 1 ); });
${unpostable[1].path}
:x:BlockingLine: ${unpostable[0].line} Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:x:BlockingLine: ${unpostable[0].line}
Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:warning:Blocking after 2024-08-16Line: ${unpostable[0].line} Level: ${unpostable[0].level}
Category: ${unpostable[0].category}
${unpostable[0].type} violation: ${unpostable[0].rule} ${unpostable[0].displayCount}
${unpostable[0].msg}
:warning:Blocking after 2024-08-16Line: ${unpostable[1].line}${unpostable[1].type} violation: ${unpostable[1].rule} ${unpostable[1].displayCount}
${unpostable[1].msg}