UNPKG

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
#!/usr/bin/env node "use strict"; 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