lula2 0.6.3 → 0.6.4-nightly.1
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/README.md +34 -0
- package/dist/_app/immutable/assets/0.BBhcdCdh.css +1 -0
- package/dist/_app/immutable/chunks/{WlyXjfrM.js → 6cSSDZaX.js} +1 -1
- package/dist/_app/immutable/chunks/{zlVqcHYm.js → B1RHKOaU.js} +1 -1
- package/dist/_app/immutable/chunks/{BXUi170M.js → BDFhLgJd.js} +1 -1
- package/dist/_app/immutable/chunks/{JMs-cfQQ.js → BGtWuPgW.js} +1 -1
- package/dist/_app/immutable/chunks/{Cq7PwVfU.js → BZU_Nz1O.js} +1 -1
- package/dist/_app/immutable/chunks/BnySH2DD.js +2 -0
- package/dist/_app/immutable/chunks/{W6j_Lifs.js → CYCHlyTI.js} +2 -2
- package/dist/_app/immutable/chunks/CfxzrOg_.js +2 -0
- package/dist/_app/immutable/chunks/{CRa0j_Fx.js → ipJF3Ffx.js} +1 -1
- package/dist/_app/immutable/entry/{app.DrhleL0r.js → app.h3tcJj9_.js} +2 -2
- package/dist/_app/immutable/entry/start.DiR5xXKY.js +1 -0
- package/dist/_app/immutable/nodes/{0.DvnxCGrF.js → 0.Y54zbvCd.js} +1 -1
- package/dist/_app/immutable/nodes/{1.CTl6z-0x.js → 1.BXHS6-Xi.js} +1 -1
- package/dist/_app/immutable/nodes/{2.Cmc1ecj4.js → 2.DsUjGnBN.js} +1 -1
- package/dist/_app/immutable/nodes/{3.DIDukog0.js → 3.CsKOxCvs.js} +1 -1
- package/dist/_app/immutable/nodes/{4.D74oitYV.js → 4.A9PtO4RK.js} +11 -11
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/crawl.js +151 -90
- package/dist/cli/commands/ui.js +49 -7
- package/dist/cli/server/index.js +49 -7
- package/dist/cli/server/server.js +49 -7
- package/dist/cli/server/serverState.js +37 -4
- package/dist/cli/server/spreadsheetRoutes.js +12 -3
- package/dist/cli/server/websocketServer.js +49 -7
- package/dist/index.html +10 -10
- package/dist/index.js +194 -98
- package/package.json +20 -21
- package/src/lib/components/setup/SpreadsheetImport.svelte +5 -0
- package/dist/_app/immutable/assets/0.CJjXKESY.css +0 -1
- package/dist/_app/immutable/chunks/DTWPdvjs.js +0 -2
- package/dist/_app/immutable/chunks/e7OeeeKP.js +0 -2
- package/dist/_app/immutable/entry/start.BMEfvcBH.js +0 -1
package/dist/index.js
CHANGED
|
@@ -1722,8 +1722,8 @@ var init_fileStore = __esm({
|
|
|
1722
1722
|
if (!this.baseDir || this.baseDir === "." || this.baseDir === process.cwd()) {
|
|
1723
1723
|
return;
|
|
1724
1724
|
}
|
|
1725
|
-
const
|
|
1726
|
-
if (!existsSync2(
|
|
1725
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1726
|
+
if (!existsSync2(lulaConfigPath2)) {
|
|
1727
1727
|
return;
|
|
1728
1728
|
}
|
|
1729
1729
|
if (!existsSync2(this.controlsDir)) {
|
|
@@ -1881,6 +1881,17 @@ var init_fileStore = __esm({
|
|
|
1881
1881
|
if (!existsSync2(this.controlsDir)) {
|
|
1882
1882
|
return [];
|
|
1883
1883
|
}
|
|
1884
|
+
let controlOrder = null;
|
|
1885
|
+
try {
|
|
1886
|
+
const lulaConfigPath2 = join2(this.baseDir, "lula.yaml");
|
|
1887
|
+
if (existsSync2(lulaConfigPath2)) {
|
|
1888
|
+
const content = readFileSync2(lulaConfigPath2, "utf8");
|
|
1889
|
+
const metadata = yaml2.load(content);
|
|
1890
|
+
controlOrder = metadata?.controlOrder || null;
|
|
1891
|
+
}
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
console.error(`Failed to load lula.yaml for controlOrder (path: ${lulaConfigPath}):`, error);
|
|
1894
|
+
}
|
|
1884
1895
|
const entries = readdirSync(this.controlsDir);
|
|
1885
1896
|
const yamlFiles = entries.filter((file) => file.endsWith(".yaml"));
|
|
1886
1897
|
if (yamlFiles.length > 0) {
|
|
@@ -1899,7 +1910,11 @@ var init_fileStore = __esm({
|
|
|
1899
1910
|
}
|
|
1900
1911
|
});
|
|
1901
1912
|
const results2 = await Promise.all(promises);
|
|
1902
|
-
|
|
1913
|
+
const controls2 = results2.filter((c) => c !== null);
|
|
1914
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1915
|
+
return this.sortControlsByOrder(controls2, controlOrder);
|
|
1916
|
+
}
|
|
1917
|
+
return controls2;
|
|
1903
1918
|
}
|
|
1904
1919
|
const families = entries.filter((name) => {
|
|
1905
1920
|
const familyPath = join2(this.controlsDir, name);
|
|
@@ -1922,7 +1937,25 @@ var init_fileStore = __esm({
|
|
|
1922
1937
|
allPromises.push(...familyPromises);
|
|
1923
1938
|
}
|
|
1924
1939
|
const results = await Promise.all(allPromises);
|
|
1925
|
-
|
|
1940
|
+
const controls = results.filter((c) => c !== null);
|
|
1941
|
+
if (controlOrder && controlOrder.length > 0) {
|
|
1942
|
+
return this.sortControlsByOrder(controls, controlOrder);
|
|
1943
|
+
}
|
|
1944
|
+
return controls;
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Sort controls based on the provided order array
|
|
1948
|
+
*/
|
|
1949
|
+
sortControlsByOrder(controls, controlOrder) {
|
|
1950
|
+
const orderMap = /* @__PURE__ */ new Map();
|
|
1951
|
+
controlOrder.forEach((controlId, index) => {
|
|
1952
|
+
orderMap.set(controlId, index);
|
|
1953
|
+
});
|
|
1954
|
+
return controls.sort((a, b) => {
|
|
1955
|
+
const aIndex = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1956
|
+
const bIndex = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
|
|
1957
|
+
return aIndex - bIndex;
|
|
1958
|
+
});
|
|
1926
1959
|
}
|
|
1927
1960
|
/**
|
|
1928
1961
|
* Load mappings from mappings directory
|
|
@@ -3042,6 +3075,7 @@ function processSpreadsheetData(rawData, headers, startRowIndex, params) {
|
|
|
3042
3075
|
continue;
|
|
3043
3076
|
}
|
|
3044
3077
|
const family = extractFamilyFromControlId(controlId);
|
|
3078
|
+
control._originalRowIndex = i;
|
|
3045
3079
|
control.family = family;
|
|
3046
3080
|
controls.push(control);
|
|
3047
3081
|
if (!families.has(family)) {
|
|
@@ -3164,6 +3198,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3164
3198
|
params.controlIdField,
|
|
3165
3199
|
params.namingConvention
|
|
3166
3200
|
);
|
|
3201
|
+
const controlOrder = controls.sort((a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)).map((control) => control[controlIdFieldNameClean]);
|
|
3167
3202
|
const controlSetData = {
|
|
3168
3203
|
name: params.controlSetName,
|
|
3169
3204
|
description: params.controlSetDescription,
|
|
@@ -3171,12 +3206,16 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3171
3206
|
control_id_field: controlIdFieldNameClean,
|
|
3172
3207
|
controlCount: controls.length,
|
|
3173
3208
|
families: uniqueFamilies,
|
|
3209
|
+
controlOrder,
|
|
3174
3210
|
fieldSchema
|
|
3175
3211
|
};
|
|
3176
3212
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3177
3213
|
const controlsDir = join4(baseDir, "controls");
|
|
3178
3214
|
const mappingsDir = join4(baseDir, "mappings");
|
|
3179
|
-
families.
|
|
3215
|
+
const sortedFamilies = Array.from(families.entries()).sort(
|
|
3216
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
3217
|
+
);
|
|
3218
|
+
sortedFamilies.forEach(([family, familyControls]) => {
|
|
3180
3219
|
const familyDir = join4(controlsDir, family);
|
|
3181
3220
|
const familyMappingsDir = join4(mappingsDir, family);
|
|
3182
3221
|
if (!existsSync3(familyDir)) {
|
|
@@ -3185,7 +3224,10 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3185
3224
|
if (!existsSync3(familyMappingsDir)) {
|
|
3186
3225
|
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3187
3226
|
}
|
|
3188
|
-
familyControls.
|
|
3227
|
+
const sortedFamilyControls = familyControls.sort(
|
|
3228
|
+
(a, b) => (a._originalRowIndex || 0) - (b._originalRowIndex || 0)
|
|
3229
|
+
);
|
|
3230
|
+
sortedFamilyControls.forEach((control) => {
|
|
3189
3231
|
const controlId = control[controlIdFieldNameClean];
|
|
3190
3232
|
if (!controlId) {
|
|
3191
3233
|
console.error("Missing control ID for control:", control);
|
|
@@ -3207,7 +3249,7 @@ async function createOutputStructure(processedData, fieldSchema, params) {
|
|
|
3207
3249
|
filteredControl.family = control.family;
|
|
3208
3250
|
}
|
|
3209
3251
|
Object.keys(control).forEach((fieldName) => {
|
|
3210
|
-
if (fieldName === "family") return;
|
|
3252
|
+
if (fieldName === "family" || fieldName === "_originalRowIndex") return;
|
|
3211
3253
|
if (params.justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3212
3254
|
justificationContents.push(control[fieldName]);
|
|
3213
3255
|
}
|
|
@@ -5638,155 +5680,209 @@ function containsLulaAnnotations(text) {
|
|
|
5638
5680
|
const lines = text.split("\n");
|
|
5639
5681
|
return lines.some((line) => line.includes("@lulaStart") || line.includes("@lulaEnd"));
|
|
5640
5682
|
}
|
|
5641
|
-
function
|
|
5642
|
-
return
|
|
5643
|
-
new Option("--post-mode <mode>", "How to post findings").choices(["review", "comment"]).default("review")
|
|
5644
|
-
).action(async (opts) => {
|
|
5645
|
-
let leavePost = false;
|
|
5646
|
-
const { owner, repo, pull_number } = getPRContext();
|
|
5647
|
-
console.log(`Analyzing PR #${pull_number} in ${owner}/${repo} for compliance changes...`);
|
|
5648
|
-
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
|
5649
|
-
const pr = await octokit.pulls.get({ owner, repo, pull_number });
|
|
5650
|
-
const prBranch = pr.data.head.ref;
|
|
5651
|
-
const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
|
|
5652
|
-
let commentBody = `${LULA_SIGNATURE}
|
|
5683
|
+
function createInitialCommentBody(filesCount) {
|
|
5684
|
+
return `${LULA_SIGNATURE}
|
|
5653
5685
|
## Lula Compliance Overview
|
|
5654
5686
|
|
|
5655
5687
|
Please review the changes to ensure they meet compliance standards.
|
|
5656
5688
|
|
|
5657
5689
|
### Reviewed Changes
|
|
5658
5690
|
|
|
5659
|
-
Lula reviewed ${
|
|
5691
|
+
Lula reviewed ${filesCount} files changed that affect compliance.
|
|
5660
5692
|
|
|
5661
5693
|
`;
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5694
|
+
}
|
|
5695
|
+
async function analyzeDeletedFiles(context) {
|
|
5696
|
+
const { octokit, owner, repo, files } = context;
|
|
5697
|
+
const deletedFilesWithAnnotations = [];
|
|
5698
|
+
for (const file of files) {
|
|
5699
|
+
if (file.status === "removed") {
|
|
5700
|
+
try {
|
|
5701
|
+
const oldText = await fetchRawFileViaAPI({
|
|
5702
|
+
octokit,
|
|
5703
|
+
owner,
|
|
5704
|
+
repo,
|
|
5705
|
+
path: file.filename,
|
|
5706
|
+
ref: "main"
|
|
5707
|
+
});
|
|
5708
|
+
if (containsLulaAnnotations(oldText)) {
|
|
5709
|
+
deletedFilesWithAnnotations.push(file.filename);
|
|
5678
5710
|
}
|
|
5711
|
+
} catch (err) {
|
|
5712
|
+
console.error(`Error checking deleted file ${file.filename}: ${err}`);
|
|
5679
5713
|
}
|
|
5680
5714
|
}
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5715
|
+
}
|
|
5716
|
+
if (deletedFilesWithAnnotations.length === 0) {
|
|
5717
|
+
return { hasFindings: false, warningContent: "" };
|
|
5718
|
+
}
|
|
5719
|
+
let warningContent = `
|
|
5684
5720
|
|
|
5685
5721
|
**Compliance Warning: Files with Lula annotations were deleted**
|
|
5686
5722
|
|
|
5687
5723
|
`;
|
|
5688
|
-
|
|
5724
|
+
warningContent += `The following files contained compliance annotations (\`@lulaStart\`/\`@lulaEnd\`) and were deleted in this PR. This may affect compliance coverage:
|
|
5689
5725
|
|
|
5690
5726
|
`;
|
|
5691
|
-
|
|
5692
|
-
|
|
5727
|
+
for (const filename of deletedFilesWithAnnotations) {
|
|
5728
|
+
warningContent += `- \`${filename}\`
|
|
5693
5729
|
`;
|
|
5694
|
-
|
|
5695
|
-
|
|
5730
|
+
}
|
|
5731
|
+
warningContent += `
|
|
5696
5732
|
Please review whether:
|
|
5697
5733
|
`;
|
|
5698
|
-
|
|
5734
|
+
warningContent += `- The compliance coverage provided by these files is still needed
|
|
5699
5735
|
`;
|
|
5700
|
-
|
|
5736
|
+
warningContent += `- Alternative compliance measures have been implemented
|
|
5701
5737
|
`;
|
|
5702
|
-
|
|
5738
|
+
warningContent += `- The deletion is intentional and compliance-approved
|
|
5703
5739
|
|
|
5704
5740
|
`;
|
|
5705
|
-
|
|
5741
|
+
warningContent += `---
|
|
5706
5742
|
|
|
5707
5743
|
`;
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
]);
|
|
5716
|
-
const changedBlocks = getChangedBlocks(oldText, newText);
|
|
5717
|
-
const removedBlocks = getRemovedBlocks(oldText, newText);
|
|
5718
|
-
for (const block of changedBlocks) {
|
|
5719
|
-
console.log(`Commenting regarding \`${file.filename}\`.`);
|
|
5720
|
-
leavePost = true;
|
|
5721
|
-
commentBody += `
|
|
5744
|
+
return { hasFindings: true, warningContent };
|
|
5745
|
+
}
|
|
5746
|
+
function generateChangedBlocksContent(filename, changedBlocks, newText) {
|
|
5747
|
+
let content = "";
|
|
5748
|
+
for (const block of changedBlocks) {
|
|
5749
|
+
console.log(`Commenting regarding \`${filename}\`.`);
|
|
5750
|
+
content += `
|
|
5722
5751
|
|
|
5723
5752
|
---
|
|
5724
5753
|
| File | Lines Changed |
|
|
5725
5754
|
| ---- | ------------- |
|
|
5726
5755
|
`;
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5756
|
+
const newBlockText = newText.split("\n").slice(block.startLine, block.endLine).join("\n");
|
|
5757
|
+
const blockSha256 = createHash2("sha256").update(newBlockText).digest("hex");
|
|
5758
|
+
content += `| \`${filename}\` | \`${block.startLine + 1}\u2013${block.endLine}\` |
|
|
5730
5759
|
> **uuid**-\`${block.uuid}\`
|
|
5731
5760
|
**sha256** \`${blockSha256}\`
|
|
5732
5761
|
|
|
5733
5762
|
`;
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5763
|
+
}
|
|
5764
|
+
return content;
|
|
5765
|
+
}
|
|
5766
|
+
function generateRemovedBlocksContent(filename, removedBlocks, oldText) {
|
|
5767
|
+
if (removedBlocks.length === 0) {
|
|
5768
|
+
return "";
|
|
5769
|
+
}
|
|
5770
|
+
console.log(`Found removed annotations in \`${filename}\`.`);
|
|
5771
|
+
let content = `
|
|
5739
5772
|
|
|
5740
|
-
**Compliance Warning: Lula annotations were removed from \`${
|
|
5773
|
+
**Compliance Warning: Lula annotations were removed from \`${filename}\`**
|
|
5741
5774
|
|
|
5742
5775
|
`;
|
|
5743
|
-
|
|
5776
|
+
content += `The following compliance annotation blocks were present in the original file but are missing in the updated version:
|
|
5744
5777
|
|
|
5745
5778
|
`;
|
|
5746
|
-
|
|
5779
|
+
content += `| File | Original Lines | UUID |
|
|
5747
5780
|
`;
|
|
5748
|
-
|
|
5781
|
+
content += `| ---- | -------------- | ---- |
|
|
5749
5782
|
`;
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5783
|
+
for (const block of removedBlocks) {
|
|
5784
|
+
const oldBlockText = oldText.split("\n").slice(block.startLine, block.endLine).join("\n");
|
|
5785
|
+
const blockSha256 = createHash2("sha256").update(oldBlockText).digest("hex");
|
|
5786
|
+
content += `| \`${filename}\` | \`${block.startLine + 1}\u2013${block.endLine}\` | \`${block.uuid}\` |
|
|
5754
5787
|
`;
|
|
5755
|
-
|
|
5788
|
+
content += `> **sha256** \`${blockSha256}\`
|
|
5756
5789
|
|
|
5757
5790
|
`;
|
|
5758
|
-
|
|
5759
|
-
|
|
5791
|
+
}
|
|
5792
|
+
content += `Please review whether:
|
|
5760
5793
|
`;
|
|
5761
|
-
|
|
5794
|
+
content += `- The removal of these compliance annotations is intentional
|
|
5762
5795
|
`;
|
|
5763
|
-
|
|
5796
|
+
content += `- Alternative compliance measures have been implemented
|
|
5764
5797
|
`;
|
|
5765
|
-
|
|
5798
|
+
content += `- The compliance coverage is still adequate
|
|
5766
5799
|
|
|
5767
5800
|
`;
|
|
5768
|
-
|
|
5801
|
+
content += `---
|
|
5769
5802
|
|
|
5770
5803
|
`;
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5804
|
+
return content;
|
|
5805
|
+
}
|
|
5806
|
+
async function analyzeModifiedFiles(context) {
|
|
5807
|
+
const { octokit, owner, repo, prBranch, files } = context;
|
|
5808
|
+
let changesContent = "";
|
|
5809
|
+
let hasFindings = false;
|
|
5810
|
+
for (const file of files) {
|
|
5811
|
+
if (file.status === "added" || file.status === "removed") continue;
|
|
5812
|
+
try {
|
|
5813
|
+
const [oldText, newText] = await Promise.all([
|
|
5814
|
+
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
|
|
5815
|
+
fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: prBranch })
|
|
5816
|
+
]);
|
|
5817
|
+
const changedBlocks = getChangedBlocks(oldText, newText);
|
|
5818
|
+
const removedBlocks = getRemovedBlocks(oldText, newText);
|
|
5819
|
+
if (changedBlocks.length > 0) {
|
|
5820
|
+
hasFindings = true;
|
|
5821
|
+
changesContent += generateChangedBlocksContent(file.filename, changedBlocks, newText);
|
|
5822
|
+
}
|
|
5823
|
+
if (removedBlocks.length > 0) {
|
|
5824
|
+
hasFindings = true;
|
|
5825
|
+
changesContent += generateRemovedBlocksContent(file.filename, removedBlocks, oldText);
|
|
5826
|
+
}
|
|
5827
|
+
} catch (err) {
|
|
5828
|
+
console.error(`Error processing ${file.filename}: ${err}`);
|
|
5781
5829
|
}
|
|
5782
|
-
|
|
5830
|
+
}
|
|
5831
|
+
return { hasFindings, changesContent };
|
|
5832
|
+
}
|
|
5833
|
+
async function performComplianceAnalysis(context) {
|
|
5834
|
+
let commentBody = createInitialCommentBody(context.files.length);
|
|
5835
|
+
let hasFindings = false;
|
|
5836
|
+
const deletedAnalysis = await analyzeDeletedFiles(context);
|
|
5837
|
+
if (deletedAnalysis.hasFindings) {
|
|
5838
|
+
hasFindings = true;
|
|
5839
|
+
commentBody += deletedAnalysis.warningContent;
|
|
5840
|
+
}
|
|
5841
|
+
const modifiedAnalysis = await analyzeModifiedFiles(context);
|
|
5842
|
+
if (modifiedAnalysis.hasFindings) {
|
|
5843
|
+
hasFindings = true;
|
|
5844
|
+
commentBody += modifiedAnalysis.changesContent;
|
|
5845
|
+
}
|
|
5846
|
+
return { hasFindings, commentBody };
|
|
5847
|
+
}
|
|
5848
|
+
async function cleanupOldPosts(context, postMode) {
|
|
5849
|
+
const { octokit, owner, repo, pull_number } = context;
|
|
5850
|
+
if (postMode === "comment") {
|
|
5851
|
+
await deleteOldIssueComments({ octokit, owner, repo, pull_number });
|
|
5852
|
+
} else {
|
|
5853
|
+
await dismissOldReviews({ octokit, owner, repo, pull_number });
|
|
5854
|
+
await deleteOldReviewComments({ octokit, owner, repo, pull_number });
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
function crawlCommand() {
|
|
5858
|
+
return new Command().command("crawl").description("Detect compliance-related changes between @lulaStart and @lulaEnd in PR files").addOption(
|
|
5859
|
+
new Option("--post-mode <mode>", "How to post findings").choices(["review", "comment"]).default("review")
|
|
5860
|
+
).action(async (opts) => {
|
|
5861
|
+
const { owner, repo, pull_number } = getPRContext();
|
|
5862
|
+
console.log(`Analyzing PR #${pull_number} in ${owner}/${repo} for compliance changes...`);
|
|
5863
|
+
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
|
5864
|
+
const pr = await octokit.pulls.get({ owner, repo, pull_number });
|
|
5865
|
+
const prBranch = pr.data.head.ref;
|
|
5866
|
+
const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
|
|
5867
|
+
const context = {
|
|
5868
|
+
octokit,
|
|
5869
|
+
owner,
|
|
5870
|
+
repo,
|
|
5871
|
+
pull_number,
|
|
5872
|
+
prBranch,
|
|
5873
|
+
files
|
|
5874
|
+
};
|
|
5875
|
+
const analysisResult = await performComplianceAnalysis(context);
|
|
5876
|
+
await cleanupOldPosts(context, opts.postMode);
|
|
5877
|
+
if (analysisResult.hasFindings) {
|
|
5878
|
+
const finalBody = analysisResult.commentBody + closingBody;
|
|
5783
5879
|
await postFinding({
|
|
5784
5880
|
octokit,
|
|
5785
5881
|
postMode: opts.postMode,
|
|
5786
5882
|
owner,
|
|
5787
5883
|
repo,
|
|
5788
5884
|
pull_number,
|
|
5789
|
-
body:
|
|
5885
|
+
body: finalBody
|
|
5790
5886
|
});
|
|
5791
5887
|
const header = `Posted (${opts.postMode})`;
|
|
5792
5888
|
const underline = "-".repeat(header.length);
|
|
@@ -5794,7 +5890,7 @@ Please review whether:
|
|
|
5794
5890
|
${header}
|
|
5795
5891
|
${underline}
|
|
5796
5892
|
|
|
5797
|
-
${
|
|
5893
|
+
${finalBody}
|
|
5798
5894
|
|
|
5799
5895
|
`);
|
|
5800
5896
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lula2",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4-nightly.1",
|
|
4
4
|
"description": "A tool for managing compliance as code in your GitHub repositories.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lula2": "./dist/lula2"
|
|
@@ -33,25 +33,6 @@
|
|
|
33
33
|
"!dist/**/*.test.js*",
|
|
34
34
|
"!dist/**/*.test.d.ts*"
|
|
35
35
|
],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"dev": "vite dev --port 5173",
|
|
38
|
-
"dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
|
|
39
|
-
"dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
|
|
40
|
-
"build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
|
|
41
|
-
"build:svelte": "vite build",
|
|
42
|
-
"build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:xlsx-republish --external:csv-parse",
|
|
43
|
-
"postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
|
|
44
|
-
"preview": "vite preview",
|
|
45
|
-
"prepare": "svelte-kit sync || echo ''",
|
|
46
|
-
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
47
|
-
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
48
|
-
"format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
49
|
-
"format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
50
|
-
"lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
|
|
51
|
-
"test": "npm run test:unit -- --run --coverage",
|
|
52
|
-
"test:integration": "vitest --config integration/vitest.config.integration.ts",
|
|
53
|
-
"test:unit": "vitest"
|
|
54
|
-
},
|
|
55
36
|
"dependencies": {
|
|
56
37
|
"@octokit/rest": "^22.0.0",
|
|
57
38
|
"@types/ws": "^8.18.1",
|
|
@@ -124,5 +105,23 @@
|
|
|
124
105
|
"main",
|
|
125
106
|
"next"
|
|
126
107
|
]
|
|
108
|
+
},
|
|
109
|
+
"scripts": {
|
|
110
|
+
"dev": "vite dev --port 5173",
|
|
111
|
+
"dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
|
|
112
|
+
"dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
|
|
113
|
+
"build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
|
|
114
|
+
"build:svelte": "vite build",
|
|
115
|
+
"build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:xlsx-republish --external:csv-parse",
|
|
116
|
+
"postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
|
|
117
|
+
"preview": "vite preview",
|
|
118
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
119
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
120
|
+
"format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
121
|
+
"format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
122
|
+
"lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
|
|
123
|
+
"test": "npm run test:unit -- --run --coverage",
|
|
124
|
+
"test:integration": "vitest --config integration/vitest.config.integration.ts",
|
|
125
|
+
"test:unit": "vitest"
|
|
127
126
|
}
|
|
128
|
-
}
|
|
127
|
+
}
|
|
@@ -154,6 +154,11 @@
|
|
|
154
154
|
fieldConfigs = new Map(); // Force reactivity
|
|
155
155
|
controlIdField = ''; // Reset control ID field selection
|
|
156
156
|
|
|
157
|
+
justificationFields = [];
|
|
158
|
+
draggedField = null;
|
|
159
|
+
dragOverTab = null;
|
|
160
|
+
dragOverField = null;
|
|
161
|
+
|
|
157
162
|
try {
|
|
158
163
|
const previewFormData = new FormData();
|
|
159
164
|
previewFormData.append('file', fileData);
|