Skip to main content

Branch Strategy & CI/CD Flow

All 14 repositories in andrelair-platform follow a single, enforced branching standard. The standard splits repos into two categories based on whether they build and push a Docker image.


Two-Category System​

Category A β€” Image repos (9 repos)​

Repos that have a Dockerfile and a CI pipeline pushing to Harbor. These use three branches:

BranchEnvironmentCI behaviourGitOps bump
devDevelopmentBuild + push dev-<sha> tag. No cosign.Yes (dev overlay / dev manifest)
stagingStagingBuild + push staging-<sha> tag. Cosign-signed.No
mainProductionBuild + push <sha> tag. Cosign + SBOM.Yes (prod manifest)

Repos in Category A:

RepoGitOps target
minicloud-backstagehelm-values/backstage-values.yaml (main only)
minicloud-rag-ingestmanifests/ai/11-rag-ingest.yaml (main only)
minicloud-markitdown-proxymanifests/ai/10-markitdown-proxy.yaml (main only)
minicloud-litellm-custommanifests/ai/01-litellm-deployment.yaml (main only)
minicloud-open-webuihelm-values/open-webui-values.yaml (main only)
minicloud-onlyofficemanifests/productivity/ (main only)
minicloud-planemanifests/ (main only)
platform-demoservices/platform-demo/overlays/{dev,staging,prod} (all 3 branches)
ktayl-solution-webmanifests/ktayl-solution-web/00-deployment.yaml (main only)

Category B β€” Config / docs / IaC repos (5 repos)​

Repos with no Docker image. These use main only β€” dev and staging branches add no value when there is no artifact to promote.

RepoPurpose
minicloud-gitopsArgoCD app-of-apps + all Helm values β€” source of truth for the cluster
minicloud-ansiblePost-MAAS node bootstrap + Day-2 rolling upgrade playbooks
minicloud-opentofuMAAS infrastructure as code (subnet, DHCP, machines)
minicloud-platform-docsDocusaurus docs site β†’ GitHub Pages
phi3-financialLLM prompt-engineering evaluation pipeline

Branch Protection β€” Enforced via GitHub Rulesets​

Branch protection is enforced through repository-level rulesets (GitHub's ruleset feature, available on the free plan). Two rulesets exist per image repo; one per config repo.

main-protection ruleset (all 14 repos)​

RuleValue
Pull request requiredYes β€” 0 approvals needed (self-merge allowed)
Signed commits requiredYes β€” GPG key FD6D39D681DEFA34
Force pushBlocked

Direct push to main without a PR is rejected at the API level. The only exception is admin bypass (org owner), which should be used only in emergencies and leaves a visible audit trail.

staging-protection ruleset (Category A repos only)​

RuleValue
Pull request requiredYes β€” 0 approvals needed
Required status checkbuild-and-push must pass
Force pushBlocked

This gates promotion from dev to staging on CI passing β€” a broken image cannot reach the staging environment. The dev branch has no protection: direct push is allowed for fast iteration.

Summary table​

BranchExists onPR requiredCI gateSigned commitsForce push
mainAll 14 reposYesNoΒΉYes (GPG)Blocked
stagingCategory A (9 repos)Yesbuild-and-pushNoBlocked
devCategory A (9 repos)NoNoNoAllowed

ΒΉ CI runs on PRs to main via pull_request: trigger, but it is not a hard merge gate on main itself β€” only on staging.


Promotion Flow​

dev branch
β”‚ git push origin dev
β”‚ β†’ CI: build + push harbor.../image:dev-<sha>
β”‚ β†’ GitOps bump (platform-demo only)
β”‚ β†’ ArgoCD auto-syncs dev namespace
β”‚
β–Ό gh pr create --base staging --head dev
PR opened β†’ CI runs (build-and-push must pass)
Self-merge once CI is green
β”‚
β–Ό staging branch
β†’ CI: build + push harbor.../image:staging-<sha> (cosign-signed)
β†’ ArgoCD manual sync of staging overlay
β”‚
β–Ό gh pr create --base main --head staging
PR opened β†’ CI runs
Fill PR template (change type, risk, ACPR ref)
Self-merge β†’ change-record GHA creates [CHANGE] issue
β”‚
β–Ό main branch
β†’ CI: build + push harbor.../image:<sha> (cosign + SBOM)
β†’ GitOps bump with GPG-signed commit
β†’ ArgoCD syncs prod overlay

Supply Chain per Branch​

Controldevstagingmain
Trivy CRITICAL scanYesYesYes
Cosign keyless signNoYesYes
SBOM (CycloneDX via syft)NoNoYes
SBOM attached as OCI referrerNoNoYes
GitOps signed commit (GPG)YesΒΉNoYes

ΒΉ platform-demo only β€” it uses Kustomize overlays on all 3 branches.


Image Tag Format​

dev push β†’ harbor.10.0.0.200.nip.io/library/<name>:dev-a1b2c3d
staging push β†’ harbor.10.0.0.200.nip.io/library/<name>:staging-a1b2c3d
main push β†’ harbor.10.0.0.200.nip.io/library/<name>:a1b2c3d

The GitOps manifests always reference the internal Harbor URL so kubelet pulls are cluster-local. CI authenticates to Harbor over the Cloudflare Tunnel (harbor.devandre.sbs).


Typical Dev Workflow​

# 1. Start work on dev
git checkout dev
git pull origin dev

# 2. Make changes, commit (no GPG signing required on dev)
git add -p
git commit -m "feat: describe what changed"
git push origin dev
# CI builds dev-<sha> and deploys to dev namespace automatically

# 3. Promote to staging (when dev looks good)
gh pr create --base staging --head dev --title "feat: describe what changed"
# Wait for build-and-push CI check to pass, then self-merge
gh pr merge <N> --merge

# 4. Verify in staging namespace, then promote to production
gh pr create --base main --head staging --title "feat: describe what changed"
# Fill in PR body: change type checkbox, risk level, ACPR reference
# Self-merge β†’ change-record issue created automatically
gh pr merge <N> --merge

Setting Up a New Repo (Checklist)​

When creating a new image repo under andrelair-platform:

# 1. Create the repo and push initial code to main

# 2. Create the dev and staging branches
sha=$(gh api repos/andrelair-platform/<REPO>/git/ref/heads/main --jq '.object.sha')
gh api repos/andrelair-platform/<REPO>/git/refs -X POST \
-f ref="refs/heads/dev" -f sha="$sha"
gh api repos/andrelair-platform/<REPO>/git/refs -X POST \
-f ref="refs/heads/staging" -f sha="$sha"

# 3. Apply main-protection ruleset
gh api repos/andrelair-platform/<REPO>/rulesets -X POST --input /tmp/main-ruleset.json

# 4. Apply staging-protection ruleset
gh api repos/andrelair-platform/<REPO>/rulesets -X POST --input /tmp/staging-ruleset.json

The ruleset JSON files are defined in the Branch Protection Rulesets section above. Save them to /tmp/ from the values documented there before running.

CI workflow trigger block β€” every image repo must declare:

on:
push:
branches: [main, staging, dev]
pull_request:
branches: [main, staging]

This ensures:

  • Every push to any branch builds an image (enables Trivy + Harbor cache warming)
  • PRs to staging and main trigger CI so the staging-protection gate can evaluate build-and-push

For config/docs repos (no Dockerfile): keep main only. Apply only the main-protection ruleset. No dev/staging branches needed.


Why Not Org-Level Rulesets?​

GitHub org-level rulesets (which would let you define one ruleset applied to all repos at once) require the GitHub Team plan. The andrelair-platform org is on the free plan. Per-repo rulesets are used instead β€” identical content on each repo, applied via the API in batch. If the org upgrades to Team in the future, the per-repo rulesets can be deleted and replaced with a single org-level ruleset.


ACPR / DORA Alignment​

  • Every merge to main produces a [CHANGE] issue (via change-record.yml) with metadata: who merged, when, what changed, risk level β€” satisfies DORA Art.9 ICT change management
  • Incident issue template covers DORA Art.19 major incident classification
  • Separate dev / staging / prod environments satisfy ACPR 2021-R-01 Β§4.2 environment segregation
  • GPG-signed commits on main provide non-repudiation of all production changes
  • Blocked force push on main and staging ensures commit history is immutable