From 239081364a3563c9df8b64bd44d26053dbdb0d7a Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 1 Sep 2025 17:35:26 +0000 Subject: [PATCH] Implement CI/CD Pipeline and Ansible Deployment (Issue #14) - Add GitHub Actions workflow for Docker build & push to private registry - Add Gitea Actions workflow (compatible with GitHub Actions) - Extend README.md with comprehensive Ansible role example: - Complete directory structure and file examples - Docker Compose template with health checks - Variable configuration for different environments - Vault integration for secure credential management - Deployment playbook and execution examples - Add .dockerignore for optimized Docker builds - Both workflows support workflow_dispatch with optional tag suffix - Multi-platform builds (linux/amd64, linux/arm64) - Smart caching and detailed build summaries --- .dockerignore | 51 ++++++ .gitea/workflows/docker-build-push.yml | 67 ++++++++ .github/workflows/docker-build-push.yml | 67 ++++++++ README.md | 213 ++++++++++++++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/docker-build-push.yml create mode 100644 .github/workflows/docker-build-push.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4616f36 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,51 @@ +# Git +.git +.gitignore +.gitea +.github + +# Python +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +*.so +.pytest_cache +.coverage +*.coverage +htmlcov/ +.venv +venv/ +env/ + +# IDE +.vscode +.idea +*.swp +*.swo + +# Documentation +*.md +!README.md + +# Test files +test.db +*.db-journal + +# CI/CD +.github/ +.gitea/ + +# Development +.devcontainer/ +.env +.env.local + +# Logs +*.log +logs/ + +# OS +.DS_Store +Thumbs.db diff --git a/.gitea/workflows/docker-build-push.yml b/.gitea/workflows/docker-build-push.yml new file mode 100644 index 0000000..cb3f6b2 --- /dev/null +++ b/.gitea/workflows/docker-build-push.yml @@ -0,0 +1,67 @@ +name: Build and Push Docker Image (Gitea) + +on: + workflow_dispatch: + inputs: + tag_suffix: + description: 'Optional tag suffix (e.g., -dev, -staging)' + required: false + default: '' + +env: + IMAGE_NAME: rs-sim-autostore + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Private Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract metadata + id: meta + run: | + echo "short_sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT + echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT + echo "timestamp=$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:latest${{ inputs.tag_suffix }} + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.short_sha }}${{ inputs.tag_suffix }} + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.branch }}-${{ steps.meta.outputs.timestamp }}${{ inputs.tag_suffix }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Image digest + run: echo "Image pushed with digest ${{ steps.docker_build.outputs.digest }}" + + - name: Summary + run: | + echo "## Build Summary 🐳" + echo "" + echo "**Image:** ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}" + echo "**Tags:**" + echo "- latest${{ inputs.tag_suffix }}" + echo "- ${{ steps.meta.outputs.short_sha }}${{ inputs.tag_suffix }}" + echo "- ${{ steps.meta.outputs.branch }}-${{ steps.meta.outputs.timestamp }}${{ inputs.tag_suffix }}" + echo "" + echo "**Commit:** ${{ github.sha }}" + echo "**Branch:** ${{ steps.meta.outputs.branch }}" diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..bb16325 --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,67 @@ +name: Build and Push Docker Image + +on: + workflow_dispatch: + inputs: + tag_suffix: + description: 'Optional tag suffix (e.g., -dev, -staging)' + required: false + default: '' + +env: + IMAGE_NAME: rs-sim-autostore + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Private Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Extract metadata + id: meta + run: | + echo "short_sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT + echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT + echo "timestamp=$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:latest${{ github.event.inputs.tag_suffix }} + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.short_sha }}${{ github.event.inputs.tag_suffix }} + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.branch }}-${{ steps.meta.outputs.timestamp }}${{ github.event.inputs.tag_suffix }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Image digest + run: echo "Image pushed with digest ${{ steps.docker_build.outputs.digest }}" + + - name: Summary + run: | + echo "## Build Summary 🐳" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Image:** \`${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Tags:**" >> $GITHUB_STEP_SUMMARY + echo "- \`latest${{ github.event.inputs.tag_suffix }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ steps.meta.outputs.short_sha }}${{ github.event.inputs.tag_suffix }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ steps.meta.outputs.branch }}-${{ steps.meta.outputs.timestamp }}${{ github.event.inputs.tag_suffix }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** \`${{ steps.meta.outputs.branch }}\`" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index 70892e0..9b9fef7 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,218 @@ Dieses Projekt ist ein Simulator für das Logistiksystem "AutoStore", entwickelt 3. Wenn du dazu aufgefordert wirst, klicke auf **"Reopen in Container"**. VS Code wird die Entwicklungsumgebung automatisch aufsetzen. 4. Nach dem Start des Containers kann die Anwendung ausgeführt werden. +## 🐳 Container Deployment + +### Docker Image Build & Push + +Das Projekt enthält CI/CD Workflows für GitHub und Gitea Actions: + +- **GitHub:** `.github/workflows/docker-build-push.yml` +- **Gitea:** `.gitea/workflows/docker-build-push.yml` + +**Benötigte Secrets:** +- `REGISTRY_URL` - URL der privaten Container Registry +- `REGISTRY_USER` - Benutzername für Registry-Zugriff +- `REGISTRY_PASSWORD` - Passwort für Registry-Zugriff + +**Ausführung:** +```bash +# Manuell über GitHub/Gitea UI (workflow_dispatch) +# oder über CLI (GitHub): +gh workflow run docker-build-push.yml +``` + +### Ansible Deployment + +Beispiel einer Ansible-Rolle für das Deployment: + +#### Verzeichnisstruktur +``` +roles/ + autostore-simulator/ + tasks/ + main.yml + templates/ + docker-compose.yml.j2 + vars/ + main.yml + defaults/ + main.yml +``` + +#### `roles/autostore-simulator/defaults/main.yml` +```yaml +--- +# Container Konfiguration +autostore_image: "{{ registry_url }}/rs-sim-autostore" +autostore_tag: "latest" +autostore_container_name: "autostore-simulator" +autostore_port: 8000 +autostore_network: "autostore-net" + +# Registry Konfiguration +registry_url: "registry.example.com" +registry_user: "deployment" +registry_password: "{{ vault_registry_password }}" + +# Datenbank Konfiguration +autostore_db_file: "/app/data/autostore.db" +autostore_data_volume: "autostore-data" + +# Systemkonfiguration +autostore_restart_policy: "unless-stopped" +autostore_log_driver: "json-file" +autostore_log_max_size: "10m" +autostore_log_max_file: "3" +``` + +#### `roles/autostore-simulator/vars/main.yml` +```yaml +--- +# Interne Variablen +autostore_full_image: "{{ autostore_image }}:{{ autostore_tag }}" +autostore_compose_file: "/opt/autostore/docker-compose.yml" +autostore_app_dir: "/opt/autostore" +``` + +#### `roles/autostore-simulator/tasks/main.yml` +```yaml +--- +- name: Create AutoStore application directory + file: + path: "{{ autostore_app_dir }}" + state: directory + mode: '0755' + owner: root + group: root + +- name: Create AutoStore data volume + docker_volume: + name: "{{ autostore_data_volume }}" + state: present + +- name: Create AutoStore network + docker_network: + name: "{{ autostore_network }}" + state: present + +- name: Login to container registry + docker_login: + registry_url: "{{ registry_url }}" + username: "{{ registry_user }}" + password: "{{ registry_password }}" + when: registry_url is defined + +- name: Pull AutoStore image + docker_image: + name: "{{ autostore_full_image }}" + source: pull + force_source: yes + +- name: Generate docker-compose.yml + template: + src: docker-compose.yml.j2 + dest: "{{ autostore_compose_file }}" + mode: '0644' + notify: restart autostore + +- name: Start AutoStore service + docker_compose: + project_src: "{{ autostore_app_dir }}" + state: present + recreate: smart + +- name: Wait for AutoStore to be ready + uri: + url: "http://localhost:{{ autostore_port }}/health" + method: GET + status_code: 200 + register: autostore_health + until: autostore_health.status == 200 + retries: 30 + delay: 2 +``` + +#### `roles/autostore-simulator/templates/docker-compose.yml.j2` +```yaml +version: '3.8' + +services: + autostore-simulator: + image: {{ autostore_full_image }} + container_name: {{ autostore_container_name }} + restart: {{ autostore_restart_policy }} + ports: + - "{{ autostore_port }}:8000" + volumes: + - {{ autostore_data_volume }}:/app/data + environment: + - DATABASE_URL=sqlite:///{{ autostore_db_file }} + networks: + - {{ autostore_network }} + logging: + driver: "{{ autostore_log_driver }}" + options: + max-size: "{{ autostore_log_max_size }}" + max-file: "{{ autostore_log_max_file }}" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +volumes: + {{ autostore_data_volume }}: + external: true + +networks: + {{ autostore_network }}: + external: true +``` + +#### `roles/autostore-simulator/handlers/main.yml` +```yaml +--- +- name: restart autostore + docker_compose: + project_src: "{{ autostore_app_dir }}" + restarted: yes +``` + +#### Playbook Beispiel +```yaml +--- +- hosts: autostore_servers + become: yes + vars: + registry_url: "my-registry.example.com" + autostore_tag: "v1.0.0" + vault_registry_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 66633...encrypted...66633 + + roles: + - autostore-simulator + + post_tasks: + - name: Verify AutoStore API is accessible + uri: + url: "http://{{ inventory_hostname }}:{{ autostore_port }}/health" + method: GET + delegate_to: localhost +``` + +#### Deployment ausführen +```bash +# Inventory und Vault-Passwort vorbereiten +ansible-playbook -i inventory/production autostore-deploy.yml --ask-vault-pass + +# Mit spezifischem Tag +ansible-playbook -i inventory/production autostore-deploy.yml \ + --ask-vault-pass \ + -e "autostore_tag=latest" +``` + --- *Dieses Projekt wurde mit Unterstützung von GitHub Copilot erstellt.*