Back openDesk Edu for a sovereign, open-source education â every vote counts.
Vote nowSave products you love by clicking the heart icon.
Kurzreferenz fĂŒr Ansible-Automatisierung, Playbooks, Module und Best Practices
Ein praktischer Quick-Reference-Guide fĂŒr die zwei populĂ€rsten CI/CD-Plattformen. Er behandelt Workflow-Syntax, Trigger, Secrets, Artifacts, Caching, Matrix-Builds sowie praxisnahe Deployment-Patterns fĂŒr GitHub Actions und GitLab CI.
GitHub Actions ist die integrierte CI/CD-Plattform fĂŒr GitHub-Repositories. Workflows werden in YAML-Dateien unter .github/workflows/ definiert und durch Repository-Events, ZeitplĂ€ne oder manuellen Dispatch ausgelöst.
Jeder Workflow befindet sich in .github/workflows/<name>.yml und benötigt einen name, einen on (Trigger) sowie mindestens einen job mit steps.
name: CI Pipeline
# Triggers â what starts this workflow
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1" # Every Monday at 06:00 UTC
workflow_dispatch: # Manual trigger from GitHub UI
# Global environment variables
env:
NODE_VERSION: "20"
REGISTRY: ghcr.io
jobs:
build-and-test:
name: Build & Test
runs-on: ubuntu-latest
timeout-minutes: 15
# Job-level environment variables
env:
APP_DIR: ./apps/web
# Job-level permissions
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- 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 linter
run: npm run lint
- name: Run tests
run: npm run test -- --coverage
- name: Build application
run: npm run build
Steuern Sie den Zeitpunkt der Workflow-AusfĂŒhrung mit feingranularen Event-Filtern.
on:
# Push auf spezifische Branches oder Tags
push:
branches:
- main
- "release/**"
tags:
- "v*"
# Pull Request auf spezifische Branches
pull_request:
types: [opened, synchronize, reopened]
branches: [main]
# AusfĂŒhrung nach Zeitplan (Cron-Syntax, UTC)
schedule:
- cron: "0 0 * * *" # TĂ€glich um Mitternacht
- cron: "0 6 * * 1" # Jeden Montag um 06:00
# Manueller Trigger mit Inputs
workflow_dispatch:
inputs:
environment:
description: "Deployment environment"
required: true
default: staging
type: choice
options:
- staging
- production
debug_mode:
description: "Enable debug logging"
required: false
type: boolean
default: false
# Trigger bei Abschluss eines Workflows
workflow_run:
workflows: ["CI Pipeline"]
types: [completed]
branches: [main]
# Trigger bei Issues oder anderen Events
issues:
types: [opened, labeled]
release:
types: [published]
### Using Actions from the Marketplace
Reference community or first-party actions with `uses`. Pin by tag or SHA (SHA is recommended for security).
```yaml
steps:
# First-party GitHub Actions
- uses: actions/checkout@v4 # Repo klonen
- uses: actions/setup-node@v4 # Node.js installieren
- uses: actions/setup-python@v5 # Python installieren
- uses: actions/setup-java@v4 # JDK installieren
- uses: actions/setup-go@v5 # Go installieren
- uses: actions/cache@v4 # AbhÀngigkeiten cachen
- uses: actions/upload-artifact@v4 # Build-Artefakte hochladen
- uses: actions/download-artifact@v4 # Artefakte herunterladen
- uses: actions/labeler@v5 # PRs automatisch labeln
- uses: actions/create-release@v1 # GitHub Release erstellen
- uses: actions/configure-pages@v5 # GitHub Pages einrichten
- uses: actions/deploy-pages@v4 # Auf Pages deployen
# Third-party (aus SicherheitsgrĂŒnden per SHA fixiert)
- uses: docker/login-action@v3 # In Container-Registry einloggen
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5 # Docker-Image bauen und pushen
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.sha }}
- uses: docker/setup-buildx-action@v3 # BuildKit einrichten
# Referenz auf eine lokale Action im selben Repo
- uses: ./.github/actions/custom-action
GitHub provides context expressions, repository secrets, and environment variables at multiple scopes.
on: push
env:
# Global fĂŒr alle Jobs
APP_NAME: myapp
NODE_ENV: production
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Erforderliche Umgebung mit Schutzregeln
env:
# Variable auf Job-Ebene
DEPLOY_PATH: /var/www/${{ env.APP_NAME }}
steps:
# Integrierte Umgebungsvariablen (immer verfĂŒgbar)
- name: Show built-in vars
run: |
echo "Home: $HOME"
echo "Runner OS: $RUNNER_OS"
echo "Runner Arch: $RUNNER_ARCH"
echo "Workspace: $GITHUB_WORKSPACE"
echo "Event: $GITHUB_EVENT_NAME"
echo "SHA: $GITHUB_SHA"
echo "Ref: $GITHUB_REF"
# Repository-Secrets verwenden (festgelegt in Settings > Secrets)
- name: Deploy with credentials
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
# GITHUB_TOKEN ist automatisch verfĂŒgbar
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Deploying to $DEPLOY_PATH"
# Variablen sicher verwenden â Secrets niemals per echo ausgeben
deploy.sh --password "$DB_PASSWORD"
# Kontext-AusdrĂŒcke verwenden
- name: Use context data
run: |
echo "Actor: ${{ github.actor }}"
echo "Repo: ${{ github.repository }}"
echo "Branch: ${{ github.ref_name }}"
echo "Event: ${{ github.event_name }}"
# Umgebungsspezifische Secrets verwenden
- name: Use environment secret
env:
PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
run: echo "Secret length: ${#PROD_API_KEY}"
# Mehrzeilige Umgebungsvariable
- name: Set multi-line variable
env:
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
Share data between jobs and speed up workflows by caching dependencies.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# npm-AbhÀngigkeiten cachen
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- run: npm ci
- run: npm run build
# Build-Output als Artifact hochladen
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 14
if-no-files-found: error
# Testergebnisse hochladen
- uses: actions/upload-artifact@v4
with:
name: test-results
path: coverage/
retention-days: 7
deploy:
needs: build
runs-on: ubuntu-latest
steps:
# Artifact aus dem Build-Job herunterladen
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy
run: deploy.sh dist/
# Deployment-Logs als Artifact hochladen
- uses: actions/upload-artifact@v4
if: always() # Auch hochladen, wenn vorherige Schritte fehlschlagen
with:
name: deploy-logs
path: logs/
Run jobs across multiple versions, platforms, or configurations in parallel.
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
# Laufende Jobs nicht abbrechen, wenn einer fehlschlÀgt
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20, 22]
# Spezifische Kombinationen ausschlieĂen
exclude:
- os: windows-latest
node-version: 18
# ZusĂ€tzliche Kombinationen hinzufĂŒgen
include:
- os: ubuntu-latest
node-version: 22
experimental: true
label: "Node 22 (experimental)"
name: Test on Node ${{ matrix.node-version }} (${{ matrix.os }})
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- run: npm run test:integration
if: matrix.experimental
Call workflows from other workflows to share CI logic across repositories.
# .github/workflows/reusable-build.yml (der wiederverwendbare Workflow)
name: Reusable Build
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: "20"
build-command:
required: false
type: string
default: "npm run build"
secrets:
registry-token:
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: ${{ inputs.build-command }}
# .github/workflows/ci.yml (der Aufrufer)
name: CI
on:
push:
branches: [main]
jobs:
call-build:
uses: ./.github/workflows/reusable-build.yml
with:
node-version: "22"
build-command: "npm run build:prod"
secrets:
registry-token: ${{ secrets.GITHUB_TOKEN }}
# Aufruf aus einem anderen Repository
call-external:
uses: myorg/shared-actions/.github/workflows/deploy.yml@v2
with:
environment: staging
secrets:
deploy-key: ${{ secrets.STAGING_DEPLOY_KEY }}
Run workflows on your own infrastructure instead of GitHub-hosted runners.
jobs:
build-on-prem:
runs-on: [self-hosted, linux, x64]
# Oder ein benutzerdefiniertes Label verwenden
# runs-on: self-hosted-gpu
steps:
- uses: actions/checkout@v4
- run: |
echo "Running on self-hosted runner"
echo "Runner name: $RUNNER_NAME"
echo "OS: $(uname -a)"
# Runner-Gruppen verwenden (Enterprise/Team)
deploy-production:
runs-on:
- self-hosted
- linux
- production # Benutzerdefiniertes Label fĂŒr Prod-Runner
environment: production
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
name: Node.js CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
id-token: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- run: npm run lint
- run: npm run typecheck
test:
name: Test
needs: lint
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run test:unit -- --coverage
- uses: actions/upload-artifact@v4
if: matrix.node-version == 20
with:
name: coverage-report
path: coverage/
build:
name: Build & Push Image
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Log in to Container 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=sha,prefix=
type=ref,event=branch
type=semver,pattern={{version}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-staging:
name: Deploy to Staging
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: |
echo "Deploying image tag: ${{ needs.build.outputs.image-tag }}"
# kubectl set image deployment/web app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image-tag }}
# kubectl rollout status deployment/web
deploy-production:
name: Deploy to Production
needs: [build, deploy-staging]
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
echo "Deploying image tag: ${{ needs.build.outputs.image-tag }}"
# kubectl set image deployment/web app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image-tag }}
# kubectl rollout status deployment/web
GitLab CI is the built-in CI/CD system for GitLab. Pipelines are defined in .gitlab-ci.yml at the repository root and executed by GitLab runners. Jobs are organized into stages that run sequentially, with jobs within each stage running in parallel.
The .gitlab-ci.yml file defines stages, jobs, and their execution order.
# Standardeinstellungen, die auf alle Jobs angewendet werden
default:
image: node:20-alpine
before_script:
- npm ci --cache .npm
- npm ci
cache:
key:
files:
- package-lock.json
paths:
- .npm/
artifacts:
expire_in: 7 days
# Globale Variablen
variables:
npm_config_cache: "$CI_PROJECT_DIR/.npm"
DOCKER_DRIVER: overlay2
# Stages werden nacheinander ausgefĂŒhrt; Jobs innerhalb einer Stage laufen parallel
stages:
- lint
- test
- build
- deploy
lint:
stage: lint
script:
- npm run lint
- npm run typecheck
test:unit:
stage: test
script:
- npm run test:unit -- --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/'
artifacts:
when: always
paths:
- coverage/
test:integration:
stage: test
script:
- npm run test:integration
needs: ["lint"]
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 30 days
deploy:staging:
stage: deploy
script:
- echo "Deploying to staging..."
environment: staging
only:
- main
deploy:production:
stage: deploy
script:
- echo "Deploying to production..."
environment: production
when: manual
only:
- main
GitLab runners execute jobs. Use shared runners, group runners, or specific project runners with tags.
# Gemeinsame Runner verwenden (fĂŒr alle Projekte verfĂŒgbar)
job-on-shared-runner:
stage: test
script:
- echo "Running on shared runner"
# Einen spezifischen Runner ĂŒber Tags verwenden
job-on-specific-runner:
stage: test
tags:
- docker
- linux
script:
- echo "Running on tagged runner"
# Einen selbst gehosteten Runner mit benutzerdefiniertem Docker-Image verwenden
job-on-custom-runner:
stage: deploy
tags:
- production
- kubernetes
image: alpine:latest
script:
- kubectl apply -f k8s/
Define variables at multiple scopes with different protection levels.
# Globale Variablen (sichtbar in den Pipeline-Logs â nicht fĂŒr Secrets geeignet)
variables:
APP_NAME: myapp
APP_ENV: production
DEPLOY_REGION: us-east-1
# Variablen vom Typ "File" (nĂŒtzlich fĂŒr Zertifikate, Konfigurationsdateien)
variables:
KUBE_CONFIG: # In der GitLab UI als Typ "File" festlegen
SSL_CERT: # In der GitLab UI als Typ "File" festlegen
build:
stage: build
variables:
# Override auf Job-Ebene
NODE_ENV: production
script:
# CI/CD-Variablen (festgelegt unter Settings > CI/CD > Variables)
# Protected: nur auf geschĂŒtzten Branches/Tags verfĂŒgbar
# Masked: in den Job-Logs ausgeblendet
# Typ: Variable oder File
- echo "Deploying $APP_NAME to $DEPLOY_REGION"
- echo "DB host: $DB_HOST" # Variablentyp
- echo "Config at $APP_CONFIG" # Dateityp (Pfad zur temporÀren Datei)
- cat "$APP_CONFIG" # Dateityp-Variable auslesen
- |
# Vordefinierte CI/CD-Variablen verwenden
echo "Pipeline ID: $CI_PIPELINE_ID"
echo "Commit SHA: $CI_COMMIT_SHA"
echo "Branch: $CI_COMMIT_BRANCH"
echo "Default branch: $CI_DEFAULT_BRANCH"
echo "Project path: $CI_PROJECT_PATH"
echo "Runner tags: $CI_RUNNER_TAGS"
deploy:
stage: deploy
script:
# Zugriff auf geschĂŒtzte Variablen (nur auf geschĂŒtzten Branches)
- echo "$PRODUCTION_DB_PASSWORD" | docker login -u admin --password-stdin
environment:
name: production
only:
- main # GeschĂŒtzter Branch â kann auf geschĂŒtzte Variablen zugreifen
Persist files between jobs (artifacts) and speed up jobs by caching dependencies (cache).
# Artefakte definieren â Dateien, die zwischen Stages ĂŒbergeben werden
build:
stage: build
script:
- npm run build
artifacts:
name: "build-$CI_COMMIT_SHORT_SHA"
paths:
- dist/
- build-report.json
exclude:
- dist/**/*.map
expire_in: 2 weeks
when: on_success # on_success (Standard), on_failure, always
expose_as: "Build Output" # Label fĂŒr den Download-Link in der UI
test:
stage: test
needs: [build] # Startet sofort nach Abschluss von build (wartet nicht auf die Stage-Reihenfolge)
script:
- npm run test
artifacts:
when: always # Upload auch bei fehlgeschlagenen Tests
paths:
- test-results/
reports:
# JUnit Testbericht (zeigt Ergebnisse im MR an)
junit: test-results/junit.xml
# Code-Coverage-Bericht
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# Artifact-Download
dependencies:
- build # Nur Artifacts aus dem build-Job herunterladen
# Cache â beschleunigt die Job-AusfĂŒhrung
.cache-template: &cache-npm
cache:
key:
files:
- package-lock.json
prefix: npm
paths:
- .npm/
policy: pull-push # Standard: Cache lesen und aktualisieren
install:
stage: .pre
<<: *cache-npm
script:
- npm ci
test:unit:
stage: test
<<: *cache-npm
cache:
<<: *cache-npm
policy: pull # Nur Cache lesen, nicht aktualisieren
script:
- npm run test:unit
# Docker Layer Caching
docker-build:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
cache:
key: "$CI_JOB_NAME"
paths:
- .docker-cache/
before_script:
- docker load -i .docker-cache/build.tar || true
script:
- docker build --cache-from myapp:latest -t myapp:$CI_COMMIT_SHA .
- docker save myapp:$CI_COMMIT_SHA -o .docker-cache/build.tar
Control when jobs run using rules (preferred) or the legacy only/except keywords.
# Moderne Rules-Syntax (bevorzugt)
test:
script: npm test
rules:
# AusfĂŒhrung auf dem main-Branch
- if: $CI_COMMIT_BRANCH == "main"
# AusfĂŒhrung in Merge-Request-Pipelines
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# AusfĂŒhrung in geplanten Pipelines (Schedules)
- if: $CI_PIPELINE_SOURCE == "schedule"
variables:
RUN_SLOW_TESTS: "true"
# AusfĂŒhrung, wenn eine bestimmte Datei geĂ€ndert wurde
- changes:
- src/**/*
- package.json
# AusfĂŒhrung bei Tags, die einem Muster entsprechen
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
# Manuelle AusfĂŒhrung
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
# Standard-Fallback (nicht ausfĂŒhren, wenn keine der obigen Bedingungen zutrifft)
- when: never
# Komplexe Rules mit Bedingungen
deploy:
script: ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
exists:
- Dockerfile
changes:
- src/**/*
variables:
DEPLOY_ENV: production
- if: $CI_COMMIT_BRANCH == "develop"
variables:
DEPLOY_ENV: staging
# Nur Legacy only/except (wird weiterhin unterstĂŒtzt)
legacy-job:
script: echo "Legacy syntax"
only:
- main
- develop
- /^release\/.*$/
- merge_requests
- tags
- schedules
- api
- web # Manueller Trigger ĂŒber die UI
except:
- feature/*
- /^hotfix\/.*/
Define deployment environments with tracking, URLs, and protection rules.
variables:
KUBE_NAMESPACE: ""
deploy:staging:
stage: deploy
script:
- echo "Deploying to staging..."
- kubectl apply -f k8s/staging/
environment:
name: staging
url: https://staging.example.com
on_stop: stop:staging # Trigger fĂŒr Cleanup-Job
only:
- develop
# Eine Umgebung stoppen/löschen
stop:staging:
stage: deploy
script:
- echo "Stopping staging environment..."
- kubectl delete namespace staging
environment:
name: staging
action: stop # Markiert die Umgebung als gestoppt
when: manual
deploy:production:
stage: deploy
script:
- echo "Deploying to production..."
- kubectl apply -f k8s/production/
environment:
name: production
url: https://example.com
# Automatisches Stoppen nach 30 Tagen InaktivitÀt (erfordert Premium+)
auto_stop_in: 30 days
only:
- main
deploy:review:
stage: deploy
script:
- echo "Deploying review app for $CI_MERGE_REQUEST_IID..."
- kubectl create namespace review-$CI_MERGE_REQUEST_IID || true
- kubectl apply -f k8s/review/ -n review-$CI_MERGE_REQUEST_IID
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.com
on_stop: stop:review
only:
- merge_requests
stop:review:
stage: deploy
script:
- kubectl delete namespace review-$CI_MERGE_REQUEST_IID
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
when: manual
only:
- merge_requests
Require human intervention before execution. Useful for production deployments and destructive operations.
stages:
- build
- test
- deploy
build:
stage: build
script: npm run build
artifacts:
paths: [dist/]
test:
stage: test
script: npm test
# Manueller Job â blockiert, bis ein Benutzer auf "Play" klickt
deploy:production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
when: manual
# Erlaubt, dass die Pipeline erfolgreich ist, auch wenn dieser Job ĂŒbersprungen wird
allow_failure: false
# Manueller Job mit optionaler AusfĂŒhrung
deploy:canary:
stage: deploy
script:
- ./deploy-canary.sh
when: manual
allow_failure: true # Pipeline gilt als bestanden, auch wenn der Job ĂŒbersprungen wurde
# Manueller Job, der nur auf main ausgelöst wird
cleanup:
stage: deploy
script:
- ./cleanup.sh
when: manual
only:
- main
allow_failure: true
Reuse configuration with include, share settings with extends, and create job templates.
# Einbinden externer Konfigurationsdateien
include:
# Datei im selben Repository
- local: .gitlab/ci/rules.yml
# Datei in einem anderen Repository
- project: myorg/shared-pipelines
ref: main
file: /templates/nodejs.yml
# Remote-URL
- remote: https://example.com/ci-templates/base.yml
# Template aus dem CI/CD-Katalog von GitLab (GitLab 16+)
- component: gitlab.com/gitlab-org/components/dotnet/dotnet-build@1.2.0
inputs:
stage: build
dotnet_version: "8.0.x"
# YAML-Anker und Aliase
.cache-npm: &cache-npm
cache:
key:
files: [package-lock.json]
paths: [.npm/]
.install-deps: &install-deps
before_script:
- npm ci --cache .npm
# Templates erweitern
.test-base:
<<: [*cache-npm, *install-deps]
stage: test
image: node:20-alpine
test:unit:
extends: .test-base
script:
- npm run test:unit
test:integration:
extends: .test-base
script:
- npm run test:integration
services:
- name: postgres:15
alias: db
# Versteckte Job-Templates (PrĂ€fix mit .) werden nicht eigenstĂ€ndig ausgefĂŒhrt
.docker-base:
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build:api:
extends: .docker-base
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE/api:$CI_COMMIT_SHA ./apps/api
- docker push $CI_REGISTRY_IMAGE/api:$CI_COMMIT_SHA
build:web:
extends: .docker-base
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE/web:$CI_COMMIT_SHA ./apps/web
- docker push $CI_REGISTRY_IMAGE/web:$CI_COMMIT_SHA
Special pipeline behavior for merge requests including MR-specific variables and workflow rules.
# Steuerung, welche Pipelines fĂŒr MRs vs. Branch-Pushes ausgefĂŒhrt werden
workflow:
rules:
# Merge-Request-Pipelines fĂŒr Branches mit offenen MRs ausfĂŒhren
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Branch-Pipelines fĂŒr den Default-Branch ausfĂŒhren
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Tag-Pipelines ausfĂŒhren
- if: $CI_COMMIT_TAG
# Geplante Pipelines ausfĂŒhren
- if: $CI_PIPELINE_SOURCE == "schedule"
# Ăber API/Web ausgelöste Pipelines ausfĂŒhren
- if: $CI_PIPELINE_SOURCE == "api"
- if: $CI_PIPELINE_SOURCE == "web"
# Keine Branch-Pipelines fĂŒr Feature-Branches ausfĂŒhren (stattdessen MR-Pipeline nutzen)
- when: never
variables:
# In MR-Pipelines nur den MR-Diff bauen
GET_SOURCES_ATTEMPT: "3"
test:
stage: test
script:
- echo "MR Title: $CI_MERGE_REQUEST_TITLE"
- echo "MR Source: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
- echo "MR Target: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
- echo "MR IID: $CI_MERGE_REQUEST_IID"
- npm run test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
lint:
stage: test
script:
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Nur geÀnderte Dateien linten
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Nur in Merge-Request-Pipelines ausfĂŒhren, nicht in Branch-Pipelines
review-deploy:
stage: deploy
script:
- echo "Deploying review app for MR !${CI_MERGE_REQUEST_IID}"
environment:
name: review/$CI_COMMIT_REF_SLUG
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
# .gitlab-ci.yml â Multi-Service Docker CI/CD
include:
- local: .gitlab/ci/variables.yml
default:
image: docker:24
services:
- docker:24-dind
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
variables:
DOCKER_TLS_CERTDIR: "/certs"
stages:
- validate
- test
- build
- deploy
# âââ Validate âââââââââââââââââââââââââââââââââââââââââââ
lint:
stage: validate
image: node:20-alpine
before_script:
- npm ci
script:
- npm run lint
- npm run typecheck
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# âââ Test âââââââââââââââââââââââââââââââââââââââââââââââ
test:unit:
stage: test
image: node:20-alpine
services:
- name: postgres:15-alpine
alias: postgres
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: postgresql://testuser:testpass@postgres:5432/testdb
before_script:
- npm ci
- npx prisma migrate deploy
script:
- npm run test:unit -- --ci --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/'
artifacts:
when: always
reports:
junit: test-results/junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# âââ Build ââââââââââââââââââââââââââââââââââââââââââââââ
.docker-build:
stage: build
variables:
IMAGE: $CI_REGISTRY_IMAGE/$SERVICE
script:
- docker pull $IMAGE:latest || true
- docker build
--cache-from $IMAGE:latest
--tag $IMAGE:$CI_COMMIT_SHA
--tag $IMAGE:latest
-f apps/$SERVICE/Dockerfile
apps/$SERVICE
- docker push $IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE:latest
build:api:
extends: .docker-build
variables:
SERVICE: api
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes: [apps/api/**/*]
build:web:
extends: .docker-build
variables:
SERVICE: web
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes: [apps/web/**/*]
build:worker:
extends: .docker-build
variables:
SERVICE: worker
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes: [apps/worker/**/*]
# âââ Deploy âââââââââââââââââââââââââââââââââââââââââââââ
.deploy-k8s:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context $KUBE_CONTEXT
- kubectl set image deployment/$SERVICE $SERVICE=$CI_REGISTRY_IMAGE/$SERVICE:$CI_COMMIT_SHA -n $KUBE_NAMESPACE
- kubectl rollout status deployment/$SERVICE -n $KUBE_NAMESPACE --timeout=300s
deploy:staging:
extends: .deploy-k8s
variables:
KUBE_CONTEXT: staging
KUBE_NAMESPACE: staging
SERVICE: api
environment:
name: staging
url: https://staging-api.example.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy:production:
extends: .deploy-k8s
variables:
KUBE_CONTEXT: production
KUBE_NAMESPACE: production
SERVICE: api
environment:
name: production
url: https://api.example.com
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
| Feature | GitHub Actions | GitLab CI |
|---|---|---|
| Konfigurationsort | .github/workflows/*.yml | .gitlab-ci.yml |
| Trigger-Syntax | on: push, pull_request, schedule | only, except, rules |
| Secrets-Scope | Repository, Environment, Org | Projekt, Gruppe, Instanz, Env |
| Artefakt-Aufbewahrung | Pro Artefakt konfigurierbar | expire_in pro Artefakt |
| Cache | actions/cache Action | Integriertes cache Keyword |
| Matrix-Builds | strategy.matrix | matrix (parallel) oder parallel: N |
| Wiederverwendbare Workflows | uses + workflow_call | include + extends |
| Environments | environment: Keyword | environment: Keyword |
| Manuelle Jobs | workflow_dispatch + when: manual nicht nativ | when: manual |
| Self-hosted Runner | runs-on: self-hosted | tags: auf Runnern |
| Container-Support | container: im Job | image: + services: |
| Merge-Request-Pipelines | pull_request Trigger | merge_request_event Quelle |