feat: Update dependencies to latest version and refactor action to ES6 module

Signed-off-by: David Dreschner <github-2017@dreschner.net>
This commit is contained in:
David Dreschner 2026-06-21 23:07:25 +02:00
commit 25ddaceda2
No known key found for this signature in database
9 changed files with 60070 additions and 43159 deletions

View file

@ -85,7 +85,7 @@ Minimum supported server version, as a single-item list
## Building ## Building
- `npm ci` - `npm ci`
- `./node_modules/.bin/ncc build src/action.js -o dist` - `./node_modules/.bin/ncc build src/action.mjs -o dist`
## License ## License
[MIT License](LICENSE.md) [MIT License](LICENSE.md)

View file

@ -48,4 +48,4 @@ outputs:
description: 'Minimum supported server version, as a single-item list' description: 'Minimum supported server version, as a single-item list'
runs: runs:
using: 'node24' using: 'node24'
main: 'dist/index.js' main: 'dist/index.mjs'

449
dist/101.index.mjs vendored Normal file
View file

@ -0,0 +1,449 @@
export const id = 101;
export const ids = [101];
export const modules = {
/***/ 9101:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ toFormData: () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9802);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3018);
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .fS();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .ZH(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};

13
dist/241.index.mjs vendored Normal file
View file

@ -0,0 +1,13 @@
export const id = 241;
export const ids = [241];
export const modules = {
/***/ 9241:
/***/ ((module) => {
module.exports = eval("require")("web-streams-polyfill/ponyfill/es2018");
/***/ })
};

42802
dist/index.js vendored

File diff suppressed because one or more lines are too long

58936
dist/index.mjs vendored Normal file

File diff suppressed because one or more lines are too long

1030
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,26 @@
{ {
"name": "nextcloud-version-matrix", "name": "nextcloud-version-matrix",
"type": "module",
"version": "1.0.0", "version": "1.0.0",
"description": "Generate nextcloud version matrix for an app", "description": "Generate nextcloud version matrix for an app",
"main": "src/action.js", "main": "src/action.mjs",
"scripts": { "scripts": {
"build": "ncc build src/action.js -o dist", "build": "ncc build src/action.mjs -o dist",
"test": "INPUT_FILENAME=.github/workflows/info.xml node src/action.js" "test": "INPUT_FILENAME=.github/workflows/info.xml node src/action.mjs"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^3.0.1",
"@actions/http-client": "^2.2.0", "@actions/exec": "^3.0.0",
"@actions/http-client": "^4.0.1",
"@xmldom/xmldom": "^0.9.10",
"deep-equal": "^2.2.3", "deep-equal": "^2.2.3",
"url-exist": "^2.0.2", "url-exist": "^3.0.1",
"xmldom": "^0.6.0", "xpath": "^0.0.34"
"xpath": "0.0.27"
}, },
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.38.1" "@vercel/ncc": "^0.44.0"
} }
} }

View file

@ -1,12 +1,11 @@
const core = require('@actions/core'); import * as core from '@actions/core'
const xpath = require('xpath'); import xpath from 'xpath'
const DOMParser = require('xmldom').DOMParser; import {DOMParser} from '@xmldom/xmldom'
const fs = require('fs'); import fs from 'fs'
const urlExist = require('url-exist'); import urlExist from 'url-exist'
const {HttpClient} = require('@actions/http-client'); import equal from 'deep-equal'
const equal = require('deep-equal'); import {getExecOutput} from '@actions/exec'
const { promisify } = require('util'); import {HttpClient} from '@actions/http-client'
const exec = promisify(require('child_process').exec)
const client = new HttpClient('nextcloud-version-matrix') const client = new HttpClient('nextcloud-version-matrix')
@ -53,7 +52,7 @@ async function isPhpVersionReleased(version) {
async function distroSupportsPhpVersion(version) { async function distroSupportsPhpVersion(version) {
try { try {
const output = await exec(`apt-cache policy php${version}`); const output = await getExecOutput('apt-cache', ['policy', `php${version}`], {silent: true});
return output.stdout.includes("Candidate") return output.stdout.includes("Candidate")
} catch (_e) { } catch (_e) {
return false; return false;
@ -130,7 +129,7 @@ function getPHPVersionID(version) {
} }
const content = fs.readFileSync(filename, 'utf8'); const content = fs.readFileSync(filename, 'utf8');
const document = new DOMParser().parseFromString(content); const document = new DOMParser().parseFromString(content, 'text/xml');
const nextcloudMinVersion = parseInt(xpath.select1("//info//dependencies//nextcloud/@min-version", document).value, 10); const nextcloudMinVersion = parseInt(xpath.select1("//info//dependencies//nextcloud/@min-version", document).value, 10);
const nextcloudMaxVersion = parseInt(xpath.select1("//info//dependencies//nextcloud/@max-version", document).value, 10); const nextcloudMaxVersion = parseInt(xpath.select1("//info//dependencies//nextcloud/@max-version", document).value, 10);