diff --git a/.github/scripts/adr-manager.sh b/.github/scripts/adr-manager.sh new file mode 100755 index 0000000..c001e45 --- /dev/null +++ b/.github/scripts/adr-manager.sh @@ -0,0 +1,372 @@ +#!/bin/bash +# Architecture Decision Record (ADR) Manager +# Generates, updates, and indexes ADRs for Git-Ape deployments + +set -euo pipefail + +ADRS_DIR=".azure/adrs" +DEPLOYMENTS_DIR=".azure/deployments" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Get the next ADR number +get_next_adr_number() { + local LAST_NUM=0 + if [[ -d "$WORKSPACE_ROOT/$ADRS_DIR" ]]; then + LAST_NUM=$(ls "$WORKSPACE_ROOT/$ADRS_DIR" 2>/dev/null | grep -oP '^\d+' | sort -n | tail -1 || echo "0") + fi + echo $((LAST_NUM + 1)) +} + +# Format ADR number with zero-padding (4 digits) +format_adr_number() { + printf "%04d" "$1" +} + +# Command: generate +# Generate an ADR from a deployment +generate_adr() { + local DEPLOYMENT_ID="$1" + local DEPLOYMENT_PATH="$WORKSPACE_ROOT/$DEPLOYMENTS_DIR/$DEPLOYMENT_ID" + + if [[ ! -d "$DEPLOYMENT_PATH" ]]; then + echo -e "${RED}Deployment not found: $DEPLOYMENT_ID${NC}" + exit 1 + fi + + if [[ ! -f "$DEPLOYMENT_PATH/metadata.json" ]]; then + echo -e "${RED}metadata.json not found for deployment: $DEPLOYMENT_ID${NC}" + exit 1 + fi + + # Read deployment metadata + local STATUS + STATUS=$(jq -r '.status // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local PROJECT + PROJECT=$(jq -r '.project // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local ENVIRONMENT + ENVIRONMENT=$(jq -r '.environment // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local REGION + REGION=$(jq -r '.region // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local TIMESTAMP + TIMESTAMP=$(jq -r '.timestamp // empty' "$DEPLOYMENT_PATH/metadata.json") + TIMESTAMP="${TIMESTAMP:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" + local USER + USER=$(jq -r '.user // .createdBy // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local RESOURCE_GROUP + RESOURCE_GROUP=$(jq -r '.resourceGroup // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + + # Read resources + local RESOURCES_LIST="" + if jq -e '.resources' "$DEPLOYMENT_PATH/metadata.json" > /dev/null 2>&1; then + RESOURCES_LIST=$(jq -r '.resources[] | "- \(.type) / \(.name)"' "$DEPLOYMENT_PATH/metadata.json" 2>/dev/null || echo "") + fi + + # Read requirements context + local CONTEXT_TEXT="Deployment of Azure resources for the $PROJECT project in $ENVIRONMENT environment." + if [[ -f "$DEPLOYMENT_PATH/requirements.json" ]]; then + local REQ_TYPE + REQ_TYPE=$(jq -r '.type // "single-resource"' "$DEPLOYMENT_PATH/requirements.json") + local REQ_RESOURCES + REQ_RESOURCES=$(jq -r '.resources[]? | "- \(.type) (\(.name)) in \(.region)"' "$DEPLOYMENT_PATH/requirements.json" 2>/dev/null || echo "") + if [[ -n "$REQ_RESOURCES" ]]; then + CONTEXT_TEXT="$CONTEXT_TEXT + +Requested resources: +$REQ_RESOURCES" + fi + fi + + # Read WAF review trade-offs if available + local TRADEOFFS="" + if [[ -f "$DEPLOYMENT_PATH/waf-review.md" ]]; then + TRADEOFFS=$(sed -n '/## Trade-off/,/## /p' "$DEPLOYMENT_PATH/waf-review.md" | head -20 || echo "") + if [[ -z "$TRADEOFFS" ]]; then + TRADEOFFS="See [WAF Review](../deployments/$DEPLOYMENT_ID/waf-review.md) for full assessment." + fi + fi + + # Read architecture diagram reference + local ARCHITECTURE_REF="" + if [[ -f "$DEPLOYMENT_PATH/architecture.md" ]]; then + ARCHITECTURE_REF="See [Architecture Diagram](../deployments/$DEPLOYMENT_ID/architecture.md) for the full resource topology." + fi + + # Read cost estimate if available + local COST_INFO="" + if jq -e '.estimatedMonthlyCost' "$DEPLOYMENT_PATH/metadata.json" > /dev/null 2>&1; then + COST_INFO=$(jq -r '.estimatedMonthlyCost' "$DEPLOYMENT_PATH/metadata.json") + fi + + # Determine ADR number and title + local ADR_NUM + ADR_NUM=$(get_next_adr_number) + local ADR_NUM_FMT + ADR_NUM_FMT=$(format_adr_number "$ADR_NUM") + local TITLE="Deploy ${PROJECT} ${ENVIRONMENT} infrastructure in ${REGION}" + local ADR_FILENAME="${ADR_NUM_FMT}-deploy-${PROJECT}-${ENVIRONMENT}.md" + + # Create ADR directory + mkdir -p "$WORKSPACE_ROOT/$ADRS_DIR" + + # Generate ADR content + cat > "$WORKSPACE_ROOT/$ADRS_DIR/$ADR_FILENAME" < "$DEPLOYMENT_PATH/metadata.json.tmp" \ + && mv "$DEPLOYMENT_PATH/metadata.json.tmp" "$DEPLOYMENT_PATH/metadata.json" + fi + + # Update the index + update_index + + echo -e "${GREEN}Generated ADR: $ADRS_DIR/$ADR_FILENAME${NC}" + echo " Number: ADR-${ADR_NUM_FMT}" + echo " Title: ${TITLE}" + echo " Deployment: ${DEPLOYMENT_ID}" +} + +# Command: amend +# Amend an existing ADR when a deployment is updated or destroyed +amend_adr() { + local DEPLOYMENT_ID="$1" + local REASON="${2:-Deployment updated}" + local DEPLOYMENT_PATH="$WORKSPACE_ROOT/$DEPLOYMENTS_DIR/$DEPLOYMENT_ID" + + if [[ ! -f "$DEPLOYMENT_PATH/metadata.json" ]]; then + echo -e "${RED}metadata.json not found for deployment: $DEPLOYMENT_ID${NC}" + exit 1 + fi + + # Find linked ADR + local ADR_FILE + ADR_FILE=$(jq -r '.adrFile // empty' "$DEPLOYMENT_PATH/metadata.json") + + if [[ -z "$ADR_FILE" || ! -f "$WORKSPACE_ROOT/$ADR_FILE" ]]; then + echo -e "${YELLOW}No ADR linked to deployment $DEPLOYMENT_ID, generating new one${NC}" + generate_adr "$DEPLOYMENT_ID" + return + fi + + local STATUS + STATUS=$(jq -r '.status // "unknown"' "$DEPLOYMENT_PATH/metadata.json") + local TIMESTAMP + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + + # Append amendment to the ADR + local AMENDMENT_TEXT="### Amendment — ${TIMESTAMP%%T*} + +- **Reason:** ${REASON} +- **New Status:** ${STATUS} +- **Deployment:** ${DEPLOYMENT_ID} +- **Date:** ${TIMESTAMP}" + + # Replace or append to amendments section + if grep -q "^_No amendments yet._$" "$WORKSPACE_ROOT/$ADR_FILE"; then + # Remove the placeholder and append amendment + sed -i '/^_No amendments yet._$/d' "$WORKSPACE_ROOT/$ADR_FILE" + echo "" >> "$WORKSPACE_ROOT/$ADR_FILE" + echo "$AMENDMENT_TEXT" >> "$WORKSPACE_ROOT/$ADR_FILE" + else + echo "" >> "$WORKSPACE_ROOT/$ADR_FILE" + echo "$AMENDMENT_TEXT" >> "$WORKSPACE_ROOT/$ADR_FILE" + fi + + # Update ADR status if deployment was destroyed + if [[ "$STATUS" == "destroyed" || "$STATUS" == "destroy-requested" ]]; then + sed -i 's/^Accepted$/Superseded/' "$WORKSPACE_ROOT/$ADR_FILE" + fi + + # Update index + update_index + + echo -e "${GREEN}Amended ADR: $ADR_FILE${NC}" + echo " Reason: ${REASON}" + echo " Status: ${STATUS}" +} + +# Command: index +# Rebuild the ADR index +update_index() { + local INDEX_FILE="$WORKSPACE_ROOT/$ADRS_DIR/INDEX.md" + mkdir -p "$WORKSPACE_ROOT/$ADRS_DIR" + + cat > "$INDEX_FILE" <<'EOF' +# Architecture Decision Records + +This index is auto-maintained by Git-Ape. Do not edit manually. + +| # | Title | Status | Date | Deployment | +|---|-------|--------|------|------------| +EOF + + # Parse each ADR file and add to index + for ADR_FILE in $(ls "$WORKSPACE_ROOT/$ADRS_DIR"/*.md 2>/dev/null | grep -v INDEX.md | sort); do + local FILENAME + FILENAME=$(basename "$ADR_FILE") + local NUM + NUM=$(echo "$FILENAME" | grep -oP '^\d+' || echo "?") + local TITLE + TITLE=$(head -1 "$ADR_FILE" | sed 's/^# //') + local STATUS + STATUS=$(sed -n '/^## Status$/,/^## /{/^## Status$/d;/^## /d;/^$/d;p;}' "$ADR_FILE" | head -1 | xargs) + local DATE + DATE=$(sed -n '/^## Date$/,/^## /{/^## Date$/d;/^## /d;/^$/d;p;}' "$ADR_FILE" | head -1 | xargs) + local DEPLOY_ID + DEPLOY_ID=$(sed -n 's/.*\*\*Deployment ID:\*\* //p' "$ADR_FILE" | head -1 | xargs) + + echo "| ${NUM} | [${TITLE}](./${FILENAME}) | ${STATUS} | ${DATE} | ${DEPLOY_ID:-—} |" >> "$INDEX_FILE" + done + + echo -e "${GREEN}Updated ADR index: $ADRS_DIR/INDEX.md${NC}" +} + +# Command: list +# List all ADRs +list_adrs() { + echo -e "${BLUE}Architecture Decision Records${NC}" + echo "-----------------------------------------------------------" + + if [[ ! -d "$WORKSPACE_ROOT/$ADRS_DIR" ]]; then + echo -e "${YELLOW}No ADRs found${NC}" + return 0 + fi + + for ADR_FILE in $(ls "$WORKSPACE_ROOT/$ADRS_DIR"/*.md 2>/dev/null | grep -v INDEX.md | sort); do + local FILENAME + FILENAME=$(basename "$ADR_FILE") + local TITLE + TITLE=$(head -1 "$ADR_FILE" | sed 's/^# //') + local STATUS + STATUS=$(sed -n '/^## Status$/,/^## /{/^## Status$/d;/^## /d;/^$/d;p;}' "$ADR_FILE" | head -1 | xargs) + + case "$STATUS" in + "Accepted") + echo -e " ${GREEN}✓${NC} ${TITLE} [${STATUS}]" + ;; + "Superseded") + echo -e " ${YELLOW}○${NC} ${TITLE} [${STATUS}]" + ;; + *) + echo -e " ${BLUE}•${NC} ${TITLE} [${STATUS}]" + ;; + esac + done +} + +# Main command dispatcher +main() { + local COMMAND="${1:-}" + + case "$COMMAND" in + generate) + if [[ -z "${2:-}" ]]; then + echo "Usage: $0 generate " + exit 1 + fi + generate_adr "$2" + ;; + amend) + if [[ -z "${2:-}" ]]; then + echo "Usage: $0 amend [reason]" + exit 1 + fi + amend_adr "$2" "${3:-Deployment updated}" + ;; + index) + update_index + ;; + list) + list_adrs + ;; + *) + echo "Architecture Decision Record (ADR) Manager" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "Commands:" + echo " generate Generate ADR from a deployment" + echo " amend [reason] Amend ADR for an updated deployment" + echo " index Rebuild the ADR index" + echo " list List all ADRs" + echo "" + echo "Examples:" + echo " $0 generate deploy-20260218-143022" + echo " $0 amend deploy-20260218-143022 \"Scaled to production SKU\"" + echo " $0 index" + exit 1 + ;; + esac +} + +# Run main function +main "$@" diff --git a/.github/scripts/deployment-manager.sh b/.github/scripts/deployment-manager.sh index 815738f..af78191 100755 --- a/.github/scripts/deployment-manager.sh +++ b/.github/scripts/deployment-manager.sh @@ -110,6 +110,18 @@ show_deployment() { jq '.tests[] | {name, status, result}' "$DEPLOYMENT_PATH/tests.json" fi + # Show linked ADR if available + if [[ -f "$DEPLOYMENT_PATH/metadata.json" ]]; then + local ADR_FILE + ADR_FILE=$(jq -r '.adrFile // empty' "$DEPLOYMENT_PATH/metadata.json") + if [[ -n "$ADR_FILE" && -f "$WORKSPACE_ROOT/$ADR_FILE" ]]; then + local ADR_NUM + ADR_NUM=$(jq -r '.adrNumber // "?"' "$DEPLOYMENT_PATH/metadata.json") + echo -e "\n${GREEN}Architecture Decision Record:${NC}" + echo " ADR-$(printf "%04d" "$ADR_NUM"): $ADR_FILE" + fi + fi + # Show errors if any if [[ -f "$DEPLOYMENT_PATH/error.log" ]]; then echo -e "\n${RED}Errors:${NC}" diff --git a/.github/skills/adr-generator/SKILL.md b/.github/skills/adr-generator/SKILL.md new file mode 100644 index 0000000..3534660 --- /dev/null +++ b/.github/skills/adr-generator/SKILL.md @@ -0,0 +1,126 @@ +--- +name: adr-generator +description: 'Generate and manage Architecture Decision Records (ADRs) for deployments. Auto-creates ADRs after successful deployment, maintains an index, and amends records when deployments are updated or destroyed.' +argument-hint: 'Deployment ID to generate or amend an ADR for' +user-invocable: true +--- + +# ADR Generator + +Generate and manage Architecture Decision Records (ADRs) that document deployment decisions, trade-offs, and architecture choices. + +## When to Use + +- After a successful deployment (auto-triggered by deploy workflow) +- When a deployment is updated or redeployed +- When a deployment is destroyed (amend existing ADR) +- To rebuild the ADR index + +## ADR Format + +Each ADR follows a standard structure: + +```markdown +# ADR-NNNN: Title + +## Status +Accepted | Superseded | Deprecated + +## Date +YYYY-MM-DD + +## Deployment +- Deployment ID, project, environment, region, resource group + +## Context +Why was this deployment needed? What requirements drove it? + +## Decision +What was deployed? What configuration choices were made? + +## Consequences +### Positive +### Negative +### Trade-offs + +## Architecture +Reference to architecture diagram + +## Amendments +Changes to the deployment over time +``` + +## Procedure + +### 1. Generate ADR After Deployment + +After a successful deployment, run: + +```bash +.github/scripts/adr-manager.sh generate +``` + +This will: +- Read deployment metadata, requirements, and WAF review +- Generate a numbered ADR in `.azure/adrs/` +- Link the ADR back to `metadata.json` via `adrFile` and `adrNumber` fields +- Update the ADR index + +### 2. Amend ADR on Update or Destroy + +When a deployment is updated or destroyed: + +```bash +.github/scripts/adr-manager.sh amend "Reason for change" +``` + +This will: +- Find the linked ADR from metadata +- Append an amendment entry with date, reason, and new status +- Update ADR status to "Superseded" if deployment was destroyed +- Update the ADR index + +### 3. Rebuild Index + +To regenerate the index from all existing ADRs: + +```bash +.github/scripts/adr-manager.sh index +``` + +### 4. List ADRs + +```bash +.github/scripts/adr-manager.sh list +``` + +## Directory Structure + +``` +.azure/adrs/ +├── INDEX.md # Auto-maintained index +├── 0001-deploy-api-dev.md # First ADR +├── 0002-deploy-webapp-prod.md # Second ADR +└── ... +``` + +## Integration with Deployment Workflow + +The ADR generation is integrated into: + +1. **`git-ape-deploy.exampleyml`** — After successful deployment, generates an ADR and commits it alongside `state.json` +2. **`git-ape-destroy.exampleyml`** — Amends the ADR when a deployment is destroyed +3. **`metadata.json`** — Contains `adrFile` and `adrNumber` fields linking to the ADR + +## Metadata Integration + +After ADR generation, `metadata.json` is updated with: + +```json +{ + "adrFile": ".azure/adrs/0001-deploy-api-dev.md", + "adrNumber": 1 +} +``` + +This allows agents and scripts to navigate from a deployment to its decision record. diff --git a/.github/workflows/git-ape-deploy.exampleyml b/.github/workflows/git-ape-deploy.exampleyml index 48c6d71..ada2181 100644 --- a/.github/workflows/git-ape-deploy.exampleyml +++ b/.github/workflows/git-ape-deploy.exampleyml @@ -369,6 +369,12 @@ jobs: } EOF + - name: Generate ADR + if: steps.deploy.outputs.deploy_status == 'succeeded' + run: | + chmod +x .github/scripts/adr-manager.sh + .github/scripts/adr-manager.sh generate "${{ matrix.deployment_id }}" + - name: Commit deployment state if: always() run: | @@ -389,6 +395,7 @@ jobs: # Stash the updated state and metadata files before switching branches cp "$DEPLOY_DIR/state.json" /tmp/state.json 2>/dev/null || true cp "$DEPLOY_DIR/metadata.json" /tmp/metadata.json 2>/dev/null || true + cp -r .azure/adrs /tmp/adrs 2>/dev/null || true # Ensure we push to main regardless of which ref was checked out git fetch origin main @@ -397,8 +404,13 @@ jobs: # Restore the updated state and metadata files onto main cp /tmp/state.json "$DEPLOY_DIR/state.json" 2>/dev/null || true cp /tmp/metadata.json "$DEPLOY_DIR/metadata.json" 2>/dev/null || true + if [[ -d /tmp/adrs ]]; then + mkdir -p .azure/adrs + cp -r /tmp/adrs/* .azure/adrs/ 2>/dev/null || true + fi git add "$DEPLOY_DIR/state.json" "$DEPLOY_DIR/metadata.json" + git add .azure/adrs/ 2>/dev/null || true git diff --cached --quiet || git commit -m "git-ape: update state for ${{ matrix.deployment_id }} [$STATUS]" git push || echo "::warning::Could not push state update to main" diff --git a/.github/workflows/git-ape-destroy.exampleyml b/.github/workflows/git-ape-destroy.exampleyml index 1afc7ae..ea9791e 100644 --- a/.github/workflows/git-ape-destroy.exampleyml +++ b/.github/workflows/git-ape-destroy.exampleyml @@ -318,6 +318,18 @@ jobs: git diff --cached --quiet || git commit -m "git-ape: mark ${{ matrix.deployment_id }} as $STATUS" git push || echo "::warning::Could not push state update" + - name: Amend ADR + if: steps.destroy.outputs.destroy_status == 'succeeded' || steps.check.outputs.exists == 'false' + run: | + chmod +x .github/scripts/adr-manager.sh + .github/scripts/adr-manager.sh amend "${{ matrix.deployment_id }}" "Deployment destroyed" + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add .azure/adrs/ 2>/dev/null || true + git diff --cached --quiet || git commit -m "git-ape: amend ADR for ${{ matrix.deployment_id }} [destroyed]" + git push || echo "::warning::Could not push ADR amendment" + - name: Post summary if: always() run: | diff --git a/website/docs/deployment/adrs.md b/website/docs/deployment/adrs.md new file mode 100644 index 0000000..b86dd41 --- /dev/null +++ b/website/docs/deployment/adrs.md @@ -0,0 +1,131 @@ +--- +title: "Architecture Decision Records" +sidebar_label: "ADRs" +sidebar_position: 3 +description: "How Git-Ape auto-generates and tracks Architecture Decision Records" +--- + +# Architecture Decision Records (ADRs) + +:::warning +EXPERIMENTAL ONLY: ADR generation formats, templates, and lifecycle behavior may change at any time. +Do **not** rely on this feature for production governance or audit compliance. +::: + +Git-Ape automatically generates Architecture Decision Records after each successful deployment, creating a governing ledger of infrastructure decisions. + +## Overview + +ADRs document: +- **What** was deployed (resources, configuration) +- **Why** it was deployed (requirements, context) +- **Trade-offs** accepted (from WAF review) +- **Architecture** (diagram reference) +- **Evolution** over time (amendments when updated or destroyed) + +## Directory Structure + +``` +.azure/adrs/ +├── INDEX.md # Auto-maintained index +├── 0001-deploy-api-dev.md # First deployment ADR +├── 0002-deploy-webapp-prod.md # Second deployment ADR +└── ... +``` + +## ADR Template + +Each ADR follows a consistent format: + +```markdown +# ADR-NNNN: Title + +## Status +Accepted | Superseded | Deprecated + +## Date +YYYY-MM-DD + +## Deployment +- Deployment ID, project, environment, region, resource group, user + +## Context +Why was this deployment needed? What requirements drove it? + +## Decision +What was deployed? Configuration choices and rationale. + +## Consequences +### Positive +### Negative +### Trade-offs + +## Architecture +Reference to architecture diagram + +## Amendments +Changes over time (updates, scaling, destruction) +``` + +## Lifecycle + +### Generation + +ADRs are generated automatically after a successful deployment: +1. The deploy workflow calls `adr-manager.sh generate ` +2. The script reads `metadata.json`, `requirements.json`, and `waf-review.md` +3. A numbered ADR is created in `.azure/adrs/` +4. The deployment's `metadata.json` is updated with `adrFile` and `adrNumber` fields +5. The `INDEX.md` is rebuilt + +### Amendment + +When a deployment is updated or destroyed: +1. The workflow calls `adr-manager.sh amend "reason"` +2. The linked ADR is found via `metadata.json` +3. An amendment entry is appended with date, reason, and new status +4. If destroyed, the ADR status changes to "Superseded" +5. The index is updated + +### Index Maintenance + +The `INDEX.md` file is automatically regenerated whenever an ADR is created or amended. It provides a table listing all ADRs with their number, title, status, date, and linked deployment. + +## Integration with Deployments + +Each deployment's `metadata.json` contains: + +```json +{ + "adrFile": ".azure/adrs/0001-deploy-api-dev.md", + "adrNumber": 1 +} +``` + +This bidirectional link allows navigation from deployment → ADR and from ADR → deployment. + +## CLI Usage + +The ADR manager script supports manual operations: + +```bash +# Generate ADR for a deployment +.github/scripts/adr-manager.sh generate deploy-20260218-143022 + +# Amend ADR when deployment changes +.github/scripts/adr-manager.sh amend deploy-20260218-143022 "Scaled to production SKU" + +# Rebuild the index +.github/scripts/adr-manager.sh index + +# List all ADRs +.github/scripts/adr-manager.sh list +``` + +## Status Values + +| Status | Meaning | +|--------|---------| +| **Accepted** | Active deployment, decision in effect | +| **Superseded** | Deployment destroyed or replaced | +| **Deprecated** | Decision no longer recommended | diff --git a/website/docs/deployment/state.md b/website/docs/deployment/state.md index 5437a4f..07b1c5f 100644 --- a/website/docs/deployment/state.md +++ b/website/docs/deployment/state.md @@ -91,6 +91,12 @@ stateDiagram-v2 ├── architecture.md ├── deployment.log └── rollback.log # Rollback actions + +.azure/adrs/ +├── INDEX.md # Auto-maintained ADR index +├── 0001-deploy-api-dev.md # ADR for first deployment +├── 0002-deploy-webapp-prod.md # ADR for second deployment +└── ... ``` @@ -123,7 +129,9 @@ Contains deployment tracking information. } ], "estimatedMonthlyCost": "$12.50", - "createdBy": "git-ape-agent" + "createdBy": "git-ape-agent", + "adrFile": ".azure/adrs/0001-deploy-api-dev.md", + "adrNumber": 1 } ``` diff --git a/website/docs/skills/adr-generator.md b/website/docs/skills/adr-generator.md new file mode 100644 index 0000000..efd5dca --- /dev/null +++ b/website/docs/skills/adr-generator.md @@ -0,0 +1,144 @@ +--- +title: "Adr Generator" +sidebar_label: "Adr Generator" +description: "Generate and manage Architecture Decision Records (ADRs) for deployments. Auto-creates ADRs after successful deployment, maintains an index, and amends records when deployments are updated or destroyed." +--- + + + + +# Adr Generator + +> Generate and manage Architecture Decision Records (ADRs) for deployments. Auto-creates ADRs after successful deployment, maintains an index, and amends records when deployments are updated or destroyed. + +## Details + +| Property | Value | +|----------|-------| +| **Skill Directory** | `.github/skills/adr-generator/` | +| **Phase** | General | +| **User Invocable** | ✅ Yes | +| **Usage** | `/adr-generator Deployment ID to generate or amend an ADR for` | + + +## Documentation + +# ADR Generator + +Generate and manage Architecture Decision Records (ADRs) that document deployment decisions, trade-offs, and architecture choices. + +## When to Use + +- After a successful deployment (auto-triggered by deploy workflow) +- When a deployment is updated or redeployed +- When a deployment is destroyed (amend existing ADR) +- To rebuild the ADR index + +## ADR Format + +Each ADR follows a standard structure: + +```markdown +# ADR-NNNN: Title + +## Status +Accepted | Superseded | Deprecated + +## Date +YYYY-MM-DD + +## Deployment +- Deployment ID, project, environment, region, resource group + +## Context +Why was this deployment needed? What requirements drove it? + +## Decision +What was deployed? What configuration choices were made? + +## Consequences +### Positive +### Negative +### Trade-offs + +## Architecture +Reference to architecture diagram + +## Amendments +Changes to the deployment over time +``` + +## Procedure + +### 1. Generate ADR After Deployment + +After a successful deployment, run: + +```bash +.github/scripts/adr-manager.sh generate +``` + +This will: +- Read deployment metadata, requirements, and WAF review +- Generate a numbered ADR in `.azure/adrs/` +- Link the ADR back to `metadata.json` via `adrFile` and `adrNumber` fields +- Update the ADR index + +### 2. Amend ADR on Update or Destroy + +When a deployment is updated or destroyed: + +```bash +.github/scripts/adr-manager.sh amend "Reason for change" +``` + +This will: +- Find the linked ADR from metadata +- Append an amendment entry with date, reason, and new status +- Update ADR status to "Superseded" if deployment was destroyed +- Update the ADR index + +### 3. Rebuild Index + +To regenerate the index from all existing ADRs: + +```bash +.github/scripts/adr-manager.sh index +``` + +### 4. List ADRs + +```bash +.github/scripts/adr-manager.sh list +``` + +## Directory Structure + +``` +.azure/adrs/ +├── INDEX.md # Auto-maintained index +├── 0001-deploy-api-dev.md # First ADR +├── 0002-deploy-webapp-prod.md # Second ADR +└── ... +``` + +## Integration with Deployment Workflow + +The ADR generation is integrated into: + +1. **`git-ape-deploy.exampleyml`** — After successful deployment, generates an ADR and commits it alongside `state.json` +2. **`git-ape-destroy.exampleyml`** — Amends the ADR when a deployment is destroyed +3. **`metadata.json`** — Contains `adrFile` and `adrNumber` fields linking to the ADR + +## Metadata Integration + +After ADR generation, `metadata.json` is updated with: + +```json +{ + "adrFile": ".azure/adrs/0001-deploy-api-dev.md", + "adrNumber": 1 +} +``` + +This allows agents and scripts to navigate from a deployment to its decision record. diff --git a/website/docs/skills/overview.md b/website/docs/skills/overview.md index ab26a81..cf47ab3 100644 --- a/website/docs/skills/overview.md +++ b/website/docs/skills/overview.md @@ -40,6 +40,12 @@ Skills are focused capabilities invoked by agents at specific stages of the depl | [Azure Rest Api Reference](./azure-rest-api-reference) | Look up Azure REST API and ARM template reference documentation for any resource type. Returns exact property schemas, required fields, valid values, and latest stable API versions. Use BEFORE generating or modifying ARM templates to ensure correctness. No Azure connection required. | ✅ | | [Git Ape Onboarding](./git-ape-onboarding) | Onboard a repository, Azure subscription(s), and user identity for Git-Ape CI/CD using a skill-driven CLI playbook. Use for first-time setup of OIDC, federated credentials, RBAC, GitHub environments, and required secrets. | ✅ | +## General Skills + +| Skill | Description | Invocable | +|-------|-------------|:---------:| +| [Adr Generator](./adr-generator) | Generate and manage Architecture Decision Records (ADRs) for deployments. Auto-creates ADRs after successful deployment, maintains an index, and amends records when deployments are updated or destroyed. | ✅ | + ## Skill Invocation in Deployment Flow ```mermaid diff --git a/website/docs/workflows/git-ape-deploy.md b/website/docs/workflows/git-ape-deploy.md index 6e23309..80ccc8e 100644 --- a/website/docs/workflows/git-ape-deploy.md +++ b/website/docs/workflows/git-ape-deploy.md @@ -53,7 +53,7 @@ description: "GitHub Actions workflow: Git-Ape: Deploy" | **Runs On** | `ubuntu-latest` | | **Environment** | `azure-deploy` | | **Depends On** | `detect-deployments`, `check-comment-trigger` | -| **Steps** | 13 | +| **Steps** | 14 | @@ -434,6 +434,12 @@ jobs: } EOF + - name: Generate ADR + if: steps.deploy.outputs.deploy_status == 'succeeded' + run: | + chmod +x .github/scripts/adr-manager.sh + .github/scripts/adr-manager.sh generate "${{ matrix.deployment_id }}" + - name: Commit deployment state if: always() run: | @@ -454,6 +460,7 @@ jobs: # Stash the updated state and metadata files before switching branches cp "$DEPLOY_DIR/state.json" /tmp/state.json 2>/dev/null || true cp "$DEPLOY_DIR/metadata.json" /tmp/metadata.json 2>/dev/null || true + cp -r .azure/adrs /tmp/adrs 2>/dev/null || true # Ensure we push to main regardless of which ref was checked out git fetch origin main @@ -462,8 +469,13 @@ jobs: # Restore the updated state and metadata files onto main cp /tmp/state.json "$DEPLOY_DIR/state.json" 2>/dev/null || true cp /tmp/metadata.json "$DEPLOY_DIR/metadata.json" 2>/dev/null || true + if [[ -d /tmp/adrs ]]; then + mkdir -p .azure/adrs + cp -r /tmp/adrs/* .azure/adrs/ 2>/dev/null || true + fi git add "$DEPLOY_DIR/state.json" "$DEPLOY_DIR/metadata.json" + git add .azure/adrs/ 2>/dev/null || true git diff --cached --quiet || git commit -m "git-ape: update state for ${{ matrix.deployment_id }} [$STATUS]" git push || echo "::warning::Could not push state update to main" diff --git a/website/docs/workflows/git-ape-destroy.md b/website/docs/workflows/git-ape-destroy.md index ee249dd..cec9cf6 100644 --- a/website/docs/workflows/git-ape-destroy.md +++ b/website/docs/workflows/git-ape-destroy.md @@ -42,7 +42,7 @@ description: "GitHub Actions workflow: Git-Ape: Destroy" | **Runs On** | `ubuntu-latest` | | **Environment** | `azure-destroy` | | **Depends On** | `detect-destroys` | -| **Steps** | 9 | +| **Steps** | 10 | @@ -372,6 +372,18 @@ jobs: git diff --cached --quiet || git commit -m "git-ape: mark ${{ matrix.deployment_id }} as $STATUS" git push || echo "::warning::Could not push state update" + - name: Amend ADR + if: steps.destroy.outputs.destroy_status == 'succeeded' || steps.check.outputs.exists == 'false' + run: | + chmod +x .github/scripts/adr-manager.sh + .github/scripts/adr-manager.sh amend "${{ matrix.deployment_id }}" "Deployment destroyed" + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add .azure/adrs/ 2>/dev/null || true + git diff --cached --quiet || git commit -m "git-ape: amend ADR for ${{ matrix.deployment_id }} [destroyed]" + git push || echo "::warning::Could not push ADR amendment" + - name: Post summary if: always() run: |