Przejdź do głównej zawartości

Wzorce pipeline CI/CD

Opanuj ciągłą integrację i wdrażanie z Cursor i Claude Code. Ten przewodnik obejmuje GitHub Actions, GitLab CI, pipeline Jenkins, automatyczne testowanie, skanowanie bezpieczeństwa i wzorce wdrażania produkcyjnego z pomocą AI.

  1. Inicjalizacja pipeline CI/CD

    Okno terminala
    # Generuj konfigurację CI/CD
    Agent: "Stwórz pipeline CI/CD z:
    - Etapami budowania i testowania
    - Skanowaniem bezpieczeństwa
    - Wdrażaniem wielośrodowiskowym
    - Automatycznym rollback
    - Integracją notyfikacji"
  2. Instalacja serwerów MCP CI/CD (opcjonalnie)

    Okno terminala
    # CircleCI
    claude mcp add circleci -- npx -y @circleci/mcp-server-circleci
    # Jenkins
    claude mcp add jenkins -- npx -y jenkins-mcp-server
    # GitHub (dla integracji Actions)
    claude mcp add --transport sse github https://api.githubcopilot.com/mcp/
  3. Konfiguracja reguł AI

    # .cursorrules lub CLAUDE.md
    Najlepsze praktyki CI/CD:
    - Używaj wersjonowania semantycznego
    - Implementuj ochronę gałęzi
    - Uruchamiaj testy równolegle
    - Cache'uj zależności
    - Używaj konfiguracji specyficznych dla środowiska
    - Przestrzegaj najlepszych praktyk bezpieczeństwa
    - Implementuj właściwe zarządzanie sekretami
# Prompt AI
Agent: "Stwórz przepływ pracy GitHub Actions z:
- Budową matrix dla wielu wersji
- Cache'owaniem zależności
- Skanowaniem bezpieczeństwa
- Budową i push Docker
- Wdrażaniem na staging/produkcję
- Możliwością rollback"
# .github/workflows/main.yml
name: Pipeline CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NODE_VERSION: '20.x'
jobs:
# Jakość kodu i bezpieczeństwo
quality:
name: Kontrole jakości kodu
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Pełna historia dla lepszej analizy
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run type checking
run: npm run type-check
- name: Check code formatting
run: npm run format:check
- name: Run security audit
run: npm audit --audit-level=moderate
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Testowanie
test:
name: Pakiet testów
runs-on: ubuntu-latest
needs: quality
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
test-suite: [unit, integration, e2e]
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run migrations
run: npm run db:migrate
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
- name: Run ${{ matrix.test-suite }} tests
run: npm run test:${{ matrix.test-suite }}
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
CI: true
- name: Upload coverage
if: matrix.test-suite == 'unit' && matrix.node-version == '20.x'
uses: codecov/codecov-action@v4
with:
file: ./coverage/lcov.info
flags: unittests
# Budowa i push obrazu Docker
build:
name: Budowa obrazu Docker
runs-on: ubuntu-latest
needs: test
permissions:
contents: read
packages: write
security-events: write
outputs:
image: ${{ steps.image.outputs.image }}
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.sha }}
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
format: spdx-json
output-file: sbom.spdx.json
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Output image
id: image
run: echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT
# Wdrażanie na staging
deploy-staging:
name: Wdrażanie na staging
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --name staging-cluster --region us-east-1
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/app app=${{ needs.build.outputs.image }} -n staging
kubectl rollout status deployment/app -n staging
- name: Run smoke tests
run: |
./scripts/smoke-test.sh https://staging.example.com
- name: Notify Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
channel: '#deployments'
text: 'Wdrażanie staging ${{ job.status }}'
# Wdrażanie na produkcję
deploy-production:
name: Wdrażanie na produkcję
runs-on: ubuntu-latest
needs: [build, deploy-staging]
if: github.event_name == 'release'
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_PROD_ROLE_ARN }}
aws-region: us-east-1
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --name production-cluster --region us-east-1
- name: Create deployment record
id: deployment
uses: actions/github-script@v7
with:
script: |
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
environment: 'production',
required_contexts: [],
production_environment: true,
auto_merge: false
});
return deployment.data.id;
- name: Blue-Green Deployment
run: |
# Wdrażanie na środowisko green
kubectl set image deployment/app-green app=${{ needs.build.outputs.image }} -n production
kubectl rollout status deployment/app-green -n production
# Uruchom kontrole zdrowia
./scripts/health-check.sh https://green.example.com
# Przełącz ruch
kubectl patch service app-service -n production -p '{"spec":{"selector":{"version":"green"}}}'
# Poczekaj i zweryfikuj
sleep 30
./scripts/verify-deployment.sh https://example.com
- name: Update deployment status
if: always()
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.result }},
state: '${{ job.status }}',
environment_url: 'https://example.com'
});
- name: Create release notes
uses: actions/github-script@v7
with:
script: |
const release = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: context.ref.replace('refs/tags/', '')
});
const newBody = release.data.body + '\n\n## Wdrażanie\n' +
`- Obraz: \`${{ needs.build.outputs.image }}\`\n` +
`- Wdrożone o: ${new Date().toISOString()}\n` +
`- Wdrożone przez: @${context.actor}`;
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
body: newBody
});
.github/workflows/reusable-node-ci.yml
# Prompt AI: "Stwórz przepływ pracy wielokrotnego użytku dla projektów Node.js"
name: Wielokrotnego użytku CI Node.js
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20.x'
run-e2e:
required: false
type: boolean
default: false
deploy-environment:
required: false
type: string
secrets:
NPM_TOKEN:
required: false
SONAR_TOKEN:
required: false
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Run tests
run: |
npm run test:unit
npm run test:integration
${{ inputs.run-e2e && 'npm run test:e2e' || '' }}
- name: Build
run: npm run build
- name: SonarCloud Scan
if: secrets.SONAR_TOKEN != ''
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ github.token }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Użycie w głównym workflow
jobs:
test:
uses: ./.github/workflows/reusable-node-ci.yml
with:
node-version: '20.x'
run-e2e: true
secrets: inherit
# Prompt AI
Agent: "Stwórz pipeline GitLab CI z:
- Pipelineiem wieloetapowym
- Docker in Docker
- Testowaniem równoległym
- Skanowaniem bezpieczeństwa
- Aplikacjami review
- Integracją Auto DevOps"
# .gitlab-ci.yml
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
POSTGRES_DB: test_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST_AUTH_METHOD: trust
stages:
- build
- test
- security
- deploy
- cleanup
# Szablony
.docker:
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
.deploy:
image: bitnami/kubectl:latest
before_script:
- kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true
- kubectl config set-credentials admin --token="$KUBE_TOKEN"
- kubectl config set-context default --cluster=k8s --user=admin
- kubectl config use-context default
# Etap budowy
build:
extends: .docker
stage: build
script:
- docker build --pull -t $CONTAINER_TEST_IMAGE .
- docker push $CONTAINER_TEST_IMAGE
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
docker push $CONTAINER_RELEASE_IMAGE
fi
rules:
- if: $CI_COMMIT_BRANCH
- if: $CI_MERGE_REQUEST_IID
# Etap testowania
test:unit:
stage: test
image: node:20-alpine
services:
- postgres:16-alpine
- redis:7-alpine
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline
script:
- npm run test:unit -- --coverage
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
junit: junit.xml
paths:
- coverage/
expire_in: 1 week
parallel:
matrix:
- NODE_VERSION: ["18", "20", "22"]
test:integration:
stage: test
image: $CONTAINER_TEST_IMAGE
services:
- postgres:16-alpine
- redis:7-alpine
script:
- npm run test:integration
needs: ["build"]
test:e2e:
stage: test
image: mcr.microsoft.com/playwright:focal
services:
- name: $CONTAINER_TEST_IMAGE
alias: app
variables:
APP_URL: http://app:3000
script:
- npm ci
- npx playwright install
- npm run test:e2e
artifacts:
when: always
paths:
- playwright-report/
- test-results/
expire_in: 1 week
needs: ["build"]
# Etap bezpieczeństwa
security:sast:
stage: security
include:
- template: Security/SAST.gitlab-ci.yml
security:dependency:
stage: security
include:
- template: Security/Dependency-Scanning.gitlab-ci.yml
security:container:
extends: .docker
stage: security
script:
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock
aquasec/trivy image --severity HIGH,CRITICAL
--exit-code 1 $CONTAINER_TEST_IMAGE
needs: ["build"]
allow_failure: true
security:license:
stage: security
include:
- template: Security/License-Scanning.gitlab-ci.yml
# Etap wdrażania
deploy:review:
extends: .deploy
stage: deploy
script:
- kubectl create namespace review-$CI_MERGE_REQUEST_IID || true
- |
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: review-$CI_MERGE_REQUEST_IID
spec:
replicas: 1
selector:
matchLabels:
app: review-app
template:
metadata:
labels:
app: review-app
spec:
containers:
- name: app
image: $CONTAINER_TEST_IMAGE
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: review
---
apiVersion: v1
kind: Service
metadata:
name: app-service
namespace: review-$CI_MERGE_REQUEST_IID
spec:
selector:
app: review-app
ports:
- port: 80
targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: review-$CI_MERGE_REQUEST_IID
spec:
rules:
- host: review-$CI_MERGE_REQUEST_IID.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
EOF
environment:
name: review/$CI_MERGE_REQUEST_IID
url: https://review-$CI_MERGE_REQUEST_IID.example.com
on_stop: stop:review
auto_stop_in: 1 week
rules:
- if: $CI_MERGE_REQUEST_IID
needs: ["build", "test:unit", "test:integration"]
stop:review:
extends: .deploy
stage: cleanup
script:
- kubectl delete namespace review-$CI_MERGE_REQUEST_IID
environment:
name: review/$CI_MERGE_REQUEST_IID
action: stop
rules:
- if: $CI_MERGE_REQUEST_IID
when: manual
deploy:staging:
extends: .deploy
stage: deploy
script:
- kubectl set image deployment/app app=$CONTAINER_TEST_IMAGE -n staging
- kubectl rollout status deployment/app -n staging
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
needs: ["build", "test:unit", "test:integration", "security:container"]
deploy:production:
extends: .deploy
stage: deploy
script:
- kubectl set image deployment/app app=$CONTAINER_RELEASE_IMAGE -n production
- kubectl rollout status deployment/app -n production
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
needs: ["build", "test:unit", "test:integration", "security:container"]
# Testowanie wydajności
performance:
stage: test
image: loadimpact/k6:latest
script:
- k6 run tests/performance/load-test.js
artifacts:
reports:
performance: performance.json
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
// Prompt AI
Agent: "Stwórz pipeline Jenkins z:
- Etapami równoległymi
- Agentami Docker
- Bibliotekami współdzielonymi
- Kompatybilnością z Blue Ocean
- Notyfikacjami Slack"
// Jenkinsfile
@Library('shared-library@main') _
pipeline {
agent none
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 1, unit: 'HOURS')
timestamps()
ansiColor('xterm')
parallelsAlwaysFailFast()
}
environment {
DOCKER_REGISTRY = 'registry.example.com'
DOCKER_IMAGE = "${DOCKER_REGISTRY}/app"
SLACK_CHANNEL = '#deployments'
SONAR_HOST = credentials('sonar-host')
SONAR_TOKEN = credentials('sonar-token')
}
stages {
stage('Checkout') {
agent any
steps {
checkout scm
script {
env.GIT_COMMIT_SHORT = sh(
script: "git rev-parse --short HEAD",
returnStdout: true
).trim()
env.VERSION = "${BUILD_NUMBER}-${GIT_COMMIT_SHORT}"
}
}
}
stage('Bramy jakości') {
parallel {
stage('Linting') {
agent {
docker {
image 'node:20-alpine'
args '-v $HOME/.npm:/root/.npm'
}
}
steps {
sh 'npm ci'
sh 'npm run lint'
}
}
stage('Skanowanie bezpieczeństwa') {
agent any
steps {
sh 'npm audit --audit-level=moderate'
dependencyCheck additionalArguments: '--scan .',
odcInstallation: 'dependency-check'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'dependency-check-report',
reportFiles: 'dependency-check-report.html',
reportName: 'Raport kontroli zależności'
])
}
}
stage('Analiza SonarQube') {
agent any
steps {
withSonarQubeEnv('SonarQube') {
sh """
sonar-scanner \
-Dsonar.projectKey=my-app \
-Dsonar.sources=. \
-Dsonar.host.url=$SONAR_HOST \
-Dsonar.login=$SONAR_TOKEN
"""
}
timeout(time: 10, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}
}
stage('Test') {
matrix {
axes {
axis {
name 'NODE_VERSION'
values '18', '20', '22'
}
axis {
name 'TEST_SUITE'
values 'unit', 'integration', 'e2e'
}
}
excludes {
exclude {
axis {
name 'NODE_VERSION'
values '18', '22'
}
axis {
name 'TEST_SUITE'
values 'e2e'
}
}
}
agent {
docker {
image "node:${NODE_VERSION}-alpine"
args '-v $HOME/.npm:/root/.npm'
}
}
stages {
stage('Uruchom testy') {
steps {
sh 'npm ci'
sh "npm run test:${TEST_SUITE}"
}
post {
always {
junit '**/test-results/*.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: "Raport pokrycia - ${NODE_VERSION} - ${TEST_SUITE}"
])
}
}
}
}
}
}
stage('Budowa obrazu Docker') {
agent any
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
def customImage = docker.build(
"${DOCKER_IMAGE}:${VERSION}",
"--build-arg VERSION=${VERSION} ."
)
customImage.push()
customImage.push('latest')
}
}
}
}
stage('Wdrażanie') {
parallel {
stage('Wdrażanie na staging') {
when {
branch 'develop'
}
agent any
steps {
deployToKubernetes(
environment: 'staging',
image: "${DOCKER_IMAGE}:${VERSION}",
namespace: 'staging'
)
}
post {
success {
runSmokeTests('https://staging.example.com')
}
}
}
stage('Wdrażanie na produkcję') {
when {
branch 'main'
}
agent any
input {
message 'Wdrożyć na produkcję?'
ok 'Wdróż'
submitter 'admin,lead-dev'
parameters {
choice(
name: 'DEPLOYMENT_TYPE',
choices: ['Blue-Green', 'Canary', 'Rolling'],
description: 'Wybierz strategię wdrażania'
)
}
}
steps {
script {
switch(params.DEPLOYMENT_TYPE) {
case 'Blue-Green':
blueGreenDeploy(
image: "${DOCKER_IMAGE}:${VERSION}",
namespace: 'production'
)
break
case 'Canary':
canaryDeploy(
image: "${DOCKER_IMAGE}:${VERSION}",
namespace: 'production',
percentage: 10
)
break
default:
deployToKubernetes(
environment: 'production',
image: "${DOCKER_IMAGE}:${VERSION}",
namespace: 'production'
)
}
}
}
}
}
}
}
post {
always {
node('any') {
cleanWs()
}
}
success {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'good',
message: "✅ Budowa udana: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|Otwórz>)"
)
}
failure {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'danger',
message: "❌ Budowa nieudana: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|Otwórz>)"
)
}
unstable {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'warning',
message: "⚠️ Budowa niestabilna: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|Otwórz>)"
)
}
}
}
# Prompt AI: "Zintegruj skanowanie bezpieczeństwa w całym CI/CD"
# security-scan.yml (GitHub Actions)
name: Skanowanie bezpieczeństwa
on:
pull_request:
push:
branches: [main, develop]
schedule:
- cron: '0 0 * * *' # Skanowanie codzienne
jobs:
# Statyczne testowanie bezpieczeństwa aplikacji (SAST)
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/r2c-best-practices
- name: Run CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
languages: javascript, typescript
- name: ESLint Security Plugin
run: |
npm ci
npx eslint . --ext .js,.ts --plugin security
# Analiza składu oprogramowania (SCA)
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'my-app'
path: '.'
format: 'HTML'
- name: License Compliance
run: |
npm ci
npx license-checker --production --summary
# Bezpieczeństwo kontenerów
container-security:
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t security-scan:${{ github.sha }} .
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: security-scan:${{ github.sha }}
format: 'table'
exit-code: '1'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
- name: Run Grype
uses: anchore/scan-action@v3
with:
image: security-scan:${{ github.sha }}
fail-build: true
severity-cutoff: high
# Bezpieczeństwo infrastruktury jako kod
iac-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkov Scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: all
output_format: sarif
output_file_path: reports/checkov.sarif
- name: Terraform Security Scan
uses: triat/terraform-security-scan@v3
with:
tfsec_actions_comment: true
tfsec_actions_fmt_comment: true
# Wykrywanie sekretów
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect Secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
- name: GitLeaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Prompt AI
Agent: "Stwórz pipeline wdrażania progresywnego z:
- Feature flags
- Releasami canary
- Testami A/B
- Automatycznym rollback
- Integracją monitorowania"
# deployment-strategy.yml
name: Wdrażanie progresywne
on:
workflow_dispatch:
inputs:
environment:
description: 'Środowisko docelowe'
required: true
type: choice
options:
- staging
- production
strategy:
description: 'Strategia wdrażania'
required: true
type: choice
options:
- rolling
- blue-green
- canary
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup deployment
id: setup
run: |
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
echo "namespace=${{ inputs.environment }}" >> $GITHUB_OUTPUT
echo "strategy=${{ inputs.strategy }}" >> $GITHUB_OUTPUT
- name: Configure kubectl
uses: azure/setup-kubectl@v3
- name: Rolling Update
if: inputs.strategy == 'rolling'
run: |
kubectl set image deployment/app \
app=${{ env.IMAGE }}:${{ github.sha }} \
-n ${{ steps.setup.outputs.namespace }}
kubectl rollout status deployment/app \
-n ${{ steps.setup.outputs.namespace }}
- name: Blue-Green Deployment
if: inputs.strategy == 'blue-green'
run: |
# Wdrażanie na nieaktywne środowisko
ACTIVE=$(kubectl get service app-service -n ${{ steps.setup.outputs.namespace }} \
-o jsonpath='{.spec.selector.color}')
INACTIVE=$([[ "$ACTIVE" == "blue" ]] && echo "green" || echo "blue")
kubectl set image deployment/app-$INACTIVE \
app=${{ env.IMAGE }}:${{ github.sha }} \
-n ${{ steps.setup.outputs.namespace }}
kubectl rollout status deployment/app-$INACTIVE \
-n ${{ steps.setup.outputs.namespace }}
# Uruchom kontrole zdrowia
./scripts/health-check.sh $INACTIVE
# Przełącz ruch
kubectl patch service app-service \
-n ${{ steps.setup.outputs.namespace }} \
-p '{"spec":{"selector":{"color":"'$INACTIVE'"}}}'
- name: Canary Deployment
if: inputs.strategy == 'canary'
run: |
# Wdrażanie canary
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-canary
namespace: ${{ steps.setup.outputs.namespace }}
spec:
replicas: 1
selector:
matchLabels:
app: app
version: canary
template:
metadata:
labels:
app: app
version: canary
spec:
containers:
- name: app
image: ${{ env.IMAGE }}:${{ github.sha }}
EOF
# Konfiguracja podziału ruchu (10% na canary)
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-vs
namespace: ${{ steps.setup.outputs.namespace }}
spec:
hosts:
- app
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: app
subset: canary
weight: 100
- route:
- destination:
host: app
subset: stable
weight: 90
- destination:
host: app
subset: canary
weight: 10
EOF
# Monitoruj metryki
./scripts/monitor-canary.sh
# Promuj lub rollback na podstawie metryk
if ./scripts/check-canary-metrics.sh; then
echo "Promowanie canary do stable"
kubectl set image deployment/app \
app=${{ env.IMAGE }}:${{ github.sha }} \
-n ${{ steps.setup.outputs.namespace }}
kubectl delete deployment app-canary \
-n ${{ steps.setup.outputs.namespace }}
else
echo "Rollback canary"
kubectl delete deployment app-canary \
-n ${{ steps.setup.outputs.namespace }}
exit 1
fi
.github/workflows/ai-assisted-ci.yml
# Prompt AI: "Zintegruj Claude Code z pipelineiem CI/CD"
name: CI/CD wspomagane AI
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude-assistance:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Claude Code
run: |
npm install -g @anthropic-ai/claude-code
claude --version
- name: Configure Claude
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude config set apiKey "$ANTHROPIC_API_KEY"
- name: Process Request
id: claude
run: |
COMMENT="${{ github.event.comment.body }}"
TASK=${COMMENT#"@claude "}
# Uruchom Claude z zadaniem
claude --yolo --max-turns 10 "$TASK" > claude-output.txt
# Wyodrębnij zmiany
git diff > changes.diff
- name: Create PR with Changes
if: steps.claude.outputs.has-changes
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "AI: ${{ github.event.comment.body }}"
title: "Asystent AI: Implementacja żądanych zmian"
body: |
Ten PR został stworzony przez Claude Code w odpowiedzi na:
> ${{ github.event.comment.body }}
## Wprowadzone zmiany
\`\`\`diff
$(cat changes.diff)
\`\`\`
## Podsumowanie Claude
$(cat claude-output.txt)
branch: ai-changes-${{ github.run_id }}

Najlepsze praktyki CI/CD

  1. Wersjonuj wszystko - Traktuj konfiguracje CI/CD jako kod
  2. Fail fast - Uruchamiaj szybkie kontrole najpierw
  3. Cache agresywnie - Przyspieszaj budowy dzięki sprytemu cache’owaniu
  4. Równoległość - Uruchamiaj niezależne zadania równocześnie
  5. Bezpieczeństwo na pierwszym miejscu - Skanuj wcześnie i często
  6. Monitoruj wszystko - Śledź metryki i awarie
# AI: "Optymalizuj cache'owanie CI/CD"
# GitHub Actions
- uses: actions/cache@v3
with:
path: |
~/.npm
~/.cache
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# GitLab CI
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
policy: pull-push
Okno terminala
# Prompt AI: "Generuj komendy debugowania CI/CD"
# Debugowanie GitHub Actions
- name: Debug Context
run: |
echo "Event: ${{ github.event_name }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
echo "Workflow: ${{ github.workflow }}"
echo "Run ID: ${{ github.run_id }}"
echo "Run Number: ${{ github.run_number }}"
# Włącz debug logging
env:
ACTIONS_RUNNER_DEBUG: true
ACTIONS_STEP_DEBUG: true
# Debugowanie GitLab CI
debug_job:
script:
- echo "CI_COMMIT_SHA=$CI_COMMIT_SHA"
- echo "CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME"
- echo "CI_PIPELINE_ID=$CI_PIPELINE_ID"
- echo "CI_JOB_ID=$CI_JOB_ID"
- env | grep CI_
# Debugowanie Jenkins
sh 'printenv | sort'
sh 'echo "Workspace: $WORKSPACE"'
sh 'echo "Build URL: $BUILD_URL"'