@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
926 lines (922 loc) • 35.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 });
exports.CICDPipelineTesting = void 0;
exports.createTestSuite = createTestSuite;
exports.createCICDJob = createCICDJob;
exports.createJobStep = createJobStep;
exports.testCICDPipelines = testCICDPipelines;
const events_1 = require("events");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const yaml = __importStar(require("js-yaml"));
class CICDPipelineTesting extends events_1.EventEmitter {
constructor(config) {
super();
this.results = [];
this.generatedConfigs = [];
this.config = {
parallel: true,
maxConcurrency: 4,
generateConfigs: true,
validateConfigs: true,
simulateRuns: false,
generateReport: true,
outputPath: './cicd-test-output',
...config
};
}
async run() {
this.emit('cicd:start', {
providers: this.config.providers.length,
suites: this.config.testSuites.length
});
const startTime = Date.now();
try {
await this.setup();
if (this.config.generateConfigs) {
await this.generateConfigurations();
}
if (this.config.validateConfigs) {
await this.validateConfigurations();
}
if (this.config.simulateRuns) {
await this.simulateRuns();
}
const report = this.generateReport(Date.now() - startTime);
if (this.config.generateReport) {
await this.saveReport(report);
}
this.emit('cicd:complete', report);
return report;
}
catch (error) {
this.emit('cicd:error', error);
throw error;
}
}
async setup() {
await fs.ensureDir(this.config.outputPath);
this.emit('setup:complete');
}
async generateConfigurations() {
this.emit('configs:start');
const enabledProviders = this.config.providers.filter(p => p.enabled !== false);
for (const provider of enabledProviders) {
for (const suite of this.config.testSuites) {
const config = await this.generateProviderConfig(provider, suite);
this.generatedConfigs.push(config);
}
}
this.emit('configs:complete', { count: this.generatedConfigs.length });
}
async generateProviderConfig(provider, suite) {
this.emit('config:generate', { provider: provider.name, suite: suite.name });
const config = {
provider: provider.name,
path: this.getConfigPath(provider.name, suite.name),
content: '',
valid: false,
errors: [],
warnings: []
};
try {
switch (provider.name) {
case 'github':
config.content = this.generateGitHubConfig(provider, suite);
break;
case 'gitlab':
config.content = this.generateGitLabConfig(provider, suite);
break;
case 'jenkins':
config.content = this.generateJenkinsConfig(provider, suite);
break;
case 'circleci':
config.content = this.generateCircleCIConfig(provider, suite);
break;
case 'azure':
config.content = this.generateAzureConfig(provider, suite);
break;
case 'bitbucket':
config.content = this.generateBitbucketConfig(provider, suite);
break;
default:
config.content = this.generateCustomConfig(provider, suite);
}
// Save config to file
const fullPath = path.join(this.config.outputPath, config.path);
await fs.ensureDir(path.dirname(fullPath));
await fs.writeFile(fullPath, config.content);
config.valid = true;
this.emit('config:generated', config);
}
catch (error) {
config.errors.push(error.message);
this.emit('config:error', { config, error });
}
return config;
}
generateGitHubConfig(provider, suite) {
const workflow = {
name: suite.name,
on: provider.config.triggers || ['push', 'pull_request'],
env: provider.config.environment || {},
jobs: {}
};
for (const job of suite.jobs) {
const ghJob = {
'runs-on': 'ubuntu-latest',
timeout: job.timeout || provider.config.timeout || 30,
steps: []
};
// Add matrix if specified
if (job.matrix || provider.matrix) {
const matrix = { ...provider.matrix, ...job.matrix };
ghJob.strategy = { matrix: this.buildMatrix(matrix) };
}
// Add services if specified
if (job.services) {
ghJob.services = this.buildGitHubServices(job.services);
}
// Add steps
for (const step of job.steps) {
const ghStep = this.buildGitHubStep(step);
ghJob.steps.push(ghStep);
}
workflow.jobs[job.name] = ghJob;
}
return yaml.dump(workflow, { indent: 2 });
}
generateGitLabConfig(provider, suite) {
const pipeline = {
stages: suite.jobs.map(job => job.name),
variables: provider.config.environment || {}
};
// Add cache configuration
if (provider.config.caching?.enabled) {
pipeline.cache = {
paths: provider.config.caching.paths || ['node_modules/'],
key: provider.config.caching.key || 'npm-cache'
};
}
for (const job of suite.jobs) {
const glJob = {
stage: job.name,
timeout: job.timeout || provider.config.timeout || '30m',
script: []
};
// Add matrix as parallel jobs
if (job.matrix || provider.matrix) {
const matrix = { ...provider.matrix, ...job.matrix };
glJob.parallel = { matrix: this.buildGitLabMatrix(matrix) };
}
// Add services
if (job.services) {
glJob.services = job.services.map(s => s.image);
}
// Add job steps as script
for (const step of job.steps) {
if (step.command) {
glJob.script.push(step.command);
}
else if (step.script) {
glJob.script.push(...step.script);
}
}
// Add artifacts
if (provider.config.artifacts) {
glJob.artifacts = {
paths: provider.config.artifacts.map(a => a.path),
expire_in: '1 week'
};
}
pipeline[job.name] = glJob;
}
return yaml.dump(pipeline, { indent: 2 });
}
generateJenkinsConfig(provider, suite) {
const pipeline = {
pipeline: {
agent: 'any',
environment: provider.config.environment || {},
stages: suite.jobs.map(job => ({
stage: job.name,
steps: job.steps.map(step => ({
script: step.command || step.script?.join('\n') || ''
}))
}))
}
};
return `pipeline {
agent any
environment {
${Object.entries(provider.config.environment || {})
.map(([key, value]) => ` ${key} = '${value}'`)
.join('\n')}
}
stages {
${suite.jobs.map(job => ` stage('${job.name}') {
steps {
${job.steps.map(step => ` script {
${step.command || step.script?.join('\n ') || ''}
}`).join('\n')}
}
}`).join('\n')}
}
}`;
}
generateCircleCIConfig(provider, suite) {
const config = {
version: '2.1',
jobs: {},
workflows: {
[suite.name]: {
jobs: suite.jobs.map(job => job.name)
}
}
};
for (const job of suite.jobs) {
const ccJob = {
docker: [{ image: 'cimg/node:18.17' }],
steps: []
};
// Add matrix as parameters
if (job.matrix || provider.matrix) {
const matrix = { ...provider.matrix, ...job.matrix };
ccJob.parameters = this.buildCircleCIParameters(matrix);
}
for (const step of job.steps) {
if (step.command) {
ccJob.steps.push({ run: step.command });
}
else if (step.script) {
ccJob.steps.push({ run: { command: step.script.join('\n') } });
}
}
config.jobs[job.name] = ccJob;
}
return yaml.dump(config, { indent: 2 });
}
generateAzureConfig(provider, suite) {
const pipeline = {
trigger: provider.config.triggers || ['main'],
pool: {
vmImage: 'ubuntu-latest'
},
variables: provider.config.environment || {},
stages: []
};
const stage = {
stage: suite.name,
jobs: []
};
for (const job of suite.jobs) {
const azJob = {
job: job.name,
timeoutInMinutes: Math.ceil((job.timeout || 30000) / 60000),
steps: []
};
// Add matrix
if (job.matrix || provider.matrix) {
const matrix = { ...provider.matrix, ...job.matrix };
azJob.strategy = { matrix: this.buildAzureMatrix(matrix) };
}
for (const step of job.steps) {
if (step.command) {
azJob.steps.push({
script: step.command,
displayName: step.name
});
}
else if (step.script) {
azJob.steps.push({
script: step.script.join('\n'),
displayName: step.name
});
}
}
stage.jobs.push(azJob);
}
pipeline.stages.push(stage);
return yaml.dump(pipeline, { indent: 2 });
}
generateBitbucketConfig(provider, suite) {
const pipeline = {
pipelines: {
default: suite.jobs.map(job => ({
step: {
name: job.name,
image: 'node:18',
caches: provider.config.caching?.enabled ? ['node'] : undefined,
script: job.steps
.map(step => step.command || step.script?.join('\n'))
.filter(Boolean)
}
}))
}
};
return yaml.dump(pipeline, { indent: 2 });
}
generateCustomConfig(provider, suite) {
return `# Custom CI/CD configuration for ${provider.name}
# Suite: ${suite.name}
# Jobs: ${suite.jobs.map(j => j.name).join(', ')}
# This is a placeholder for custom provider configurations
# Implement specific logic based on your custom CI/CD system
`;
}
buildMatrix(matrix) {
const result = {};
if (matrix.os)
result.os = matrix.os;
if (matrix.nodeVersion)
result['node-version'] = matrix.nodeVersion;
if (matrix.packageManager)
result['package-manager'] = matrix.packageManager;
if (matrix.variables) {
Object.assign(result, matrix.variables);
}
return result;
}
buildGitLabMatrix(matrix) {
const combinations = [];
// Simple matrix generation for GitLab
const os = matrix.os || ['ubuntu-latest'];
const nodeVersions = matrix.nodeVersion || ['18'];
for (const osValue of os) {
for (const nodeValue of nodeVersions) {
combinations.push({
OS: osValue,
NODE_VERSION: nodeValue
});
}
}
return combinations;
}
buildCircleCIParameters(matrix) {
const params = {};
if (matrix.nodeVersion) {
params.node_version = {
type: 'string',
default: matrix.nodeVersion[0]
};
}
return params;
}
buildAzureMatrix(matrix) {
const result = {};
if (matrix.os) {
result.vmImage = matrix.os;
}
if (matrix.nodeVersion) {
result.node_version = matrix.nodeVersion;
}
return result;
}
buildGitHubServices(services) {
const result = {};
for (const service of services) {
result[service.name] = {
image: service.image,
ports: service.ports,
env: service.env
};
}
return result;
}
buildGitHubStep(step) {
const ghStep = {
name: step.name
};
if (step.uses) {
ghStep.uses = step.uses;
if (step.with) {
ghStep.with = step.with;
}
}
else if (step.command) {
ghStep.run = step.command;
}
else if (step.script) {
ghStep.run = step.script.join('\n');
}
if (step.env) {
ghStep.env = step.env;
}
if (step.condition) {
ghStep.if = step.condition;
}
if (step.continueOnError) {
ghStep['continue-on-error'] = true;
}
return ghStep;
}
getConfigPath(provider, suite) {
const filename = `${suite}-${provider}`;
switch (provider) {
case 'github':
return `.github/workflows/${filename}.yml`;
case 'gitlab':
return `.gitlab-ci-${filename}.yml`;
case 'jenkins':
return `Jenkinsfile-${filename}`;
case 'circleci':
return `.circleci/config-${filename}.yml`;
case 'azure':
return `azure-pipelines-${filename}.yml`;
case 'bitbucket':
return `bitbucket-pipelines-${filename}.yml`;
default:
return `${provider}-${filename}.yml`;
}
}
async validateConfigurations() {
this.emit('validation:start');
for (const config of this.generatedConfigs) {
await this.validateConfig(config);
}
this.emit('validation:complete');
}
async validateConfig(config) {
try {
switch (config.provider) {
case 'github':
case 'gitlab':
case 'circleci':
case 'azure':
case 'bitbucket':
// Validate YAML syntax
yaml.load(config.content);
break;
case 'jenkins':
// Basic Jenkinsfile validation
if (!config.content.includes('pipeline {')) {
config.warnings.push('Jenkinsfile should start with pipeline block');
}
break;
}
// Additional validation rules
this.validateCommonPatterns(config);
}
catch (error) {
config.valid = false;
config.errors.push(`Validation failed: ${error.message}`);
}
}
validateCommonPatterns(config) {
const content = config.content;
// Check for security issues
if (content.includes('password') || content.includes('secret')) {
config.warnings.push('Potential hardcoded secrets detected');
}
// Check for performance issues
if (!content.includes('cache') && !content.includes('Cache')) {
config.warnings.push('No caching configuration found - consider adding caching for better performance');
}
// Check for timeout configuration
if (!content.includes('timeout')) {
config.warnings.push('No timeout configuration - jobs may hang indefinitely');
}
}
async simulateRuns() {
this.emit('simulation:start');
for (const provider of this.config.providers.filter(p => p.enabled !== false)) {
for (const suite of this.config.testSuites) {
for (const job of suite.jobs) {
const result = await this.simulateJob(provider, suite, job);
this.results.push(result);
}
}
}
this.emit('simulation:complete');
}
async simulateJob(provider, suite, job) {
this.emit('job:simulate', { provider: provider.name, suite: suite.name, job: job.name });
const result = {
provider: provider.name,
suite: suite.name,
job: job.name,
success: true,
duration: 0,
steps: [],
artifacts: [],
errors: [],
warnings: [],
timestamp: new Date()
};
const startTime = Date.now();
try {
for (const step of job.steps) {
const stepResult = await this.simulateStep(step, provider);
result.steps.push(stepResult);
if (!stepResult.success && !step.continueOnError) {
result.success = false;
break;
}
}
result.duration = Date.now() - startTime;
}
catch (error) {
result.success = false;
result.errors.push({
type: 'execution',
message: error.message
});
result.duration = Date.now() - startTime;
}
return result;
}
async simulateStep(step, provider) {
// Simulate step execution with random delays and occasional failures
const duration = Math.random() * 5000 + 1000; // 1-6 seconds
await new Promise(resolve => setTimeout(resolve, duration));
const success = Math.random() > 0.1; // 90% success rate
return {
name: step.name,
success,
duration,
output: success ? 'Step completed successfully' : undefined,
error: success ? undefined : 'Simulated step failure',
exitCode: success ? 0 : 1
};
}
generateReport(duration) {
const summary = this.generateSummary(duration);
const analysis = this.analyzeResults();
const recommendations = this.generateRecommendations(analysis);
const bestPractices = this.generateBestPractices();
return {
summary,
results: this.results,
configs: this.generatedConfigs,
analysis,
recommendations,
bestPractices,
timestamp: new Date()
};
}
generateSummary(duration) {
const totalSteps = this.results.reduce((sum, r) => sum + r.steps.length, 0);
const passedSteps = this.results.reduce((sum, r) => sum + r.steps.filter(s => s.success).length, 0);
return {
totalProviders: this.config.providers.length,
totalSuites: this.config.testSuites.length,
totalJobs: this.results.length,
totalSteps,
passed: this.results.filter(r => r.success).length,
failed: this.results.filter(r => !r.success).length,
warnings: this.generatedConfigs.reduce((sum, c) => sum + c.warnings.length, 0),
duration: duration / 1000,
coverage: totalSteps > 0 ? (passedSteps / totalSteps) * 100 : 0
};
}
analyzeResults() {
const compatibility = this.analyzeCompatibility();
const performance = this.analyzePerformance();
const reliability = this.analyzeReliability();
const security = this.analyzeSecurity();
const optimization = this.generateOptimizations();
return {
compatibility,
performance,
reliability,
security,
optimization
};
}
analyzeCompatibility() {
const providers = this.config.providers.map(p => p.name);
const features = ['caching', 'matrix', 'services', 'artifacts', 'parallel'];
const support = [];
const gaps = [];
for (const feature of features) {
const row = [];
const unsupportedProviders = [];
for (const provider of providers) {
const supported = this.checkFeatureSupport(provider, feature);
row.push(supported);
if (!supported) {
unsupportedProviders.push(provider);
}
}
support.push(row);
if (unsupportedProviders.length > 0) {
gaps.push({
feature,
providers: unsupportedProviders,
impact: unsupportedProviders.length > providers.length / 2 ? 'high' : 'medium'
});
}
}
return { providers, features, support, gaps };
}
checkFeatureSupport(provider, feature) {
// Simplified feature support matrix
const supportMatrix = {
github: ['caching', 'matrix', 'services', 'artifacts', 'parallel'],
gitlab: ['caching', 'matrix', 'services', 'artifacts', 'parallel'],
jenkins: ['caching', 'parallel'],
circleci: ['caching', 'matrix', 'services', 'artifacts', 'parallel'],
azure: ['caching', 'matrix', 'artifacts', 'parallel'],
bitbucket: ['caching', 'parallel']
};
return supportMatrix[provider]?.includes(feature) ?? false;
}
analyzePerformance() {
const jobDurations = this.results.map(r => r.duration);
const averageJobDuration = jobDurations.length > 0 ?
jobDurations.reduce((a, b) => a + b, 0) / jobDurations.length : 0;
const slowestJobs = this.results
.sort((a, b) => b.duration - a.duration)
.slice(0, 5)
.map(r => ({ job: `${r.suite}/${r.job}`, duration: r.duration }));
return {
averageJobDuration,
slowestJobs,
parallelizationOpportunities: this.identifyParallelizationOpportunities(),
cacheEffectiveness: this.calculateCacheEffectiveness()
};
}
identifyParallelizationOpportunities() {
const opportunities = [];
// Check for sequential jobs that could be parallel
for (const suite of this.config.testSuites) {
const independentJobs = suite.jobs.filter(job => !job.dependencies?.length);
if (independentJobs.length > 1) {
opportunities.push(`Suite "${suite.name}" has ${independentJobs.length} independent jobs that could run in parallel`);
}
}
return opportunities;
}
calculateCacheEffectiveness() {
const cachingConfigs = this.config.providers.filter(p => p.config.caching?.enabled);
return (cachingConfigs.length / this.config.providers.length) * 100;
}
analyzeReliability() {
const total = this.results.length;
const successful = this.results.filter(r => r.success).length;
const failed = total - successful;
const timeouts = this.results.filter(r => r.errors.some(e => e.type === 'timeout')).length;
return {
successRate: total > 0 ? (successful / total) * 100 : 0,
errorRate: total > 0 ? (failed / total) * 100 : 0,
timeoutRate: total > 0 ? (timeouts / total) * 100 : 0,
retryRate: 0, // Would be calculated from actual retry data
flakyTests: [] // Would be identified from multiple runs
};
}
analyzeSecurity() {
const secretsExposed = [];
const permissionsIssues = [];
const vulnerabilities = [];
const recommendations = [];
// Analyze configurations for security issues
for (const config of this.generatedConfigs) {
if (config.content.includes('password') || config.content.includes('token')) {
secretsExposed.push(`${config.provider}: Potential secrets in configuration`);
}
if (config.content.includes('chmod 777') || config.content.includes('sudo')) {
permissionsIssues.push(`${config.provider}: Overly permissive operations detected`);
}
}
if (secretsExposed.length > 0) {
recommendations.push('Use secure secret management instead of hardcoded values');
}
if (permissionsIssues.length > 0) {
recommendations.push('Review and minimize required permissions');
}
return {
secretsExposed,
permissionsIssues,
vulnerabilities,
recommendations
};
}
generateOptimizations() {
const optimizations = [];
// Cache optimization
const noCacheProviders = this.config.providers.filter(p => !p.config.caching?.enabled);
if (noCacheProviders.length > 0) {
optimizations.push({
type: 'cache',
description: 'Enable dependency caching to speed up builds',
impact: 'high',
implementation: 'Add cache configuration to CI/CD pipelines',
estimatedSavings: '30-50% build time reduction'
});
}
// Parallel optimization
const sequentialSuites = this.config.testSuites.filter(s => s.jobs.length > 1 && !s.jobs.some(j => j.matrix));
if (sequentialSuites.length > 0) {
optimizations.push({
type: 'parallel',
description: 'Run independent jobs in parallel',
impact: 'medium',
implementation: 'Configure job dependencies and parallel execution',
estimatedSavings: '20-40% total execution time'
});
}
// Matrix optimization
const noMatrixJobs = this.config.testSuites
.flatMap(s => s.jobs)
.filter(j => !j.matrix);
if (noMatrixJobs.length > 0) {
optimizations.push({
type: 'matrix',
description: 'Use build matrices for multi-environment testing',
impact: 'medium',
implementation: 'Configure test matrices for OS, Node.js versions, etc.',
estimatedSavings: 'Better test coverage with organized execution'
});
}
return optimizations;
}
generateRecommendations(analysis) {
const recommendations = [];
if (analysis.reliability.successRate < 90) {
recommendations.push(`Improve pipeline reliability - current success rate: ${analysis.reliability.successRate.toFixed(1)}%`);
}
if (analysis.performance.cacheEffectiveness < 50) {
recommendations.push('Enable caching on more providers to improve build performance');
}
if (analysis.compatibility.gaps.length > 0) {
const highImpactGaps = analysis.compatibility.gaps.filter(g => g.impact === 'high');
if (highImpactGaps.length > 0) {
recommendations.push(`Address high-impact compatibility gaps: ${highImpactGaps.map(g => g.feature).join(', ')}`);
}
}
recommendations.push(...analysis.security.recommendations);
return recommendations;
}
generateBestPractices() {
return [
{
category: 'performance',
title: 'Use Dependency Caching',
description: 'Cache dependencies between builds to reduce installation time',
implementation: 'Configure cache paths and keys in your CI/CD pipeline',
providers: ['github', 'gitlab', 'circleci', 'azure']
},
{
category: 'reliability',
title: 'Set Appropriate Timeouts',
description: 'Prevent jobs from hanging indefinitely',
implementation: 'Set reasonable timeout values for jobs and steps',
providers: ['github', 'gitlab', 'jenkins', 'circleci', 'azure', 'bitbucket']
},
{
category: 'security',
title: 'Use Secret Management',
description: 'Store sensitive data in secure secret stores',
implementation: 'Use provider-specific secret management features',
providers: ['github', 'gitlab', 'jenkins', 'circleci', 'azure', 'bitbucket']
},
{
category: 'maintainability',
title: 'Use Build Matrices',
description: 'Test across multiple environments systematically',
implementation: 'Configure test matrices for different OS, runtime versions',
providers: ['github', 'gitlab', 'circleci', 'azure']
}
];
}
async saveReport(report) {
const reportPath = path.join(this.config.outputPath, 'cicd-test-report.json');
await fs.writeJson(reportPath, report, { spaces: 2 });
const summaryPath = path.join(this.config.outputPath, 'cicd-test-summary.md');
await fs.writeFile(summaryPath, this.formatMarkdownReport(report));
this.emit('report:saved', { json: reportPath, markdown: summaryPath });
}
formatMarkdownReport(report) {
const lines = [
'# CI/CD Pipeline Testing Report',
'',
`**Date:** ${report.timestamp.toISOString()}`,
`**Duration:** ${report.summary.duration.toFixed(2)}s`,
'',
'## Summary',
'',
`- **Providers:** ${report.summary.totalProviders}`,
`- **Test Suites:** ${report.summary.totalSuites}`,
`- **Jobs:** ${report.summary.totalJobs}`,
`- **Steps:** ${report.summary.totalSteps}`,
`- **Success Rate:** ${((report.summary.passed / report.summary.totalJobs) * 100).toFixed(1)}%`,
`- **Coverage:** ${report.summary.coverage.toFixed(1)}%`,
'',
'## Generated Configurations',
''
];
const configsByProvider = new Map();
for (const config of report.configs) {
if (!configsByProvider.has(config.provider)) {
configsByProvider.set(config.provider, []);
}
configsByProvider.get(config.provider).push(config);
}
for (const [provider, configs] of configsByProvider) {
lines.push(`### ${provider.charAt(0).toUpperCase() + provider.slice(1)}`);
lines.push('');
for (const config of configs) {
const status = config.valid ? '✅' : '❌';
lines.push(`- ${status} \`${config.path}\``);
if (config.errors.length > 0) {
config.errors.forEach(error => {
lines.push(` - ❌ ${error}`);
});
}
if (config.warnings.length > 0) {
config.warnings.forEach(warning => {
lines.push(` - ⚠️ ${warning}`);
});
}
}
lines.push('');
}
lines.push('## Compatibility Analysis', '');
const matrix = report.analysis.compatibility;
lines.push('| Feature | ' + matrix.providers.join(' | ') + ' |');
lines.push('|---------|' + matrix.providers.map(() => '---').join('|') + '|');
for (let i = 0; i < matrix.features.length; i++) {
const feature = matrix.features[i];
const support = matrix.support[i].map(s => s ? '✅' : '❌').join(' | ');
lines.push(`| ${feature} | ${support} |`);
}
lines.push('', '## Recommendations', '');
report.recommendations.forEach(rec => {
lines.push(`- ${rec}`);
});
if (report.analysis.optimization.length > 0) {
lines.push('', '## Optimization Opportunities', '');
report.analysis.optimization.forEach(opt => {
lines.push(`### ${opt.type.charAt(0).toUpperCase() + opt.type.slice(1)}`);
lines.push(`- **Impact:** ${opt.impact}`);
lines.push(`- **Description:** ${opt.description}`);
lines.push(`- **Implementation:** ${opt.implementation}`);
if (opt.estimatedSavings) {
lines.push(`- **Estimated Savings:** ${opt.estimatedSavings}`);
}
lines.push('');
});
}
return lines.join('\n');
}
}
exports.CICDPipelineTesting = CICDPipelineTesting;
// Export utility functions
function createTestSuite(name, jobs, options) {
return {
name,
jobs,
...options
};
}
function createCICDJob(name, steps, options) {
return {
name,
steps,
...options
};
}
function createJobStep(name, type, options) {
return {
name,
type,
...options
};
}
async function testCICDPipelines(config) {
const tester = new CICDPipelineTesting(config);
return tester.run();
}