opencode-sonarqube 0.1.19 → 0.1.20
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/dist/index.js +69 -93
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20188,62 +20188,58 @@ function shouldIgnoreFile2(filePath) {
|
|
|
20188
20188
|
return IGNORED_FILE_PATTERNS2.some((pattern) => pattern.test(filePath));
|
|
20189
20189
|
}
|
|
20190
20190
|
var SonarQubePlugin = async ({ client, directory, worktree }) => {
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
try {
|
|
20195
|
-
appendFileSync4("/tmp/sonarqube-plugin-debug.log", `${new Date().toISOString()} [RESOLVE] ${msg}
|
|
20191
|
+
const safeLog = (msg) => {
|
|
20192
|
+
try {
|
|
20193
|
+
appendFileSync4("/tmp/sonarqube-plugin-debug.log", `${new Date().toISOString()} [PLUGIN] ${msg}
|
|
20196
20194
|
`);
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20195
|
+
} catch {}
|
|
20196
|
+
};
|
|
20197
|
+
safeLog(`=== PLUGIN START === directory=${directory} worktree=${worktree} cwd=${process.cwd()}`);
|
|
20198
|
+
const resolveValidDirectory = () => {
|
|
20200
20199
|
if (worktree && worktree !== "/" && worktree.length > 1) {
|
|
20201
|
-
|
|
20200
|
+
safeLog(`USING worktree=${worktree}`);
|
|
20202
20201
|
return worktree;
|
|
20203
20202
|
}
|
|
20204
20203
|
if (directory && directory !== "/" && directory.length > 1) {
|
|
20205
|
-
|
|
20204
|
+
safeLog(`USING directory=${directory}`);
|
|
20206
20205
|
return directory;
|
|
20207
20206
|
}
|
|
20208
20207
|
const cwd = process.cwd();
|
|
20209
20208
|
if (cwd && cwd !== "/" && cwd.length > 1) {
|
|
20210
|
-
|
|
20209
|
+
safeLog(`USING cwd=${cwd}`);
|
|
20211
20210
|
return cwd;
|
|
20212
20211
|
}
|
|
20213
20212
|
try {
|
|
20214
20213
|
const pluginUrl = import.meta.url;
|
|
20215
|
-
|
|
20216
|
-
const pluginPath = pluginUrl.replace("file://", "");
|
|
20214
|
+
const pluginPath = decodeURIComponent(pluginUrl.replace("file://", ""));
|
|
20217
20215
|
const pathParts = pluginPath.split("/");
|
|
20218
20216
|
const nodeModulesIndex = pathParts.findIndex((p) => p === "node_modules");
|
|
20219
|
-
log(`pathParts nodeModulesIndex=${nodeModulesIndex}`);
|
|
20220
20217
|
if (nodeModulesIndex > 0) {
|
|
20221
20218
|
const projectPath = pathParts.slice(0, nodeModulesIndex).join("/");
|
|
20222
|
-
log(`extracted projectPath=${projectPath}`);
|
|
20223
20219
|
if (projectPath && projectPath !== "/" && projectPath.length > 1) {
|
|
20220
|
+
safeLog(`USING import.meta.url derived path=${projectPath}`);
|
|
20224
20221
|
return projectPath;
|
|
20225
20222
|
}
|
|
20226
20223
|
}
|
|
20227
|
-
} catch
|
|
20228
|
-
log(`import.meta.url FAILED: ${e}`);
|
|
20229
|
-
}
|
|
20224
|
+
} catch {}
|
|
20230
20225
|
const homeDir = process.env["HOME"] || "/Users";
|
|
20231
|
-
|
|
20226
|
+
safeLog(`FALLBACK home=${homeDir}`);
|
|
20232
20227
|
return homeDir;
|
|
20233
20228
|
};
|
|
20234
20229
|
const effectiveDirectory = resolveValidDirectory();
|
|
20230
|
+
safeLog(`FINAL effectiveDirectory=${effectiveDirectory}`);
|
|
20235
20231
|
try {
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
}
|
|
20232
|
+
await client.app.log({
|
|
20233
|
+
body: {
|
|
20234
|
+
service: "opencode-sonarqube",
|
|
20235
|
+
level: "info",
|
|
20236
|
+
message: "SonarQube plugin initialized",
|
|
20237
|
+
extra: { directory, worktree, effectiveDirectory }
|
|
20238
|
+
}
|
|
20239
|
+
});
|
|
20240
|
+
} catch {
|
|
20241
|
+
safeLog("client.app.log failed (non-fatal)");
|
|
20242
|
+
}
|
|
20247
20243
|
let pluginConfig;
|
|
20248
20244
|
let lastAnalysisResult;
|
|
20249
20245
|
const getConfig = () => pluginConfig;
|
|
@@ -20616,8 +20612,17 @@ Git operation completed with changes. Consider running:
|
|
|
20616
20612
|
return;
|
|
20617
20613
|
await showToast("Code pushed - SonarQube will analyze on server", "info");
|
|
20618
20614
|
};
|
|
20615
|
+
const safeAsync = (fn, name) => {
|
|
20616
|
+
return async (...args) => {
|
|
20617
|
+
try {
|
|
20618
|
+
return await fn(...args);
|
|
20619
|
+
} catch (error45) {
|
|
20620
|
+
safeLog(`[ERROR] ${name} failed: ${error45}`);
|
|
20621
|
+
}
|
|
20622
|
+
};
|
|
20623
|
+
};
|
|
20619
20624
|
const returnHooks = {
|
|
20620
|
-
event: async ({ event }) => {
|
|
20625
|
+
event: safeAsync(async ({ event }) => {
|
|
20621
20626
|
handleSessionTrackingEvent(event);
|
|
20622
20627
|
handleFileEditedEvent(event);
|
|
20623
20628
|
if (event.type === "session.created" && currentSessionId) {
|
|
@@ -20629,8 +20634,8 @@ Git operation completed with changes. Consider running:
|
|
|
20629
20634
|
if (event.type === "session.error") {
|
|
20630
20635
|
await showToast("SonarQube: Analysis error occurred", "error");
|
|
20631
20636
|
}
|
|
20632
|
-
},
|
|
20633
|
-
"tool.execute.before": async (input, output) => {
|
|
20637
|
+
}, "event"),
|
|
20638
|
+
"tool.execute.before": safeAsync(async (input, output) => {
|
|
20634
20639
|
if (input.tool === "sonarqube") {
|
|
20635
20640
|
await loadPluginConfig();
|
|
20636
20641
|
}
|
|
@@ -20638,71 +20643,49 @@ Git operation completed with changes. Consider running:
|
|
|
20638
20643
|
if (isBashTool && currentSessionId) {
|
|
20639
20644
|
await handlePreCommitCheck(output);
|
|
20640
20645
|
}
|
|
20641
|
-
},
|
|
20642
|
-
"tool.execute.after": async (input, output) => {
|
|
20646
|
+
}, "tool.execute.before"),
|
|
20647
|
+
"tool.execute.after": safeAsync(async (input, output) => {
|
|
20643
20648
|
await logSonarQubeResult(input, output);
|
|
20644
20649
|
await trackFileChanges(input, output);
|
|
20645
20650
|
await handleGitOperations(input, output);
|
|
20646
|
-
},
|
|
20647
|
-
"experimental.session.compacting": async (_input, output) => {
|
|
20651
|
+
}, "tool.execute.after"),
|
|
20652
|
+
"experimental.session.compacting": safeAsync(async (_input, output) => {
|
|
20648
20653
|
const context = buildCompactionContext();
|
|
20649
20654
|
if (context) {
|
|
20650
20655
|
output.context.push(context);
|
|
20651
20656
|
}
|
|
20652
|
-
},
|
|
20653
|
-
"experimental.chat.system.transform": async (_input, output) => {
|
|
20654
|
-
|
|
20655
|
-
const { appendFileSync: appendFileSync5 } = await import("node:fs");
|
|
20656
|
-
appendFileSync5("/tmp/sonarqube-plugin-debug.log", `${new Date().toISOString()} [HOOK] experimental.chat.system.transform ENTERED
|
|
20657
|
-
`);
|
|
20658
|
-
} catch {}
|
|
20659
|
-
debugLog.info("=== experimental.chat.system.transform START ===");
|
|
20657
|
+
}, "experimental.session.compacting"),
|
|
20658
|
+
"experimental.chat.system.transform": safeAsync(async (_input, output) => {
|
|
20659
|
+
safeLog("experimental.chat.system.transform ENTERED");
|
|
20660
20660
|
await loadPluginConfig();
|
|
20661
20661
|
const sonarConfig = pluginConfig?.["sonarqube"];
|
|
20662
|
-
debugLog.info("system.transform: Loading config", { hasSonarConfig: !!sonarConfig });
|
|
20663
20662
|
const config2 = loadConfig(sonarConfig);
|
|
20664
|
-
debugLog.info("system.transform: Config result", { hasConfig: !!config2, level: config2?.level });
|
|
20665
20663
|
if (!config2 || config2.level === "off") {
|
|
20666
|
-
debugLog.info("system.transform: No config or level=off, skipping");
|
|
20667
20664
|
return;
|
|
20668
20665
|
}
|
|
20669
|
-
|
|
20670
|
-
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
debugLog.info("system.transform: No state or projectKey, skipping");
|
|
20680
|
-
return;
|
|
20681
|
-
}
|
|
20682
|
-
debugLog.info("system.transform: Creating API", {
|
|
20683
|
-
url: config2.url,
|
|
20666
|
+
const dir = getDirectory();
|
|
20667
|
+
const state = await getProjectState(dir);
|
|
20668
|
+
if (!state || !state.projectKey) {
|
|
20669
|
+
return;
|
|
20670
|
+
}
|
|
20671
|
+
const api2 = createSonarQubeAPI(config2, state);
|
|
20672
|
+
const [qgStatus, counts, newCodeResponse] = await Promise.all([
|
|
20673
|
+
api2.qualityGate.getStatus(state.projectKey),
|
|
20674
|
+
api2.issues.getCounts(state.projectKey),
|
|
20675
|
+
api2.issues.search({
|
|
20684
20676
|
projectKey: state.projectKey,
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
}).catch(() => ({ paging: { total: 0 } }))
|
|
20698
|
-
]);
|
|
20699
|
-
const hasIssues = counts.blocker > 0 || counts.critical > 0;
|
|
20700
|
-
const qgFailed = qgStatus.projectStatus.status !== "OK";
|
|
20701
|
-
const newCodeIssues = newCodeResponse.paging.total;
|
|
20702
|
-
if (!hasIssues && !qgFailed && newCodeIssues === 0) {
|
|
20703
|
-
return;
|
|
20704
|
-
}
|
|
20705
|
-
const systemContext = `## SonarQube Code Quality Status
|
|
20677
|
+
inNewCode: true,
|
|
20678
|
+
resolved: false,
|
|
20679
|
+
pageSize: 1
|
|
20680
|
+
}).catch(() => ({ paging: { total: 0 } }))
|
|
20681
|
+
]);
|
|
20682
|
+
const hasIssues = counts.blocker > 0 || counts.critical > 0;
|
|
20683
|
+
const qgFailed = qgStatus.projectStatus.status !== "OK";
|
|
20684
|
+
const newCodeIssues = newCodeResponse.paging.total;
|
|
20685
|
+
if (!hasIssues && !qgFailed && newCodeIssues === 0) {
|
|
20686
|
+
return;
|
|
20687
|
+
}
|
|
20688
|
+
const systemContext = `## SonarQube Code Quality Status
|
|
20706
20689
|
|
|
20707
20690
|
**Quality Gate:** ${qgStatus.projectStatus.status}${qgFailed ? " (FAILED)" : ""}
|
|
20708
20691
|
**Outstanding Issues:** ${counts.blocker} blockers, ${counts.critical} critical, ${counts.major} major
|
|
@@ -20716,15 +20699,8 @@ ${config2.level === "enterprise" ? `This project follows enterprise-level qualit
|
|
|
20716
20699
|
- \`sonarqube({ action: "newissues" })\` - See issues in your recent changes (Clean as You Code)
|
|
20717
20700
|
- \`sonarqube({ action: "worstfiles" })\` - Find files needing most attention
|
|
20718
20701
|
- \`sonarqube({ action: "issues" })\` - See all issues`;
|
|
20719
|
-
|
|
20720
|
-
|
|
20721
|
-
try {
|
|
20722
|
-
const { appendFileSync: appendFileSync5 } = await import("node:fs");
|
|
20723
|
-
appendFileSync5("/tmp/sonarqube-plugin-debug.log", `${new Date().toISOString()} [ERROR] experimental.chat.system.transform FAILED: ${error45}
|
|
20724
|
-
`);
|
|
20725
|
-
} catch {}
|
|
20726
|
-
}
|
|
20727
|
-
},
|
|
20702
|
+
output.system.push(systemContext);
|
|
20703
|
+
}, "experimental.chat.system.transform"),
|
|
20728
20704
|
tool: {
|
|
20729
20705
|
sonarqube: tool({
|
|
20730
20706
|
description: `Run SonarQube code analysis and get quality metrics.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-sonarqube",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "OpenCode Plugin for SonarQube integration - Enterprise-level code quality from the start",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"homepage": "https://github.com/mguttmann/opencode-sonarqube#readme",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@opencode-ai/plugin": "^1.1.34",
|
|
41
|
-
"opencode-sonarqube": "0.1.
|
|
41
|
+
"opencode-sonarqube": "0.1.20",
|
|
42
42
|
"zod": "^3.24.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|