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.gitGitHub Mirror:
git@github.com:kup6s/workspace-kup6s.gitTracks:
CLAUDE.md,repos.yaml,scripts/,README.md,.gitignoreIgnores: 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.gitGitHub Mirror:
git@github.com:kup6s/argoapps.gitTechnology: 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.gitTechnology: Sphinx, MyST Markdown, mxmake, Diátaxis framework
Organization: Different GitLab group (
kup/redbluevskup6s)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.gitGitHub Mirror:
git@github.com:kup6s/apps-infra.gitContains:
gitlabbda/(GitLab BDA platform), future infrastructure appsTechnology: 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.gitTechnology: 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:
clone-all.shreadsrepos.yamlParses repository names, URLs, and branches
Clones each repo if not already present
Checks out correct branch
Reports success/skip status
Scenario 2: Update all repositories
# Pull latest changes across all repos
./scripts/update-all.sh
What Happened:
Script checks each repo for uncommitted changes
Skips repos with local modifications (safety)
Switches to correct branch if needed
Pulls from
origin/<branch>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 (
--recursiveflags 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/pullcommandsHistory 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 controlledNo 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 evolverepos.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)