Neue Funktionalität bauen¶
Neue Funktionalität ist eine Änderung im Code-Repository. Eine Änderung, die nur andere Werte braucht, ist eine Konfigurationsänderung; siehe Konfiguration. Jedes neue Feature folgt den Regeln zur Abwärtskompatibilität: opt-in, standardmäßig aus.
flowchart LR
a["1. Module"] --> b["2. Wire into a layer<br/>+ feature flag"]
b --> c["3. Test"]
c --> d["4. Deploy to dev<br/>(local / Demo canary)"]
d --> e["5. PR + CI"]
e --> f["Release"]
0. Deine Umgebung einrichten¶
- Klone
xms-cloud-platform-codeund öffne es im Dev Container (VS Code: Reopen in Container). Er enthält PowerShell 7, Terraform, die Azure CLI und Pester. - Der Dev Container mountet
../config, relativ zum Code-Repository, nach/workspaces/config. Klone ein Konfigurations- Repository alsconfigneben den Code oder nutze die Demo-Konfiguration inconfig/Demo/canary. - Kopiere
.env.examplenach.envund setzeMCP_CONFIG, zum BeispielMCP_CONFIG=/workspaces/xms-cloud-platform-code/config/Demo/canary/mcp.json. - Installiere die Git-Hooks:
lefthook install. - Branche von
mainab.feature/<name>erhält ein GitVersion-Label (-<name>);users/<initials>/<name>ist die übliche Konvention für persönliche Arbeit.
1. Module¶
Terraform ist in drei Ebenen organisiert:
| Ebene | Ordner | Regel |
|---|---|---|
| Module | infra/modules/<name>/ |
Ein Ressourcentyp oder eine enge Gruppe. Kein Wissen über Layer oder Config-Dateien. Dateien: main.tf, variables.tf, output.tf/outputs.tf, versions.tf, optional tests/. |
| Template | infra/templates/<name>/ |
Setzt Module zu einem Feature zusammen (zum Beispiel application_gateway, virtual_hub). Berechnet Namen aus den scaffold_*-Variablen. |
| Layer | infra/deploy/<layer>/ |
Root Module mit Backend, Providern und Variablen. Ruft Templates hinter Feature Flags auf. Variablen sind nach Feature aufgeteilt: variables_<feature>.tf. |
Richtlinien (siehe auch .agents/skills/terraform-style-guide im Code-Repository):
- Führe
terraform fmtaus. Nutzecreate_*/use_*_module-Booleans für optionale Features und*_id-Inputs für Abhängigkeiten. - Nutze
dynamic-Blöcke für optionale verschachtelte Konfiguration. - Bilde Namen aus
scaffold_company_short_name,scaffold_environmentundscaffold_location_short_nameund füge eineoverwrite_*_name-Variable hinzu, damit Kunden mit bestehenden Namen diese behalten können. - Wenn das Feature JSON aus dem Konfigurations-Repository liest, nimm eine
*_relative_path-Variable und bilde den Pfad als"${var.config_path}/${var.<x>_relative_path}". Denk daran, dassfileset()bei einem falschen Pfad nichts zurückgibt. - Pinne Provider in Root Modules auf eine exakte Version. Sonst schlägt der Pin-Check fehl.
Spoke-Features¶
Das spoke-Terraform liegt in src/Xebia.Mcp/Files/terraform/spoke-canvas/ (deploy/subscription, deploy/spoke,
modules/). Eine neue spoke-Eigenschaft braucht:
- das Terraform, das sie liest, mit einem Standardwert für den Fall, dass die Eigenschaft fehlt;
- eine optionale Eigenschaft in
src/Xebia.Mcp/Files/json/spokeschema.json; - eine Zeile in der spoke-JSON-Referenz.
2. In einen Layer einbinden, hinter einem Flag¶
# infra/deploy/connectivity/variables_my_feature.tf
variable "use_my_feature_module" {
description = "Deploy my feature. Default false keeps existing deployments unchanged."
type = bool
default = false
}
# infra/deploy/connectivity/main.tf
module "my_feature" {
source = "../../templates/my_feature"
count = var.use_my_feature_module ? 1 : 0
# ...
}
Danach:
- füge das Flag und seine zugehörigen Variablen zur Demo-Konfiguration hinzu (
config/Demo/canary/<region>/<layer>.auto.tfvars); - füge das Flag zur Feature-Flag-Referenz hinzu;
- falls Konsumenten oder Operatoren es anfragen werden, ergänze oder erweitere ein Runbook unter Häufige Anfragen;
- falls die Entscheidung architektonisch ist, schreibe einen ADR (
docs/adr/, Vorlageadr-template.md).
Änderungen an der Orchestrierung¶
Logik, die entscheidet, was läuft (if/else, Schleifen, Matrizen, Parsing), gehört in eine PowerShell-Funktion in
src/Xebia.Mcp/Functions/, nicht in YAML (ADR-0011, CONTRIBUTING.md):
- Öffentliche Funktionen in
Functions/Public/<Domain>/, interne inFunctions/Private/<Domain>/. - Comment-based Help mit
.SYNOPSIS,.DESCRIPTION,.PARAMETER,.EXAMPLE,.NOTES. begin/clean-Blöcke, diebegin.ps1/clean.ps1per Dot-Sourcing einbinden.- Ändere das Azure-DevOps-Template und den GitHub-Workflow im selben pull request.
3. Test¶
| Test | Wo | Ausführen mit | Läuft in |
|---|---|---|---|
| Terraform validate + docs | jeder Layer | automatisch beim Commit | lefthook pre-commit |
| Terraform-Modul-Tests | infra/modules/<name>/tests/*.tftest.hcl (mit mock_provider) |
terraform -chdir=infra/modules/<name> test, oder automatisch für gestagte Module (scripts/Run-TerraformTest.ps1) |
lefthook pre-commit |
| Provider-Pin-Check | infra/deploy/** |
pwsh scripts/Test-TerraformRootProviderPins.ps1 |
lefthook + GitHub CI |
| PowerShell-Unit-Tests | Functions/**/Tests/<Function>.Unit.Tests.ps1 |
pwsh ./dev/dev.ps1 -Action Test |
Prepare-Schritt jeder Pipeline |
| Paritätsprüfungen | modulweit (Dateiname = Funktionsname, jede öffentliche Funktion hat einen Unit-Test, …) | pwsh ./dev/dev.ps1 -Action Test |
Prepare-Schritt |
| Config-Tests | src/Xebia.Mcp/Tests/{Config,SpokeConfig,Smoke} |
pwsh ./dev/dev.ps1 -Action Test (verwendet MCP_CONFIG) |
Prepare-Schritt jeder Pipeline |
Jede öffentliche Funktion braucht einen Unit-Test; sonst schlägt Assert-XebiaPublicFunctionUnitTestParity fehl. Mocke Azure-CLI- und
Terraform-Aufrufe.
4. Auf dev deployen¶
Das Entwicklungsziel ist der Demo-Tenant, Konfiguration config/Demo/canary im Code-Repository.
Lokal (schnelles Feedback, dein eigener Azure-Login):
Import-Module ./src/Xebia.Mcp/Xebia.Mcp.psd1 -Force
Initialize-XebiaDotEnv
Sync-XebiaInfraToMcpModule -Mappings @(@{ Description = 'cloud-platform'; Source = './infra'; Target = 'terraform/cloud-platform/' }) -WhatIf:$false
$config = Resolve-XebiaMcpConfiguration -Path $env:MCP_CONFIG
# Plan one layer (WhatIf on: plan, no apply) — same as dev.ps1 does by default
$global:WhatIfPreference = $true
Deploy-XebiaLocal -Config $config -DeployName connectivity -Region weu
# Plan and apply
$global:WhatIfPreference = $false
Deploy-XebiaLocal -Config $config -DeployName connectivity -Region weu
(dev/dev.ps1 -Action HubDeploy ist die vorgesehene Abkürzung, ist aber auf main defekt, siehe
Bekannte Probleme.)
Über Workflow/Pipeline (wie bei Kunden): Führe die Entry Pipelines des Code-Repositorys (hub.yaml, spoke.yaml)
von deinem Branch aus gegen die Demo-Konfiguration aus. Nutze mode: Plan oder ApproveModifications.
Prüfe zwei Dinge:
- Mit ausgeschaltetem Flag: Der Plan zeigt keine Änderungen. Das belegt die Abwärtskompatibilität.
- Mit eingeschaltetem Flag: Der Plan zeigt nur die neuen Ressourcen, und das Apply funktioniert.
5. Pull Request und CI¶
- Committe mit Conventional Commits (
feat(connectivity): add …,fix(ps): …), auf US-Englisch (docs/coding-standards/). - Öffne einen pull request nach
main. Beschreibe die Änderung und das Flag und verlinke das Work Item. - Setze den Versionssprung in die Merge-Message:
+semver: minorfür ein Feature,+semver: patchfür einen Fix,+semver: majorfür einen Breaking Change,+semver: nonenur für Doku. Siehe Releasing. -
CI läuft:
Plattform Pipeline Was sie macht Azure DevOps pull-request.yaml(PR nachmain)Baut den Container aus dem PR-Branch und führt dann die hub-Stages (deploy und destroy) und spoke-Stages (deploy und destroy, Glob **/*str*.json) im Plan / WhatIf gegen canary ausGitHub ci.yml(PR, derinfra/deploy/**berührt)Provider-Pin-Check -
Ein Reviewer prüft: Flag standardmäßig aus,
moved-Blöcke bei Umbenennungen, Unit-Tests, sowohl ADO als auch GitHub geändert, Doku aktualisiert. - Merge. Wenn der Fix auch in eine unterstützte Version muss, cherry-picke ihn auf den Branch
release/vX.Y(siehe Releasing).