Files
root-at-skicandClaude Opus 5 a2200988b5 Add cluster-side connect operations to the ArgoCD skill
Fold the operational half of ArgoCD work into this skill, which until now
covered only authoring the Copier-templated repo. The two never overlapped:
authoring edits files in git, connecting applies objects to a cluster.

- Add references/connecting-a-repo.md: host-key trust, repository Secret
  registration with its project-scoping decision table, bootstrap apply
  ordering, SSO RBAC, prerequisites, and verification commands.
- Add references/connect-env-vars.md: layered .env loading, variable table,
  CLI flags, and SSH deploy-key rules.
- Add SKILL.md section 11 plus scope-routing rows, and state the split:
  sections 1-10 author, section 11 operates.
- Map the numbered cluster/0000-bootstrap layout onto the cluster/argocd
  layout, since the connect scripts reference those folder names directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 22:45:21 +03:00

8.0 KiB

Connecting a Repository to ArgoCD

Cluster-side operations that make an already-rendered GitOps repo reachable by an already-installed ArgoCD: trusting the git host key, registering the repository Secret, applying the app-of-apps bootstrap, and granting SSO users read access.

Read this when onboarding a repo to a cluster for the first time, rotating or scoping the repository credential, or debugging a connect run — kube context, namespaces, repo credentials, bootstrap paths. Every other reference in this skill concerns authoring the repo; this one concerns operating ArgoCD against it.

Driven by the wrapper-first .scripts/argo-cd/ shell module (Taskfile -> api -> lib; see the development-scripts skill for that architecture). Variables and CLI flags: references/connect-env-vars.md.


1. Operations

Operation Task Purpose
Trust the git repo SSH host key task argo-cd:0000-trust-repo-host run once per git host, before connect
Onboard / connect a repo task argo-cd:0100-connect-one preflight → repo Secret → bootstrap apply
Let SSO users view apps (RBAC) task argo-cd:0200-grant-sso-readonly ArgoCD install config, not repo state

The numbered task names encode run order. The module is expandable — sync, app management, UI, and uninstall operations belong here as they are built.

2. Trust the repo host (run once, before connect)

For SSH repos, ArgoCD's repo-server verifies the git server's host key against argocd-ssh-known-hosts-cm. A self-hosted host is not there by default, so connect-one fails with:

ssh: handshake failed: knownhosts: key is unknown
task argo-cd:0000-trust-repo-host      # parses host+port from the repo URL

lib/-trust-repo-host.sh parses host and port from the repo URL, ssh-keyscans it, appends to argocd-ssh-known-hosts-cm — never overwriting the shipped defaults — and restarts argocd-repo-server so the mounted ConfigMap reloads. Idempotent: it skips when the host is already present.

A ConfigMap patch alone can take ~60s to remount. The restart makes it deterministic.

3. Onboard / connect

task argo-cd:0000-trust-repo-host      # once per host
task argo-cd:0100-connect-one          # reads the .env layers; override with --repo-url=… etc.

Entry chain: api/connect-one.sh → lib/--index.sh (base loader → argo-cd reader → api index) → _argocd_connect_one:

require KUBE_CONFIG_PATH KUBE_CONFIG_CONTEXT ARGOCD_NAMESPACE ARGOCD_TARGET_DIR ARGOCD_GIT_REPO_URL
export KUBECONFIG=<kube config path>
preflight → repo-register → bootstrap-apply

Step 1 — preflight

  • command -v kubectl
  • kubectl --context <ctx> get namespace <ns> — ArgoCD reachable
  • the bootstrap directory exists in the target repo — the render must already have run

Step 2 — repo-register

Builds a kubectl create secret generic, pipes it to kubectl apply -f -, then labels it argocd.argoproj.io/secret-type=repository:

--from-literal=type=git
--from-literal=url=<repo url>                    # SSH form: git@host:owner/repo.git
[--from-literal=project=<appproject>]            # when set → project-scoped
[--from-file=sshPrivateKey=<key path>]           # when set
ssh key project Result
none none skip Secret (public, global repo)
none set create Secret (type + url + project) — project-scoped public repo
set any create Secret with sshPrivateKey
set, file missing any fail fast

The matching public key must be a read-only deploy key on the repository.

Step 3 — bootstrap-apply

Applies the bootstrap manifests in filename order — the AppProject (0000-*) before the app-of-apps (0100-*). ArgoCD then reconciles everything else from Git.

The app-of-apps source must set directory.recurse: true, because feature Applications live in nested subdirectories. Without it only the top-level manifest is read and children never appear.

4. Project-scoped repositories

Setting an AppProject name makes the repo a project-scoped repository: the Secret carries a project field, so only Applications in that AppProject may use it.

  • The SSH key is optional (public repo); if a key path is set, the file must exist.
  • The Secret is skipped only when there is neither a key nor a project scope.
  • A public repo with a project scope still gets a Secret — type, url, project, no key.

The AppProject's sourceRepos must permit the repo URL, and the app-of-apps spec.project must match.

5. Let SSO users view apps (RBAC)

With an empty argocd-rbac-cm policy.default, non-admin SSO/OIDC users see an empty Applications list. admin bypasses RBAC, so the apps exist but are invisible to SSO logins — which reads as "my apps disappeared" rather than as a permissions problem.

task argo-cd:0200-grant-sso-readonly   # sets policy.default (default role:readonly)

Idempotent; argocd-server reloads live, no restart needed.

This is ArgoCD install configuration living on the cluster — not part of the GitOps repo, so it is neither rendered by Copier nor reconciled from Git.

role:readonly grants all authenticated users read access to all applications. Scope a role to a specific SSO group when you need tighter control.

6. Prerequisites the scripts do NOT handle

  • ArgoCD must already be installed. Preflight only checks that the namespace exists.
  • The rendered repo must be pushed. bootstrap-apply applies local manifests, but those manifests point ArgoCD at a repoURL + path in Git. Unpushed → the app-of-apps cannot find its path.
  • The public deploy key must be on the repo, read-only, for SSH auth to work.
  • A successful render must exist first — the target's bootstrap directory has to be on disk. See references/environments.md for rendering an environment.
  • kubectl reachable at the configured kubeconfig and context.

7. Bootstrap paths — two repository shapes

The connect scripts reference the rendered repo's cluster folders by name, so those names are a contract between the template and the scripts. Two shapes are in use:

Purpose cluster/argocd/ shape (SKILL.md) numbered shape
Bootstrap — AppProject + app-of-apps root app applies it cluster/0000-bootstrap
Child Application manifests cluster/argocd/<NNN-name>/ cluster/0300-applications
Helm values / resources cluster/resources/<NNN-name>/ cluster/0500-resources

The authoring rules in SKILL.md describe the first shape. Whichever a repo uses, preflight and bootstrap-apply must point at its real bootstrap directory — if the template renames cluster folders, update both step scripts in the same change.

The deployed surface is only the manifests under paths an ArgoCD Application actually references.

8. Verify without a cluster

Render the repository Secret client-side to confirm project scoping before applying:

kubectl create secret generic <secret-name> \
  --from-literal=type=git --from-literal=url=<repo-url> \
  --from-literal=project=<appproject> \
  --from-file=sshPrivateKey=<key-path> \
  --dry-run=client -o yaml    # decode data.project → must equal the AppProject name

9. Post-onboard checks

kubectl -n <ns> get appproject <project>
kubectl -n <ns> get applications
kubectl -n <ns> get secret <repo-secret> -o jsonpath='{.data.project}' | base64 -d
argocd app list    # if the argocd CLI is available

10. Anti-patterns

  • Committing the SSH private key instead of keeping it a local file referenced by path.
  • Letting .env* values reach the deployed cluster/** manifests — they are tooling inputs.
  • Adding a :default task wrapper; the env layers already supply values. Keep one env-driven task and override with flags after --.
  • Hardcoding cluster folder names in step scripts without keeping them in sync with the template (§7).
  • Keeping two connect modules. If a duplicate exists, prefer the wired one and delete the other.