feat(deployment): update Semaphore stack and Traefik configuration
- Add QUICKSTART.md and SETUP_REPOSITORY.md for Semaphore stack - Add playbooks directory for Semaphore deployment - Update Semaphore docker-compose.yml, env.example, and README - Add Traefik local configuration files - Disable semaphore.yml in Traefik dynamic config - Update docker-compose.local.yml and build-image workflow
This commit is contained in:
103
deployment/stacks/semaphore/playbooks/ci-tests.yml
Normal file
103
deployment/stacks/semaphore/playbooks/ci-tests.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
# CI Tests Playbook f?r Semaphore
|
||||
# F?hrt PHP Tests und Quality Checks aus
|
||||
|
||||
- name: Run CI Tests and Quality Checks
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
|
||||
repo_branch: "{{ repo_branch | default('main') }}"
|
||||
build_dir: "/tmp/ci-build"
|
||||
|
||||
tasks:
|
||||
- name: Clean up build directory
|
||||
file:
|
||||
path: "{{ build_dir }}"
|
||||
state: absent
|
||||
|
||||
- name: Checkout repository
|
||||
git:
|
||||
repo: "{{ repo_url }}"
|
||||
dest: "{{ build_dir }}"
|
||||
version: "{{ repo_branch }}"
|
||||
force: yes
|
||||
register: git_result
|
||||
|
||||
- name: Display checked out commit
|
||||
debug:
|
||||
msg: "Checked out commit: {{ git_result.after }}"
|
||||
|
||||
- name: Install Composer if not present
|
||||
get_url:
|
||||
url: https://getcomposer.org/installer
|
||||
dest: /tmp/composer-installer.php
|
||||
mode: '0755'
|
||||
when: ansible_facts.os_family == "Debian"
|
||||
|
||||
- name: Install Composer (Debian)
|
||||
shell: php /tmp/composer-installer.php && mv composer.phar /usr/local/bin/composer
|
||||
when: ansible_facts.os_family == "Debian"
|
||||
args:
|
||||
creates: /usr/local/bin/composer
|
||||
|
||||
- name: Install PHP dependencies
|
||||
command: composer install --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-req=php
|
||||
args:
|
||||
chdir: "{{ build_dir }}"
|
||||
register: composer_result
|
||||
|
||||
- name: Display composer output
|
||||
debug:
|
||||
var: composer_result.stdout_lines
|
||||
when: composer_result.stdout_lines is defined
|
||||
|
||||
- name: Run PHP tests
|
||||
command: ./vendor/bin/pest
|
||||
args:
|
||||
chdir: "{{ build_dir }}"
|
||||
register: test_result
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display test results
|
||||
debug:
|
||||
msg: "{{ test_result.stdout_lines }}"
|
||||
when: test_result.stdout_lines is defined
|
||||
|
||||
- name: Run PHPStan
|
||||
command: composer phpstan
|
||||
args:
|
||||
chdir: "{{ build_dir }}"
|
||||
register: phpstan_result
|
||||
ignore_errors: yes
|
||||
when: test_result.rc == 0
|
||||
|
||||
- name: Display PHPStan results
|
||||
debug:
|
||||
msg: "{{ phpstan_result.stdout_lines }}"
|
||||
when: phpstan_result.stdout_lines is defined and phpstan_result.rc == 0
|
||||
|
||||
- name: Run code style check
|
||||
command: composer cs
|
||||
args:
|
||||
chdir: "{{ build_dir }}"
|
||||
register: cs_result
|
||||
ignore_errors: yes
|
||||
when: test_result.rc == 0
|
||||
|
||||
- name: Display code style results
|
||||
debug:
|
||||
msg: "{{ cs_result.stdout_lines }}"
|
||||
when: cs_result.stdout_lines is defined
|
||||
|
||||
- name: Fail if tests failed
|
||||
fail:
|
||||
msg: "Tests failed! Check output above."
|
||||
when: test_result.rc != 0
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
- "? CI Tests completed successfully!"
|
||||
- "Commit: {{ git_result.after }}"
|
||||
- "Branch: {{ repo_branch }}"
|
||||
129
deployment/stacks/semaphore/playbooks/deploy-production.yml
Normal file
129
deployment/stacks/semaphore/playbooks/deploy-production.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
# Production Deployment Playbook f?r Semaphore
|
||||
# Deployed die Anwendung auf Production-Server
|
||||
|
||||
- name: Deploy to Production
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: yes
|
||||
vars:
|
||||
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
|
||||
image_name: "{{ image_name | default('framework') }}"
|
||||
image_tag: "{{ image_tag | default('latest') }}"
|
||||
registry_user: "{{ registry_user | default('admin') }}"
|
||||
registry_password: "{{ registry_password | required }}"
|
||||
deployment_path: "{{ deployment_path | default('~/deployment/stacks/application') }}"
|
||||
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
|
||||
|
||||
tasks:
|
||||
- name: Display deployment info
|
||||
debug:
|
||||
msg:
|
||||
- "?? Starting production deployment..."
|
||||
- "Host: {{ inventory_hostname }}"
|
||||
- "Registry: {{ registry_url }}"
|
||||
- "Image: {{ image_name }}:{{ image_tag }}"
|
||||
- "Path: {{ deployment_path }}"
|
||||
|
||||
- name: Ensure deployment directory exists
|
||||
file:
|
||||
path: "{{ deployment_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Login to Docker registry
|
||||
docker_login:
|
||||
username: "{{ registry_user }}"
|
||||
password: "{{ registry_password }}"
|
||||
registry_url: "{{ registry_url }}"
|
||||
|
||||
- name: Pull Docker image
|
||||
docker_image:
|
||||
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
source: pull
|
||||
|
||||
- name: Check if docker-compose files exist
|
||||
stat:
|
||||
path: "{{ deployment_path }}/docker-compose.base.yml"
|
||||
register: base_compose_exists
|
||||
|
||||
- name: Check if docker-compose.production.yml exists
|
||||
stat:
|
||||
path: "{{ deployment_path }}/docker-compose.production.yml"
|
||||
register: production_compose_exists
|
||||
|
||||
- name: Copy docker-compose files if missing
|
||||
copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ deployment_path }}/{{ item.dest }}"
|
||||
mode: '0644'
|
||||
loop:
|
||||
- { src: "docker-compose.base.yml", dest: "docker-compose.base.yml" }
|
||||
- { src: "docker-compose.production.yml", dest: "docker-compose.production.yml" }
|
||||
when: not (item.dest == "docker-compose.base.yml" and base_compose_exists.stat.exists) or
|
||||
not (item.dest == "docker-compose.production.yml" and production_compose_exists.stat.exists)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Update docker-compose.production.yml with new image
|
||||
replace:
|
||||
path: "{{ deployment_path }}/docker-compose.production.yml"
|
||||
regexp: 'image:.*{{ image_name }}:.*'
|
||||
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
|
||||
|
||||
- name: Update docker-compose.production.yml with new image (alternative format)
|
||||
replace:
|
||||
path: "{{ deployment_path }}/docker-compose.production.yml"
|
||||
regexp: 'image:.*{{ image_name }}@.*'
|
||||
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
|
||||
|
||||
- name: Ensure Docker networks exist
|
||||
docker_network:
|
||||
name: traefik-public
|
||||
state: present
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Deploy services
|
||||
docker_compose_v2:
|
||||
project_src: "{{ deployment_path }}"
|
||||
files:
|
||||
- "{{ deployment_path }}/docker-compose.base.yml"
|
||||
- "{{ deployment_path }}/docker-compose.production.yml"
|
||||
pull: missing
|
||||
state: present
|
||||
recreate: always
|
||||
|
||||
- name: Wait for services to start
|
||||
pause:
|
||||
seconds: 15
|
||||
|
||||
- name: Check container status
|
||||
command: docker compose -f {{ deployment_path }}/docker-compose.base.yml -f {{ deployment_path }}/docker-compose.production.yml ps
|
||||
register: container_status
|
||||
|
||||
- name: Display container status
|
||||
debug:
|
||||
var: container_status.stdout_lines
|
||||
|
||||
- name: Health check
|
||||
uri:
|
||||
url: https://michaelschiemer.de/health
|
||||
method: GET
|
||||
status_code: [200, 201, 204]
|
||||
validate_certs: no
|
||||
register: health_result
|
||||
retries: 10
|
||||
delay: 10
|
||||
until: health_result.status == 200
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display health check result
|
||||
debug:
|
||||
msg: "? Health check passed!" if health_result.status == 200 else "?? Health check failed"
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
- "? Production deployment completed!"
|
||||
- "Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
- "URL: https://michaelschiemer.de"
|
||||
110
deployment/stacks/semaphore/playbooks/deploy-staging.yml
Normal file
110
deployment/stacks/semaphore/playbooks/deploy-staging.yml
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
# Staging Deployment Playbook f?r Semaphore
|
||||
# Deployed die Anwendung auf Staging-Server
|
||||
|
||||
- name: Deploy to Staging
|
||||
hosts: staging
|
||||
gather_facts: yes
|
||||
become: yes
|
||||
vars:
|
||||
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
|
||||
image_name: "{{ image_name | default('framework') }}"
|
||||
image_tag: "{{ image_tag | default('latest') }}"
|
||||
registry_user: "{{ registry_user | default('admin') }}"
|
||||
registry_password: "{{ registry_password | required }}"
|
||||
deployment_path: "{{ deployment_path | default('~/deployment/stacks/staging') }}"
|
||||
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
|
||||
|
||||
tasks:
|
||||
- name: Display deployment info
|
||||
debug:
|
||||
msg:
|
||||
- "?? Starting staging deployment..."
|
||||
- "Host: {{ inventory_hostname }}"
|
||||
- "Registry: {{ registry_url }}"
|
||||
- "Image: {{ image_name }}:{{ image_tag }}"
|
||||
- "Path: {{ deployment_path }}"
|
||||
|
||||
- name: Ensure deployment directory exists
|
||||
file:
|
||||
path: "{{ deployment_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Login to Docker registry
|
||||
docker_login:
|
||||
username: "{{ registry_user }}"
|
||||
password: "{{ registry_password }}"
|
||||
registry_url: "{{ registry_url }}"
|
||||
|
||||
- name: Pull Docker image
|
||||
docker_image:
|
||||
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
source: pull
|
||||
|
||||
- name: Check if docker-compose files exist
|
||||
stat:
|
||||
path: "{{ deployment_path }}/docker-compose.base.yml"
|
||||
register: base_compose_exists
|
||||
|
||||
- name: Check if docker-compose.staging.yml exists
|
||||
stat:
|
||||
path: "{{ deployment_path }}/docker-compose.staging.yml"
|
||||
register: staging_compose_exists
|
||||
|
||||
- name: Copy docker-compose files if missing
|
||||
copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ deployment_path }}/{{ item.dest }}"
|
||||
mode: '0644'
|
||||
loop:
|
||||
- { src: "docker-compose.base.yml", dest: "docker-compose.base.yml" }
|
||||
- { src: "docker-compose.staging.yml", dest: "docker-compose.staging.yml" }
|
||||
when: not (item.dest == "docker-compose.base.yml" and base_compose_exists.stat.exists) or
|
||||
not (item.dest == "docker-compose.staging.yml" and staging_compose_exists.stat.exists)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Update docker-compose.staging.yml with new image
|
||||
replace:
|
||||
path: "{{ deployment_path }}/docker-compose.staging.yml"
|
||||
regexp: 'image:.*{{ image_name }}:.*'
|
||||
replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}'
|
||||
|
||||
- name: Ensure Docker networks exist
|
||||
docker_network:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- traefik-public
|
||||
- staging-internal
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Deploy services
|
||||
docker_compose_v2:
|
||||
project_src: "{{ deployment_path }}"
|
||||
files:
|
||||
- "{{ deployment_path }}/docker-compose.base.yml"
|
||||
- "{{ deployment_path }}/docker-compose.staging.yml"
|
||||
pull: missing
|
||||
state: present
|
||||
recreate: always
|
||||
|
||||
- name: Wait for services to start
|
||||
pause:
|
||||
seconds: 15
|
||||
|
||||
- name: Check container status
|
||||
command: docker compose -f {{ deployment_path }}/docker-compose.base.yml -f {{ deployment_path }}/docker-compose.staging.yml ps
|
||||
register: container_status
|
||||
|
||||
- name: Display container status
|
||||
debug:
|
||||
var: container_status.stdout_lines
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
- "? Staging deployment completed!"
|
||||
- "Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
- "URL: https://staging.michaelschiemer.de"
|
||||
103
deployment/stacks/semaphore/playbooks/docker-build.yml
Normal file
103
deployment/stacks/semaphore/playbooks/docker-build.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
# Docker Build Playbook f?r Semaphore
|
||||
# Baut Docker Image und pusht es zur Registry
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
hosts: localhost
|
||||
gather_facts: no
|
||||
vars:
|
||||
repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}"
|
||||
repo_branch: "{{ repo_branch | default('main') }}"
|
||||
build_dir: "/tmp/ci-build"
|
||||
registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}"
|
||||
image_name: "{{ image_name | default('framework') }}"
|
||||
image_tag: "{{ image_tag | default('latest') }}"
|
||||
registry_user: "{{ registry_user | default('admin') }}"
|
||||
registry_password: "{{ registry_password | required }}"
|
||||
|
||||
tasks:
|
||||
- name: Clean up build directory
|
||||
file:
|
||||
path: "{{ build_dir }}"
|
||||
state: absent
|
||||
|
||||
- name: Checkout repository
|
||||
git:
|
||||
repo: "{{ repo_url }}"
|
||||
dest: "{{ build_dir }}"
|
||||
version: "{{ repo_branch }}"
|
||||
force: yes
|
||||
register: git_result
|
||||
|
||||
- name: Get short commit SHA
|
||||
shell: echo "{{ git_result.after }}" | cut -c1-7
|
||||
register: short_sha
|
||||
|
||||
- name: Generate image tags
|
||||
set_fact:
|
||||
tags:
|
||||
- "{{ registry_url }}/{{ image_name }}:latest"
|
||||
- "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
- "{{ registry_url }}/{{ image_name }}:git-{{ short_sha.stdout }}"
|
||||
|
||||
- name: Display image tags
|
||||
debug:
|
||||
msg:
|
||||
- "??? Building Docker image..."
|
||||
- "Registry: {{ registry_url }}"
|
||||
- "Image: {{ image_name }}"
|
||||
- "Tags: {{ tags | join(', ') }}"
|
||||
|
||||
- name: Ensure Docker is available
|
||||
command: docker --version
|
||||
register: docker_version
|
||||
|
||||
- name: Display Docker version
|
||||
debug:
|
||||
msg: "Docker version: {{ docker_version.stdout }}"
|
||||
|
||||
- name: Login to Docker registry
|
||||
docker_login:
|
||||
username: "{{ registry_user }}"
|
||||
password: "{{ registry_password }}"
|
||||
registry_url: "{{ registry_url }}"
|
||||
register: login_result
|
||||
|
||||
- name: Verify registry login
|
||||
debug:
|
||||
msg: "? Successfully logged in to {{ registry_url }}"
|
||||
when: login_result.failed == false
|
||||
|
||||
- name: Build Docker image
|
||||
docker_image:
|
||||
name: "{{ registry_url }}/{{ image_name }}"
|
||||
tag: "{{ image_tag }}"
|
||||
source: build
|
||||
build:
|
||||
path: "{{ build_dir }}"
|
||||
dockerfile: Dockerfile.production
|
||||
push: yes
|
||||
state: present
|
||||
register: build_result
|
||||
|
||||
- name: Tag image with additional tags
|
||||
docker_image:
|
||||
name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}"
|
||||
repository: "{{ registry_url }}/{{ image_name }}"
|
||||
tag: "{{ item }}"
|
||||
source: local
|
||||
push: yes
|
||||
state: present
|
||||
loop:
|
||||
- "latest"
|
||||
- "git-{{ short_sha.stdout }}"
|
||||
when: build_result.changed
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
- "? Docker image built and pushed successfully!"
|
||||
- "Registry: {{ registry_url }}"
|
||||
- "Image: {{ image_name }}"
|
||||
- "Tags: {{ tags | join(', ') }}"
|
||||
- "Commit: {{ git_result.after }}"
|
||||
Reference in New Issue
Block a user