Implementation Plan · solcom

Deploy SOLCOM auf Mittwald via Azure DevOps — Bootstrap + erste Pipeline

Kontext (Stand 2026-05-06) Scope: nur Test-Pfad (Mittwald-App staging, App-ID 61284ca9-0a65-495d-bda9-2edfe8bf26a1). Bezug: ADR-0019. Phasen 1–6 erledigt. Offen: Cronjobs (Phase 7), Document-Root-Mapping + Domain-Mapping (Phase 8). Betriebliche Besonderheit: SSH-Deploy-User landet auf SSH-Gateway (100.121.108.117), nicht PHP-App-Host. DB-User: dbu_dqgxag_1. Details: docs/context/0007-mittwald-staging-live.md.
Status
In Progress
Phasen gesamt
8
Erledigt
6 / 8
Scope
Staging only
01

Milestones

8 Phasen als Tracer-Bullets — jede Phase produziert einen definierten lauffähigen Endzustand. Phase 1+2+3 = erster Lebenszeichen-Deploy. Phase 5+6 = Auto-CI.

Phase 1

Mittwald-Bootstrap

MySQL-DB (mysql_dqgxag) + Redis anlegen, mit staging-App verknüpfen, Capistrano-Layout-Verzeichnisse (releases/, shared/files/, shared/private/) via mw app exec anlegen. App-Root ermitteln → in Azure-Variable-Group eintragen.

erledigtmStudiomw-CLI
Phase 2

Repo-Vorbereitung

drupal/redis installieren + config:export. web/sites/example.settings.mittwald.php als Template (DB, Redis, trusted_host_patterns, reverse_proxy). settings.php conditional include. .gitignore ergänzen.

erledigtsettings.mittwald.phpdrupal/redis
Phase 3

Erster manueller Deploy (Capistrano-Layout)

Build-Artefakt lokal: composer install --no-dev + npm build. Upload via mw app upload in releases/$RELEASE. Shared-Symlinks setzen (files/, private/, settings.mittwald.php). Atomarer Symlink-Switch current → releases/$RELEASE. Kein drush site:install — DB folgt in Phase 4.

erledigtcapistrano-layoutmw-app-upload
Phase 4

DB-Befüllung (DDEV-Dump)

ddev export-db → optional MariaDB/MySQL Collation-Sanity-Sed → Restore via mw db push oder SSH+mysql. Config-Re-Import + cache:rebuild. shared/files/ via rsync rüberziehen.

erledigt 2026-05-06mw-database-port-forward
Phase 5

Azure-DevOps-Pipeline + Variable Group

Variable Group solcom-mittwald-secrets-test mit MITTWALD_API_TOKEN, MITTWALD_PROJECT_ID, MITTWALD_APP_ID_TEST, MITTWALD_APP_ROOT_TEST, MITTWALD_TEST_URL. azure-pipelines.yml mit 4 Stages (PR-Validation, Build, DeployTest, DeployProd). scripts/smoke-test.sh.

erledigtazure-pipelines.ymlvariable-group
Phase 6

Erster CI-Deploy + Smoke-Test

Trockenlauf-PR → PR-Validation läuft grün → Merge → Build + DeployTest automatisch → Test-Site hat neuen Stand in ~5–10 Min. Rollback-Trockenlauf: atomarer Symlink-Switch auf vorletzten Release via Manual Job.

erledigt 2026-05-06end-to-end-pipelinesmoke-test-aktiv
Phase 7

Cronjobs aktivieren

drupal-cron-15min (*/15 * * * * drush cron) und solcom-vacancy-migrate-hourly (0 * * * * drush migrate:import solcom_vacancy --update) via mStudio anlegen. Manuelle Trigger-Verifikation. Watchdog-Logs prüfen.

offenmStudio-Cronjobs
Phase 8

Stabilisierung / Übergabe

Document Root auf /current/web setzen. Domain p-nxm58l.project.space auf App mappen. End-to-End-Smoke-Suite (Homepage, /admin, Vacancy-Detail, Image-Styles). Performance-Spot-Check. Runbook docs/runbooks/deploy-test.md. Folge-ADR für Prod-Bootstrap queuen.

in Arbeitdocument-rootdomain-mapping
02

Pipeline-Architektur

4 Stages in azure-pipelines.yml. Alle Stages teilen sich den Build-Artifact via Azure Pipelines Artifact-Store. DeployProd erfordert Manual-Approval-Gate.

Stage 1 + 2 — PR-Validation + Build
  • PR-Validation (Trigger: pr: branches: [main]) — composer install, phpcs, phpstan, PHPUnit Unit + Kernel
  • Build (Trigger: trigger: branches: [main], tags: [v*]) — composer install --no-dev, npm build (byte_theme), tarball, publish artifact
  • Maintenance-Window < 60 s dank vorgebautem Artifact
Stage 3 + 4 — DeployTest + DeployProd
  • DeployTest — download artifact, mw app upload → Shared-Symlinks → atomarer Symlink-Switch → smoke-test.sh → cleanup releases/ auf 5
  • DeployProd (Trigger: tags: [v*], Manual-Approval-Gate) — gleiche Sequenz gegen MITTWALD_APP_ID_PRODUCTION
  • Variable Group solcom-mittwald-secrets-test: MITTWALD_API_TOKEN (secret), APP_ID, APP_ROOT, TEST_URL
03

Key Code

Kritischste Stellen: Smoke-Test-Skript und settings.mittwald.php Conditional-Include-Pattern.

scripts/smoke-test.sh
#!/usr/bin/env bash
set -euo pipefail

APP_ID="${1:?app-id required}"
APP_ROOT="${2:?app-root required}"
URL="${3:?public URL required}"

mw app exec \
  --installation-id="$APP_ID" \
  "$APP_ROOT/current/vendor/bin/drush \
   --root=$APP_ROOT/current/web \
   status --field=bootstrap" \
  | grep -q "Successful"

curl -sf -o /dev/null \
  -w "%{http_code}\n" "$URL" \
  | grep -qE "^(200|301|302)$"
web/sites/default/settings.php — Conditional Include
// Anhängen ans Ende von settings.php:
if (file_exists(
  $app_root . '/' . $site_path
  . '/settings.mittwald.php'
)) {
  include $app_root . '/'
    . $site_path
    . '/settings.mittwald.php';
}

// settings.mittwald.php liegt auf Mittwald
// unter /shared/ und wird pro Release
// nach web/sites/default/ symlinkt.
// .gitignore: settings.mittwald.php
04

Risks & Mitigations

Risk
Sev
Mitigation
MariaDB ↔ MySQL Engine-Mismatch bei DB-Restore (utf8mb4_uca1400_ai_ci vs. utf8mb4_0900_ai_ci).
HIGH
Phase-4-Sanity-Sed: sed -i.bak 's/utf8mb4_uca1400_ai_ci/utf8mb4_0900_ai_ci/g'. Wenn Restore systematisch hängt: DDEV-Engine in Folge-ADR umstellen.
API-Erreichbarkeit Mittwald → SOLCOM-Backend für Vacancy-Migration (Firewall/IP-Whitelist).
HIGH
Phase-1.8-Pre-Check: curl -sI auf Vacancy-Endpoint. Bei 401/403/timeout: Firewall-Klärung mit Backend-Team vor Phase 7.
SSH-Deploy-User landet auf SSH-Gateway, nicht PHP-App-Host — mw app exec-Befehle schlagen fehl.
MED
Etabliertes Pattern: immer via mw app exec --installation-id=... arbeiten, nie direkt SSH auf App-Host. Details in docs/context/0007-mittwald-staging-live.md.
composer.lock veraltet in CI → Drift gegen lokale DDEV-Umgebung.
MED
composer.lock ist immer committed. CI führt composer install (nicht update) aus.
mw app upload ohne --delete addiert alte Files (keine Cleanup-Semantik).
LOW
Cleanup-Step in Phase 5 DeployTest-Stage: releases/ explizit auf 5 begrenzen via mw app exec rm -rf.
05

Open Questions

Document Root + Domain-Mapping noch ausstehend (Phase 8)
Document Root muss in mStudio auf /current/web gesetzt werden (macht App temporär 404 bis Symlink existiert). Domain p-nxm58l.project.space auf App mappen. Beide Schritte aus Phase 1 wurden bewusst auf Phase 8 verschoben, weil zunächst kein externer Traffic erwartet wird.
Erledigen in Phase 8 · marc
Prod-App-Provisionierung (Post-Staging-Stabilisierung)
Prod-Bootstrap ist explizit out-of-scope für diesen Plan. Sobald Staging-Stabilisierung abgeschlossen: eigener ADR (höhere Nummer als ADR-0019) + eigener Plan. Vector-DB-Anbindung und Custom-Test-Domain test.solcom.de ebenfalls Prod-Plan-Items.
Deferred · nach Phase 8 · marc