opencode-sonarqube 1.2.1 → 1.2.3
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 +19 -1
- package/dist/index.js +175 -170
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,18 @@ The installer will:
|
|
|
53
53
|
4. Configure `opencode.json`
|
|
54
54
|
5. Set up environment variables
|
|
55
55
|
|
|
56
|
-
**
|
|
56
|
+
**After installation, complete these steps:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 1. Load environment variables
|
|
60
|
+
source ~/.zshrc # or ~/.bashrc
|
|
61
|
+
|
|
62
|
+
# 2. Restart OpenCode (or start a new session)
|
|
63
|
+
|
|
64
|
+
# 3. Initialize SonarQube for your project (in OpenCode):
|
|
65
|
+
# Tell the AI: "Setup SonarQube for this project"
|
|
66
|
+
# Or use the tool: sonarqube({ action: "setup" })
|
|
67
|
+
```
|
|
57
68
|
|
|
58
69
|
### Manual Installation
|
|
59
70
|
|
|
@@ -83,6 +94,13 @@ export SONAR_PASSWORD="your-password"
|
|
|
83
94
|
|
|
84
95
|
Add these to your `~/.zshrc` or `~/.bashrc` to make them permanent.
|
|
85
96
|
|
|
97
|
+
**After installation:**
|
|
98
|
+
|
|
99
|
+
1. Restart OpenCode (or start a new session)
|
|
100
|
+
2. Initialize SonarQube for your project:
|
|
101
|
+
- Tell the AI: "Setup SonarQube for this project"
|
|
102
|
+
- Or use: `sonarqube({ action: "setup" })`
|
|
103
|
+
|
|
86
104
|
## Configuration
|
|
87
105
|
|
|
88
106
|
### Environment Variables (Required)
|
package/dist/index.js
CHANGED
|
@@ -20003,6 +20003,178 @@ function getSeveritiesFromLevel(level) {
|
|
|
20003
20003
|
|
|
20004
20004
|
// src/index.ts
|
|
20005
20005
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
20006
|
+
|
|
20007
|
+
// src/cli.ts
|
|
20008
|
+
var CLI_HELP = `
|
|
20009
|
+
opencode-sonarqube - SonarQube integration for OpenCode
|
|
20010
|
+
|
|
20011
|
+
Usage:
|
|
20012
|
+
bun run src/index.ts [options]
|
|
20013
|
+
|
|
20014
|
+
Options:
|
|
20015
|
+
--setup Initialize project (first run)
|
|
20016
|
+
--analyze Run SonarQube analysis
|
|
20017
|
+
--status Show quality gate status
|
|
20018
|
+
--issues Show current issues
|
|
20019
|
+
--hotspots Show security hotspots
|
|
20020
|
+
--project-key=KEY Override project key
|
|
20021
|
+
--help, -h Show this help
|
|
20022
|
+
|
|
20023
|
+
Environment Variables:
|
|
20024
|
+
SONAR_HOST_URL SonarQube server URL
|
|
20025
|
+
SONAR_USER SonarQube username
|
|
20026
|
+
SONAR_PASSWORD SonarQube password
|
|
20027
|
+
`;
|
|
20028
|
+
async function handleSetup2(config2, directory, force) {
|
|
20029
|
+
const result = await bootstrap({ config: config2, directory, force });
|
|
20030
|
+
return {
|
|
20031
|
+
success: result.success,
|
|
20032
|
+
output: result.success ? `Initializing SonarQube project...
|
|
20033
|
+
${result.message}` : result.message,
|
|
20034
|
+
exitCode: result.success ? 0 : 1
|
|
20035
|
+
};
|
|
20036
|
+
}
|
|
20037
|
+
async function handleAnalyze2(config2, state, projectKey, directory) {
|
|
20038
|
+
const result = await runAnalysis(config2, state, { projectKey }, directory);
|
|
20039
|
+
return {
|
|
20040
|
+
success: true,
|
|
20041
|
+
output: `Running analysis for ${projectKey}...
|
|
20042
|
+
${formatAnalysisResult(result)}`,
|
|
20043
|
+
exitCode: 0
|
|
20044
|
+
};
|
|
20045
|
+
}
|
|
20046
|
+
async function handleStatus2(config2, state, projectKey) {
|
|
20047
|
+
const api2 = createSonarQubeAPI(config2, state);
|
|
20048
|
+
const status = await api2.qualityGate.getStatus(projectKey);
|
|
20049
|
+
return {
|
|
20050
|
+
success: true,
|
|
20051
|
+
output: api2.qualityGate.formatStatus(status),
|
|
20052
|
+
exitCode: 0
|
|
20053
|
+
};
|
|
20054
|
+
}
|
|
20055
|
+
async function handleIssues2(config2, state, projectKey) {
|
|
20056
|
+
const api2 = createSonarQubeAPI(config2, state);
|
|
20057
|
+
const issues = await api2.issues.getFormattedIssues({ projectKey });
|
|
20058
|
+
const lines = [`Found ${issues.length} issues`];
|
|
20059
|
+
for (const issue2 of issues.slice(0, 20)) {
|
|
20060
|
+
lines.push(`- [${issue2.severity}] ${issue2.file}:${issue2.line ?? "?"} - ${issue2.message}`);
|
|
20061
|
+
}
|
|
20062
|
+
return {
|
|
20063
|
+
success: true,
|
|
20064
|
+
output: lines.join(`
|
|
20065
|
+
`),
|
|
20066
|
+
exitCode: 0
|
|
20067
|
+
};
|
|
20068
|
+
}
|
|
20069
|
+
async function handleHotspots2(config2, state, projectKey) {
|
|
20070
|
+
const api2 = createSonarQubeAPI(config2, state);
|
|
20071
|
+
try {
|
|
20072
|
+
const hotspots = await api2.issues.getSecurityHotspots(projectKey);
|
|
20073
|
+
if (hotspots.length === 0) {
|
|
20074
|
+
return {
|
|
20075
|
+
success: true,
|
|
20076
|
+
output: "No security hotspots found. Good job!",
|
|
20077
|
+
exitCode: 0
|
|
20078
|
+
};
|
|
20079
|
+
}
|
|
20080
|
+
const hotspotLines = hotspots.slice(0, 10).flatMap((hotspot) => [
|
|
20081
|
+
`- [${hotspot.vulnerabilityProbability}] ${hotspot.component}:${hotspot.line ?? "?"}`,
|
|
20082
|
+
` ${hotspot.message}`
|
|
20083
|
+
]);
|
|
20084
|
+
const output = [
|
|
20085
|
+
`Found ${hotspots.length} security hotspots to review:`,
|
|
20086
|
+
...hotspotLines
|
|
20087
|
+
].join(`
|
|
20088
|
+
`);
|
|
20089
|
+
return {
|
|
20090
|
+
success: true,
|
|
20091
|
+
output,
|
|
20092
|
+
exitCode: 0
|
|
20093
|
+
};
|
|
20094
|
+
} catch {
|
|
20095
|
+
return {
|
|
20096
|
+
success: true,
|
|
20097
|
+
output: "Security hotspots API not available (requires SonarQube 8.2+)",
|
|
20098
|
+
exitCode: 0
|
|
20099
|
+
};
|
|
20100
|
+
}
|
|
20101
|
+
}
|
|
20102
|
+
function getProjectKeyFromArgs(args, state) {
|
|
20103
|
+
const projectKeyArg = args.find((a) => a.startsWith("--project-key="));
|
|
20104
|
+
return projectKeyArg ? projectKeyArg.split("=")[1] : state.projectKey;
|
|
20105
|
+
}
|
|
20106
|
+
async function runCLI(args, directory = process.cwd()) {
|
|
20107
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
20108
|
+
return { success: true, output: CLI_HELP, exitCode: 0 };
|
|
20109
|
+
}
|
|
20110
|
+
const config2 = loadConfig();
|
|
20111
|
+
if (!config2) {
|
|
20112
|
+
return {
|
|
20113
|
+
success: false,
|
|
20114
|
+
output: `Error: SonarQube not configured
|
|
20115
|
+
Set SONAR_HOST_URL, SONAR_USER, and SONAR_PASSWORD environment variables`,
|
|
20116
|
+
exitCode: 1
|
|
20117
|
+
};
|
|
20118
|
+
}
|
|
20119
|
+
if (args.includes("--setup")) {
|
|
20120
|
+
return handleSetup2(config2, directory, args.includes("--force"));
|
|
20121
|
+
}
|
|
20122
|
+
if (await needsBootstrap(directory)) {
|
|
20123
|
+
const result = await bootstrap({ config: config2, directory });
|
|
20124
|
+
if (!result.success) {
|
|
20125
|
+
return {
|
|
20126
|
+
success: false,
|
|
20127
|
+
output: `Project not initialized. Running setup...
|
|
20128
|
+
Setup failed: ${result.message}`,
|
|
20129
|
+
exitCode: 1
|
|
20130
|
+
};
|
|
20131
|
+
}
|
|
20132
|
+
}
|
|
20133
|
+
const state = await getProjectState(directory);
|
|
20134
|
+
if (!state) {
|
|
20135
|
+
return {
|
|
20136
|
+
success: false,
|
|
20137
|
+
output: "Error: Could not load project state",
|
|
20138
|
+
exitCode: 1
|
|
20139
|
+
};
|
|
20140
|
+
}
|
|
20141
|
+
const projectKey = getProjectKeyFromArgs(args, state);
|
|
20142
|
+
if (!projectKey) {
|
|
20143
|
+
return {
|
|
20144
|
+
success: false,
|
|
20145
|
+
output: "Error: Could not determine project key",
|
|
20146
|
+
exitCode: 1
|
|
20147
|
+
};
|
|
20148
|
+
}
|
|
20149
|
+
if (args.includes("--analyze")) {
|
|
20150
|
+
return handleAnalyze2(config2, state, projectKey, directory);
|
|
20151
|
+
}
|
|
20152
|
+
if (args.includes("--status")) {
|
|
20153
|
+
return handleStatus2(config2, state, projectKey);
|
|
20154
|
+
}
|
|
20155
|
+
if (args.includes("--issues")) {
|
|
20156
|
+
return handleIssues2(config2, state, projectKey);
|
|
20157
|
+
}
|
|
20158
|
+
if (args.includes("--hotspots")) {
|
|
20159
|
+
return handleHotspots2(config2, state, projectKey);
|
|
20160
|
+
}
|
|
20161
|
+
return {
|
|
20162
|
+
success: true,
|
|
20163
|
+
output: "Use --help for usage information",
|
|
20164
|
+
exitCode: 0
|
|
20165
|
+
};
|
|
20166
|
+
}
|
|
20167
|
+
async function executeCLI(args = process.argv.slice(2), log = console.log, exit = process.exit) {
|
|
20168
|
+
const result = await runCLI(args);
|
|
20169
|
+
log(result.output);
|
|
20170
|
+
exit(result.exitCode);
|
|
20171
|
+
}
|
|
20172
|
+
var isDirectCLI = false;
|
|
20173
|
+
if (isDirectCLI) {
|
|
20174
|
+
await executeCLI();
|
|
20175
|
+
}
|
|
20176
|
+
|
|
20177
|
+
// src/index.ts
|
|
20006
20178
|
var SHARED_STATE_FILE = "/tmp/sonarqube-plugin-shared-state.json";
|
|
20007
20179
|
var readSharedState = () => {
|
|
20008
20180
|
try {
|
|
@@ -20710,178 +20882,11 @@ Example usage:
|
|
|
20710
20882
|
return returnHooks;
|
|
20711
20883
|
};
|
|
20712
20884
|
var src_default = SonarQubePlugin;
|
|
20713
|
-
var
|
|
20714
|
-
|
|
20715
|
-
|
|
20716
|
-
Usage:
|
|
20717
|
-
bun run src/index.ts [options]
|
|
20718
|
-
|
|
20719
|
-
Options:
|
|
20720
|
-
--setup Initialize project (first run)
|
|
20721
|
-
--analyze Run SonarQube analysis
|
|
20722
|
-
--status Show quality gate status
|
|
20723
|
-
--issues Show current issues
|
|
20724
|
-
--hotspots Show security hotspots
|
|
20725
|
-
--project-key=KEY Override project key
|
|
20726
|
-
--help, -h Show this help
|
|
20727
|
-
|
|
20728
|
-
Environment Variables:
|
|
20729
|
-
SONAR_HOST_URL SonarQube server URL
|
|
20730
|
-
SONAR_USER SonarQube username
|
|
20731
|
-
SONAR_PASSWORD SonarQube password
|
|
20732
|
-
`;
|
|
20733
|
-
async function handleSetup2(config2, directory, force) {
|
|
20734
|
-
const result = await bootstrap({ config: config2, directory, force });
|
|
20735
|
-
return {
|
|
20736
|
-
success: result.success,
|
|
20737
|
-
output: result.success ? `Initializing SonarQube project...
|
|
20738
|
-
${result.message}` : result.message,
|
|
20739
|
-
exitCode: result.success ? 0 : 1
|
|
20740
|
-
};
|
|
20741
|
-
}
|
|
20742
|
-
async function handleAnalyze2(config2, state, projectKey, directory) {
|
|
20743
|
-
const result = await runAnalysis(config2, state, { projectKey }, directory);
|
|
20744
|
-
return {
|
|
20745
|
-
success: true,
|
|
20746
|
-
output: `Running analysis for ${projectKey}...
|
|
20747
|
-
${formatAnalysisResult(result)}`,
|
|
20748
|
-
exitCode: 0
|
|
20749
|
-
};
|
|
20750
|
-
}
|
|
20751
|
-
async function handleStatus2(config2, state, projectKey) {
|
|
20752
|
-
const api2 = createSonarQubeAPI(config2, state);
|
|
20753
|
-
const status = await api2.qualityGate.getStatus(projectKey);
|
|
20754
|
-
return {
|
|
20755
|
-
success: true,
|
|
20756
|
-
output: api2.qualityGate.formatStatus(status),
|
|
20757
|
-
exitCode: 0
|
|
20758
|
-
};
|
|
20759
|
-
}
|
|
20760
|
-
async function handleIssues2(config2, state, projectKey) {
|
|
20761
|
-
const api2 = createSonarQubeAPI(config2, state);
|
|
20762
|
-
const issues = await api2.issues.getFormattedIssues({ projectKey });
|
|
20763
|
-
const lines = [`Found ${issues.length} issues`];
|
|
20764
|
-
for (const issue2 of issues.slice(0, 20)) {
|
|
20765
|
-
lines.push(`- [${issue2.severity}] ${issue2.file}:${issue2.line ?? "?"} - ${issue2.message}`);
|
|
20766
|
-
}
|
|
20767
|
-
return {
|
|
20768
|
-
success: true,
|
|
20769
|
-
output: lines.join(`
|
|
20770
|
-
`),
|
|
20771
|
-
exitCode: 0
|
|
20772
|
-
};
|
|
20773
|
-
}
|
|
20774
|
-
async function handleHotspots2(config2, state, projectKey) {
|
|
20775
|
-
const api2 = createSonarQubeAPI(config2, state);
|
|
20776
|
-
try {
|
|
20777
|
-
const hotspots = await api2.issues.getSecurityHotspots(projectKey);
|
|
20778
|
-
if (hotspots.length === 0) {
|
|
20779
|
-
return {
|
|
20780
|
-
success: true,
|
|
20781
|
-
output: "No security hotspots found. Good job!",
|
|
20782
|
-
exitCode: 0
|
|
20783
|
-
};
|
|
20784
|
-
}
|
|
20785
|
-
const hotspotLines = hotspots.slice(0, 10).flatMap((hotspot) => [
|
|
20786
|
-
`- [${hotspot.vulnerabilityProbability}] ${hotspot.component}:${hotspot.line ?? "?"}`,
|
|
20787
|
-
` ${hotspot.message}`
|
|
20788
|
-
]);
|
|
20789
|
-
const output = [
|
|
20790
|
-
`Found ${hotspots.length} security hotspots to review:`,
|
|
20791
|
-
...hotspotLines
|
|
20792
|
-
].join(`
|
|
20793
|
-
`);
|
|
20794
|
-
return {
|
|
20795
|
-
success: true,
|
|
20796
|
-
output,
|
|
20797
|
-
exitCode: 0
|
|
20798
|
-
};
|
|
20799
|
-
} catch {
|
|
20800
|
-
return {
|
|
20801
|
-
success: true,
|
|
20802
|
-
output: "Security hotspots API not available (requires SonarQube 8.2+)",
|
|
20803
|
-
exitCode: 0
|
|
20804
|
-
};
|
|
20805
|
-
}
|
|
20806
|
-
}
|
|
20807
|
-
function getProjectKeyFromArgs(args, state) {
|
|
20808
|
-
const projectKeyArg = args.find((a) => a.startsWith("--project-key="));
|
|
20809
|
-
return projectKeyArg ? projectKeyArg.split("=")[1] : state.projectKey;
|
|
20810
|
-
}
|
|
20811
|
-
async function runCLI(args, directory = process.cwd()) {
|
|
20812
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
20813
|
-
return { success: true, output: CLI_HELP, exitCode: 0 };
|
|
20814
|
-
}
|
|
20815
|
-
const config2 = loadConfig();
|
|
20816
|
-
if (!config2) {
|
|
20817
|
-
return {
|
|
20818
|
-
success: false,
|
|
20819
|
-
output: `Error: SonarQube not configured
|
|
20820
|
-
Set SONAR_HOST_URL, SONAR_USER, and SONAR_PASSWORD environment variables`,
|
|
20821
|
-
exitCode: 1
|
|
20822
|
-
};
|
|
20823
|
-
}
|
|
20824
|
-
if (args.includes("--setup")) {
|
|
20825
|
-
return handleSetup2(config2, directory, args.includes("--force"));
|
|
20826
|
-
}
|
|
20827
|
-
if (await needsBootstrap(directory)) {
|
|
20828
|
-
const result = await bootstrap({ config: config2, directory });
|
|
20829
|
-
if (!result.success) {
|
|
20830
|
-
return {
|
|
20831
|
-
success: false,
|
|
20832
|
-
output: `Project not initialized. Running setup...
|
|
20833
|
-
Setup failed: ${result.message}`,
|
|
20834
|
-
exitCode: 1
|
|
20835
|
-
};
|
|
20836
|
-
}
|
|
20837
|
-
}
|
|
20838
|
-
const state = await getProjectState(directory);
|
|
20839
|
-
if (!state) {
|
|
20840
|
-
return {
|
|
20841
|
-
success: false,
|
|
20842
|
-
output: "Error: Could not load project state",
|
|
20843
|
-
exitCode: 1
|
|
20844
|
-
};
|
|
20845
|
-
}
|
|
20846
|
-
const projectKey = getProjectKeyFromArgs(args, state);
|
|
20847
|
-
if (!projectKey) {
|
|
20848
|
-
return {
|
|
20849
|
-
success: false,
|
|
20850
|
-
output: "Error: Could not determine project key",
|
|
20851
|
-
exitCode: 1
|
|
20852
|
-
};
|
|
20853
|
-
}
|
|
20854
|
-
if (args.includes("--analyze")) {
|
|
20855
|
-
return handleAnalyze2(config2, state, projectKey, directory);
|
|
20856
|
-
}
|
|
20857
|
-
if (args.includes("--status")) {
|
|
20858
|
-
return handleStatus2(config2, state, projectKey);
|
|
20859
|
-
}
|
|
20860
|
-
if (args.includes("--issues")) {
|
|
20861
|
-
return handleIssues2(config2, state, projectKey);
|
|
20862
|
-
}
|
|
20863
|
-
if (args.includes("--hotspots")) {
|
|
20864
|
-
return handleHotspots2(config2, state, projectKey);
|
|
20865
|
-
}
|
|
20866
|
-
return {
|
|
20867
|
-
success: true,
|
|
20868
|
-
output: "Use --help for usage information",
|
|
20869
|
-
exitCode: 0
|
|
20870
|
-
};
|
|
20871
|
-
}
|
|
20872
|
-
async function executeCLI(args = process.argv.slice(2), log = console.log, exit = process.exit) {
|
|
20873
|
-
const result = await runCLI(args);
|
|
20874
|
-
log(result.output);
|
|
20875
|
-
exit(result.exitCode);
|
|
20876
|
-
}
|
|
20877
|
-
var isDirectCLI = __require.main == __require.module && process.argv[1]?.includes("index.ts");
|
|
20878
|
-
if (isDirectCLI) {
|
|
20885
|
+
var isDirectCLI2 = __require.main == __require.module && process.argv[1]?.includes("index.ts");
|
|
20886
|
+
if (isDirectCLI2) {
|
|
20879
20887
|
await executeCLI();
|
|
20880
20888
|
}
|
|
20881
20889
|
export {
|
|
20882
|
-
runCLI,
|
|
20883
|
-
executeCLI,
|
|
20884
20890
|
src_default as default,
|
|
20885
|
-
SonarQubePlugin
|
|
20886
|
-
CLI_HELP
|
|
20891
|
+
SonarQubePlugin
|
|
20887
20892
|
};
|