CI/CD Pipelines: Multi-Platform Production Infrastructure
Write, review, and architect CI/CD pipelines across GitHub Actions, GitLab CI/CD, Forgejo Actions, Gitea Actions, and Woodpecker. The goal is secure, fast, auditable pipelines that satisfy both engineering needs and compliance requirements (PCI-DSS 4.0).
Target versions: May 2026 snapshot. Read references/target-versions.md before
pinning forge, runner, CI, or supply-chain tool versions.
This skill covers workflow design, security, compliance, cross-platform migration, runners, dependency updates, scanning, review gates, and rollout order.
When to use
- Writing or reviewing CI/CD pipeline configs (GitHub/Forgejo/Gitea Actions,
.gitlab-ci.yml,.woodpecker/*.yaml) - Designing pipeline architecture (stages, parallelism, caching, deployment strategies)
- Hardening pipelines against supply chain attacks (SHA pinning, image signing, provenance)
- Setting up security scanning in CI (SAST, SCA, container scanning, secret detection)
- Configuring runners (install, register, executor choice, hardening) - see
references/runners.md - Setting up caching strategies or artifact management
- PCI-DSS 4.0 compliance for CI/CD (Req 6.2.1, 6.2.4, 6.3.2, 6.4.2, 6.5.3)
- Migrating pipelines between platforms (GitLab -> GitHub, GitHub -> Forgejo)
- Troubleshooting failed pipelines, flaky jobs, or runner issues
When NOT to use
- Kubernetes manifests, Helm charts, cluster architecture - use kubernetes
- Dockerfiles, Compose stacks, container image optimization - use docker
- Terraform/OpenTofu infrastructure-as-code - use terraform
- Ansible playbooks, configuration management - use ansible
- Security audits of application code (SAST findings, auth bugs) - use security-audit
- Code review of pipeline-adjacent code (the app itself) - use code-review
- The code-review skill has a
cicd-pipelines.mdreference for bug patterns in existing pipelines. This skill is for writing and architecting pipelines.
AI Self-Check
AI tools consistently produce the same CI/CD mistakes. Before returning any generated pipeline config, verify against this list.
Review mode: if auditing an existing pipeline rather than generating one, invert this checklist - each item that fails is a finding. Work through the list top-to-bottom and report every failure with file and line reference.
- SHA pinning: all third-party actions/images pinned to full commit SHA or digest, not mutable tags. Add
# vX.Y.Zcomment for readability. - Permissions: explicit
permissions:block on every GitHub Actions workflow (read-only default). GitLab: protected variables scoped correctly. - No secrets in config: no hardcoded tokens, passwords, or API keys. Use CI/CD secret variables or vault integration.
- No
latesttags: runner images, tool images, and base images pinned to specific versions or SHA256 digests. - Caching strategy: dependencies cached correctly (lockfile-based keys), build outputs use artifacts (not cache).
- Fail-fast security: SAST, dependency scanning, and secret detection run early (not after deployment).
- Manual gates for production: production deployments require explicit approval (not auto-deploy on merge).
- SBOM generation: release pipelines generate and attach SBOMs (SPDX or CycloneDX). Required for PCI-DSS 4.0.
- Minimal scope: jobs have minimum required permissions, access only needed secrets, and run only needed steps.
- No
allow_failurewithout justification: if a job can fail, explain why in a comment. - Version pinning on tools:
node:22, notnode:lts.python:3.13, notpython:3. Specific versions prevent silent breakage. - Trigger scoping:
on: pushwithout branch/path filters runs on every push to every branch - scope tobranches: [main]and/orpaths:filters. Same for GitLab:rules:withifconditions, not bareonly: [pushes]. - No expression injection (GitHub Actions):
${{ }}expressions never used directly inrun:blocks. Assign toenv:first.github.event.*is attacker-controlled. Avoidgithub.ref_namein security-sensitive contexts (injectable via crafted tag/branch names). - Self-hosted runners ephemeral on public/untrusted repos: non-ephemeral shell runners on repos that accept outside PRs is the top self-hosted-runner compromise vector. Verify
--ephemeral(GitHub, Gitea) or capacity-based single-job runners (Forgejo) + approval gates for outside contributors. Seereferences/runners.md. - Docker socket mount scope:
/var/run/docker.sockmounted into a job gives it root on the host. Only acceptable for trusted internal pipelines. Public/shared runners need DinD sidecar or rootless buildkit instead. - Scan gate has a baseline, not a blanket block: container/IaC/SAST scanners introduced with
exit-code 1and zero suppression always get disabled. Use the ratchet pattern (non-blocking -> baseline -> block new only) fromreferences/best-practices.md. - Ignore-list entries have expiry dates: every
.trivyignore,.grype.yaml, Dependabotignore, or RenovateignoreDepsentry includes a comment with revisit date + owner. No dates = zombie tech debt. - Lockfiles committed:
package-lock.json,bun.lock,Cargo.lock,go.sum,uv.lockbelong in version control for applications. Manifest-only commits break reproducibility. - Auto-merge gated on tests, not just lint: Dependabot/Renovate auto-merge without test coverage of the changed area is a supply-chain shortcut.
- Current source checked: dated versions, CLI flags, API names, and support windows are verified against primary docs before repeating them
- Hidden state identified: local config, credentials, caches, contexts, branches, cluster targets, or previous runs are made explicit before acting
- Verification is real: final checks exercise the actual runtime, parser, service, or integration point instead of only linting prose or happy paths
- Routing overlap checked: overlapping skills, trigger terms, and "When NOT to use" boundaries are checked before returning guidance
- Spec claims verified: claims about tool behavior, output contracts, or repo conventions are checked against current docs, scripts, or skill files
- Runner trust checked: workflow advice distinguishes hosted, self-hosted, fork, and protected-branch execution
- Mutable references controlled: actions, images, includes, and templates are pinned where supply-chain risk matters
Performance
- Key caches by lockfiles and toolchain versions; avoid broad caches that restore stale dependencies.
- Split quick lint/unit gates from slow integration, image, and deployment jobs.
- Use path filters and matrix limits to keep monorepo pipelines proportional to the change.
Best Practices
- Use OIDC or short-lived federation for cloud deploys instead of long-lived static secrets.
- Keep pull-request workflows from forks read-only unless explicitly isolated.
- Generate provenance or attestations for release artifacts where the forge supports it.
Workflow
Step 1: Identify the platform
| Signal | Platform |
|---|---|
.github/workflows/*.yml | GitHub Actions |
.gitlab-ci.yml | GitLab CI/CD |
.forgejo/workflows/*.yml | Forgejo Actions |
.gitea/workflows/*.yml | Gitea Actions |
.woodpecker/*.yaml or .woodpecker.yaml | Woodpecker (Gitea/Forgejo) |
User says "work" / "gitlab" / glab | GitLab CI/CD |
User says "home" / "forgejo" / fj | Forgejo Actions |
| User says "gitea" | Gitea Actions (or Woodpecker if 1.20 or older) |
User says "github" / "ghcr" / gh | GitHub Actions |
If unclear, ask. The platforms have significant differences despite surface similarity.
Step 2: Determine the domain
- "Create a CI pipeline for my project" -> Workflow d