// engine/detectors/cicd-detector.ts — CI/CD platform detection (SPEC-026a)
// Hierarchical detection: GitHub Actions > GitLab CI > CircleCI > Buildkite >
// Azure DevOps > Jenkins > Drone > Woodpecker > Tekton > rest
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
// === Detection rules (ordered by priority) ===
// Each entry specifies the platform, static file paths, and optional glob patterns.
const CICD_SIGNALS = [
    {
        platform: 'github-actions',
        paths: [],
        globPatterns: ['.github/workflows/*.yml', '.github/workflows/*.yaml'],
    },
    {
        platform: 'gitlab-ci',
        paths: ['.gitlab-ci.yml', '.gitlab-ci.yaml'],
    },
    {
        platform: 'circleci',
        paths: ['.circleci/config.yml', '.circleci/config.yaml'],
    },
    {
        platform: 'buildkite',
        paths: ['.buildkite/pipeline.yml', '.buildkite/pipeline.yaml'],
        globPatterns: ['.buildkite/*.yml'],
    },
    {
        platform: 'azure-devops',
        paths: ['azure-pipelines.yml', 'azure-pipelines.yaml'],
        globPatterns: ['azure-pipelines*.yml'],
    },
    {
        platform: 'jenkins',
        paths: ['Jenkinsfile'],
        globPatterns: ['Jenkinsfile*'],
    },
    {
        platform: 'drone',
        paths: ['.drone.yml', '.drone.yaml'],
    },
    {
        platform: 'woodpecker',
        paths: ['.woodpecker.yml', '.woodpecker.yaml'],
        globPatterns: ['.woodpecker/*.yml'],
    },
    {
        platform: 'tekton',
        paths: [],
        globPatterns: ['tekton/**/*.yaml', '.tekton/**/*.yaml'],
    },
    {
        platform: 'travis-ci',
        paths: ['.travis.yml', '.travis.yaml'],
    },
    {
        platform: 'bitbucket-pipelines',
        paths: ['bitbucket-pipelines.yml'],
    },
    {
        platform: 'teamcity',
        paths: ['.teamcity/settings.kts'],
        globPatterns: ['.teamcity/**/*.kts'],
    },
    {
        platform: 'concourse',
        paths: ['ci/pipeline.yml', 'concourse/pipeline.yml'],
    },
    {
        platform: 'harness',
        paths: ['.harness/pipeline.yaml'],
        globPatterns: ['.harness/**/*.yaml'],
    },
];
// === Public API ===
/**
 * Detects CI/CD platforms present in the project.
 * Uses hierarchical ordering — first match wins for primary platform.
 */
export async function detectCiCd(projectPath) {
    const detected = [];
    const foundFiles = {};
    for (const signal of CICD_SIGNALS) {
        const files = await findSignalFiles(projectPath, signal);
        if (files.length > 0) {
            detected.push(signal.platform);
            foundFiles[signal.platform] = files;
        }
    }
    const primary = detected[0] ?? null;
    const multipleDetected = detected.slice(1);
    const configFile = primary !== null ? (foundFiles[primary]?.[0] ?? null) : null;
    // Analyze primary config for CI stage details
    const stageInfo = primary !== null && configFile !== null
        ? await analyzeCiCdStages(projectPath, configFile)
        : {
            hasDeployStage: false,
            hasDependencyAudit: false,
            hasTestStage: false,
            hasLintStage: false,
        };
    return {
        platform: primary,
        configFile,
        multipleDetected,
        ...stageInfo,
    };
}
// === Internal helpers ===
async function findSignalFiles(projectPath, signal) {
    const found = [];
    // Check explicit paths
    for (const p of signal.paths) {
        const exists = await fileExists(join(projectPath, p));
        if (exists) {
            found.push(p);
        }
    }
    // Check glob patterns
    if (signal.globPatterns && signal.globPatterns.length > 0) {
        for (const pattern of signal.globPatterns) {
            try {
                const matches = await glob(pattern, { cwd: projectPath, nodir: true });
                found.push(...matches);
            }
            catch {
                // ignore glob errors
            }
        }
    }
    return found;
}
async function fileExists(filePath) {
    try {
        await readFile(filePath, 'utf-8');
        return true;
    }
    catch {
        return false;
    }
}
async function analyzeCiCdStages(projectPath, configFile) {
    try {
        const content = await readFile(join(projectPath, configFile), 'utf-8');
        const lower = content.toLowerCase();
        return {
            hasDeployStage: lower.includes('deploy') ||
                lower.includes('release') ||
                lower.includes('publish') ||
                lower.includes('ship'),
            hasDependencyAudit: lower.includes('audit') ||
                lower.includes('snyk') ||
                lower.includes('trivy') ||
                lower.includes('dependabot') ||
                lower.includes('safety'),
            hasTestStage: lower.includes('test') ||
                lower.includes('spec') ||
                lower.includes('vitest') ||
                lower.includes('pytest') ||
                lower.includes('jest'),
            hasLintStage: lower.includes('lint') ||
                lower.includes('eslint') ||
                lower.includes('prettier') ||
                lower.includes('ruff') ||
                lower.includes('biome'),
        };
    }
    catch {
        return {
            hasDeployStage: false,
            hasDependencyAudit: false,
            hasTestStage: false,
            hasLintStage: false,
        };
    }
}
//# sourceMappingURL=cicd-detector.js.map