Onboarding nueva app por GitOps (RUN-NEW-APP-GITOPS)
Status: Active standard
Owner: Wetopi platform architecture
Applies to: a new service repository that will run on the RKE2 platform
Last reviewed: 2026-09-23
Purpose¶
Use this runbook to create a new application repository with the same minimum
engineering and operational contract as the rest of the Wetopi platform, then
register it once in platform-gitops so Argo CD can reconcile it.
The application repository owns the application and its workload manifests.
platform-gitops owns only the Argo CD registration and project allow-list. The
cluster is an observed runtime, never a place to edit the source of truth.
This runbook is separate from the Paperclip fleet framework: Paperclip issues and agents may execute it, but they do not change the ownership boundaries below.
Authority and ownership¶
| Concern | Canonical authority |
|---|---|
| Application source, tests and Docker build | Application repository |
| Workload manifests and release configuration | Application repository |
Argo Application/AppProject registration |
wetopi-platform/platform-gitops |
| Secret values | Wetopi secret manager (currently Infisical) |
| Desired cluster state | Git repositories consumed by Argo CD |
| Live health, revision and events | Kubernetes and Argo CD |
| Work assignment and execution evidence | Paperclip issue/run/documents |
Never put workload manifests in platform-gitops. Never put secret values in
either Git repository. A kubectl apply is not an onboarding step; it bypasses
the GitOps review and is prohibited by this runbook.
Inputs and exit result¶
Before starting, the request must identify:
- application name and owning team;
- Git repository URL and release branch;
- runtime namespace and environment;
- image registry/repository and promotion policy;
- service ports, health endpoints and persistence needs;
- required secrets, external dependencies and data classification;
- whether the service is internal-only or needs an explicitly approved route;
- rollback target and an owner for the first on-call response.
The runbook is complete only when both reviewed changes have landed and the
child Argo application is Synced and Healthy with the expected immutable
image digest:
- application-repository change: code, tests, build and workload manifests;
platform-gitopschange: Argo registration and, if required, project source allow-list.
Phase 1 — application repository contract¶
Create the repository with a README that states ownership, purpose, local
development, test commands, release channel, runtime namespace, dependencies,
health endpoints, secrets policy and rollback procedure. Add a docs/ section
for architecture and operations when the service is more than a trivial
utility.
Use a predictable tree. Names may vary by language, but the boundaries must be visible:
app-repository/
├── README.md
├── AGENTS.md # repository-specific agent rules, if needed
├── Dockerfile
├── .dockerignore
├── src/ # or the language's conventional source tree
├── tests/
├── docs/ # architecture, operations, ADRs as needed
├── .github/workflows/ # test, build and release workflows
└── manifests/<release-channel>/
├── namespace.yaml # only when this repo owns the namespace
├── serviceaccount.yaml
├── role.yaml # omit when no API access is needed
├── rolebinding.yaml
├── configmap.yaml
├── deployment.yaml # or statefulset/job as appropriate
├── service.yaml # when the workload is networked
├── networkpolicy/
└── secrets/infisical-*.yaml # references only; no secret values
Manifests must be renderable and reviewable from the repository. Use immutable
image references (prefer a digest). Make the destination namespace explicit and
use the same namespace in every namespaced manifest; do not leave placeholders
such as <namespace> in files consumed by Argo.
The minimum workload contract is:
- non-root or otherwise justified security context;
- dedicated ServiceAccount and least-privilege RBAC;
- CPU/memory requests and limits;
- startup, readiness and liveness probes where the process supports them;
- default-deny ingress/egress posture, with narrowly scoped exceptions;
- no public exposure without an approved route and security review;
- graceful shutdown and a documented rollback path;
- persistence and backup/restore requirements explicitly recorded;
- external secrets represented as references managed by the existing secret mechanism.
Phase 2 — build and promotion contract¶
The application repository CI must, at minimum:
- run unit/integration tests and the repository's static checks;
- validate Kubernetes YAML, including namespace consistency and schema;
- scan the image and fail on the repository's blocking findings;
- build and publish an immutable image;
- record the image digest as release evidence;
- update the manifest digest through a reviewable commit or an approved promotion bot.
Wetopi builds use the platform's self-hosted GitHub Actions runners where the
workflow requires internal network or registry access. Images are published to
the private Wetopi registry, registry.wetopi.net, unless an approved project
contract says otherwise. The workflow must make the runner class and registry
destination explicit; do not silently fall back to a public runner or registry
for a production-bound image.
A source-code push is not a deployment. Argo cannot deploy a new image until the desired manifest revision points at that image digest. Keep the build PR and the promotion change separate when that makes the review boundary clearer.
Phase 3 — one-time GitOps registration¶
Work in a branch of wetopi-platform/platform-gitops and use a pull request.
Do not apply an Application directly with kubectl.
3.1 Allow the source repository¶
If the application repository is not already allowed by the relevant
AppProject, add its exact URL to spec.sourceRepos in the same reviewed
change. Keep the allow-list narrow; do not add a wildcard to make onboarding
convenient.
3.2 Add the Argo application¶
Create apps/<app-name>.yaml following the existing platform-gitops pattern:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-name
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: <approved-project>
source:
repoURL: <canonical-application-repository-url>
targetRevision: <release-branch-or-tag>
path: manifests/<release-channel>
directory:
recurse: true
include: '{namespace.yaml,serviceaccount.yaml,role.yaml,rolebinding.yaml,configmap.yaml,deployment.yaml,service.yaml,networkpolicy/*.yaml,secrets/infisical-*.yaml}'
destination:
server: https://kubernetes.default.svc
namespace: <runtime-namespace>
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- PruneLast=true
Only include files that exist and are intentionally owned by the application. If the existing project uses a different sync policy, copy that policy and record the reason in the PR rather than inventing a second convention.
The apps/<app-name>.yaml registration and any change to its include glob
must land together with the application manifest change. An Argo Application
that watches a path but excludes a new manifest produces a misleadingly
healthy-looking partial deployment.
3.3 Review and merge¶
The PR must show:
- source repository, branch and manifest path;
- destination namespace and project;
- exact image digest and promotion evidence;
- secret references without values;
- RBAC, network policy, persistence and rollback review;
- CI results and an explicit statement that no imperative cluster mutation was used.
After merge, the root gitops Application discovers apps/<app-name>.yaml.
That child Application then reconciles the application repository. This is the
only recurring registration step; subsequent releases update the application
repository's desired revision and do not require a new Argo Application.
Phase 4 — verification and evidence¶
Verify in this order, using read-only Argo/Kubernetes commands or the platform's approved API:
- root
gitopsApplication isSyncedandHealthyat the expectedplatform-gitopsrevision; - child application is
SyncedandHealthyat the expected app-repository revision; - child resources are in the intended namespace;
- Deployment/StatefulSet image and live pod
imageIDmatch the approved digest; - desired replica count, readiness, probes and recent events are healthy;
- service reachability is limited to the approved network path;
- secret references resolve without exposing values;
- the Argo history, PR, commit and verification report are linked from the Paperclip issue.
Record the evidence in the application issue/run and, for a new platform
pattern or a failed rollout, in a dated docs/reports/ document. If any check
is inconclusive, leave the Application unchanged and mark the work blocked.
Recurring release flow¶
Once onboarding is complete, the normal release is:
feature branch
→ tests and review in application repository
→ merge/build/publish immutable image
→ update manifest image digest
→ Argo root discovers/maintains child Application
→ child Application syncs
→ verify health, digest and evidence
The GitOps registration is a one-time platform operation. The application
repository remains responsible for its own manifests and release evidence;
platform-gitops remains responsible for the registration that makes Argo
watch that repository.
Stop conditions¶
Stop and create a reviewable blocker when any of these is true:
- owner, namespace, source path or rollback target is missing;
- a manifest contains a secret value or a mutable image tag where an immutable release is required;
- the repository is not allowed by the Argo project;
- a namespaced manifest uses a different or unresolved namespace;
- an include glob omits a manifest that the repository expects Argo to apply;
- the requested route exposes the service publicly without approval;
- the change requires cluster, RBAC, network or persistent-data mutation beyond the approved PR;
- root or child Argo is
OutOfSync,Degradedor reports a failed sync.
Do not work around a stop condition with a direct cluster edit. Fix the source of truth, review it, and let Argo reconcile it.