sonarqube-issues-exporter
Version:
Enterprise-level SonarQube issues exporter with TypeScript support for generating comprehensive HTML reports with dark theme
313 lines • 12.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const config_1 = require("./config");
const utils_1 = require("./utils");
const index_1 = require("./index");
const fs_1 = require("fs");
const path_1 = require("path");
const readline = __importStar(require("readline"));
// Read package version
const packageJsonPath = (0, path_1.join)(__dirname, '../package.json');
const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8'));
// Helper function to build configuration overrides for validate command
function buildValidateConfigOverrides(options) {
const sonarQubeOverrides = buildSonarQubeOverrides(options);
return Object.keys(sonarQubeOverrides).length > 0
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
{ sonarqube: sonarQubeOverrides }
: undefined;
}
// Helper function to create a safe configuration summary for logging
function createConfigSummary(config) {
return {
sonarqube: {
url: config.sonarqube.url,
projectKey: config.sonarqube.projectKey,
organization: config.sonarqube.organization || 'N/A',
tokenConfigured: !!config.sonarqube.token,
},
export: {
outputPath: config.export.outputPath,
filename: config.export.filename,
template: config.export.template,
maxIssues: config.export.maxIssues,
excludeStatuses: config.export.excludeStatuses.join(', '),
includeResolvedIssues: config.export.includeResolvedIssues,
},
logging: {
level: config.logging.level,
},
};
}
// Helper function to create configuration file content
function createConfigFileContent(sonarQubeConfig) {
return JSON.stringify({
sonarqube: {
url: sonarQubeConfig.url,
token: sonarQubeConfig.token,
projectKey: sonarQubeConfig.projectKey,
...(sonarQubeConfig.organization && { organization: sonarQubeConfig.organization }),
},
export: {
outputPath: './reports',
filename: 'sonarqube-issues-report.html',
excludeStatuses: ['CLOSED'],
includeResolvedIssues: false,
maxIssues: 10000,
template: 'default',
},
logging: {
level: 'info',
},
}, null, 2);
}
// Helper function to build SonarQube configuration overrides
function buildSonarQubeOverrides(options) {
if (!options.url && !options.token && !options.project && !options.organization) {
return {};
}
const sonarQubeOverrides = {};
if (options.url)
sonarQubeOverrides.url = options.url;
if (options.token)
sonarQubeOverrides.token = options.token;
if (options.project)
sonarQubeOverrides.projectKey = options.project;
if (options.organization)
sonarQubeOverrides.organization = options.organization;
return sonarQubeOverrides;
}
// Helper function to build export configuration overrides
function buildExportOverrides(options) {
const hasExportOptions = options.output ||
options.filename ||
options.template ||
options.includeResolved !== undefined ||
options.excludeStatuses !== 'CLOSED' ||
parseInt(options.maxIssues, 10) !== 10000;
if (!hasExportOptions) {
return {};
}
const exportOverrides = {};
if (options.output)
exportOverrides.outputPath = options.output;
if (options.filename)
exportOverrides.filename = options.filename;
if (options.template)
exportOverrides.template = options.template;
if (parseInt(options.maxIssues, 10) !== 10000) {
exportOverrides.maxIssues = parseInt(options.maxIssues, 10);
}
if (options.includeResolved) {
exportOverrides.includeResolvedIssues = options.includeResolved;
}
if (options.excludeStatuses !== 'CLOSED') {
exportOverrides.excludeStatuses = options.excludeStatuses.split(',');
}
return exportOverrides;
}
// Helper function to build configuration overrides
function buildConfigOverrides(options) {
const sonarQubeOverrides = buildSonarQubeOverrides(options);
const exportOverrides = buildExportOverrides(options);
const configOverrides = {};
if (Object.keys(sonarQubeOverrides).length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
configOverrides.sonarqube = sonarQubeOverrides;
}
if (Object.keys(exportOverrides).length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
configOverrides.export = exportOverrides;
}
if (options.verbose) {
configOverrides.logging = { level: 'debug' };
}
return Object.keys(configOverrides).length > 0 ? configOverrides : undefined;
}
// Helper function to log export results
function logExportResults(logger, result) {
if (result.success) {
logger.info(`✅ Report generated successfully!`);
}
else {
logger.error(`❌ Failed to generate report: ${result.error}`);
process.exit(1);
}
}
const program = new commander_1.Command();
program
.name('sonarqube-exporter')
.description('Export SonarQube issues to HTML reports')
.version(packageJson.version);
program
.command('export')
.description('Export SonarQube issues to HTML report')
.option('-c, --config <path>', 'Path to configuration file')
.option('--url <url>', 'SonarQube server URL')
.option('--token <token>', 'SonarQube authentication token')
.option('--project <key>', 'SonarQube project key')
.option('--organization <org>', 'SonarQube organization (for SonarCloud)')
.option('-o, --output <path>', 'Output directory path')
.option('-f, --filename <name>', 'Output filename')
.option('--template <name>', 'Template: "default" (classic table view) or "enhanced" (interactive dashboard with charts)', 'default')
.option('--max-issues <number>', 'Maximum number of issues to fetch', '10000')
.option('--include-resolved', 'Include resolved issues in the report')
.option('--exclude-statuses <statuses>', 'Comma-separated list of statuses to exclude', 'CLOSED')
.option('-v, --verbose', 'Enable verbose logging')
.action(async (options) => {
try {
// Load configuration
const configOverrides = buildConfigOverrides(options);
const config = (0, config_1.loadConfig)({
configPath: options.config,
...(configOverrides && { overrides: configOverrides }),
});
// Initialize logger
(0, utils_1.initLogger)(config.logging);
const logger = (0, utils_1.getLogger)();
logger.info('Starting SonarQube issues export...');
// Log configuration safely (without sensitive data) only in debug mode
if (config.logging.level === 'debug') {
logger.debug('Configuration (sanitized):', createConfigSummary(config));
}
// Progress reporting
let lastLoggedPercentage = 0;
const progressCallback = (current, total) => {
const percentage = Math.round((current / total) * 100);
// Only log every 10% to reduce terminal noise
if (percentage >= lastLoggedPercentage + 10 || percentage === 100) {
logger.info(`Progress: ${current}/${total} issues (${percentage}%)`);
lastLoggedPercentage = percentage;
}
};
// Export issues using the library function
const result = await (0, index_1.exportSonarQubeIssues)(config, {
onProgress: progressCallback,
validateConnection: true,
logProjectInfo: true,
});
// Log results
logExportResults(logger, result);
}
catch (error) {
console.error('❌ Export failed:', error);
process.exit(1);
}
});
program
.command('validate')
.description('Validate SonarQube connection and configuration')
.option('-c, --config <path>', 'Path to configuration file')
.option('--url <url>', 'SonarQube server URL')
.option('--token <token>', 'SonarQube authentication token')
.option('--project <key>', 'SonarQube project key')
.option('--organization <org>', 'SonarQube organization (for SonarCloud)')
.action(async (options) => {
try {
const configOverrides = buildValidateConfigOverrides(options);
const config = (0, config_1.loadConfig)({
configPath: options.config,
...(configOverrides && { overrides: configOverrides }),
});
(0, utils_1.initLogger)(config.logging);
const logger = (0, utils_1.getLogger)();
logger.info('Validating configuration...');
// Use the library function for validation
const isValid = await (0, index_1.validateSonarQubeConnection)(config);
if (!isValid) {
process.exit(1);
}
}
catch (error) {
console.error('❌ Validation failed:', error);
process.exit(1);
}
});
program
.command('setup')
.description('Setup configuration interactively')
.option('--global', 'Create global configuration file')
.action(async (options) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function question(prompt) {
return new Promise((resolve) => {
rl.question(prompt, resolve);
});
}
try {
console.log('\n🛠️ SonarQube Issues Exporter Setup\n');
console.log('This will help you create a configuration file.\n');
const url = await question('SonarQube Server URL (e.g., https://sonarcloud.io): ');
const token = await question('SonarQube Token: ');
const projectKey = await question('Project Key: ');
const organization = await question('Organization (optional, for SonarCloud): ');
const configContent = createConfigFileContent({
url: url.trim(),
token: token.trim(),
projectKey: projectKey.trim(),
...(organization.trim() && { organization: organization.trim() }),
});
const configPath = options.global
? (0, path_1.join)(process.env.HOME || process.env.USERPROFILE || '.', '.sonarqube-exporter.json')
: '.sonarqube-exporter.json';
(0, fs_1.writeFileSync)(configPath, configContent);
console.log(`\n✅ Configuration saved to: ${configPath}`);
console.log('\nYou can now run:');
console.log(' sonarqube-exporter export');
console.log(' sonarqube-exporter validate');
console.log('\n💡 You can also override settings using environment variables or CLI options.');
}
catch (error) {
console.error('❌ Setup failed:', error);
process.exit(1);
}
finally {
rl.close();
}
});
// Parse command line arguments
if (process.argv.length === 2) {
program.help();
}
else {
program.parse();
}
//# sourceMappingURL=cli.js.map