Skip to content

AI Prompt: Forma3D.Connect — CodeCharta City Visualization (Option C)

Purpose: Integrate CodeCharta into the CI/CD pipeline to generate a 3D city map from SonarCloud + git history, served from the existing docs container with shareable URLs
Estimated Effort: 2–4 hours
Prerequisites: Human has completed the manual setup steps listed in Section: Human Setup Required
Output: Pipeline stage, Nginx CORS config, Dockerfile change, shareable visualization URLs
Research: codecharta-city-visualization-research.md
Status:DONE


🎯 Mission

Add a CodeCharta pipeline stage that generates a .cc.json city map by pulling metrics from SonarCloud and parsing git history, then serve the resulting file from the existing docs container so the team can visualize the codebase as a 3D city via the publicly hosted CodeCharta Web Studio.

What this delivers:

  1. A new CodeCharta generation step in the BuildAndPackage stage that produces forma3d.cc.json — a merged map combining SonarCloud metrics (complexity, code smells, coverage, tech debt) with git history metrics (authors, commits, churn, coupling)
  2. The .cc.json file baked into the existing docs Docker image at /codecharta/forma3d.cc.json
  3. A CORS-enabled Nginx location (/codecharta/) on the docs container so the hosted CodeCharta Web Studio can fetch the file via XHR
  4. Shareable URLs that open the city map with preconfigured views — anyone with the link sees the visualization instantly in their browser

Why this matters:

SonarCloud provides numbers. CodeCharta makes them tangible. The city metaphor immediately surfaces hotspots (large, complex, frequently-changed files), knowledge silos (single-author files), and temporal coupling (files that always change together). This is invaluable for sprint planning, retrospectives, and onboarding.

Critical constraints:

  • No new Docker container — reuse the existing forma3d-connect-docs container
  • No new DNS record — the file is served from staging-connect-docs.forma3d.be
  • No changes to the existing SonarCloud analysis stage — CodeCharta reads from the SonarCloud API after analysis completes
  • The pipeline checkout must use fetchDepth: 0 (full git history) for the CodeCharta step
  • The codecharta/codecharta-analysis Docker image (~1.2 GB) is used only during CI — it is NOT deployed to staging
  • The SonarCloud token for CodeCharta is a read-only user token, separate from the service connection used for analysis
  • The .cc.json file contains only file paths and numeric metrics — no source code

⚠️ Human Setup Required (Before You Start)

The following steps must be completed by a human before the AI begins implementation.

Checklist

# Action Where Output Needed
1 Generate a SonarCloud user token (read-only) sonarcloud.io → My Account → Security → Generate Token (type: User) Token string (starts with squ_)
2 Add the token as a secret pipeline variable Azure DevOps → Pipelines → Library → forma3d-staging variable group → Add SONARCLOUD_CODECHARTA_TOKEN Secret variable available to pipeline
3 Verify SonarCloud project key SonarCloud → Project → Information Confirm project key is devgem_forma-3d-connect

Values the AI Needs

Once the human has completed the checklist above, confirm the following values:

Value Expected Where It Goes
SONARCLOUD_CODECHARTA_TOKEN Secret variable in forma3d-staging Pipeline script (masked)
SonarCloud URL https://sonarcloud.io ccsh sonarimport command
Project Key devgem_forma-3d-connect ccsh sonarimport command
Docs domain staging-connect-docs.forma3d.be Shareable URLs

📌 Context (Current State)

Pipeline Structure

The relevant pipeline stages in azure-pipelines.yml:

ValidateAndTest
├── Lint              (MS-hosted: ubuntu-latest)
├── TypeCheck         (Self-hosted: DO-Build-Agents)
├── UnitTests         (Self-hosted: DO-Build-Agents)
└── CodeQuality       (MS-hosted: ubuntu-latest, depends on UnitTests)
    ├── SonarCloudPrepare@4
    ├── SonarCloudAnalyze@4
    └── SonarCloudPublish@4

BuildAndPackage
├── DetectAffected
├── BuildAll          (all service Docker images)
├── PackageDocs       (depends on DetectAffected, builds docs Docker image)
│   └── docker buildx build --file deployment/docs/Dockerfile ... --push .
└── PackageEventCatalog

Key observations: - The PackageDocs job uses the repo root (.) as Docker build context - The PackageDocs job depends on DetectAffected and only runs when docsAffected == true - The Dockerfile at deployment/docs/Dockerfile is a multi-stage build: Python builder → Nginx runtime - The current checkout in PackageDocs uses fetchDepth: 1 (shallow clone)

Docs Nginx Configuration

Current deployment/docs/nginx.conf serves static HTML from /usr/share/nginx/html. There is no CORS configuration. The server block has locations for / (static files), cached assets, /health, and /health/live.

Docs Dockerfile

Current deployment/docs/Dockerfile: - Builder stage: Python 3.12, Zensical, PlantUML rendering, builds to ./site - Production stage: nginx:alpine, copies built site to /usr/share/nginx/html, injects build number, generates build-info.json


📋 Step-by-Step Implementation

Step 1: Add CodeCharta Generation to the Pipeline

Add a new job GenerateCodeCharta in the BuildAndPackage stage. This job: - Depends on the ValidateAndTest stage completing (SonarCloud analysis must finish first so its API has results) - Runs on MS-hosted ubuntu-latest (no load on self-hosted agents) - Uses codecharta/codecharta-analysis Docker image to generate the map - Publishes the .cc.json as a pipeline artifact AND places it where PackageDocs can consume it

Insert this job in the BuildAndPackage stage, before PackageDocs:

      # ----------------------------------------------------------------------
      # Job: Generate CodeCharta City Map
      # ----------------------------------------------------------------------
      # Generates a merged .cc.json from SonarCloud metrics + git history.
      # The file is published as an artifact and downloaded by PackageDocs
      # to be baked into the docs Docker image.
      # Only runs on main branch (SonarCloud main analysis must exist).
      # ----------------------------------------------------------------------
      - job: GenerateCodeCharta
        displayName: 'Generate CodeCharta City Map'
        cancelTimeoutInMinutes: 0
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
            fetchDepth: 0
            clean: true

          - script: |
              set -e
              echo "🏙️ Generating CodeCharta city map..."

              docker run --rm \
                -v $(Build.SourcesDirectory):/src \
                -w /src \
                codecharta/codecharta-analysis bash -c '
                  git config --global --add safe.directory /src &&

                  echo "📊 Importing SonarCloud metrics..." &&
                  ccsh sonarimport \
                    https://sonarcloud.io \
                    devgem_forma-3d-connect \
                    --user-token='"'"'$(SONARCLOUD_CODECHARTA_TOKEN)'"'"' \
                    -o sonar.cc.json -nc &&

                  echo "📜 Parsing git history..." &&
                  ccsh gitlogparser repo-scan \
                    --repo-path /src \
                    --add-author \
                    -o git.cc.json -nc &&

                  echo "🔀 Merging Sonar + Git metrics..." &&
                  ccsh merge --leaf sonar.cc.json git.cc.json \
                    -o /src/codecharta/forma3d.cc.json -nc
                '

              echo "✅ CodeCharta map generated: codecharta/forma3d.cc.json"
              ls -la codecharta/
            displayName: 'Generate city map from SonarCloud + Git'
            env:
              SONARCLOUD_CODECHARTA_TOKEN: $(SONARCLOUD_CODECHARTA_TOKEN)

          - task: PublishPipelineArtifact@1
            displayName: 'Publish CodeCharta Artifact'
            inputs:
              targetPath: '$(Build.SourcesDirectory)/codecharta'
              artifact: 'codecharta-map'
              publishLocation: 'pipeline'

Important: The output directory codecharta/ is created inside the repo root. Since the docs Docker build context is . (the repo root), the file will be available to the Dockerfile's COPY instruction.

Step 2: Make PackageDocs Depend on GenerateCodeCharta

Update the PackageDocs job to depend on GenerateCodeCharta in addition to DetectAffected. The CodeCharta artifact must be available before the docs image is built.

Current:

      - job: PackageDocs
        displayName: 'Build & Push Docs Image'
        cancelTimeoutInMinutes: 0
        dependsOn: DetectAffected
        condition: eq(dependencies.DetectAffected.outputs['affected.docsAffected'], 'true')

Change to:

      - job: PackageDocs
        displayName: 'Build & Push Docs Image'
        cancelTimeoutInMinutes: 0
        dependsOn:
          - DetectAffected
          - GenerateCodeCharta
        condition: |
          and(
            not(failed('GenerateCodeCharta')),
            eq(dependencies.DetectAffected.outputs['affected.docsAffected'], 'true')
          )

Why not(failed(...)) instead of succeeded(...): The GenerateCodeCharta job only runs on main. On PR branches it is skipped. Using not(failed(...)) means the docs build proceeds whether CodeCharta succeeded OR was skipped.

Add a download step at the beginning of the PackageDocs job steps, after checkout:

          - task: DownloadPipelineArtifact@2
            displayName: 'Download CodeCharta Map'
            condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
            inputs:
              source: 'current'
              artifact: 'codecharta-map'
              path: '$(Build.SourcesDirectory)/codecharta'

On non-main branches the download is skipped, and the Dockerfile handles the missing file gracefully (see Step 4).

Step 3: Update Nginx Configuration

Add a CORS-enabled location block to deployment/docs/nginx.conf for serving the .cc.json file to the CodeCharta Web Studio at codecharta.com.

Add this location block inside the server { } block, after the existing location / { } block:

        # CodeCharta city map — served to codecharta.com Web Studio via XHR
        location /codecharta/ {
            add_header Access-Control-Allow-Origin "https://codecharta.com" always;
            add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
            add_header Access-Control-Allow-Headers "Range" always;
            add_header Cache-Control "no-cache";

            # Handle CORS preflight
            if ($request_method = 'OPTIONS') {
                add_header Access-Control-Allow-Origin "https://codecharta.com" always;
                add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
                add_header Access-Control-Allow-Headers "Range" always;
                add_header Access-Control-Max-Age 86400;
                add_header Content-Length 0;
                add_header Content-Type text/plain;
                return 204;
            }

            try_files $uri =404;
        }

Why these specific headers: - Access-Control-Allow-Origin: https://codecharta.com — restricts CORS to only the CodeCharta Web Studio (not a wildcard *) - Access-Control-Allow-Headers: Range — CodeCharta may use range requests for large files - Cache-Control: no-cache — ensures users always see the latest map after a pipeline run - The OPTIONS preflight handler is needed because the browser sends a preflight request for cross-origin XHR

Step 4: Update Docs Dockerfile

Add a COPY instruction to the production stage of deployment/docs/Dockerfile to include the CodeCharta map in the docs image.

Add this after the existing COPY --from=builder /repo/site /usr/share/nginx/html line (line 84) and before the RUN find ... sed line (line 89):

# Copy CodeCharta city map (generated by CI pipeline).
# On PR branches the file may not exist — the wildcard glob ensures the
# COPY does not fail when the source is absent.
COPY codecharta/forma3d.cc.jso[n] /usr/share/nginx/html/codecharta/forma3d.cc.json

Why the [n] glob trick: Docker's COPY instruction fails if the source file does not exist. On PR branches, the CodeCharta generation step is skipped, so codecharta/forma3d.cc.json won't be present. Using forma3d.cc.jso[n] is a glob pattern that matches the file if it exists but does not fail if it doesn't (Docker treats glob patterns as optional). This avoids the need for a separate Dockerfile or conditional logic.

Alternative if the glob trick doesn't work with buildx: Create an empty placeholder file in the checkout step of PackageDocs:

          - script: |
              mkdir -p codecharta
              [ -f codecharta/forma3d.cc.json ] || echo '{}' > codecharta/forma3d.cc.json
            displayName: 'Ensure CodeCharta placeholder exists'

Step 5: Ensure Docs Rebuild Is Triggered on Main

The PackageDocs job currently only runs when docsAffected == true (detected by nx affected). The CodeCharta .cc.json file is generated outside of Nx's affected detection — it changes every build even if docs content hasn't changed.

Option A (simple): The docs image should always rebuild on main when CodeCharta runs. Update the condition:

        condition: |
          or(
            eq(dependencies.DetectAffected.outputs['affected.docsAffected'], 'true'),
            and(
              eq(variables['Build.SourceBranch'], 'refs/heads/main'),
              succeeded('GenerateCodeCharta')
            )
          )

This means: build docs if either (a) docs content changed, or (b) we're on main and CodeCharta succeeded.

Option B (conservative): Keep the existing condition unchanged. The docs image only updates when docs files change. The CodeCharta map updates at the same time. Downside: the map could be stale if only code changes occur without doc changes. Since docs tend to change frequently in this project, this may be acceptable initially.

Choose Option A for freshness or Option B for minimal pipeline change. Option A is recommended.


📁 Files to Create / Modify

Action File What Changes
Modify azure-pipelines.yml Add GenerateCodeCharta job; update PackageDocs dependency and condition; add artifact download step
Modify deployment/docs/nginx.conf Add /codecharta/ location with CORS headers
Modify deployment/docs/Dockerfile Add COPY for codecharta/forma3d.cc.json
No change sonar-project.properties Already configured — CodeCharta reads via API
No change deployment/staging/docker-compose.yml No new container needed

✅ Verification

After First Pipeline Run on Main

  1. Check GenerateCodeCharta job — should complete in ~2–3 minutes with log output showing sonarimport, gitlogparser, and merge steps
  2. Check pipeline artifacts — a codecharta-map artifact should be published containing forma3d.cc.json
  3. Check PackageDocs job — should show "Download CodeCharta Map" step completing successfully
  4. Check deployed docs sitecurl -I https://staging-connect-docs.forma3d.be/codecharta/forma3d.cc.json should return 200 with Access-Control-Allow-Origin: https://codecharta.com header

Shareable URL Test

Open the following URL in a browser:

https://codecharta.com/visualization/app/index.html?file=https://staging-connect-docs.forma3d.be/codecharta/forma3d.cc.json&area=ncloc&height=cognitive_complexity&color=code_smells

Expected result: - The CodeCharta Web Studio loads - A 3D city map of the Forma3D.Connect codebase appears - Buildings represent source files — area shows lines of code, height shows cognitive complexity, color shows code smells

CORS Verification

curl -H "Origin: https://codecharta.com" \
     -I https://staging-connect-docs.forma3d.be/codecharta/forma3d.cc.json

Expected headers in response:

Access-Control-Allow-Origin: https://codecharta.com
Access-Control-Allow-Methods: GET, OPTIONS

Troubleshooting

Symptom Likely Cause Fix
GenerateCodeCharta fails with "401 Unauthorized" SonarCloud token is invalid or expired Regenerate token at sonarcloud.io → My Account → Security; update SONARCLOUD_CODECHARTA_TOKEN in variable group
GenerateCodeCharta fails with "Project not found" Project key mismatch Verify project key is devgem_forma-3d-connect at sonarcloud.io → Project → Information
.cc.json shows 0 files from git Shallow clone (fetchDepth != 0) Verify fetchDepth: 0 in the checkout step of GenerateCodeCharta
Web Studio shows CORS error Missing or wrong CORS header Check deployment/docs/nginx.conf has the /codecharta/ location with Access-Control-Allow-Origin: https://codecharta.com; verify Nginx was reloaded
Web Studio loads but city is empty .cc.json is the empty placeholder {} Check that GenerateCodeCharta ran on main and the artifact was downloaded by PackageDocs
PackageDocs fails with "COPY failed: file not found" The glob trick didn't work Use the placeholder fallback (create empty file in checkout step)
PackageDocs skipped even though CodeCharta ran Condition not updated Verify the PackageDocs condition includes the or(...) logic from Step 5 Option A

🔗 Shareable URL Reference

Once deployed, share these bookmarkable views with the team:

View URL Parameters
Complexity hotspots ?file=https://staging-connect-docs.forma3d.be/codecharta/forma3d.cc.json&area=ncloc&height=cognitive_complexity&color=code_smells
Change frequency ?file=...&area=ncloc&height=number_of_commits&color=number_of_authors
Tech debt vs coverage ?file=...&area=ncloc&height=sqale_index&color=coverage
Bug-prone files ?file=...&area=ncloc&height=fix_commits&color=bugs

Base URL: https://codecharta.com/visualization/app/index.html


🚫 Out of Scope

  • Self-hosted CodeCharta visualization container (Option D in research — only needed if codecharta.com dependency is unacceptable)
  • Delta maps for sprint-over-sprint comparison (Phase 3 — requires archiving historical maps)
  • Custom CodeCharta configuration or metric selection (start with defaults, tune later)
  • Per-project city maps (start with full monorepo map; per-service maps can be added later)
  • Adding CodeCharta link to the docs site navigation (separate small task)

🎬 Execution Order

  1. Read azure-pipelines.yml — understand the BuildAndPackage stage structure, PackageDocs job, and DetectAffected job
  2. Read deployment/docs/nginx.conf — understand current server block structure
  3. Read deployment/docs/Dockerfile — understand the production stage COPY sequence
  4. Add GenerateCodeCharta job to azure-pipelines.yml
  5. Update PackageDocs dependencies, condition, and add download step
  6. Add /codecharta/ location block to deployment/docs/nginx.conf
  7. Add COPY instruction to deployment/docs/Dockerfile
  8. Validate no lint or build errors were introduced

END OF PROMPT


Generate a CodeCharta city map from SonarCloud + git history in CI, serve the .cc.json from the existing docs container with CORS headers, and provide shareable URLs that open the hosted CodeCharta Web Studio with preconfigured views.