mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
feat: dependency management with git dependency checks and format token validation
This commit is contained in:
parent
2eef1cdf70
commit
c3ffd7c38a
8 changed files with 524 additions and 4 deletions
73
Taskfile.yml
73
Taskfile.yml
|
|
@ -103,6 +103,51 @@ tasks:
|
|||
- echo "Warning - Installing packages over RNS via pip-rns is slow. Use for mesh-only environments."
|
||||
- bash scripts/pip-rns-deps.sh {{.CLI_ARGS}}
|
||||
|
||||
deps:git-pins:
|
||||
aliases: [deps:pins]
|
||||
desc: Fail if github/git deps or WASM download URLs drift from scripts/deps-allowlist.json
|
||||
cmds:
|
||||
- node scripts/check-git-deps.mjs
|
||||
|
||||
deps:why:
|
||||
desc: Explain why a package is installed (pnpm why and uv tree --package)
|
||||
cmds:
|
||||
- |
|
||||
PKG="{{.CLI_ARGS}}"
|
||||
if [ -z "$PKG" ]; then
|
||||
echo "Usage: task deps:why -- <package>" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "=== pnpm why ${PKG} ==="
|
||||
{{.NPM}} why "$PKG" || true
|
||||
echo
|
||||
echo "=== uv tree --package ${PKG} --invert ==="
|
||||
uv tree --package "$PKG" --invert || true
|
||||
|
||||
check:format-tokens:
|
||||
desc: Fail if formatDate/meshDate patterns use unsupported datetime tokens
|
||||
cmds:
|
||||
- node scripts/check-format-tokens.mjs
|
||||
|
||||
licenses:refresh:
|
||||
desc: Regenerate embedded license artifacts and fail if the tree is dirty afterward
|
||||
cmds:
|
||||
- uv run python -m meshchatx.src.backend.licenses_collector --write-artifacts
|
||||
- |
|
||||
if ! git diff --quiet -- \
|
||||
meshchatx/src/backend/data/licenses_frontend.json \
|
||||
meshchatx/src/backend/data/licenses_backend.json \
|
||||
meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt
|
||||
then
|
||||
echo "licenses:refresh left a dirty tree. Commit the updated license artifacts." >&2
|
||||
git status --short -- \
|
||||
meshchatx/src/backend/data/licenses_frontend.json \
|
||||
meshchatx/src/backend/data/licenses_backend.json \
|
||||
meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "licenses:refresh: artifacts up to date"
|
||||
|
||||
docs:rns:
|
||||
aliases: [build-docs:rns]
|
||||
desc: Fetch Reticulum manual from rngit website remote over RNS
|
||||
|
|
@ -180,11 +225,13 @@ tasks:
|
|||
|
||||
lint:frontend:
|
||||
aliases: [lint:fe]
|
||||
desc: Lint frontend code (ESLint, vue-tsc, knip)
|
||||
desc: Lint frontend code (ESLint, vue-tsc, knip, format-token and git-dep pin checks)
|
||||
cmds:
|
||||
- "{{.NPM}} run lint"
|
||||
- "{{.NPM}} run typecheck"
|
||||
- "{{.NPM}} exec knip"
|
||||
- task: check:format-tokens
|
||||
- task: deps:git-pins
|
||||
|
||||
lint:backend:
|
||||
aliases: [lint:be]
|
||||
|
|
@ -283,6 +330,30 @@ tasks:
|
|||
cmds:
|
||||
- node scripts/mutation/run.mjs --source meshchatx/src/frontend/js/rnode/Capabilities.js --max-per-file 20
|
||||
|
||||
test:mutation:frontend:libs:
|
||||
desc: MeshMut on meshchatx/src/frontend/libs (emitter uuid datetime clickOutside)
|
||||
cmds:
|
||||
- >-
|
||||
node scripts/mutation/run.mjs
|
||||
--source meshchatx/src/frontend/libs/emitter.js
|
||||
--source meshchatx/src/frontend/libs/uuid.js
|
||||
--source meshchatx/src/frontend/libs/datetime.js
|
||||
--source meshchatx/src/frontend/libs/clickOutside.js
|
||||
{{.CLI_ARGS}}
|
||||
|
||||
test:libs:
|
||||
aliases: [test:fe:libs]
|
||||
desc: Run in-repo frontend libs tests with coverage gate
|
||||
cmds:
|
||||
- >-
|
||||
{{.NPM}} exec vitest run
|
||||
tests/frontend/libs.core.test.js
|
||||
tests/frontend/libs.oracle.test.js
|
||||
tests/frontend/libs.index.test.js
|
||||
--coverage
|
||||
--coverage.include=meshchatx/src/frontend/libs/**
|
||||
- node scripts/check-format-tokens.mjs
|
||||
|
||||
test:frontend:
|
||||
aliases: [test:fe]
|
||||
desc: Run frontend + Electron shell unit tests (vitest, excludes i18n, see test:lang, excludes LoadTimePerformance, use test:fe:loadtime locally)
|
||||
|
|
|
|||
BIN
meshchatx.rsm
BIN
meshchatx.rsm
Binary file not shown.
|
|
@ -90,7 +90,7 @@
|
|||
"globals": "^17.6.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"knip": "^6.24.0",
|
||||
"micron-parser": "github:RFnexus/micron-parser-js",
|
||||
"micron-parser": "github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098",
|
||||
"prettier": "^3.9.3",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"terser": "^5.48.0",
|
||||
|
|
|
|||
4
pnpm-lock.yaml
generated
4
pnpm-lock.yaml
generated
|
|
@ -193,7 +193,7 @@ importers:
|
|||
specifier: ^6.24.0
|
||||
version: 6.24.0
|
||||
micron-parser:
|
||||
specifier: github:RFnexus/micron-parser-js
|
||||
specifier: github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098
|
||||
version: https://codeload.github.com/RFnexus/micron-parser-js/tar.gz/33feb1054c8b2cb3f5f05abbb8903360d3f0c098
|
||||
prettier:
|
||||
specifier: ^3.9.3
|
||||
|
|
@ -4270,7 +4270,7 @@ snapshots:
|
|||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
string-width-cjs: string-width@4.2.0
|
||||
string-width-cjs: string-width@4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
strip-ansi-cjs: strip-ansi@6.0.1
|
||||
wrap-ansi: 8.1.0
|
||||
|
|
|
|||
125
scripts/check-format-tokens.mjs
Normal file
125
scripts/check-format-tokens.mjs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#!/usr/bin/env node
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
/**
|
||||
* Fail when formatDate / meshDate(...).format patterns use unsupported tokens.
|
||||
* Keeps meshchatx/src/frontend/libs/datetime.js as the single source of truth.
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, "..");
|
||||
const FRONTEND_ROOT = path.join(ROOT, "meshchatx", "src", "frontend");
|
||||
const DATETIME_LIB = path.join(FRONTEND_ROOT, "libs", "datetime.js");
|
||||
|
||||
const SCAN_EXTENSIONS = new Set([".js", ".mjs", ".cjs", ".vue", ".ts", ".tsx"]);
|
||||
|
||||
/** @type {RegExp[]} */
|
||||
const PATTERN_EXTRACTORS = [
|
||||
/\bformatDate\s*\(\s*[^,]+?\s*,\s*(['"`])([^'"`]+?)\1/g,
|
||||
/\bmeshDate\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/g,
|
||||
/\bdayjs\s*\(\s*[^)]*?\)\s*\.\s*format\s*\(\s*(['"`])([^'"`]+?)\1/g,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string} source
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function readSupportedTokens(source) {
|
||||
const match = source.match(/export const SUPPORTED_FORMAT_TOKENS = Object\.freeze\(\[([\s\S]*?)\]\)/);
|
||||
if (!match) {
|
||||
return [];
|
||||
}
|
||||
return Array.from(match[1].matchAll(/"([^"]+)"/g), (m) => m[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} dir
|
||||
* @param {(filePath: string) => void} visit
|
||||
*/
|
||||
function walk(dir, visit) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
if (entry.name === "node_modules" || entry.name === "public" || entry.name.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
walk(full, visit);
|
||||
continue;
|
||||
}
|
||||
if (SCAN_EXTENSIONS.has(path.extname(entry.name))) {
|
||||
visit(full);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pattern
|
||||
* @param {ReadonlySet<string>} supported
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function unsupportedTokens(pattern, supported) {
|
||||
const tokenRe = /YYYY|MMM|MM|M|DD|D|HH|H|hh|h|mm|A|a|ss|SSS|Z|z|X|x|Do|ddd|dddd/g;
|
||||
const found = pattern.match(tokenRe) || [];
|
||||
return [...new Set(found.filter((token) => !supported.has(token)))];
|
||||
}
|
||||
|
||||
function main() {
|
||||
const datetimeSource = fs.readFileSync(DATETIME_LIB, "utf8");
|
||||
const supportedList = readSupportedTokens(datetimeSource);
|
||||
if (supportedList.length === 0) {
|
||||
console.error("check-format-tokens: SUPPORTED_FORMAT_TOKENS missing from datetime.js");
|
||||
process.exit(1);
|
||||
}
|
||||
const supported = new Set(supportedList);
|
||||
|
||||
/** @type {{ file: string, pattern: string, tokens: string[] }[]} */
|
||||
const failures = [];
|
||||
/** @type {{ file: string, pattern: string }[]} */
|
||||
const dayjsHits = [];
|
||||
|
||||
walk(FRONTEND_ROOT, (filePath) => {
|
||||
const rel = path.relative(ROOT, filePath).replace(/\\/g, "/");
|
||||
if (rel.endsWith("libs/datetime.js")) {
|
||||
return;
|
||||
}
|
||||
const text = fs.readFileSync(filePath, "utf8");
|
||||
for (const extractor of PATTERN_EXTRACTORS) {
|
||||
extractor.lastIndex = 0;
|
||||
let match;
|
||||
while ((match = extractor.exec(text)) !== null) {
|
||||
const pattern = match[2];
|
||||
if (extractor.source.includes("dayjs")) {
|
||||
dayjsHits.push({ file: rel, pattern });
|
||||
}
|
||||
const bad = unsupportedTokens(pattern, supported);
|
||||
if (bad.length > 0) {
|
||||
failures.push({ file: rel, pattern, tokens: bad });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (dayjsHits.length > 0) {
|
||||
console.error("check-format-tokens: dayjs(...).format still used in frontend sources:");
|
||||
for (const hit of dayjsHits) {
|
||||
console.error(` ${hit.file}: ${hit.pattern}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error("check-format-tokens: unsupported format tokens:");
|
||||
for (const failure of failures) {
|
||||
console.error(` ${failure.file}: "${failure.pattern}" -> ${failure.tokens.join(", ")}`);
|
||||
}
|
||||
console.error(`Supported tokens: ${supportedList.join(" ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("check-format-tokens: ok");
|
||||
}
|
||||
|
||||
main();
|
||||
267
scripts/check-git-deps.mjs
Normal file
267
scripts/check-git-deps.mjs
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
#!/usr/bin/env node
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
/**
|
||||
* Fail when git-hosted deps or remote download URLs drift from scripts/deps-allowlist.json.
|
||||
* Covers package.json github:/git+ specs, pnpm-lock pins, flatpak git sources, and Micron WASM URLs.
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { MICRON_PARSER_GO_RELEASE_TAG } from "./micron-parser-go-version.mjs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, "..");
|
||||
const ALLOWLIST_PATH = path.join(ROOT, "scripts", "deps-allowlist.json");
|
||||
const PACKAGE_JSON_PATH = path.join(ROOT, "package.json");
|
||||
const LOCKFILE_PATH = path.join(ROOT, "pnpm-lock.yaml");
|
||||
const FETCH_MICRON_WASM_PATH = path.join(ROOT, "scripts", "fetch-micron-wasm.mjs");
|
||||
|
||||
const GIT_SPEC_RE = /^(github:|git\+|git:)/i;
|
||||
const FULL_COMMIT_RE = /^[0-9a-f]{40}$/i;
|
||||
const CODELOAD_COMMIT_RE = /codeload\.github\.com\/[^/]+\/[^/]+\/tar\.gz\/([0-9a-f]{40})/i;
|
||||
|
||||
/**
|
||||
* @param {string} pattern
|
||||
* @param {string} value
|
||||
*/
|
||||
function matchPattern(pattern, value) {
|
||||
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
return new RegExp(`^${escaped}$`).test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} spec
|
||||
*/
|
||||
function splitGitSpec(spec) {
|
||||
const hashIndex = spec.indexOf("#");
|
||||
if (hashIndex < 0) {
|
||||
return { base: spec, ref: null };
|
||||
}
|
||||
return {
|
||||
base: spec.slice(0, hashIndex),
|
||||
ref: spec.slice(hashIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} lockText
|
||||
* @param {string} name
|
||||
*/
|
||||
function lockfileCommitForPackage(lockText, name) {
|
||||
const lines = lockText.split(/\r?\n/);
|
||||
let inPackage = false;
|
||||
for (const line of lines) {
|
||||
if (line.match(new RegExp(`^\\s{4}${name}:\\s*$`))) {
|
||||
inPackage = true;
|
||||
continue;
|
||||
}
|
||||
if (inPackage) {
|
||||
if (/^\s{4}\S/.test(line) && !line.trim().startsWith(`${name}:`)) {
|
||||
break;
|
||||
}
|
||||
const versionMatch = line.match(/^\s+version:\s+(\S+)\s*$/);
|
||||
if (versionMatch) {
|
||||
const codeload = versionMatch[1].match(CODELOAD_COMMIT_RE);
|
||||
if (codeload) {
|
||||
return codeload[1].toLowerCase();
|
||||
}
|
||||
}
|
||||
const resolutionMatch = line.match(/^\s+resolution:\s+\{[^}]*tarball:\s+(\S+?)[,}]/);
|
||||
if (resolutionMatch) {
|
||||
const codeload = resolutionMatch[1].match(CODELOAD_COMMIT_RE);
|
||||
if (codeload) {
|
||||
return codeload[1].toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const globalCodeload = lockText.match(
|
||||
new RegExp(`${name}@https://codeload\\.github\\.com/[^\\s]+/tar\\.gz/([0-9a-f]{40})`, "i")
|
||||
);
|
||||
if (globalCodeload) {
|
||||
return globalCodeload[1].toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {string} constName
|
||||
*/
|
||||
function readExportedStringConst(text, constName) {
|
||||
const re = new RegExp(`const\\s+${constName}\\s*=\\s*[\`"']([^\`"']+)[\`"']`);
|
||||
const match = text.match(re);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function main() {
|
||||
/** @type {string[]} */
|
||||
const errors = [];
|
||||
|
||||
if (!fs.existsSync(ALLOWLIST_PATH)) {
|
||||
console.error(`check-git-deps: missing allowlist ${path.relative(ROOT, ALLOWLIST_PATH)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const allowlist = JSON.parse(fs.readFileSync(ALLOWLIST_PATH, "utf8"));
|
||||
const packageJson = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, "utf8"));
|
||||
const lockText = fs.readFileSync(LOCKFILE_PATH, "utf8");
|
||||
const fetchMicronText = fs.readFileSync(FETCH_MICRON_WASM_PATH, "utf8");
|
||||
|
||||
const depSections = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
||||
/** @type {{ name: string, spec: string }[]} */
|
||||
const gitDeps = [];
|
||||
for (const section of depSections) {
|
||||
const block = packageJson[section];
|
||||
if (!block || typeof block !== "object") continue;
|
||||
for (const [name, spec] of Object.entries(block)) {
|
||||
if (typeof spec === "string" && GIT_SPEC_RE.test(spec)) {
|
||||
gitDeps.push({ name, spec });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const allowByName = new Map(
|
||||
(allowlist.git_dependencies || []).map((entry) => [entry.name, entry])
|
||||
);
|
||||
|
||||
for (const dep of gitDeps) {
|
||||
const allowed = allowByName.get(dep.name);
|
||||
if (!allowed) {
|
||||
errors.push(`git dependency "${dep.name}" (${dep.spec}) is not in deps-allowlist.json`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const { base, ref } = splitGitSpec(dep.spec);
|
||||
const allowedBases = new Set(
|
||||
(allowed.specifiers || []).map((spec) => splitGitSpec(spec).base)
|
||||
);
|
||||
if (!allowedBases.has(base) && !(allowed.specifiers || []).includes(dep.spec)) {
|
||||
errors.push(
|
||||
`git dependency "${dep.name}" specifier base "${base}" is not allowlisted (have: ${(allowed.specifiers || []).join(", ")})`
|
||||
);
|
||||
}
|
||||
|
||||
const lockCommit = lockfileCommitForPackage(lockText, dep.name);
|
||||
if (!lockCommit) {
|
||||
errors.push(`git dependency "${dep.name}" is unpinned in pnpm-lock.yaml (no 40-char commit)`);
|
||||
continue;
|
||||
}
|
||||
if (!FULL_COMMIT_RE.test(lockCommit)) {
|
||||
errors.push(`git dependency "${dep.name}" lock commit is not a full SHA: ${lockCommit}`);
|
||||
continue;
|
||||
}
|
||||
if (lockCommit !== String(allowed.commit).toLowerCase()) {
|
||||
errors.push(
|
||||
`git dependency "${dep.name}" lock commit ${lockCommit} drifts from allowlist ${allowed.commit}`
|
||||
);
|
||||
}
|
||||
if (ref && FULL_COMMIT_RE.test(ref) && ref.toLowerCase() !== String(allowed.commit).toLowerCase()) {
|
||||
errors.push(
|
||||
`git dependency "${dep.name}" package.json ref #${ref} drifts from allowlist ${allowed.commit}`
|
||||
);
|
||||
}
|
||||
if (!ref) {
|
||||
errors.push(
|
||||
`git dependency "${dep.name}" package.json specifier is floating (${dep.spec}). Pin with #${allowed.commit}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const allowed of allowlist.git_dependencies || []) {
|
||||
if (!gitDeps.some((dep) => dep.name === allowed.name)) {
|
||||
errors.push(
|
||||
`allowlist git dependency "${allowed.name}" is missing from package.json (remove from allowlist or restore dep)`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const flatpakModules = packageJson.build?.flatpak?.modules || [];
|
||||
const allowGitSources = allowlist.git_sources || [];
|
||||
/** @type {object[]} */
|
||||
const declaredGitSources = [];
|
||||
for (const mod of flatpakModules) {
|
||||
for (const source of mod.sources || []) {
|
||||
if (source && source.type === "git" && source.url) {
|
||||
declaredGitSources.push(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const source of declaredGitSources) {
|
||||
const allowed = allowGitSources.find((entry) => entry.url === source.url);
|
||||
if (!allowed) {
|
||||
errors.push(`flatpak git source ${source.url} is not in deps-allowlist.json`);
|
||||
continue;
|
||||
}
|
||||
if (allowed.commit && source.commit && source.commit !== allowed.commit) {
|
||||
errors.push(
|
||||
`flatpak git source ${source.url} commit ${source.commit} drifts from allowlist ${allowed.commit}`
|
||||
);
|
||||
}
|
||||
if (allowed.tag && source.tag && source.tag !== allowed.tag) {
|
||||
errors.push(
|
||||
`flatpak git source ${source.url} tag ${source.tag} drifts from allowlist ${allowed.tag}`
|
||||
);
|
||||
}
|
||||
if (!source.commit) {
|
||||
errors.push(`flatpak git source ${source.url} is missing commit pin`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const allowed of allowGitSources) {
|
||||
if (!declaredGitSources.some((source) => source.url === allowed.url)) {
|
||||
errors.push(
|
||||
`allowlist git source ${allowed.url} is missing from package.json flatpak modules`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const wasmUrlTemplate = readExportedStringConst(fetchMicronText, "DEFAULT_WASM_URL");
|
||||
const wasmExecUrl = readExportedStringConst(fetchMicronText, "DEFAULT_WASM_EXEC_URL");
|
||||
// DEFAULT_WASM_URL is a template literal with ${MICRON_PARSER_GO_RELEASE_TAG}
|
||||
const wasmUrlResolved = `https://github.com/Quad4-Software/Micron-Parser-Go/releases/download/${MICRON_PARSER_GO_RELEASE_TAG}/micron-parser-go.wasm`;
|
||||
|
||||
for (const entry of allowlist.download_urls || []) {
|
||||
if (entry.name === "micron-parser-go-wasm") {
|
||||
if (entry.pinned_tag && entry.pinned_tag !== MICRON_PARSER_GO_RELEASE_TAG) {
|
||||
errors.push(
|
||||
`Micron WASM tag ${MICRON_PARSER_GO_RELEASE_TAG} drifts from allowlist ${entry.pinned_tag}`
|
||||
);
|
||||
}
|
||||
if (entry.pattern && !matchPattern(entry.pattern, wasmUrlResolved)) {
|
||||
errors.push(
|
||||
`Micron WASM URL ${wasmUrlResolved} does not match allowlist pattern ${entry.pattern}`
|
||||
);
|
||||
}
|
||||
if (wasmUrlTemplate && !wasmUrlTemplate.includes("Micron-Parser-Go/releases/download/")) {
|
||||
errors.push(`fetch-micron-wasm.mjs DEFAULT_WASM_URL looks unexpected: ${wasmUrlTemplate}`);
|
||||
}
|
||||
}
|
||||
if (entry.name === "golang-wasm-exec") {
|
||||
if (!wasmExecUrl) {
|
||||
errors.push("fetch-micron-wasm.mjs DEFAULT_WASM_EXEC_URL missing");
|
||||
} else if (entry.url && wasmExecUrl !== entry.url) {
|
||||
errors.push(
|
||||
`wasm_exec URL ${wasmExecUrl} drifts from allowlist ${entry.url}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error("check-git-deps: failed");
|
||||
for (const error of errors) {
|
||||
console.error(` - ${error}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("check-git-deps: ok");
|
||||
console.log(` micron-parser commit: ${(allowByName.get("micron-parser") || {}).commit}`);
|
||||
console.log(` micron wasm tag: ${MICRON_PARSER_GO_RELEASE_TAG}`);
|
||||
}
|
||||
|
||||
main();
|
||||
35
scripts/deps-allowlist.json
Normal file
35
scripts/deps-allowlist.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"version": 1,
|
||||
"git_dependencies": [
|
||||
{
|
||||
"name": "micron-parser",
|
||||
"specifiers": [
|
||||
"github:RFnexus/micron-parser-js",
|
||||
"github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098"
|
||||
],
|
||||
"commit": "33feb1054c8b2cb3f5f05abbb8903360d3f0c098",
|
||||
"repository": "https://github.com/RFnexus/micron-parser-js"
|
||||
}
|
||||
],
|
||||
"git_sources": [
|
||||
{
|
||||
"name": "zypak",
|
||||
"url": "https://github.com/refi64/zypak",
|
||||
"tag": "v2025.09",
|
||||
"commit": "693a71c5ffa80ec9c9ce2ae03b1ccc493c698e53",
|
||||
"declared_in": "package.json#build.flatpak.modules"
|
||||
}
|
||||
],
|
||||
"download_urls": [
|
||||
{
|
||||
"name": "micron-parser-go-wasm",
|
||||
"pattern": "https://github.com/Quad4-Software/Micron-Parser-Go/releases/download/*/micron-parser-go.wasm",
|
||||
"pinned_tag": "v1.0.5",
|
||||
"tag_source": "scripts/micron-parser-go-version.mjs"
|
||||
},
|
||||
{
|
||||
"name": "golang-wasm-exec",
|
||||
"url": "https://raw.githubusercontent.com/golang/go/go1.26.2/lib/wasm/wasm_exec.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
/** @typedef {{ source: string, tests: string[] }} MutationTarget */
|
||||
|
||||
const LIBS_TESTS = [
|
||||
"tests/frontend/libs.core.test.js",
|
||||
"tests/frontend/libs.oracle.test.js",
|
||||
"tests/frontend/libs.index.test.js",
|
||||
];
|
||||
|
||||
/** @type {MutationTarget[]} */
|
||||
export const DEFAULT_FRONTEND_TARGETS = [
|
||||
{
|
||||
|
|
@ -26,6 +32,22 @@ export const DEFAULT_FRONTEND_TARGETS = [
|
|||
source: "meshchatx/src/frontend/js/reticulumPathfinding.js",
|
||||
tests: ["tests/frontend/reticulumPathfinding.test.js"],
|
||||
},
|
||||
{
|
||||
source: "meshchatx/src/frontend/libs/emitter.js",
|
||||
tests: LIBS_TESTS,
|
||||
},
|
||||
{
|
||||
source: "meshchatx/src/frontend/libs/uuid.js",
|
||||
tests: LIBS_TESTS,
|
||||
},
|
||||
{
|
||||
source: "meshchatx/src/frontend/libs/datetime.js",
|
||||
tests: LIBS_TESTS,
|
||||
},
|
||||
{
|
||||
source: "meshchatx/src/frontend/libs/clickOutside.js",
|
||||
tests: LIBS_TESTS,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue