aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
509 lines (508 loc) • 16 kB
JavaScript
/**
* @file cicd-integrator.ts
* @description CI/CD pipeline integration and automation
*
* Implements F-009/UC-009: CI/CD Integration
* - GitHub Actions workflow generation
* - GitLab CI/CD configuration
* - Jenkins pipeline support
* - Build automation
* - Test execution integration
* - Deployment automation
* - Status badge generation
*
* @implements NFR-CICD-001: Pipeline generation <30s
* @implements NFR-CICD-002: Support 3+ major CI platforms
* @implements NFR-CICD-003: 100% valid YAML/pipeline syntax
*/
import { promises as fs } from 'fs';
import path from 'path';
import YAML from 'yaml';
// ============================================================================
// CI/CD Integrator Class
// ============================================================================
export class CICDIntegrator {
/**
* Generate CI/CD pipeline configuration
*/
async generatePipeline(config) {
const startTime = Date.now();
try {
const pipeline = this.createPipeline(config);
const { content, filePath } = await this.renderPipeline(pipeline, config);
// Write to project
const fullPath = path.join(config.projectPath, filePath);
const dir = path.dirname(fullPath);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(fullPath, content, 'utf-8');
return {
success: true,
platform: config.platform,
filePath,
content,
duration: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
platform: config.platform,
error: error instanceof Error ? error.message : String(error),
duration: Date.now() - startTime
};
}
}
/**
* Generate badge markdown for CI status
*/
generateBadge(config, repoOwner, repoName) {
switch (config.platform) {
case 'github-actions':
return ``;
case 'gitlab-ci':
return ``;
case 'circleci':
return ``;
case 'travis':
return ``;
default:
return '';
}
}
/**
* Validate pipeline configuration
*/
async validatePipeline(filePath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
YAML.parse(content); // Will throw if invalid
return true;
}
catch {
return false;
}
}
// ========================================================================
// Pipeline Creation Methods
// ========================================================================
/**
* Create pipeline structure
*/
createPipeline(config) {
return {
name: config.projectName || 'CI/CD Pipeline',
triggers: this.createTriggers(),
jobs: this.createJobs(config),
env: this.createEnv(config)
};
}
/**
* Create pipeline triggers
*/
createTriggers() {
return [
{
type: 'push',
branches: ['main', 'develop']
},
{
type: 'pull_request',
branches: ['main']
}
];
}
/**
* Create pipeline jobs
*/
createJobs(config) {
const jobs = [];
// Build & Test job
jobs.push({
name: 'build-and-test',
runsOn: 'ubuntu-latest',
steps: [
...this.createSetupSteps(config),
...this.createInstallSteps(config),
...this.createBuildSteps(config),
...this.createTestSteps(config)
]
});
// Deploy job (if deploy target specified)
if (config.deployTarget) {
jobs.push({
name: 'deploy',
runsOn: 'ubuntu-latest',
dependsOn: ['build-and-test'],
steps: this.createDeploySteps(config)
});
}
return jobs;
}
/**
* Create environment variables
*/
createEnv(config) {
const env = {};
if (config.nodeVersion) {
env.NODE_VERSION = config.nodeVersion;
}
return env;
}
/**
* Create setup steps (checkout, runtime setup)
*/
createSetupSteps(config) {
const steps = [
{
name: 'Checkout code',
uses: 'actions/checkout@v4'
}
];
// Setup runtime based on project type
if (config.nodeVersion || config.buildTool?.startsWith('npm')) {
steps.push({
name: 'Setup Node.js',
uses: 'actions/setup-node@v4',
with: {
'node-version': config.nodeVersion || '20'
}
});
}
if (config.javaVersion) {
steps.push({
name: 'Setup Java',
uses: 'actions/setup-java@v4',
with: {
'java-version': config.javaVersion,
'distribution': 'temurin'
}
});
}
if (config.pythonVersion) {
steps.push({
name: 'Setup Python',
uses: 'actions/setup-python@v5',
with: {
'python-version': config.pythonVersion
}
});
}
return steps;
}
/**
* Create dependency installation steps
*/
createInstallSteps(config) {
const buildTool = config.buildTool || this.detectBuildTool(config.projectPath);
const installCommands = {
npm: 'npm ci',
yarn: 'yarn install --frozen-lockfile',
pnpm: 'pnpm install --frozen-lockfile',
maven: 'mvn install -DskipTests',
gradle: './gradlew build -x test',
make: 'make deps'
};
return [
{
name: 'Install dependencies',
command: installCommands[buildTool] || 'npm ci'
}
];
}
/**
* Create build steps
*/
createBuildSteps(config) {
const buildCommand = config.buildCommand || this.detectBuildCommand(config);
if (!buildCommand) {
return [];
}
return [
{
name: 'Build project',
command: buildCommand
}
];
}
/**
* Create test steps
*/
createTestSteps(config) {
const testCommand = config.testCommand || this.detectTestCommand(config);
if (!testCommand) {
return [];
}
return [
{
name: 'Run tests',
command: testCommand
}
];
}
/**
* Create deployment steps
*/
createDeploySteps(config) {
if (!config.deployTarget) {
return [];
}
switch (config.deployTarget) {
case 'vercel':
return [
{
name: 'Deploy to Vercel',
uses: 'amondnet/vercel-action@v25',
with: {
'vercel-token': '${{ secrets.VERCEL_TOKEN }}',
'vercel-org-id': '${{ secrets.VERCEL_ORG_ID }}',
'vercel-project-id': '${{ secrets.VERCEL_PROJECT_ID }}'
}
}
];
case 'netlify':
return [
{
name: 'Deploy to Netlify',
uses: 'nwtgck/actions-netlify@v3',
with: {
'publish-dir': './dist',
'production-branch': 'main',
'github-token': '${{ secrets.GITHUB_TOKEN }}',
'deploy-message': 'Deploy from GitHub Actions'
}
}
];
case 'aws':
return [
{
name: 'Configure AWS credentials',
uses: 'aws-actions/configure-aws-credentials@v4',
with: {
'aws-access-key-id': '${{ secrets.AWS_ACCESS_KEY_ID }}',
'aws-secret-access-key': '${{ secrets.AWS_SECRET_ACCESS_KEY }}',
'aws-region': 'us-east-1'
}
},
{
name: 'Deploy to AWS',
command: 'aws s3 sync ./dist s3://${{ secrets.S3_BUCKET }}'
}
];
default:
return [];
}
}
// ========================================================================
// Platform-Specific Rendering
// ========================================================================
/**
* Render pipeline to platform-specific format
*/
async renderPipeline(pipeline, config) {
switch (config.platform) {
case 'github-actions':
return this.renderGitHubActions(pipeline, config);
case 'gitlab-ci':
return this.renderGitLabCI(pipeline, config);
case 'jenkins':
return this.renderJenkins(pipeline, config);
case 'circleci':
return this.renderCircleCI(pipeline, config);
case 'travis':
return this.renderTravis(pipeline, config);
default:
throw new Error(`Unsupported platform: ${config.platform}`);
}
}
/**
* Render GitHub Actions workflow
*/
renderGitHubActions(pipeline, _config) {
const workflow = {
name: pipeline.name,
on: {}
};
// Triggers
for (const trigger of pipeline.triggers) {
if (trigger.type === 'push' || trigger.type === 'pull_request') {
workflow.on[trigger.type] = {
branches: trigger.branches
};
}
}
// Jobs
workflow.jobs = {};
for (const job of pipeline.jobs) {
const jobConfig = {
'runs-on': job.runsOn || 'ubuntu-latest',
steps: []
};
for (const step of job.steps) {
const stepConfig = { name: step.name };
if (step.uses) {
stepConfig.uses = step.uses;
if (step.with) {
stepConfig.with = step.with;
}
}
else if (step.command) {
stepConfig.run = step.command;
}
jobConfig.steps.push(stepConfig);
}
if (job.dependsOn && job.dependsOn.length > 0) {
jobConfig.needs = job.dependsOn;
}
workflow.jobs[job.name] = jobConfig;
}
const content = YAML.stringify(workflow);
return {
content,
filePath: '.github/workflows/ci.yml'
};
}
/**
* Render GitLab CI configuration
*/
renderGitLabCI(pipeline, _config) {
const gitlabConfig = {
stages: pipeline.jobs.map(j => j.name)
};
for (const job of pipeline.jobs) {
gitlabConfig[job.name] = {
stage: job.name,
script: job.steps
.filter(s => s.command)
.map(s => s.command)
};
if (job.dependsOn && job.dependsOn.length > 0) {
gitlabConfig[job.name].needs = job.dependsOn;
}
}
const content = YAML.stringify(gitlabConfig);
return {
content,
filePath: '.gitlab-ci.yml'
};
}
/**
* Render Jenkins pipeline
*/
renderJenkins(pipeline, _config) {
const stages = pipeline.jobs.map(job => {
const commands = job.steps
.filter(s => s.command)
.map(s => s.command)
.join('\n ');
return ` stage('${job.name}') {
steps {
${commands}
}
}`;
}).join('\n\n');
const content = `pipeline {
agent any
stages {
${stages}
}
}`;
return {
content,
filePath: 'Jenkinsfile'
};
}
/**
* Render CircleCI configuration
*/
renderCircleCI(pipeline, _config) {
const circleConfig = {
version: 2.1,
jobs: {},
workflows: {
main: {
jobs: pipeline.jobs.map(j => j.name)
}
}
};
for (const job of pipeline.jobs) {
circleConfig.jobs[job.name] = {
docker: [{ image: 'cimg/node:20.0' }],
steps: [
'checkout',
...job.steps.map(s => ({ run: s.command }))
]
};
}
const content = YAML.stringify(circleConfig);
return {
content,
filePath: '.circleci/config.yml'
};
}
/**
* Render Travis CI configuration
*/
renderTravis(pipeline, config) {
const travisConfig = {
language: 'node_js',
'node_js': [config.nodeVersion || '20'],
script: []
};
for (const job of pipeline.jobs) {
for (const step of job.steps) {
if (step.command) {
travisConfig.script.push(step.command);
}
}
}
const content = YAML.stringify(travisConfig);
return {
content,
filePath: '.travis.yml'
};
}
// ========================================================================
// Detection Methods
// ========================================================================
/**
* Detect build tool from project
*/
/**
* Detect build tool from project
*/
detectBuildTool(_projectPath) {
// Note: fs.access returns a Promise, cannot synchronously check in this context
// This method would need refactoring to be async or use synchronous fs
// For now, return default
return 'npm';
}
/**
* Detect build command
*/
detectBuildCommand(config) {
const buildTool = config.buildTool || 'npm';
const buildCommands = {
npm: 'npm run build',
yarn: 'yarn build',
pnpm: 'pnpm build',
maven: 'mvn package',
gradle: './gradlew build'
};
return buildCommands[buildTool] || null;
}
/**
* Detect test command
*/
detectTestCommand(config) {
const buildTool = config.buildTool || 'npm';
const testCommands = {
npm: 'npm test',
yarn: 'yarn test',
pnpm: 'pnpm test',
maven: 'mvn test',
gradle: './gradlew test'
};
return testCommands[buildTool] || null;
}
}
//# sourceMappingURL=cicd-integrator.js.map