Generate CI/CD pipeline (GitHub Actions / GitLab CI) with linting, static analysis, tests, security. Use when user says "ci", "setup ci", "github actions", "gitlab ci", "pipeline".
77%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./skills/aif-ci/SKILL.mdBefore mode routing or work, read existing files relative to the current project root in this order (reuse them if already loaded):
.ai-factory/skill-context/global-skill-context.md — all bundled skills..ai-factory/skill-context/aif-ci/SKILL.md — this skill.Missing files are skipped; an existing unreadable file stops dependent work with its path.
Precedence: individual > build (when applicable) > global > skill defaults; retain non-conflicting instructions and check all outputs against the effective rules.
These layers customize workflow; they do not replace the project's rules/architecture hierarchy or relax explicit requirements, ownership, scope, safety/privacy, validation, or approval boundaries. Report conflicts with those boundaries instead of overriding them.
global is a project-local layer, not home-directory configuration. Custom/extension skills do not automatically use it.
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 |
Read the project description if available:
Read .ai-factory/DESCRIPTION.mdStore project context for later steps. If absent, Step 2 detects everything.
Read .ai-factory/skill-context/aif-ci/SKILL.md — MANDATORY if the file exists.
This file contains project-specific rules accumulated by /aif-evolve from patches,
codebase conventions, and tech-stack analysis. These rules are tailored to the current project.
How to apply skill-context rules:
Enforcement: After generating any output artifact, verify it against all skill-context rules. If any rule is violated — fix the output before presenting it to the user.
Glob: .github/workflows/*.yml, .github/workflows/*.yaml, .gitlab-ci.yml, .circleci/config.yml, Jenkinsfile, .travis.yml, bitbucket-pipelines.ymlClassify found files:
HAS_GITHUB_ACTIONS: .github/workflows/ contains YAML filesHAS_GITLAB_CI: .gitlab-ci.yml existsHAS_OTHER_CI: CircleCI, Jenkins, Travis, or Bitbucket detectedIf $ARGUMENTS contains --enhance -> set MODE = "enhance" regardless.
Path A: No CI config exists (!HAS_GITHUB_ACTIONS && !HAS_GITLAB_CI && !HAS_OTHER_CI):
MODE = "generate"Path B: CI config exists but is incomplete (e.g., has only tests, no linting):
MODE = "enhance"EXISTING_CONTENTPath C: Full CI setup (has linting + tests + static analysis):
MODE = "audit"EXISTING_CONTENTDetermine 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.ymlAsk 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 testsStore choices:
PLATFORM: github | gitlabWANT_SECURITY: booleanWANT_COVERAGE: booleanWANT_MATRIX: booleanRead all existing CI files and store as EXISTING_CONTENT:
.github/workflows/*.yml files.gitlab-ci.ymlinclude: directives)Determine PLATFORM from existing files.
Scan the project thoroughly — every decision in the generated pipeline depends on this profile.
| 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) |
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.
| 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.
Detect project tools by scanning config files and dependencies. For the complete tool-to-command mapping → read references/TOOL-COMMANDS.md
Categories: Linters & Formatters (PHP-CS-Fixer, ESLint, Prettier, Biome, Ruff, golangci-lint, clippy, Checkstyle), Static Analysis (PHPStan, Psalm, Rector, mypy, tsc), Test Frameworks (PHPUnit, Pest, Jest, Vitest, pytest, go test, cargo test) with coverage flags, Security Audit (composer audit, npm audit, pip-audit, govulncheck, cargo audit).
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.ymlIf services are needed, they will be configured in the CI pipeline as service containers.
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 | — |
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/)Read skills/ci/references/BEST-PRACTICES.mdSelect 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>.ymlUsing the PROJECT_PROFILE, best practices, and template as a base, generate a customized CI pipeline.
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:
security-events: writetests but not security)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: readPer-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:
shivammathur/setup-php@v2 with tools: parameteractions/setup-node@v4 with cache: parameterastral-sh/setup-uv@v5 (if uv) or actions/setup-python@v5 (if pip)actions/setup-go@v5 (auto-caches)dtolnay/rust-toolchain@stable + Swatinem/rust-cache@v2actions/setup-java@v4 with cache: parameterfail-fast: false in matrix buildsWANT_COVERAGEMatrix 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-cs-fixer check + other lint -> code-style jobeslint + prettier -> lint job. Biome replaces BOTH ESLint and Prettier — if Biome is detected, use only npx biome check . in a single lint jobruff check + ruff format --check -> lint job (Ruff handles both)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.
Use the templates in templates/github/ and templates/gitlab/ as a base for generating workflow files. Follow the header pattern (name, on, concurrency, permissions) and per-file job organization described above.
Output file: .gitlab-ci.yml
For GitLab-specific pipeline structure, cache strategy, report format integration, and language-specific patterns → read references/GITLAB-PATTERNS.md
Pipeline stages: install → lint → test → build → security
If services_needed is not empty, add service containers to the test job.
For GitHub Actions and GitLab CI service container syntax → read references/SERVICE-CONTAINERS.md
Verify generated pipeline before writing:
Correctness:
Best practices:
concurrency group set (GitHub Actions)permissions: contents: read set (GitHub Actions)interruptible: true set (GitLab CI)workflow.rules defined (GitLab CI)needs)fail-fast: false on matrix buildsNo over-engineering:
When MODE = "enhance" or MODE = "audit", analyze EXISTING_CONTENT against the project profile and best practices.
Compare existing pipeline against PROJECT_PROFILE:
Missing jobs:
Configuration issues:
actions-rs instead of dtolnay/rust-toolchain)?fail-fast: false on matrix?policy: pull-push on all GitLab jobs instead of pull on non-install jobs?Missing features:
workflow_dispatch trigger (GitHub Actions)?For audit report format, fix flow options, and display templates → read references/AUDIT-REPORT.md
Present results as tables with ✅/❌/⚠️ per check. Categorize recommendations by severity (CRITICAL, HIGH, MEDIUM, LOW). Ask user to choose: fix all, fix critical only, or show details first.
If fixing: preserve existing structure, job names, and ordering conventions.
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_SECURITYOnly 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.ymlGitLab CI uses a single .gitlab-ci.yml — stages and DAG (needs:) handle separation.
Edit existing files using the Edit tool. Preserve the original structure and only add/modify what's needed.
Display summary using format from references/AUDIT-REPORT.md (Summary Display Template section). Show platform, files created, features, and quick start commands.
Suggest: /aif-build-automation for CI targets in Makefile/Taskfile, /aif-dockerize for containerization.
When /aif-quality-setup hands off a selected setup, reuse its native check commands,
exact repository/component CWD, runtime versions, coverage and blocking policy.
Run full selected checks without autofix in CI. Inspect existing CI before changing
it; do not regenerate native tool configs or run setup again. Discovery of a command
does not execute, verify or authorize it. The CI-only ownership below still applies.
.github/workflows/* and .gitlab-ci.yml.config.yaml.lee-to/ai-factory
9771b77
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.