Skip to main content

W'xOps Portal v0.2.0 — Golden-Path Scaffolding & Developer Experience

Xeus Nguyen
Platform Engineer

The first release where the portal can create and manage a full service lifecycle end-to-end: a developer fills in a wizard, the portal commits XTenantApp + XTenantDatabase + ExternalSecrets + Kustomize overlays + catalog entities to gitops-infra, ArgoCD and Crossplane provision everything, and the entity detail page shows live CI status, releases, packages, and activity.

What's New

Golden-path scaffolding

Project creation wizard

Four-step wizard — template selection → runtime config → platform features → review and create. Each step is independently navigable; the final step shows a per-step progress screen with timing as the portal commits files and creates resources.

Template system

Templates live in gitops-infra under a configurable directory. Each template carries a template.yaml that declares metadata, defaults, and recommends so the wizard can pre-populate fields and suggest optional features. Runtimes (language, version, package manager) are selectable per template.

Manifest generation

The portal generates and commits:

  • tenants-apps/{team}/{app}/base/xtenant-app.yaml, xtenant-database.yaml (when database enabled), external-secret-*.yaml, catalog entity files
  • tenants-apps/{team}/{app}/overlays/dev/kustomization.yaml, image-transformer.yaml, patch-xtenant-app.yaml
  • tenants/{team}/{app}.yaml — ArgoCD Image Updater CR

ArgoCD Image Updater integration

image-transformer.yaml is required for Kustomize to reach spec/parameters/image on the XTenantApp CRD (Kustomize only natively substitutes images in standard Deployment/StatefulSet specs). The generated kustomization.yaml intentionally has no images: block — Image Updater adds it on first build. A static block would be overwritten on every reconcile, resetting the tag to latest.

The Image Updater CR is committed to tenants/{team}/{appName}.yaml (not inside tenants-apps/) with metadata.name: {appName} for a clean ArgoCD control-plane view.

Overlay patch correctness

patch-xtenant-app.yaml contains only env-specific overrides (replicas, resources, ingress, secretsFrom). appName, namespace, and image use omitempty so they are absent from the patch and inherit from base — previously empty string values were overriding the base and breaking the deployed config.

Git-flow branching and branch protection

Scaffolded repos have three branches: develop (default, direct push allowed), staging and main (PR required; main has a CI bot whitelist). CI bot identity (GITEA_BOT_USERNAME / GITEA_BOT_EMAIL) is injected into scaffolded CI files.

Vault and ExternalSecret

The portal writes a Vault KV secret at secret/{team}/{app} (create/update only — never reads or deletes) and generates ExternalSecret manifests that reference it. Existence of the Gitea repo and gitops config is checked before any Vault write.

Import existing repos

Register an existing Gitea project into the catalog without re-scaffolding. The import wizard lists all org repos, auto-fills the entity YAML, and commits catalog entity files to gitops-infra.

Config management via PR

Edit-config wizard

Update XTenantApp platform features (replicas, resources, ingress, secrets, probes, rollout strategy) through a three-step diff-review UI:

  1. Edit form with YAML preview
  2. Review — structured summary of changes
  3. Success — "what was updated" checklist and PR status

The form matches the scaffold wizard's visual language: rounded-xl border cards, lg:grid-cols-2 equal columns, pill step badges, nav buttons outside the grid.

templateId preserved on update

Updating a project's config previously cleared templateId from the XTenantApp manifest — the backend rebuilt the manifest from the request struct without that field. templateId is now echoed back from the frontend and threaded through the backend rebuild.

Gitops PR URL security

gitops-infra PR URLs are not included in any API response. Activity feed and success panels show titles and status only.

CI/CD and release visibility

Each catalog Component entity (with a gitea/source-location annotation) now shows three live-data cards pulled from the Gitea API:

CI/CD status card

Latest Gitea Actions workflow runs with status badges (passed / failed / running), workflow name, branch, and relative time. Auto-refreshes every 30 seconds.

Releases card

Git releases and container images from the Gitea Package Registry. Shows version timeline, pre-release badge, release notes preview, image tags with pull command.

Packages card

Dependency tracking from the repo's manifest files: go.mod, package.json, requirements.txt, pyproject.toml. Shows direct vs. dev dependencies per ecosystem. Single fetch on mount (deps don't change often enough to poll).

Activity feed

Two-tier loading: five most-recent portal-managed PRs load immediately; "Show all" expands to a full paginated view. Filtered by the portal-managed Gitea label so only portal-created PRs appear. Role-scoped: tenant teams see only their own team's activity.

Lifecycle webhook

The lifecycle sync CI workflow lives in gitops-infra (not in the scaffolded project). It fires on push to main when tenants-apps/**/overlays/dev/kustomization.yaml changes — Image Updater's write-back after a successful build triggers the webhook, which promotes the entity lifecycle to development.

Team managers ({orgName}:{teamName}:Managers) can promote lifecycle stages alongside platform-team. PORTAL_EXTERNAL_URL has been removed; CI uses PORTAL_URL from Gitea repository secrets on gitops-infra.

Cluster views

Namespace listing from Pinniped groups

GET /api/v1/clusters/:id/namespaces previously called the K8s namespaces endpoint, which returns everything (cluster-admin) or 403 (everyone else). Namespaces are now derived from the user's Pinniped group membership: Gitea OIDC groups arrive as orgName:teamName, mapping to tenant-{orgName}. Platform-team users fall through to the K8s API.

Identity card reload button

Reload re-fetches the Pinniped WhoAmI, refreshes namespace listing, and triggers a full refresh of pods and deployments — no browser reload required.

Upgrade Notes

New environment variables

VariableRequiredDefaultNotes
GITEA_TEMPLATE_OWNERNo(GITEA_CATALOG_OWNER)Org owning the scaffold template repo
GITEA_TEMPLATE_REPONoscaffold-templatesRepo containing scaffold templates
GITEA_BOT_USERNAMENoCI bot username — added to main branch push whitelist
GITEA_BOT_EMAILNoCI bot email — substituted into scaffold template CI files
VAULT_ADDRNoVault server URL. When empty, Vault write is skipped silently.
VAULT_TOKENNoShort-lived token with wxops-portal policy.
VAULT_KV_MOUNTNosecretKV v2 mount. Secrets written to {mount}/{team}/{appName}.
WEBHOOK_TOKENNoShared secret for lifecycle webhook.
DEV_BYPASS_AUTHNofalseSkip OIDC — auto-login as dev / platform-team. Local dev only.

PORTAL_EXTERNAL_URL removed

Rename the secret used by the Gitea CI runner: PORTAL_EXTERNAL_URLPORTAL_URL (Gitea repository secret on gitops-infra). The webhook token secret name is unchanged.

Namespace RBAC

Ensure ClusterRoleBindings on spoke clusters use the full orgName:teamName string as the subject (not plain teamName). Tenant namespaces must be named tenant-{orgName}.

ArgoCD Image Updater

Remove any static images: block from existing overlay kustomization.yaml files before enabling Image Updater — the two will fight over the tag value on every reconcile.

Resources