Files
asepharyana-hub-guide/skills/ci-cd/SKILL.md
T
asepharyana 5780d94ccf feat(hub-guide): language-agnostic auto-trigger across all skills
- Add principle 27: 'Language-Agnostic Auto-Trigger' to engineering-principles
- Remove hardcoded English-only keyword lists from all 26 skill descriptions
- Replace with concept-based detection: triggers from code context, project files,
  and file types regardless of spoken language
- Update detect-project.sh hook output with language-agnostic message
2026-07-25 12:59:43 +07:00

169 lines
4.8 KiB
Markdown

---
name: ci-cd
description: CI/CD best practices — GitHub Actions, pipeline design, Docker build + push, deployment workflows, testing in CI, and environment management. Use when designing CI/CD pipelines, debugging workflow failures. Triggers from project files and configuration, not just keyword matching."
---
# CI/CD Best Practices
## Pipeline Design Principles
1. **Fast feedback** — failing fast is better than failing comprehensively.
2. **Deterministic** — same commit = same result, same artifacts.
3. **Immutable artifacts** — build once, promote through environments.
4. **Idempotent deployments** — deploying the same artifact again produces the same result.
5. **Security gates** — scan dependencies, secrets, and code before production.
## GitHub Actions Structure
```
.github/workflows/
├── lint.yml # Quick checks — runs in <2 min
├── docker-build-push.yml # Build + push images
├── deploy-docker.yml # Deploy to VPS
├── security.yml # CodeQL + dependency scan
├── update-submodule.yml # Update submodule pointer
└── ...other workflows
```
### Workflow Patterns
**Lint (fast — gates everything else)**
```yaml
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run ci # Biome lint + format
```
**Build + Push (on push to main)**
```yaml
name: Build Docker
on:
push:
branches: [main]
paths: ['apps/**', 'infra/**']
repository_dispatch:
types: [build]
jobs:
build:
strategy:
matrix:
service: [scraper, hub]
steps:
- uses: actions/checkout@v4
- run: |
docker build \
-f infra/docker/${{ matrix.service }}.Dockerfile \
-t ghcr.io/.../${{ matrix.service }}:sha-${{ github.sha }} \
-t ghcr.io/.../${{ matrix.service }}:latest \
.
- run: docker push --all-tags ghcr.io/.../${{ matrix.service }}
```
**Deploy (after build)**
```yaml
name: Deploy
on:
workflow_run:
workflows: ['Build Docker']
types: [completed]
push:
branches: [main]
paths: ['infra/**']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd ${{ secrets.VPS_TARGET_DIR }}
docker compose pull <service>
docker compose up -d <service>
```
## Key Patterns
### Matrix Builds
```yaml
jobs:
build:
strategy:
matrix:
service: [scraper, hub, api]
fail-fast: false # Let others complete even if one fails
```
### Conditional Jobs
```yaml
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
```
### Caching
```yaml
- uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
```
### Secrets
```yaml
# All secrets in GitHub Secrets, never in code
secrets:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
VPS_HOST: ${{ secrets.VPS_HOST }}
ENV_FILE_PRODUCTION: ${{ secrets.ENV_FILE_PRODUCTION }}
```
## Environment Strategy
| Environment | Purpose | Deploy Method |
|-------------|---------|---------------|
| `development` | Local dev | Manual `docker compose up` |
| `staging` | Pre-production | Auto-deploy from PR branches |
| `production` | Live | Auto-deploy from main |
## Quality Gates (run order)
1. **Lint** (<1 min) — Biome/Ruff/clippy + format check
2. **Type check** (<2 min) — tsc/pyright/cargo check
3. **Unit tests** (<3 min) — fast, no external deps
4. **Build** (<5 min) — compile/transpile, build Docker images
5. **Integration tests** (<10 min) — with DB, external services
6. **Security scan** (<5 min) — CodeQL, dependency audit, secret scan
7. **Deploy** (<2 min) — SSH, pull, restart
## Deployment (this repo's pattern)
1. SSH to VPS (`orangevps`)
2. Pull latest images from GHCR
3. Restart specific container (not all)
4. Health check after restart
5. Rollback if health check fails
## Anti-patterns
-**Large CI configs** — extract repeated blocks into actions. Use YAML anchors.
-**Building in deployment** — build once in CI, deploy artifact. Avoid `docker compose build` on production.
-**Hardcoded values** — use env vars, secrets, and GitHub variables.
-**Skipping lint** — lint should run first and gate everything.
-**No caching** — each run fetches deps fresh = 2x+ slower.
-**Manual deployment steps** — automate everything. If it's manual, it will be wrong.
-**Deploying untested artifacts** — run tests before build, not after.