opencode-sonarqube 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +158 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4205,20 +4205,30 @@ async function hasProjectState(directory) {
4205
4205
  }
4206
4206
  async function loadProjectState(directory) {
4207
4207
  const statePath = getStatePath(directory);
4208
+ logger4.info(">>> loadProjectState called", { directory, statePath });
4208
4209
  const exists = await Bun.file(statePath).exists();
4210
+ logger4.info("State file exists check", { exists, statePath });
4209
4211
  if (!exists) {
4210
- logger4.debug("No project state found", { directory });
4212
+ logger4.info("No project state file found", { directory, statePath });
4211
4213
  return null;
4212
4214
  }
4213
4215
  try {
4214
4216
  const content = await Bun.file(statePath).text();
4217
+ logger4.info("State file content loaded", { contentLength: content.length });
4215
4218
  const data = JSON.parse(content);
4219
+ logger4.info("State file parsed", { keys: Object.keys(data) });
4216
4220
  const state = ProjectStateSchema.parse(data);
4217
- logger4.debug("Loaded project state", { projectKey: state.projectKey });
4221
+ logger4.info("<<< loadProjectState success", {
4222
+ projectKey: state.projectKey,
4223
+ projectKeyLength: state.projectKey?.length,
4224
+ hasToken: !!state.projectToken,
4225
+ tokenLength: state.projectToken?.length
4226
+ });
4218
4227
  return state;
4219
4228
  } catch (error45) {
4220
- logger4.warn("Failed to load project state", {
4221
- error: error45 instanceof Error ? error45.message : String(error45)
4229
+ logger4.error("Failed to load project state", {
4230
+ error: error45 instanceof Error ? error45.message : String(error45),
4231
+ statePath
4222
4232
  });
4223
4233
  return null;
4224
4234
  }
@@ -16601,6 +16611,8 @@ function tool(input) {
16601
16611
  tool.schema = exports_external;
16602
16612
  // src/utils/config.ts
16603
16613
  init_types2();
16614
+ init_logger();
16615
+ var configLogger = new Logger("sonarqube-config");
16604
16616
  var DEFAULT_CONFIG = {
16605
16617
  level: "enterprise",
16606
16618
  autoAnalyze: true,
@@ -16609,9 +16621,16 @@ var DEFAULT_CONFIG = {
16609
16621
  sources: "src"
16610
16622
  };
16611
16623
  function loadConfig(rawConfig) {
16624
+ configLogger.info(">>> loadConfig called", { hasRawConfig: !!rawConfig });
16612
16625
  const envUrl = process.env["SONAR_HOST_URL"];
16613
16626
  const envUser = process.env["SONAR_USER"];
16614
16627
  const envPassword = process.env["SONAR_PASSWORD"];
16628
+ configLogger.info("Environment variables", {
16629
+ hasEnvUrl: !!envUrl,
16630
+ hasEnvUser: !!envUser,
16631
+ hasEnvPassword: !!envPassword,
16632
+ envUrl: envUrl ? `${envUrl.substring(0, 20)}...` : undefined
16633
+ });
16615
16634
  const configToValidate = {
16616
16635
  ...DEFAULT_CONFIG,
16617
16636
  ...rawConfig,
@@ -16620,13 +16639,19 @@ function loadConfig(rawConfig) {
16620
16639
  ...envPassword && { password: envPassword }
16621
16640
  };
16622
16641
  if (!configToValidate.url || !configToValidate.user || !configToValidate.password) {
16642
+ configLogger.warn("Missing required config", {
16643
+ hasUrl: !!configToValidate.url,
16644
+ hasUser: !!configToValidate.user,
16645
+ hasPassword: !!configToValidate.password
16646
+ });
16623
16647
  return null;
16624
16648
  }
16625
16649
  const result = SonarQubeConfigSchema.safeParse(configToValidate);
16626
16650
  if (!result.success) {
16627
- console.error("SonarQube config validation failed:", result.error.format());
16651
+ configLogger.error("Config validation failed", { errors: result.error.format() });
16628
16652
  return null;
16629
16653
  }
16654
+ configLogger.info("<<< loadConfig success", { url: result.data.url, level: result.data.level });
16630
16655
  return result.data;
16631
16656
  }
16632
16657
  async function deriveProjectKey(directory) {
@@ -17049,19 +17074,36 @@ class SonarQubeClient {
17049
17074
  if (requestBody) {
17050
17075
  headers["Content-Type"] = "application/x-www-form-urlencoded";
17051
17076
  }
17052
- this.logger.debug(`API Request: ${method} ${endpoint}`, { params, hasBody: !!body });
17077
+ this.logger.info(`>>> API Request: ${method} ${endpoint}`, {
17078
+ url: url2,
17079
+ params: JSON.stringify(params),
17080
+ hasBody: !!body,
17081
+ bodyKeys: body ? Object.keys(body) : []
17082
+ });
17053
17083
  try {
17054
17084
  const response = await fetch(url2, {
17055
17085
  method,
17056
17086
  headers,
17057
17087
  body: requestBody
17058
17088
  });
17089
+ this.logger.info(`<<< API Response: ${method} ${endpoint}`, {
17090
+ status: response.status,
17091
+ ok: response.ok
17092
+ });
17059
17093
  if (!response.ok) {
17094
+ this.logger.error(`API Error: ${method} ${endpoint}`, {
17095
+ status: response.status,
17096
+ url: url2
17097
+ });
17060
17098
  await handleResponseError(response);
17061
17099
  }
17062
17100
  const text = await response.text();
17063
17101
  return parseResponseBody(text);
17064
17102
  } catch (error45) {
17103
+ this.logger.error(`API Exception: ${method} ${endpoint}`, {
17104
+ error: String(error45),
17105
+ url: url2
17106
+ });
17065
17107
  handleFetchError(error45, this.baseUrl);
17066
17108
  }
17067
17109
  }
@@ -18199,7 +18241,14 @@ class ProjectAnalysesAPI {
18199
18241
  this.logger = logger3 ?? new Logger("sonarqube-analyses");
18200
18242
  }
18201
18243
  async getAnalyses(options) {
18202
- this.logger.debug(`Getting analyses for ${options.projectKey}`);
18244
+ this.logger.info(`>>> getAnalyses called`, {
18245
+ projectKey: options.projectKey,
18246
+ projectKeyLength: options.projectKey?.length
18247
+ });
18248
+ if (!options.projectKey) {
18249
+ this.logger.error(`getAnalyses: projectKey is empty/undefined!`);
18250
+ return [];
18251
+ }
18203
18252
  try {
18204
18253
  const response = await this.client.get("/api/project_analyses/search", {
18205
18254
  project: options.projectKey,
@@ -18209,9 +18258,10 @@ class ProjectAnalysesAPI {
18209
18258
  to: options.to,
18210
18259
  ps: options.pageSize ?? 10
18211
18260
  });
18261
+ this.logger.info(`<<< getAnalyses success`, { analysesCount: response.analyses.length });
18212
18262
  return response.analyses;
18213
18263
  } catch (error45) {
18214
- this.logger.warn(`Failed to get analyses: ${error45}`);
18264
+ this.logger.error(`getAnalyses failed`, { error: String(error45), projectKey: options.projectKey });
18215
18265
  return [];
18216
18266
  }
18217
18267
  }
@@ -18261,12 +18311,17 @@ class QualityProfilesAPI {
18261
18311
  this.logger = logger3 ?? new Logger("sonarqube-profiles");
18262
18312
  }
18263
18313
  async getProjectProfiles(projectKey) {
18264
- this.logger.debug(`Getting profiles for project ${projectKey}`);
18314
+ this.logger.info(`>>> getProjectProfiles called`, { projectKey, projectKeyLength: projectKey?.length });
18315
+ if (!projectKey) {
18316
+ this.logger.error(`getProjectProfiles: projectKey is empty/undefined!`);
18317
+ return [];
18318
+ }
18265
18319
  try {
18266
18320
  const response = await this.client.get("/api/qualityprofiles/search", { project: projectKey });
18321
+ this.logger.info(`<<< getProjectProfiles success`, { profileCount: response.profiles.length });
18267
18322
  return response.profiles;
18268
18323
  } catch (error45) {
18269
- this.logger.warn(`Failed to get project profiles: ${error45}`);
18324
+ this.logger.error(`getProjectProfiles failed`, { error: String(error45), projectKey });
18270
18325
  return [];
18271
18326
  }
18272
18327
  }
@@ -18350,12 +18405,17 @@ class BranchesAPI {
18350
18405
  this.logger = logger3 ?? new Logger("sonarqube-branches");
18351
18406
  }
18352
18407
  async getBranches(projectKey) {
18353
- this.logger.debug(`Getting branches for ${projectKey}`);
18408
+ this.logger.info(`>>> getBranches called`, { projectKey, projectKeyLength: projectKey?.length });
18409
+ if (!projectKey) {
18410
+ this.logger.error(`getBranches: projectKey is empty/undefined!`);
18411
+ return [];
18412
+ }
18354
18413
  try {
18355
18414
  const response = await this.client.get("/api/project_branches/list", { project: projectKey });
18415
+ this.logger.info(`<<< getBranches success`, { branchCount: response.branches.length });
18356
18416
  return response.branches;
18357
18417
  } catch (error45) {
18358
- this.logger.warn(`Failed to get branches: ${error45}`);
18418
+ this.logger.error(`getBranches failed`, { error: String(error45), projectKey });
18359
18419
  return [];
18360
18420
  }
18361
18421
  }
@@ -18797,7 +18857,18 @@ function createSonarQubeAPIWithToken(url2, token, logger3) {
18797
18857
  return new SonarQubeAPI(client, logger3);
18798
18858
  }
18799
18859
  function createSonarQubeAPI(config2, state, logger3) {
18800
- return createSonarQubeAPIWithToken(config2.url, state.projectToken, logger3);
18860
+ const apiLogger = logger3 ?? new Logger("sonarqube-api");
18861
+ apiLogger.info(">>> createSonarQubeAPI called", {
18862
+ url: config2.url,
18863
+ projectKey: state.projectKey,
18864
+ projectKeyLength: state.projectKey?.length,
18865
+ hasToken: !!state.projectToken,
18866
+ tokenLength: state.projectToken?.length
18867
+ });
18868
+ if (!state.projectToken) {
18869
+ apiLogger.error("createSonarQubeAPI: projectToken is missing!");
18870
+ }
18871
+ return createSonarQubeAPIWithToken(config2.url, state.projectToken, apiLogger);
18801
18872
  }
18802
18873
 
18803
18874
  // src/utils/severity.ts
@@ -19085,12 +19156,22 @@ var QUALITY_GATE_MAPPING = {
19085
19156
  off: "Sonar way"
19086
19157
  };
19087
19158
  async function needsBootstrap(directory) {
19159
+ logger5.info(">>> needsBootstrap called", { directory });
19088
19160
  const hasState = await hasProjectState(directory);
19161
+ logger5.info("hasProjectState result", { hasState, directory });
19089
19162
  if (!hasState) {
19163
+ logger5.info("<<< needsBootstrap: true (no state file)");
19090
19164
  return true;
19091
19165
  }
19092
19166
  const state = await loadProjectState(directory);
19093
- return !state?.setupComplete;
19167
+ const needsBoot = !state?.setupComplete;
19168
+ logger5.info("<<< needsBootstrap result", {
19169
+ needsBoot,
19170
+ hasState: !!state,
19171
+ setupComplete: state?.setupComplete,
19172
+ projectKey: state?.projectKey
19173
+ });
19174
+ return needsBoot;
19094
19175
  }
19095
19176
  async function getProjectState(directory) {
19096
19177
  return loadProjectState(directory);
@@ -19990,7 +20071,9 @@ function getSeveritiesFromLevel(level) {
19990
20071
 
19991
20072
  // src/index.ts
19992
20073
  init_logger();
20074
+ init_logger();
19993
20075
  init_types2();
20076
+ var debugLog = new Logger("sonarqube-debug");
19994
20077
  var IGNORED_FILE_PATTERNS2 = [
19995
20078
  /node_modules/,
19996
20079
  /\.git/,
@@ -20007,6 +20090,7 @@ function shouldIgnoreFile2(filePath) {
20007
20090
  return IGNORED_FILE_PATTERNS2.some((pattern) => pattern.test(filePath));
20008
20091
  }
20009
20092
  var SonarQubePlugin = async ({ client, directory, worktree }) => {
20093
+ debugLog.info("=== PLUGIN START ===", { directory, worktree });
20010
20094
  await client.app.log({
20011
20095
  body: {
20012
20096
  service: "opencode-sonarqube",
@@ -20020,38 +20104,71 @@ var SonarQubePlugin = async ({ client, directory, worktree }) => {
20020
20104
  const getConfig = () => pluginConfig;
20021
20105
  const getDirectory = () => worktree ?? directory;
20022
20106
  const loadPluginConfig = async () => {
20023
- if (pluginConfig)
20107
+ debugLog.info("loadPluginConfig called", { hasExistingConfig: !!pluginConfig });
20108
+ if (pluginConfig) {
20109
+ debugLog.info("Config already loaded, skipping");
20024
20110
  return;
20111
+ }
20025
20112
  try {
20026
- const configFile = Bun.file(`${getDirectory()}/opencode.json`);
20113
+ const configPath = `${getDirectory()}/opencode.json`;
20114
+ debugLog.info("Loading config from", { configPath });
20115
+ const configFile = Bun.file(configPath);
20027
20116
  if (await configFile.exists()) {
20028
20117
  pluginConfig = await configFile.json();
20118
+ debugLog.info("Config loaded", { keys: Object.keys(pluginConfig ?? {}) });
20119
+ } else {
20120
+ debugLog.info("No opencode.json found");
20029
20121
  }
20030
- } catch {}
20122
+ } catch (error45) {
20123
+ debugLog.warn("Config load error", { error: String(error45) });
20124
+ }
20031
20125
  };
20032
20126
  const hooks = createHooks(getConfig, getDirectory);
20033
20127
  let currentSessionId;
20034
20128
  let initialCheckDone = false;
20035
20129
  const performInitialQualityCheck = async (sessionId) => {
20036
- if (initialCheckDone)
20130
+ debugLog.info("=== performInitialQualityCheck START ===", { sessionId, initialCheckDone });
20131
+ if (initialCheckDone) {
20132
+ debugLog.info("Initial check already done, skipping");
20037
20133
  return;
20134
+ }
20038
20135
  initialCheckDone = true;
20039
20136
  try {
20040
20137
  await loadPluginConfig();
20041
20138
  const sonarConfig = pluginConfig?.["sonarqube"];
20139
+ debugLog.info("Loading SonarQube config", { hasSonarConfig: !!sonarConfig });
20042
20140
  const config2 = loadConfig(sonarConfig);
20043
- if (!config2 || config2.level === "off")
20141
+ debugLog.info("Config loaded", { hasConfig: !!config2, level: config2?.level });
20142
+ if (!config2 || config2.level === "off") {
20143
+ debugLog.info("Config missing or level=off, skipping");
20044
20144
  return;
20045
- if (await needsBootstrap(getDirectory()))
20145
+ }
20146
+ const dir = getDirectory();
20147
+ debugLog.info("Checking needsBootstrap", { directory: dir });
20148
+ const needsBoot = await needsBootstrap(dir);
20149
+ debugLog.info("needsBootstrap result", { needsBoot });
20150
+ if (needsBoot) {
20151
+ debugLog.info("Bootstrap needed, skipping initial check");
20046
20152
  return;
20047
- const state = await getProjectState(getDirectory());
20048
- if (!state || !state.projectKey)
20153
+ }
20154
+ debugLog.info("Loading project state");
20155
+ const state = await getProjectState(dir);
20156
+ debugLog.info("Project state loaded", {
20157
+ hasState: !!state,
20158
+ projectKey: state?.projectKey,
20159
+ hasToken: !!state?.projectToken
20160
+ });
20161
+ if (!state || !state.projectKey) {
20162
+ debugLog.info("No state or projectKey, skipping");
20049
20163
  return;
20164
+ }
20165
+ debugLog.info("Creating API and fetching quality status", { projectKey: state.projectKey });
20050
20166
  const api2 = createSonarQubeAPI(config2, state);
20051
20167
  const [qgStatus, counts] = await Promise.all([
20052
20168
  api2.qualityGate.getStatus(state.projectKey),
20053
20169
  api2.issues.getCounts(state.projectKey)
20054
20170
  ]);
20171
+ debugLog.info("Quality status fetched", { qgStatus: qgStatus.projectStatus.status, counts });
20055
20172
  const hasIssues = counts.blocker > 0 || counts.critical > 0 || counts.major > 0;
20056
20173
  const qgFailed = qgStatus.projectStatus.status !== "OK";
20057
20174
  if (hasIssues || qgFailed) {
@@ -20383,18 +20500,36 @@ Git operation completed with changes. Consider running:
20383
20500
  }
20384
20501
  },
20385
20502
  "experimental.chat.system.transform": async (_input, output) => {
20503
+ debugLog.info("=== experimental.chat.system.transform START ===");
20386
20504
  await loadPluginConfig();
20387
20505
  const sonarConfig = pluginConfig?.["sonarqube"];
20506
+ debugLog.info("system.transform: Loading config", { hasSonarConfig: !!sonarConfig });
20388
20507
  const config2 = loadConfig(sonarConfig);
20508
+ debugLog.info("system.transform: Config result", { hasConfig: !!config2, level: config2?.level });
20389
20509
  if (!config2 || config2.level === "off") {
20510
+ debugLog.info("system.transform: No config or level=off, skipping");
20390
20511
  return;
20391
20512
  }
20392
20513
  try {
20393
- const state = await getProjectState(getDirectory());
20514
+ const dir = getDirectory();
20515
+ debugLog.info("system.transform: Loading project state", { directory: dir });
20516
+ const state = await getProjectState(dir);
20517
+ debugLog.info("system.transform: State loaded", {
20518
+ hasState: !!state,
20519
+ projectKey: state?.projectKey,
20520
+ hasToken: !!state?.projectToken
20521
+ });
20394
20522
  if (!state || !state.projectKey) {
20523
+ debugLog.info("system.transform: No state or projectKey, skipping");
20395
20524
  return;
20396
20525
  }
20526
+ debugLog.info("system.transform: Creating API", {
20527
+ url: config2.url,
20528
+ projectKey: state.projectKey,
20529
+ tokenLength: state.projectToken?.length
20530
+ });
20397
20531
  const api2 = createSonarQubeAPI(config2, state);
20532
+ debugLog.info("system.transform: Fetching quality data", { projectKey: state.projectKey });
20398
20533
  const [qgStatus, counts, newCodeResponse] = await Promise.all([
20399
20534
  api2.qualityGate.getStatus(state.projectKey),
20400
20535
  api2.issues.getCounts(state.projectKey),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sonarqube",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",