diff --git a/.github/workflows/report_test.yml b/.github/workflows/report_test.yml new file mode 100644 index 0000000..a0c748b --- /dev/null +++ b/.github/workflows/report_test.yml @@ -0,0 +1,21 @@ +name: Local Markdown Lint Test + +on: [workflow_dispatch] + +jobs: + lint_local_test: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Run Local Markdown Lint Action + uses: ./ + with: + globs: | + **/*.md + !node_modules/**/*.md + results_file: ./local/lint_results.json + + - name: Keep container alive for debugging + run: sleep 3600 \ No newline at end of file diff --git a/action.yml b/action.yml index 36756b5..f855c74 100644 --- a/action.yml +++ b/action.yml @@ -21,6 +21,10 @@ inputs: description: String to use as a separator for the "globs" input (defaults to newline) default: "\n" required: false + results_file: + description: File to write json result report + default: "" + required: false runs: using: node20 main: dist/index.js diff --git a/dist/index.js b/dist/index.js index c69a7b9..e5dc8cd 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6471,11 +6471,19 @@ class EntryFilter { this.index = new Map(); } getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, Object.assign(Object.assign({}, this._micromatchOptions), { dot: true })); - return (entry) => this._filter(entry, positiveRe, negativeRe); + const [absoluteNegative, relativeNegative] = utils.pattern.partitionAbsoluteAndRelative(negative); + const patterns = { + positive: { + all: utils.pattern.convertPatternsToRe(positive, this._micromatchOptions) + }, + negative: { + absolute: utils.pattern.convertPatternsToRe(absoluteNegative, Object.assign(Object.assign({}, this._micromatchOptions), { dot: true })), + relative: utils.pattern.convertPatternsToRe(relativeNegative, Object.assign(Object.assign({}, this._micromatchOptions), { dot: true })) + } + }; + return (entry) => this._filter(entry, patterns); } - _filter(entry, positiveRe, negativeRe) { + _filter(entry, patterns) { const filepath = utils.path.removeLeadingDotSegment(entry.path); if (this._settings.unique && this._isDuplicateEntry(filepath)) { return false; @@ -6483,11 +6491,7 @@ class EntryFilter { if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { return false; } - if (this._isSkippedByAbsoluteNegativePatterns(filepath, negativeRe)) { - return false; - } - const isDirectory = entry.dirent.isDirectory(); - const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(filepath, negativeRe, isDirectory); + const isMatched = this._isMatchToPatternsSet(filepath, patterns, entry.dirent.isDirectory()); if (this._settings.unique && isMatched) { this._createIndexRecord(filepath); } @@ -6505,14 +6509,32 @@ class EntryFilter { _onlyDirectoryFilter(entry) { return this._settings.onlyDirectories && !entry.dirent.isDirectory(); } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { + _isMatchToPatternsSet(filepath, patterns, isDirectory) { + const isMatched = this._isMatchToPatterns(filepath, patterns.positive.all, isDirectory); + if (!isMatched) { + return false; + } + const isMatchedByRelativeNegative = this._isMatchToPatterns(filepath, patterns.negative.relative, isDirectory); + if (isMatchedByRelativeNegative) { + return false; + } + const isMatchedByAbsoluteNegative = this._isMatchToAbsoluteNegative(filepath, patterns.negative.absolute, isDirectory); + if (isMatchedByAbsoluteNegative) { + return false; + } + return true; + } + _isMatchToAbsoluteNegative(filepath, patternsRe, isDirectory) { + if (patternsRe.length === 0) { return false; } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); + const fullpath = utils.path.makeAbsolute(this._settings.cwd, filepath); + return this._isMatchToPatterns(fullpath, patternsRe, isDirectory); } _isMatchToPatterns(filepath, patternsRe, isDirectory) { + if (patternsRe.length === 0) { + return false; + } // Trying to match files and directories by patterns. const isMatched = utils.pattern.matchAny(filepath, patternsRe); // A pattern with a trailling slash can be used for directory matching. @@ -7254,7 +7276,7 @@ exports.convertPosixPathToPattern = convertPosixPathToPattern; "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; +exports.isAbsolute = exports.partitionAbsoluteAndRelative = exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; const path = __nccwpck_require__(6928); const globParent = __nccwpck_require__(8188); const micromatch = __nccwpck_require__(8785); @@ -7440,6 +7462,24 @@ function removeDuplicateSlashes(pattern) { return pattern.replace(DOUBLE_SLASH_RE, '/'); } exports.removeDuplicateSlashes = removeDuplicateSlashes; +function partitionAbsoluteAndRelative(patterns) { + const absolute = []; + const relative = []; + for (const pattern of patterns) { + if (isAbsolute(pattern)) { + absolute.push(pattern); + } + else { + relative.push(pattern); + } + } + return [absolute, relative]; +} +exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative; +function isAbsolute(pattern) { + return path.isAbsolute(pattern); +} +exports.isAbsolute = isAbsolute; /***/ }), @@ -7782,19 +7822,19 @@ function queueAsPromised (context, worker, _concurrency) { } function drained () { - if (queue.idle()) { - return new Promise(function (resolve) { - resolve() - }) - } - - var previousDrain = queue.drain - var p = new Promise(function (resolve) { - queue.drain = function () { - previousDrain() - resolve() - } + process.nextTick(function () { + if (queue.idle()) { + resolve() + } else { + var previousDrain = queue.drain + queue.drain = function () { + if (typeof previousDrain === 'function') previousDrain() + resolve() + queue.drain = previousDrain + } + } + }) }) return p @@ -31693,7 +31733,7 @@ module.exports = { const { parseSetCookie } = __nccwpck_require__(8915) -const { stringify, getHeadersList } = __nccwpck_require__(3834) +const { stringify } = __nccwpck_require__(3834) const { webidl } = __nccwpck_require__(4222) const { Headers } = __nccwpck_require__(6349) @@ -31769,14 +31809,13 @@ function getSetCookies (headers) { webidl.brandCheck(headers, Headers, { strict: false }) - const cookies = getHeadersList(headers).cookies + const cookies = headers.getSetCookie() if (!cookies) { return [] } - // In older versions of undici, cookies is a list of name:value. - return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair)) + return cookies.map((pair) => parseSetCookie(pair)) } /** @@ -32204,14 +32243,15 @@ module.exports = { /***/ }), /***/ 3834: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ ((module) => { "use strict"; -const assert = __nccwpck_require__(2613) -const { kHeadersList } = __nccwpck_require__(6443) - +/** + * @param {string} value + * @returns {boolean} + */ function isCTLExcludingHtab (value) { if (value.length === 0) { return false @@ -32472,31 +32512,13 @@ function stringify (cookie) { return out.join('; ') } -let kHeadersListNode - -function getHeadersList (headers) { - if (headers[kHeadersList]) { - return headers[kHeadersList] - } - - if (!kHeadersListNode) { - kHeadersListNode = Object.getOwnPropertySymbols(headers).find( - (symbol) => symbol.description === 'headers list' - ) - - assert(kHeadersListNode, 'Headers cannot be parsed') - } - - const headersList = headers[kHeadersListNode] - assert(headersList) - - return headersList -} - module.exports = { isCTLExcludingHtab, - stringify, - getHeadersList + validateCookieName, + validateCookiePath, + validateCookieValue, + toIMFDate, + stringify } @@ -34425,6 +34447,14 @@ const { isUint8Array, isArrayBuffer } = __nccwpck_require__(8253) const { File: UndiciFile } = __nccwpck_require__(3041) const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(4322) +let random +try { + const crypto = __nccwpck_require__(7598) + random = (max) => crypto.randomInt(0, max) +} catch { + random = (max) => Math.floor(Math.random(max)) +} + let ReadableStream = globalThis.ReadableStream /** @type {globalThis['File']} */ @@ -34510,7 +34540,7 @@ function extractBody (object, keepalive = false) { // Set source to a copy of the bytes held by object. source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)) } else if (util.isFormDataLike(object)) { - const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}` + const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, '0')}` const prefix = `--${boundary}\r\nContent-Disposition: form-data` /*! formdata-polyfill. MIT License. Jimmy Wärting */ @@ -36492,6 +36522,7 @@ const { isValidHeaderName, isValidHeaderValue } = __nccwpck_require__(5523) +const util = __nccwpck_require__(9023) const { webidl } = __nccwpck_require__(4222) const assert = __nccwpck_require__(2613) @@ -37045,6 +37076,9 @@ Object.defineProperties(Headers.prototype, { [Symbol.toStringTag]: { value: 'Headers', configurable: true + }, + [util.inspect.custom]: { + enumerable: false } }) @@ -46221,6 +46255,20 @@ class Pool extends PoolBase { ? { ...options.interceptors } : undefined this[kFactory] = factory + + this.on('connectionError', (origin, targets, error) => { + // If a connection error occurs, we remove the client from the pool, + // and emit a connectionError event. They will not be re-used. + // Fixes https://github.com/nodejs/undici/issues/3895 + for (const target of targets) { + // Do not use kRemoveClient here, as it will close the client, + // but the client cannot be closed in this state. + const idx = this[kClients].indexOf(target) + if (idx !== -1) { + this[kClients].splice(idx, 1) + } + } + }) } [kGetDispatcher] () { @@ -48629,6 +48677,14 @@ module.exports = require("net"); /***/ }), +/***/ 7598: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:crypto"); + +/***/ }), + /***/ 8474: /***/ ((module) => { @@ -58724,6 +58780,8 @@ var __webpack_exports__ = {}; const core = __nccwpck_require__(7484); const { "main": markdownlintCli2 } = __nccwpck_require__(2039); +const fs = __nccwpck_require__(3024); +const path = __nccwpck_require__(6760); const logMessage = core.info; const outputFormatter = (options) => { @@ -58769,6 +58827,38 @@ const outputFormatter = (options) => { } }; +const makeFileFormatter = (destinationPath) => (options) => { + const { results } = options; + + const findings = results.map((reportItem) => ({ + 'file': reportItem.fileName, + 'line': reportItem.lineNumber, + 'column': reportItem.errorRange ? reportItem.errorRange[0] : null, + 'endColumn': reportItem.errorRange ? reportItem.errorRange[0] + reportItem.errorRange[1] - 1 : null, + 'rule': reportItem.ruleNames.join("/"), + 'rulePrimary': reportItem.ruleNames[0], + 'description': reportItem.ruleDescription, + 'detail': reportItem.errorDetail || null, + 'context': reportItem.errorContext || null, + 'infoUrl': reportItem.ruleInformation || null + })); + + const outFile = path.resolve(destinationPath); + try { + fs.mkdirSync(path.dirname(outFile), { recursive: true }); + const payload = { + tool: "markdownlint-cli2", + version: 1, + count: findings.length, + results: findings + }; + fs.writeFileSync(outFile, JSON.stringify(payload, null, 2)); + logMessage(`Wrote markdownlint results to: ${outFile} (${findings.length} issues)`); + } catch (e) { + core.warning(`Failed to write results file: ${e instanceof Error ? e.message : String(e)}`); + } +}; + const separator = core.getInput("separator") || "\n"; const argv = core.getInput("globs"). @@ -58784,11 +58874,21 @@ if (fix) { argv.push("--fix"); } +const outputFormatters = [ [ outputFormatter ] ]; + +const resultsFile = core.getInput("results_file"); +if (resultsFile && resultsFile.length > 0) { + logMessage(`Markdown lint report will be recorded in file ${resultsFile}`) + outputFormatters.push([ makeFileFormatter(resultsFile) ]); +} else { + logMessage(`Markdown lint creating file report skipped`) +} + const parameters = { argv, logMessage, "optionsOverride": { - "outputFormatters": [ [ outputFormatter ] ] + "outputFormatters": outputFormatters } }; markdownlintCli2(parameters).then( diff --git a/markdownlint-cli2-action.js b/markdownlint-cli2-action.js index 24f2930..8ba224c 100644 --- a/markdownlint-cli2-action.js +++ b/markdownlint-cli2-action.js @@ -4,6 +4,8 @@ const core = require("@actions/core"); const { "main": markdownlintCli2 } = require("markdownlint-cli2"); +const fs = require("node:fs"); +const path = require("node:path"); const logMessage = core.info; const outputFormatter = (options) => { @@ -49,6 +51,38 @@ const outputFormatter = (options) => { } }; +const makeFileFormatter = (destinationPath) => (options) => { + const { results } = options; + + const findings = results.map((reportItem) => ({ + "file": reportItem.fileName, + "line": reportItem.lineNumber, + "column": reportItem.errorRange ? reportItem.errorRange[0] : null, + "endColumn": reportItem.errorRange ? reportItem.errorRange[0] + reportItem.errorRange[1] - 1 : null, + "rule": reportItem.ruleNames.join("/"), + "rulePrimary": reportItem.ruleNames[0], + "description": reportItem.ruleDescription, + "detail": reportItem.errorDetail || null, + "context": reportItem.errorContext || null, + "infoUrl": reportItem.ruleInformation || null + })); + + const outFile = path.resolve(destinationPath); + try { + fs.mkdirSync(path.dirname(outFile), { "recursive": true }); + const payload = { + "tool": "markdownlint-cli2", + "version": 1, + "count": findings.length, + "results": findings + }; + fs.writeFileSync(outFile, JSON.stringify(payload, null, 2)); + logMessage(`Wrote markdownlint results to: ${outFile} (${findings.length} issues)`); + } catch (error) { + core.warning(`Failed to write results file: ${error instanceof Error ? error.message : String(error)}`); + } +}; + const separator = core.getInput("separator") || "\n"; const argv = core.getInput("globs"). @@ -64,11 +98,21 @@ if (fix) { argv.push("--fix"); } +const outputFormatters = [ [ outputFormatter ] ]; + +const resultsFile = core.getInput("results_file"); +if (resultsFile && resultsFile.length > 0) { + logMessage(`Markdown lint report will be recorded in file ${resultsFile}`); + outputFormatters.push([ makeFileFormatter(resultsFile) ]); +} else { + logMessage(`Markdown lint creating file report skipped`); +} + const parameters = { argv, logMessage, "optionsOverride": { - "outputFormatters": [ [ outputFormatter ] ] + "outputFormatters": outputFormatters } }; markdownlintCli2(parameters).then(