ai-factory.ci
CI — Pipeline Configuration Generator
Analyze a project and generate production-grade CI/CD pipeline configuration for GitHub Actions or GitLab CI. Generates separate jobs for linting, static analysis, tests, and security scanning — adapted to the project's language, framework, and existing tooling.
Three modes based on what exists:
| What exists | Mode | Action |
|---|---|---|
| No CI config | generate |
Create pipeline from scratch with interactive setup |
| CI config exists but incomplete | enhance |
Audit & improve, add missing jobs |
| Full CI config | audit |
Audit against best practices, fix gaps |
Step 0: Load Project Context
Read the project description if available:
Read .ai-factory/DESCRIPTION.md
Store project context for later steps. If absent, Step 2 detects everything.
Step 1: Detect Existing CI & Determine Mode
1.1 Scan for Existing CI Configuration
Glob: .github/workflows/*.yml, .github/workflows/*.yaml, .gitlab-ci.yml, .circleci/config.yml, Jenkinsfile, .travis.yml, bitbucket-pipelines.yml
Classify found files:
HAS_GITHUB_ACTIONS:.github/workflows/contains YAML filesHAS_GITLAB_CI:.gitlab-ci.ymlexistsHAS_OTHER_CI: CircleCI, Jenkins, Travis, or Bitbucket detected
1.2 Determine Mode
If $ARGUMENTS contains --enhance -> set MODE = "enhance" regardless.
Path A: No CI config exists (!HAS_GITHUB_ACTIONS && !HAS_GITLAB_CI && !HAS_OTHER_CI):
- Set
MODE = "generate" - Proceed to Step 1.3: Interactive Setup
Path B: CI config exists but is incomplete (e.g., has only tests, no linting):
- Set
MODE = "enhance" - Read all existing CI files -> store as
EXISTING_CONTENT - Log: "Found existing CI configuration. Will analyze and add missing jobs."
Path C: Full CI setup (has linting + tests + static analysis):
- Set
MODE = "audit" - Read all existing CI files -> store as
EXISTING_CONTENT - Log: "Found complete CI setup. Will audit against best practices and fix gaps."
1.3 Interactive Setup (Generate Mode Only)
Determine CI platform from $ARGUMENTS or ask:
If $ARGUMENTS contains github -> set PLATFORM = "github"
If $ARGUMENTS contains gitlab -> set PLATFORM = "gitlab"
Otherwise:
AskUserQuestion: Which CI/CD platform do you use?
Options:
1. GitHub Actions (Recommended) — .github/workflows/*.yml
2. GitLab CI — .gitlab-ci.yml
Ask about optional features:
AskUserQuestion: Which additional CI features do you need?
Options (multiSelect):
1. Security scanning — Dependency audit, SAST
2. Coverage reporting — Upload test coverage
3. Matrix builds — Test across multiple language versions
4. None — Just linting, static analysis, and tests
Store choices:
PLATFORM: github | gitlabWANT_SECURITY: booleanWANT_COVERAGE: booleanWANT_MATRIX: boolean
1.4 Read Existing Files (Enhance / Audit Modes)
Read all existing CI files and store as EXISTING_CONTENT:
- All
.github/workflows/*.ymlfiles .gitlab-ci.yml- Any included GitLab CI files (check
include:directives)
Determine PLATFORM from existing files.
Step 2: Deep Project Analysis
Scan the project thoroughly — every decision in the generated pipeline depends on this profile.
2.1 Language & Runtime
| File | Language |
|---|---|
composer.json |
PHP |
package.json |
Node.js / TypeScript |
pyproject.toml / setup.py / setup.cfg |
Python |
go.mod |
Go |
Cargo.toml |
Rust |
pom.xml |
Java (Maven) |
build.gradle / build.gradle.kts |
Java/Kotlin (Gradle) |
2.2 Language Version
Detect the project's language version to use in CI:
| Language | Version Source | Example |
|---|---|---|
| PHP | composer.json -> require.php |
>=8.2 -> ['8.2', '8.3', '8.4'] |
| Node.js | package.json -> engines.node, .nvmrc, .node-version |
>=18 -> [18, 20, 22] |
| Python | pyproject.toml -> requires-python, .python-version |
>=3.11 -> ['3.11', '3.12', '3.13'] |
| Go | go.mod -> go directive |
go 1.23 -> '1.23' |
| Rust | Cargo.toml -> rust-version, rust-toolchain.toml |
1.82 -> '1.82' |
| Java | pom.xml -> maven.compiler.source, build.gradle -> sourceCompatibility |
17 -> [17, 21] |
For matrix builds: use the minimum version from the project config as the lowest, and include the latest stable version. For non-matrix builds: use the latest version that satisfies the constraint.
2.3 Package Manager & Lock File
| File | Package Manager | Install Command |
|---|---|---|
composer.lock |
Composer | composer install --no-interaction --prefer-dist |
bun.lockb |
Bun | bun install --frozen-lockfile |
pnpm-lock.yaml |
pnpm | pnpm install --frozen-lockfile |
yarn.lock |
Yarn | yarn install --frozen-lockfile |
package-lock.json |
npm | npm ci |
uv.lock |
uv | uv sync --all-extras --dev |
poetry.lock |
Poetry | poetry install |
Pipfile.lock |
Pipenv | pipenv install --dev |
requirements.txt |
pip | pip install -r requirements.txt |
go.sum |
Go modules | go mod download |
Cargo.lock |
Cargo | (built-in) |
Store: PACKAGE_MANAGER, LOCK_FILE, INSTALL_CMD.
2.4 Linters & Formatters
Detect existing tools by scanning config files and dependency files:
PHP (scan composer.json -> require-dev):
| Tool | Config File | CI Command |
|---|---|---|
| PHP-CS-Fixer | .php-cs-fixer.php, .php-cs-fixer.dist.php |
vendor/bin/php-cs-fixer fix --dry-run --diff |
| PHP_CodeSniffer | phpcs.xml, phpcs.xml.dist |
vendor/bin/phpcs |
| Pint | pint.json |
vendor/bin/pint --test |
Node.js (scan package.json -> devDependencies):
| Tool | Config File | CI Command |
|---|---|---|
| ESLint | eslint.config.*, .eslintrc.* |
npx eslint . |
| Prettier | .prettierrc*, prettier.config.* |
npx prettier --check . |
| Biome | biome.json, biome.jsonc |
npx biome check . |
Python (scan pyproject.toml -> [tool.*] sections, requirements-dev.txt):
| Tool | Config File | CI Command |
|---|---|---|
| Ruff | ruff.toml, pyproject.toml [tool.ruff] |
ruff check . / ruff format --check . |
| Black | pyproject.toml [tool.black] |
black --check . |
| isort | pyproject.toml [tool.isort] |
isort --check-only . |
| Flake8 | .flake8, setup.cfg [flake8] |
flake8 . |
| Pylint | .pylintrc, pyproject.toml [tool.pylint] |
pylint src/ |
Go:
| Tool | Config File | CI Command |
|---|---|---|
| golangci-lint | .golangci.yml, .golangci.yaml |
golangci-lint run |
Rust (built-in):
| Tool | CI Command |
|---|---|
| clippy | cargo clippy --all-targets --all-features -- -D warnings |
| rustfmt | cargo fmt --all -- --check |
Java:
| Tool | Config File | CI Command (Maven) | CI Command (Gradle) |
|---|---|---|---|
| Checkstyle | checkstyle.xml |
mvn checkstyle:check -B |
./gradlew checkstyleMain |
| PMD | pmd-ruleset.xml |
mvn pmd:check -B |
./gradlew pmdMain |
| SpotBugs | — | mvn compile spotbugs:check -B |
./gradlew spotbugsMain |
2.5 Static Analysis Tools
PHP (scan composer.json -> require-dev):
| Tool | Config File | CI Command |
|---|---|---|
| PHPStan | phpstan.neon, phpstan.neon.dist |
vendor/bin/phpstan analyse --memory-limit=512M |
| Psalm | psalm.xml, psalm.xml.dist |
vendor/bin/psalm --no-cache |
| Rector | rector.php |
vendor/bin/rector process --dry-run |
Python:
| Tool | CI Command |
|---|---|
| mypy | mypy src/ |
| pyright | pyright |
Node.js (TypeScript):
| Tool | CI Command |
|---|---|
| tsc | npx tsc --noEmit |
Go:
golangci-lintincludes static analysis (go vet, staticcheck, etc.)
Rust:
cargo clippycovers static analysis
2.6 Test Framework
| Language | Detect By | Test Command |
|---|---|---|
| PHP | phpunit/phpunit in composer.json |
vendor/bin/phpunit |
| PHP | pestphp/pest in composer.json |
vendor/bin/pest --ci |
| Node.js | jest in package.json |
npx jest --ci |
| Node.js | vitest in package.json |
npx vitest run |
| Python | pytest in pyproject.toml |
pytest -v |
| Go | Built-in | go test -race -v ./... |
| Rust | Built-in | cargo test --all-features |
| Java | Built-in (JUnit) | mvn verify -B / ./gradlew test |
Also detect coverage tools:
| Language | Coverage Flag |
|---|---|
| PHP | --coverage-clover coverage.xml |
| Node.js (Jest) | --coverage |
| Node.js (Vitest) | --coverage |
| Python | --cov=src --cov-report=xml |
| Go | -coverprofile=coverage.out -covermode=atomic |
| Rust | cargo tarpaulin --ignore-tests --out xml |
| Java | mvn jacoco:report / ./gradlew jacocoTestReport |
2.7 Security Audit Tools
| Language | Tool | CI Command |
|---|---|---|
| PHP | Composer audit | composer audit |
| Node.js | npm audit | npm audit --audit-level=high |
| Python | pip-audit | pip-audit or uv run pip-audit (dependency vulnerabilities) |
| Python | bandit | bandit -r src/ or uv run bandit -r src/ (code security) |
| Go | govulncheck | govulncheck ./... |
| Rust | cargo audit | cargo audit |
| Rust | cargo deny | cargo deny check |
| Java | OWASP | mvn dependency-check:check -B |
2.8 Services Detection
Check if tests require external services (database, Redis, etc.):
Grep in tests/: postgres|mysql|redis|mongo|rabbitmq|elasticsearch
Glob: docker-compose.test.yml, docker-compose.ci.yml
If services are needed, they will be configured in the CI pipeline as service containers.
2.9 Build Output
Does the project have a build step?
| Language | Has Build | Build Command |
|---|---|---|
Node.js (with build script) |
Yes | npm run build / pnpm build |
| Go | Yes | go build ./... |
| Rust | Yes | cargo build --release |
| Java | Yes | mvn package -DskipTests -B / ./gradlew assemble |
| PHP | Usually no | — |
| Python | Usually no | — |
Summary
Build PROJECT_PROFILE:
language,language_version,language_versions(for matrix)package_manager,lock_file,install_cmdlinters: list of {name, command, config_file}static_analyzers: list of {name, command}test_framework,test_cmd,coverage_cmdsecurity_tools: list of {name, command}has_build_step,build_cmdhas_typescript: boolean (for typecheck job)services_needed: list of services for CIsource_dir: main source directory (src/, app/, lib/)
Step 3: Read Best Practices & Templates
Read skills/ci/references/BEST-PRACTICES.md
Select templates matching the platform and language:
GitHub Actions:
| Language | Template |
|---|---|
| PHP | templates/github/php.yml |
| Node.js | templates/github/node.yml |
| Python | templates/github/python.yml |
| Go | templates/github/go.yml |
| Rust | templates/github/rust.yml |
| Java | templates/github/java.yml |
GitLab CI:
| Language | Template |
|---|---|
| PHP | templates/gitlab/php.yml |
| Node.js | templates/gitlab/node.yml |
| Python | templates/gitlab/python.yml |
| Go | templates/gitlab/go.yml |
| Rust | templates/gitlab/rust.yml |
| Java | templates/gitlab/java.yml |
Read the selected template:
Read skills/ci/templates/<platform>/<language>.yml
Step 4: Generate Pipeline (Generate Mode)
Using the PROJECT_PROFILE, best practices, and template as a base, generate a customized CI pipeline.
4.1 GitHub Actions Generation
One workflow per concern — each file has its own triggers, permissions, concurrency:
| File | Name | Jobs | When to create |
|---|---|---|---|
lint.yml |
Lint | code-style, static-analysis, rector | Linters or SA detected |
tests.yml |
Tests | tests (+ service containers) | Always |
build.yml |
Build | build | has_build_step |
security.yml |
Security | dependency-audit, dependency-review | WANT_SECURITY |
Why one file per concern:
- Each check is a separate status check in PR — instantly see what failed
- Independent triggers — security on schedule, tests on push/PR, build only after tests
- Independent permissions — security may need
security-events: write - Can disable/re-run one workflow without touching others
- Branch protection rules can require specific workflows (e.g. require
testsbut notsecurity)
When to keep single file: Only for very small projects with just lint + tests (2 jobs). As soon as there are 3+ concerns — split.
Every workflow gets the same header pattern:
name: <Name>
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
Per-file job organization:
lint.yml — all code quality checks in parallel:
| Job | Purpose | When to include |
|---|---|---|
code-style |
Formatting (CS-Fixer, Prettier, Ruff format, rustfmt) | Formatter detected |
lint |
Linting (ESLint, Ruff check, Clippy, golangci-lint) | Linter detected |
static-analysis |
Type checking / SA (PHPStan, Psalm, mypy, tsc) | SA tools detected |
rector |
Rector dry-run (PHP only) | Rector detected |
All jobs run in parallel (no needs). If only one tool detected (e.g. Go with just golangci-lint) — single job in the file is fine.
tests.yml — test suite:
| Job | Purpose | When to include |
|---|---|---|
tests |
Unit/integration tests | Always |
tests-<service> |
Tests requiring service containers | services_needed detected |
Matrix builds (multiple language versions) only in this file.
build.yml — build verification:
| Job | Purpose | Notes |
|---|---|---|
build |
Verify compilation/bundling | Can depend on external workflow via workflow_run or just run independently |
security.yml — security scanning:
| Job | Purpose | Extra triggers |
|---|---|---|
dependency-audit |
Vulnerability scan | schedule: cron '0 6 * * 1' (weekly) |
dependency-review |
PR dependency diff | Only on pull_request |
Per-job rules:
- Each job gets its own setup (checkout, language setup, cache, dependency install)
- Use language-specific setup actions with built-in cache:
- PHP:
shivammathur/setup-php@v2withtools:parameter - Node.js:
actions/setup-node@v4withcache:parameter - Python:
astral-sh/setup-uv@v5(if uv) oractions/setup-python@v5(if pip) - Go:
actions/setup-go@v5(auto-caches) - Rust:
dtolnay/rust-toolchain@stable+Swatinem/rust-cache@v2 - Java:
actions/setup-java@v4withcache:parameter
- PHP:
- Use
fail-fast: falsein matrix builds - Upload coverage as artifact when
WANT_COVERAGE
Matrix builds (when WANT_MATRIX):
Only the tests job uses a matrix. Lint/SA jobs run on the latest version only.
tests:
name: Tests (${{ matrix.<language>-version }})
strategy:
fail-fast: false
matrix:
<language>-version: <language_versions from PROJECT_PROFILE>
Combining linter jobs:
If the project has both a formatter AND a linter from the same ecosystem, combine them into one job:
- PHP:
php-cs-fixercheck + other lint ->code-stylejob - Node.js:
eslint+prettier->lintjob. Biome replaces BOTH ESLint and Prettier — if Biome is detected, use onlynpx biome check .in a singlelintjob - Python:
ruff check+ruff format --check->lintjob (Ruff handles both) - Rust:
cargo fmt+cargo clippy-> can be separate (fmt is fast, clippy needs compilation)
Do NOT combine lint/SA with tests — they should fail independently with clear feedback.
security.yml example (when WANT_SECURITY):
name: Security
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 6 * * 1' # Weekly on Monday
permissions:
contents: read
jobs:
dependency-audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Language-specific: composer audit / govulncheck / npm audit / cargo deny
dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
tests.yml example:
name: Tests
on:
push:
branches: [main]
pull_request:
concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
tests:
name: Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Language-specific setup + test command
4.2 GitLab CI Generation
Output file: .gitlab-ci.yml
Pipeline structure:
stages:
- install
- lint
- test
- build
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
default:
interruptible: true
Job organization:
| Stage | Jobs | Notes |
|---|---|---|
install |
install |
Install dependencies, cache + artifact for downstream |
lint |
code-style, lint, static-analysis, rector |
All needs: [install], run in parallel |
test |
tests |
needs: [install] |
build |
build |
needs: [tests, lint, ...] |
security |
security |
needs: [install], allow_failure: true |
GitLab-specific features:
- Cache strategy: Use
policy: pull-pushoninstalljob,policy: pullon all others - Cache key: Use
key: files:with lock file for automatic invalidation - Artifacts: Pass
vendor//node_modules/via artifacts from install job (faster than cache for same-pipeline) - Reports: Use
artifacts.reports.junitfor test results,artifacts.reports.codequalityfor lint output - DAG: Use
needs:keyword for parallel execution within stages - Hidden jobs: Use
.setupanchors for sharedbefore_scriptand cache config - Coverage regex: Add
coverage:regex for test jobs
PHP-specific GitLab patterns:
image: php:8.3-cli
variables:
COMPOSER_HOME: $CI_PROJECT_DIR/.composer
.composer-setup:
before_script:
- apt-get update && apt-get install -y git unzip
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
Report formats for GitLab integration:
| Tool | Flag | Report Type |
|---|---|---|
| PHPStan | --error-format=gitlab |
codequality |
| ESLint | --format json |
codequality |
| Ruff | --output-format=gitlab |
codequality |
| golangci-lint | --out-format code-climate |
codequality |
| PHPUnit | --log-junit report.xml |
junit |
| Jest | --reporters=jest-junit |
junit |
| pytest | --junitxml=report.xml |
junit |
4.3 Service Containers
If services_needed is not empty, add service containers to the test job:
GitHub Actions:
tests:
services:
postgres:
image: postgres:17
env:
POSTGRES_DB: test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
GitLab CI:
tests:
services:
- name: postgres:17
alias: db
variables:
POSTGRES_DB: test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
DATABASE_URL: "postgresql://test:test@db:5432/test"
Quality Checks (Before Writing)
Verify generated pipeline before writing:
Correctness:
- Every job has checkout/setup/install steps
- Cache is configured for the correct lock file
- All commands match tools actually present in the project
- Matrix versions match the project's version constraints
- Service containers have health checks
Best practices:
-
concurrencygroup set (GitHub Actions) -
permissions: contents: readset (GitHub Actions) -
interruptible: trueset (GitLab CI) -
workflow.rulesdefined (GitLab CI) - Jobs are parallel where possible (no unnecessary
needs) -
fail-fast: falseon matrix builds
No over-engineering:
- No jobs for tools not present in the project
- No matrix builds if the project only targets one version
- No security scanning unless requested or tools are installed
- No build job if the project has no build step
Step 5: Enhance / Audit Existing Pipeline
When MODE = "enhance" or MODE = "audit", analyze EXISTING_CONTENT against the project profile and best practices.
5.1 Gap Analysis
Compare existing pipeline against PROJECT_PROFILE:
Missing jobs:
- Linter installed but no lint job in CI?
- SA tool installed but no SA job?
- Tests exist but no test job?
- Security tools installed but no security job?
Configuration issues:
- No caching configured?
- No concurrency group (GitHub Actions)?
- Using deprecated actions (e.g.,
actions-rsinstead ofdtolnay/rust-toolchain)? - Hardcoded language versions instead of variable/matrix?
- Missing
fail-fast: falseon matrix? - Using
policy: pull-pushon all GitLab jobs instead ofpullon non-install jobs?
Missing features:
- No coverage reporting when coverage tools are available?
- No JUnit/codequality report integration (GitLab)?
- No path filtering for monorepos?
- No
workflow_dispatchtrigger (GitHub Actions)?
5.2 Audit Report
## CI Pipeline Audit
### Jobs
| Check | Status | Detail |
|-------|--------|--------|
| Code style job | ✅ | php-cs-fixer dry-run |
| Static analysis | ❌ | PHPStan installed but no CI job |
| Rector check | ❌ | rector.php exists but no CI job |
| Tests | ✅ | PHPUnit with coverage |
| Security audit | ❌ | No dependency scanning |
### Configuration
| Check | Status | Detail |
|-------|--------|--------|
| Caching | ⚠️ | Missing composer cache |
| Concurrency | ❌ | No concurrency group |
| Permissions | ❌ | No explicit permissions |
| Matrix builds | ⚠️ | Only PHP 8.3, missing 8.2 |
### Recommendations
1. CRITICAL: Add PHPStan job — phpstan.neon exists
2. CRITICAL: Add Rector dry-run job — rector.php exists
3. HIGH: Add concurrency group to cancel redundant runs
4. HIGH: Add composer cache for faster installs
5. MEDIUM: Add security audit job (composer audit)
6. LOW: Add PHP 8.2 to test matrix
5.3 Fix Issues
AskUserQuestion: CI audit found issues. What should we do?
Options:
1. Fix all — Apply all recommendations
2. Fix critical only — Add missing jobs, skip configuration improvements
3. Show details — Explain each issue before deciding
If fixing:
- For missing jobs -> add new jobs to existing pipeline
- For configuration issues -> edit existing jobs
- Preserve existing structure, job names, and ordering conventions
- For GitHub Actions: edit in-place or add new workflow files
- For GitLab CI: edit
.gitlab-ci.ymlin-place
Step 6: Write Files
6.1 Generate Mode — Write Pipeline
GitHub Actions:
Bash: mkdir -p .github/workflows
Write .github/workflows/lint.yml # If linters/SA detected
Write .github/workflows/tests.yml # Always
Write .github/workflows/build.yml # If has_build_step
Write .github/workflows/security.yml # If WANT_SECURITY
Only create files for detected concerns. If only lint + tests — two files. If the project is trivially small (single lint + single test job) — a single ci.yml is acceptable.
GitLab CI:
Write .gitlab-ci.yml
GitLab CI uses a single .gitlab-ci.yml — stages and DAG (needs:) handle separation.
6.2 Enhance / Audit Mode — Update Existing
Edit existing files using the Edit tool. Preserve the original structure and only add/modify what's needed.
Step 7: Summary & Follow-Up
7.1 Display Summary
## CI Pipeline Generated
### Platform
GitHub Actions
### Files Created
| File | Purpose |
|------|---------|
| .github/workflows/lint.yml | code-style, static-analysis, rector |
| .github/workflows/tests.yml | phpunit (PHP 8.2, 8.3, 8.4) |
| .github/workflows/security.yml | composer audit |
### Features
- Composer caching via shivammathur/setup-php
- Concurrency groups (cancel redundant runs)
- Matrix builds for PHP 8.2, 8.3, 8.4
- Coverage upload as artifact
### Quick Start
# Trigger manually
gh workflow run ci.yml
# View runs
gh run list --workflow=ci.yml
7.2 Suggest Follow-Up Skills
AskUserQuestion: CI pipeline is ready. What's next?
Options:
1. Build automation — Run /ai-factory.build-automation to add CI targets to Makefile/Taskfile
2. Docker setup — Run /ai-factory.dockerize to containerize the project
3. Both
4. Done — Skip follow-ups