Explanation

Workspace Structure

Overview

The kup6s infrastructure is organized as a meta repository workspace containing four independent git repositories managed through a lightweight parent repository.

The Problem

Managing a Kubernetes infrastructure requires multiple related but independent code repositories:

  • Cluster provisioning (kube-hetzner) - Changes infrequently, infrastructure foundation

  • ArgoCD app definitions (argoapps) - Changes frequently as new apps are added

  • Infrastructure deployments (dp-infra) - Application-specific deployment configs

  • Documentation (documentation) - Different organizational ownership, different audience

Challenges:

  • Different update cadences and lifecycles

  • Different organizational ownership (argoapps vs documentation)

  • ArgoCD needs to reference external repositories (dp-infra)

  • Need version control for workspace-level files (CLAUDE.md, scripts)

  • Onboarding friction (which repos to clone? how to set up?)

Traditional Approaches Fall Short:

  • Submodules: Complex workflow, detached HEAD, ceremony for every change

  • Subtrees: Repository bloat, history pollution, manual sync complexity

  • Monorepo: Breaks ArgoCD external refs, loses org boundaries, massive migration

  • No structure: Workspace files untracked, manual onboarding

The Solution: Meta Repository Pattern

A lightweight parent repository that:

  • Tracks workspace-level files (CLAUDE.md, repos.yaml, scripts/)

  • Provides automation scripts (clone-all.sh, update-all.sh, status-all.sh)

  • Ignores all subrepo directories (they remain independent)

  • Requires no changes to existing subrepos

Structure

kup6s/  (workspace meta repo)
├── .git/                  # Workspace git repository
├── .gitignore             # Ignores all subrepo directories
├── CLAUDE.md              # Tracked: Development guidelines
├── README.md              # Tracked: Workspace overview  
├── repos.yaml             # Tracked: Repository metadata
├── scripts/               # Tracked: Automation scripts
│   ├── clone-all.sh
│   ├── update-all.sh
│   └── status-all.sh
├── argoapps/              # Git ignored (independent repo)
├── documentation/         # Git ignored (independent repo)
├── dp-infra/              # Git ignored (independent repo)
└── kube-hetzner/          # Git ignored (independent repo)

Repository Details

Workspace Repository

Purpose: Track workspace-level files and provide automation

  • Repository: git@git.bluedynamics.eu:kup6s/workspace-kup6s.git

  • GitHub Mirror: git@github.com:kup6s/workspace-kup6s.git

  • Tracks: CLAUDE.md, repos.yaml, scripts/, README.md, .gitignore

  • Ignores: All subrepo directories via .gitignore

Key Insight: The workspace repo contains metadata about the subrepos, not the subrepos themselves.

argoapps/

Purpose: ArgoCD Application definitions (CDK8S/TypeScript)

  • Repository: git@git.bluedynamics.eu:kup6s/argoapps.git

  • GitHub Mirror: git@github.com:kup6s/argoapps.git

  • Technology: TypeScript, CDK8S 2.x

  • Outputs: Kubernetes manifests in dist/

  • Update Frequency: High (whenever new apps are added)

What It Does: Defines ArgoCD Application custom resources that tell ArgoCD what to deploy and where to deploy it.

documentation/

Purpose: Cluster documentation (Sphinx/MyST)

  • Repository: git@git.bluedynamics.eu:kup/redblue/documentation.git

  • Technology: Sphinx, MyST Markdown, mxmake, Diátaxis framework

  • Organization: Different GitLab group (kup/redblue vs kup6s)

  • Update Frequency: Medium (as infrastructure evolves)

Why Separate Org: Documentation serves multiple projects, not just kup6s cluster.

dp-infra/

Purpose: Infrastructure application deployments (CDK8S)

  • Repository: git@git.bluedynamics.eu:kup6s/dp/dp-infra.git

  • GitHub Mirror: git@github.com:kup6s/apps-infra.git

  • Contains: gitlabbda/ (GitLab BDA platform), future infrastructure apps

  • Technology: CDK8S, TypeScript

  • ArgoCD: Referenced as external repository by ArgoCD Applications

Why Separate: GitLab deployment has its own lifecycle, ArgoCD reads manifests directly from this git repo.

kube-hetzner/

Purpose: K3S cluster provisioning on Hetzner Cloud

  • Repository: git@git.bluedynamics.eu:kup6s/kube-hetzner.git

  • Technology: OpenTofu (Terraform fork), kube-hetzner project

  • Outputs: Cluster infrastructure, kubeconfig file

  • Update Frequency: Low (infrastructure changes are rare and planned)

Foundation Layer: Must be applied before any applications can be deployed.

How It Works

Developer Workflow

Scenario 1: New developer onboarding

# Step 1: Clone workspace repo
git clone git@git.bluedynamics.eu:kup6s/workspace-kup6s.git kup6s
cd kup6s

# Step 2: Run one command to clone everything
./scripts/clone-all.sh

# Done! All 4 repos cloned, ready to work

What Happened:

  1. clone-all.sh reads repos.yaml

  2. Parses repository names, URLs, and branches

  3. Clones each repo if not already present

  4. Checks out correct branch

  5. Reports success/skip status

Scenario 2: Update all repositories

# Pull latest changes across all repos
./scripts/update-all.sh

What Happened:

  1. Script checks each repo for uncommitted changes

  2. Skips repos with local modifications (safety)

  3. Switches to correct branch if needed

  4. Pulls from origin/<branch>

  5. Reports update status

Scenario 3: Working on specific repo

# Work in any repo normally - it's independent
cd argoapps
git checkout -b add-new-app
vim apps/infra/myapp.ts
npm run build
git commit -am "Add new application"
git push origin add-new-app

# Workspace repo unchanged - subrepos are independent!

Scenario 4: Update workspace automation

# Make changes to workspace files
vim scripts/clone-all.sh  # Improve script

# Commit to workspace repo
git add scripts/clone-all.sh
git commit -m "Improve clone script"
git push origin main

# Subrepos unchanged - scripts live in workspace repo

Why Not Other Approaches?

Git Submodules

How They Work: Parent repo tracks specific commits of subrepos.

Problems:

  • Complex workflow (commit in subrepo, then commit pointer in parent)

  • Detached HEAD state confuses developers

  • Easy to mess up (--recursive flags everywhere)

  • Version pinning not needed for active development

  • Multiple remotes complicate already-complex workflow

Verdict: Adds ceremony without benefits for our use case.

Git Subtrees

How They Work: Merge subrepos into parent as subdirectories.

Problems:

  • Repository bloat (parent contains full history of all subrepos)

  • Long git subtree push/pull commands

  • History pollution from merge commits

  • Breaks with multiple remotes (origin + GitHub “all”)

Verdict: Simpler than submodules, but still adds complexity we don’t need.

Monorepo

How It Works: Single git repository with multiple project directories.

Problems:

  • BREAKS existing ArgoCD setup (dp-infra externally referenced)

  • DESTROYS git history or requires complex rewriting

  • LOSES organizational boundaries (documentation under different GitLab group)

  • FORCES everyone to clone everything (even if only working on docs)

  • ELIMINATES multiple remotes per project

Verdict: Major migration for no benefit. Our repos should be separate.

No Structure (Status Quo Before)

How It Works: Just a directory with independent repos.

Problems:

  • Workspace files (CLAUDE.md) not version controlled

  • No centralized automation

  • Manual onboarding (developers must know all 4 repo URLs)

  • Scripts scattered or missing

Verdict: Simple but doesn’t solve workspace file tracking.

Design Rationale

Independence Preserved

Each subrepo:

  • Has its own git history

  • Can be cloned/used independently

  • Has its own remotes (including “all” remote for dual push)

  • Updates on its own schedule

  • Can be referenced by ArgoCD as external repo

Minimal Complexity

Meta repository:

  • No special git features (no submodules/subtrees)

  • Simple bash scripts (no external dependencies)

  • Small codebase (few hundred lines of bash + YAML)

  • Easy to understand and modify

Version Control Where It Matters

Workspace-level files that need versioning:

  • CLAUDE.md - Development guidelines evolve

  • repos.yaml - Repository metadata changes (new repos added)

  • scripts/ - Automation improves over time

Files that don’t need workspace tracking:

  • Subrepo code (tracked in their own repos)

Onboarding Simplicity

Before: “Clone these 4 repos: [long list of URLs]”
After: “Run git clone <workspace> && ./scripts/clone-all.sh

Limitations

No Version Pinning

Meta repository does not track specific commits of subrepos (unlike submodules).

Implication: Can’t lock workspace to exact state like “argoapps at commit abc123”.

Why Acceptable: We don’t need reproducible environments across time - we want latest development.

If Needed: Add git describe --always to repos.yaml and checkout specific SHAs in scripts.

Manual Coordination

Multiple people changing multiple repos must coordinate manually.

Example: If argoapps references dp-infra/gitlabbda, and both change, you must merge both PRs.

Why Acceptable: This coordination is inherent to multi-repo architectures. Meta repo doesn’t make it worse.

No Transactional Updates

Can’t update multiple subrepos atomically.

Example: Can’t commit “change argoapps AND kube-hetzner” as one operation.

Why Acceptable: Our repos have different lifecycles. Atomic cross-repo commits would be code smell.

Best Practices

When to Update Workspace

Add to workspace repo:

  • New automation scripts useful for all developers

  • Changes to repos.yaml (new repo added, URL changed)

  • Updates to CLAUDE.md guidelines

Don’t add to workspace repo:

  • Code belonging to specific project (put in appropriate subrepo)

  • Temporary/experimental scripts (keep local)

When to Add New Subrepo

Create new independent repo when:

  • Component has different lifecycle/ownership

  • Component will be referenced by ArgoCD as external repo

  • Team structure suggests separation

Add to existing repo when:

  • Tightly coupled to existing code

  • Same update cadence

  • Shared deployment unit

Workspace Maintenance

Keep repos.yaml updated:

  • Add new repos when created

  • Update URLs if repos move

  • Update descriptions as purposes evolve

Keep scripts simple:

  • No external dependencies (use built-in tools)

  • Clear error messages

  • Idempotent (safe to run multiple times)