mason-context 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -1
- package/README.md +39 -513
- package/dist/mason-audit.js +92 -26
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +2885 -548
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-drift.js +1 -1
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +1 -1
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +4535 -3418
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +1 -1
- package/dist/mason-review.js.map +1 -1
- package/dist/mason.js +4842 -15
- package/dist/mason.js.map +1 -1
- package/package.json +5 -2
package/dist/mason-audit.js
CHANGED
|
@@ -133,7 +133,7 @@ async function readStoreJson(root, relative) {
|
|
|
133
133
|
return parsed;
|
|
134
134
|
} catch (error) {
|
|
135
135
|
if (error.code === "ENOENT") return null;
|
|
136
|
-
throw new Error(`Invalid Mason store ${relative}: ${error instanceof Error ? error.message : String(error)}
|
|
136
|
+
throw new Error(`Invalid Mason store ${relative}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
async function writeStoreJson(root, relative, value) {
|
|
@@ -622,24 +622,24 @@ async function isDirty(resolvedRoot, relPath) {
|
|
|
622
622
|
}
|
|
623
623
|
}
|
|
624
624
|
async function discoverDocs(resolvedRoot) {
|
|
625
|
-
const docs =
|
|
626
|
-
for (const candidate of DOC_CANDIDATES) {
|
|
625
|
+
const docs = await Promise.all(DOC_CANDIDATES.map(async (candidate) => {
|
|
627
626
|
let content;
|
|
628
627
|
try {
|
|
629
628
|
content = await fs4.readFile(path7.join(resolvedRoot, candidate), "utf-8");
|
|
630
629
|
} catch {
|
|
631
|
-
|
|
630
|
+
return null;
|
|
632
631
|
}
|
|
633
|
-
|
|
632
|
+
const [lastCommit, dirty] = await Promise.all([lastCommitOf(resolvedRoot, candidate), isDirty(resolvedRoot, candidate)]);
|
|
633
|
+
return {
|
|
634
634
|
path: candidate,
|
|
635
635
|
content,
|
|
636
636
|
lineCount: content.split("\n").length,
|
|
637
|
-
lastCommit
|
|
638
|
-
dirty
|
|
637
|
+
lastCommit,
|
|
638
|
+
dirty,
|
|
639
639
|
claims: extractClaims(content)
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
return docs;
|
|
640
|
+
};
|
|
641
|
+
}));
|
|
642
|
+
return docs.filter((doc) => doc !== null);
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
// src/audit/types.ts
|
|
@@ -814,12 +814,19 @@ async function checkNewModules(ctx) {
|
|
|
814
814
|
}
|
|
815
815
|
});
|
|
816
816
|
};
|
|
817
|
-
for (const
|
|
818
|
-
|
|
817
|
+
for (const candidate of await moduleCandidates(ctx.root, combinedDocs)) {
|
|
818
|
+
await flag(candidate.dir, candidate.sourceFileCount);
|
|
819
|
+
}
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
async function moduleCandidates(root, combinedDocs) {
|
|
823
|
+
const candidates = [];
|
|
824
|
+
for (const topDir of await listSubdirs(root)) {
|
|
825
|
+
const absTop = path9.join(root, topDir);
|
|
819
826
|
const topMentioned = isMentioned(combinedDocs, topDir);
|
|
820
827
|
if (!topMentioned) {
|
|
821
828
|
const count2 = await countSourceFiles(absTop);
|
|
822
|
-
if (count2 >= 1)
|
|
829
|
+
if (count2 >= 1) candidates.push({ dir: topDir, sourceFileCount: count2 });
|
|
823
830
|
continue;
|
|
824
831
|
}
|
|
825
832
|
const subdirs = await listSubdirs(absTop);
|
|
@@ -829,11 +836,11 @@ async function checkNewModules(ctx) {
|
|
|
829
836
|
if (isMentioned(combinedDocs, sub)) continue;
|
|
830
837
|
const count2 = await countSourceFiles(path9.join(absTop, sub));
|
|
831
838
|
if (count2 >= SECOND_LEVEL_MIN_SOURCE_FILES) {
|
|
832
|
-
|
|
839
|
+
candidates.push({ dir: `${topDir}/${sub}`, sourceFileCount: count2 });
|
|
833
840
|
}
|
|
834
841
|
}
|
|
835
842
|
}
|
|
836
|
-
return
|
|
843
|
+
return candidates;
|
|
837
844
|
}
|
|
838
845
|
|
|
839
846
|
// src/audit/checks/stale-count.ts
|
|
@@ -1016,15 +1023,7 @@ async function checkDeadCommands(ctx) {
|
|
|
1016
1023
|
const loadWorkspaceScripts = async () => {
|
|
1017
1024
|
if (workspaceScripts !== null) return workspaceScripts;
|
|
1018
1025
|
workspaceScripts = /* @__PURE__ */ new Set();
|
|
1019
|
-
const manifests = await
|
|
1020
|
-
cwd: ctx.root,
|
|
1021
|
-
ignore: [
|
|
1022
|
-
"**/node_modules/**",
|
|
1023
|
-
"**/dist/**",
|
|
1024
|
-
"**/build/**",
|
|
1025
|
-
"package.json"
|
|
1026
|
-
]
|
|
1027
|
-
});
|
|
1026
|
+
const manifests = await commandManifests(ctx.root);
|
|
1028
1027
|
manifestsChecked = ["package.json", ...manifests.sort()];
|
|
1029
1028
|
for (const manifest of manifests) {
|
|
1030
1029
|
const scripts = await scriptsOf(path11.join(ctx.root, manifest));
|
|
@@ -1052,6 +1051,64 @@ async function checkDeadCommands(ctx) {
|
|
|
1052
1051
|
}
|
|
1053
1052
|
return result;
|
|
1054
1053
|
}
|
|
1054
|
+
function commandManifests(root) {
|
|
1055
|
+
return fg4("**/package.json", {
|
|
1056
|
+
cwd: root,
|
|
1057
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", ".mason/reports/**", "package.json"]
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/audit/release-metadata.ts
|
|
1062
|
+
import { execFile as execFile6 } from "child_process";
|
|
1063
|
+
import { promisify as promisify6 } from "util";
|
|
1064
|
+
var exec6 = promisify6(execFile6);
|
|
1065
|
+
async function git(root, args) {
|
|
1066
|
+
return (await exec6("git", args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
|
|
1067
|
+
}
|
|
1068
|
+
function withoutAndroidReleaseValues(text2) {
|
|
1069
|
+
if (/\/\*|"""|'''/.test(text2) || !/id\s*\(?\s*["']com\.android\.(application|library)["']/.test(text2)) return null;
|
|
1070
|
+
const scopes = [];
|
|
1071
|
+
const normalized = [];
|
|
1072
|
+
let assignments = 0;
|
|
1073
|
+
for (const line of text2.split("\n")) {
|
|
1074
|
+
const code = line.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/\/.*$/g, (token) => token.startsWith("//") ? "" : '""');
|
|
1075
|
+
const assignment = line.match(/^(\s*)(versionName|versionCode)(\s*(?:=\s*|\s+))("[A-Za-z0-9._+-]+"|'[A-Za-z0-9._+-]+'|\d+)(\s*)$/);
|
|
1076
|
+
if (assignment && scopes.join("/") === "android/defaultConfig" && (assignment[2] === "versionCode" ? /^\d+$/.test(assignment[4]) : /^["']/.test(assignment[4]))) {
|
|
1077
|
+
normalized.push(assignment[1] + assignment[2] + assignment[3] + "<release-value>" + assignment[5]);
|
|
1078
|
+
assignments++;
|
|
1079
|
+
} else {
|
|
1080
|
+
if (/\bversion(?:Name|Code)\b/.test(line)) return null;
|
|
1081
|
+
normalized.push(line);
|
|
1082
|
+
}
|
|
1083
|
+
const named = code.match(/^\s*(android|defaultConfig)\s*\{\s*$/)?.[1];
|
|
1084
|
+
for (const brace of code.matchAll(/[{}]/g)) {
|
|
1085
|
+
if (brace[0] === "{") scopes.push(named ?? "unknown");
|
|
1086
|
+
else if (!scopes.length) return null;
|
|
1087
|
+
else scopes.pop();
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return assignments && !scopes.length ? normalized.join("\n") : null;
|
|
1091
|
+
}
|
|
1092
|
+
async function releaseMetadataOnly(root, commit) {
|
|
1093
|
+
if (!commit.files.length || !commit.files.every((file) => /(^|\/)build\.gradle(?:\.kts)?$/.test(file))) return false;
|
|
1094
|
+
try {
|
|
1095
|
+
const parents = (await git(root, ["rev-list", "--parents", "-n", "1", commit.hash])).trim().split(/\s+/);
|
|
1096
|
+
if (parents.length !== 2) return false;
|
|
1097
|
+
for (const file of commit.files) {
|
|
1098
|
+
const raw = await git(root, ["diff", "--raw", "-z", "--no-renames", "--no-ext-diff", "--no-textconv", parents[1], commit.hash, "--", file]);
|
|
1099
|
+
if (!/^:(100644|100755) \1 [a-f0-9]+ [a-f0-9]+ M\0/.test(raw)) return false;
|
|
1100
|
+
const [before, after] = await Promise.all([
|
|
1101
|
+
git(root, ["show", parents[1] + ":" + file]),
|
|
1102
|
+
git(root, ["show", commit.hash + ":" + file])
|
|
1103
|
+
]);
|
|
1104
|
+
const previous = withoutAndroidReleaseValues(before);
|
|
1105
|
+
if (previous === null || previous !== withoutAndroidReleaseValues(after)) return false;
|
|
1106
|
+
}
|
|
1107
|
+
return true;
|
|
1108
|
+
} catch {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1055
1112
|
|
|
1056
1113
|
// src/audit/checks/deps-changed.ts
|
|
1057
1114
|
var MANIFEST_COMMITS_CAP = 10;
|
|
@@ -1072,6 +1129,7 @@ var MANIFEST_PATHSPECS = [
|
|
|
1072
1129
|
async function checkDepsChanged(ctx) {
|
|
1073
1130
|
const result = emptyResult();
|
|
1074
1131
|
result.suppressedAdvisories = [];
|
|
1132
|
+
const releaseOnly = /* @__PURE__ */ new Map();
|
|
1075
1133
|
for (const doc of ctx.docs) {
|
|
1076
1134
|
if (!doc.lastCommit) {
|
|
1077
1135
|
result.skipped.push({
|
|
@@ -1101,6 +1159,15 @@ async function checkDepsChanged(ctx) {
|
|
|
1101
1159
|
});
|
|
1102
1160
|
continue;
|
|
1103
1161
|
}
|
|
1162
|
+
const relevant = [];
|
|
1163
|
+
for (const commit of range.commits) {
|
|
1164
|
+
if (!releaseOnly.has(commit.hash) && releaseOnly.size < 100) {
|
|
1165
|
+
releaseOnly.set(commit.hash, await releaseMetadataOnly(ctx.root, commit));
|
|
1166
|
+
}
|
|
1167
|
+
if (!releaseOnly.get(commit.hash)) relevant.push(commit);
|
|
1168
|
+
}
|
|
1169
|
+
range.commits = relevant;
|
|
1170
|
+
range.total = relevant.length;
|
|
1104
1171
|
if (range.total === 0) continue;
|
|
1105
1172
|
const latest = range.commits[0];
|
|
1106
1173
|
(doc.dirty ? result.suppressedAdvisories : result.advisories).push({
|
|
@@ -1381,9 +1448,8 @@ function emptyResult() {
|
|
|
1381
1448
|
// src/audit/audit.ts
|
|
1382
1449
|
async function computeAudit(rootDir, options = {}) {
|
|
1383
1450
|
const resolvedRoot = path15.resolve(rootDir);
|
|
1384
|
-
const docs = await discoverDocs(resolvedRoot);
|
|
1451
|
+
const [docs, headHash] = await Promise.all([discoverDocs(resolvedRoot), getCurrentGitHash(resolvedRoot)]);
|
|
1385
1452
|
if (docs.length === 0) return null;
|
|
1386
|
-
const headHash = await getCurrentGitHash(resolvedRoot);
|
|
1387
1453
|
const report = {
|
|
1388
1454
|
version: 1,
|
|
1389
1455
|
root: resolvedRoot,
|