From fb77ea9264734c7ed68066c2a0c80804e41abd0b Mon Sep 17 00:00:00 2001 From: Johnny Bouder Date: Thu, 22 Oct 2026 08:24:51 -0400 Subject: [PATCH] chore(images)!: drop the apps- prefix from image names The org's pack-build-image workflow already prefixes the repo name, so the matrix's apps-* names produced ghcr.io/nebari-dev/apps-pack/apps-operator and quay.io/nebari/apps-pack-apps-operator. Name the images operator, authz, api, ui and mcp instead, giving ghcr.io/nebari-dev/apps-pack/operator and quay.io/nebari/apps-pack-operator, like the other packs. BREAKING CHANGE: new image tags are published only under the new names. Installs that set *.image.repository must point it at them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ap1Tqx1QLHRGVVSXdxeduM --- .github/workflows/images.yaml | 24 +++++++++++----------- README.md | 10 ++++----- charts/nebari-apps/README.md | 10 ++++----- charts/nebari-apps/values.yaml | 10 ++++----- docs/src/content/docs/branding.md | 2 +- docs/src/content/docs/ci-cd.md | 5 ++--- docs/src/content/docs/configuration.md | 10 ++++----- docs/src/content/docs/upgrading.md | 9 ++++++++ operator/internal/controller/authz_test.go | 2 +- 9 files changed, 45 insertions(+), 37 deletions(-) diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml index c162622..71fbd42 100644 --- a/.github/workflows/images.yaml +++ b/.github/workflows/images.yaml @@ -51,15 +51,15 @@ jobs: fail-fast: false matrix: include: - - image: apps-operator + - image: operator context: operator - - image: apps-authz + - image: authz context: authz - - image: apps-api + - image: api context: api - - image: apps-ui + - image: ui context: ui - - image: apps-mcp + - image: mcp context: mcp env: IMAGE: ${{ matrix.image }} @@ -75,8 +75,8 @@ jobs: # Boots the real image so the assertion covers nginx's header # inheritance, not just the config text. - name: Security headers - if: matrix.image == 'apps-ui' - run: ui/scripts/check-headers.sh apps-ui:ci + if: matrix.image == 'ui' + run: ui/scripts/check-headers.sh ui:ci # Not `needs: check`: a failing check must not stop `latest` tracking main, # or the release commit's sha images from existing (release.yaml depends on @@ -87,15 +87,15 @@ jobs: fail-fast: false matrix: include: - - image: apps-operator + - image: operator context: operator - - image: apps-authz + - image: authz context: authz - - image: apps-api + - image: api context: api - - image: apps-ui + - image: ui context: ui - - image: apps-mcp + - image: mcp context: mcp permissions: contents: read diff --git a/README.md b/README.md index 3cff642..e5814e3 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ start again on the next request | Component | What it does | Image | |---|---|---| -| apps-operator | Reconciles `App` resources into a Deployment, Service, ConfigMap and `NebariApp`, reports status, scales idle Apps to zero and serves the page they show while they start. | `apps-operator` | -| apps-api | REST API for CRUD, status, logs, events, analytics and zip/`.html` upload; writes `App` resources (and each App's write-only Secret) for the UI and the MCP server. | `apps-api` | -| apps-ui | The apps list as the home page (with a Needs attention banner, a Mine filter and Open links), app detail with logs and events, the launch form, and a Settings page (the UI and API versions, your groups, the cluster's settings, and how to connect an agent). | `apps-ui` | -| apps-mcp | MCP server at `/mcp` that lets coding agents launch and manage apps (tools), attach an app's spec, status, logs and events as context (resources), and follow the triage, launch and access-review workflows (prompts), with Keycloak device-flow login. | `apps-mcp` | -| apps-authz | Sidecar in the pods of Apps that can idle (records when the app last served a request) and of image Apps (gives them the one front port). It checks no tokens: `access.groups` are enforced at the gateway. | `apps-authz` | +| apps-operator | Reconciles `App` resources into a Deployment, Service, ConfigMap and `NebariApp`, reports status, scales idle Apps to zero and serves the page they show while they start. | `operator` | +| apps-api | REST API for CRUD, status, logs, events, analytics and zip/`.html` upload; writes `App` resources (and each App's write-only Secret) for the UI and the MCP server. | `api` | +| apps-ui | The apps list as the home page (with a Needs attention banner, a Mine filter and Open links), app detail with logs and events, the launch form, and a Settings page (the UI and API versions, your groups, the cluster's settings, and how to connect an agent). | `ui` | +| apps-mcp | MCP server at `/mcp` that lets coding agents launch and manage apps (tools), attach an app's spec, status, logs and events as context (resources), and follow the triage, launch and access-review workflows (prompts), with Keycloak device-flow login. | `mcp` | +| apps-authz | Sidecar in the pods of Apps that can idle (records when the app last served a request) and of image Apps (gives them the one front port). It checks no tokens: `access.groups` are enforced at the gateway. | `authz` | Images are published to `ghcr.io/nebari-dev/apps-pack/` and `quay.io/nebari/apps-pack-`. The UI signs in with keycloak-js and the API validates its diff --git a/charts/nebari-apps/README.md b/charts/nebari-apps/README.md index b27bddb..68de78d 100644 --- a/charts/nebari-apps/README.md +++ b/charts/nebari-apps/README.md @@ -29,7 +29,7 @@ wrong type fails the install. | api.enabled | bool | `true` | Deploy apps-api (the REST API behind the UI and MCP server). | | api.extraEnv | list | `[]` | Extra environment variables for apps-api, after the chart's own (an entry with the same name wins). | | api.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| api.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-api"` | apps-api image repository. | +| api.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/api"` | apps-api image repository. | | api.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. | | api.replicaCount | int | `1` | apps-api replicas. | | api.resources | object | `{"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"500m","memory":"256Mi"}}` | Container resources. Requests equal limits, so the pod is Guaranteed. | @@ -47,7 +47,7 @@ wrong type fails the install. | apps.networkPolicy.gatewayNamespace | string | `"envoy-gateway-system"` | Namespace of the Envoy gateway pods allowed to reach app pods. | | appsDomain | string | `""` | Domain apps are exposed under: `https://.`. Empty defaults to `apps.`, e.g. `my-app.apps.example.ai`. | | authz.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| authz.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-authz"` | apps-authz image repository. | +| authz.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/authz"` | apps-authz image repository. | | authz.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. | | authz.resources | object | `{"limits":{"cpu":"100m","memory":"64Mi"},"requests":{"cpu":"10m","memory":"32Mi"}}` | Sidecar resources, per app pod that runs it (on top of the app container's own). | | clusterDomain | string | `""` | Required. The cluster's base domain; there is no sensible default. | @@ -60,7 +60,7 @@ wrong type fails the install. | mcp.enabled | bool | `true` | Deploy the MCP server. Agents connect at `https:///mcp` (streamable HTTP); the UI's nginx proxies `/mcp` to it, so there is no separate hostname. When off, `/mcp` returns 404. | | mcp.extraEnv | list | `[]` | Extra environment variables for apps-mcp, after the chart's own (an entry with the same name wins). | | mcp.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| mcp.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-mcp"` | apps-mcp image repository. | +| mcp.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/mcp"` | apps-mcp image repository. | | mcp.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. | | mcp.oauth.callbackPort | int | `33418` | Port of the MCP client's OAuth callback (`--callback-port` in Claude Code); must match the client's redirect URI. | | mcp.oauth.clientId | string | `""` | Client id of a public Keycloak client (standard flow, PKCE S256) with the redirect URI `http://localhost:/callback`. Its tokens are accepted by apps-api and apps-mcp, and the UI's Settings page puts it in the `claude mcp add` command. Empty: MCP clients must bring their own way to a token. | @@ -71,7 +71,7 @@ wrong type fails the install. | nameOverride | string | `""` | Override the chart name used in resource names. | | operator.extraEnv | list | `[]` | Extra environment variables for the operator container. | | operator.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| operator.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-operator"` | Operator image repository. | +| operator.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/operator"` | Operator image repository. | | operator.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. | | operator.leaderElection | bool | `true` | Elect a leader so only one replica reconciles at a time. | | operator.replicaCount | int | `1` | Operator replicas (one leads when `leaderElection` is on). | @@ -97,7 +97,7 @@ wrong type fails the install. | ui.extraEnv | list | `[]` | Extra environment variables for the UI's nginx container. | | ui.hostname | string | `""` | Hostname for the UI. Empty defaults to the apps domain itself, so the UI lives at `https://apps.` and launched apps at `https://.apps.`. | | ui.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| ui.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/apps-ui"` | apps-ui image repository. | +| ui.image.repository | string | `"ghcr.io/nebari-dev/apps-pack/ui"` | apps-ui image repository. | | ui.image.tag | string | `""` | Image tag. Empty defaults to the chart's appVersion. | | ui.landingPage.category | string | `"Platform"` | Landing-page group the tile appears in. | | ui.landingPage.description | string | `"Launch and manage web applications on this cluster."` | Tile blurb. | diff --git a/charts/nebari-apps/values.yaml b/charts/nebari-apps/values.yaml index 9d8332c..7ad633a 100644 --- a/charts/nebari-apps/values.yaml +++ b/charts/nebari-apps/values.yaml @@ -115,7 +115,7 @@ apps: operator: image: # -- Operator image repository. - repository: ghcr.io/nebari-dev/apps-pack/apps-operator + repository: ghcr.io/nebari-dev/apps-pack/operator # -- Image tag. Empty defaults to the chart's appVersion. tag: "" # -- Image pull policy. @@ -142,7 +142,7 @@ api: enabled: true image: # -- apps-api image repository. - repository: ghcr.io/nebari-dev/apps-pack/apps-api + repository: ghcr.io/nebari-dev/apps-pack/api # -- Image tag. Empty defaults to the chart's appVersion. tag: "" # -- Image pull policy. @@ -185,7 +185,7 @@ ui: enabled: true image: # -- apps-ui image repository. - repository: ghcr.io/nebari-dev/apps-pack/apps-ui + repository: ghcr.io/nebari-dev/apps-pack/ui # -- Image tag. Empty defaults to the chart's appVersion. tag: "" # -- Image pull policy. @@ -281,7 +281,7 @@ mcp: enabled: true image: # -- apps-mcp image repository. - repository: ghcr.io/nebari-dev/apps-pack/apps-mcp + repository: ghcr.io/nebari-dev/apps-pack/mcp # -- Image tag. Empty defaults to the chart's appVersion. tag: "" # -- Image pull policy. @@ -393,7 +393,7 @@ pythonImage: ghcr.io/prefix-dev/pixi:0.68.1-noble authz: image: # -- apps-authz image repository. - repository: ghcr.io/nebari-dev/apps-pack/apps-authz + repository: ghcr.io/nebari-dev/apps-pack/authz # -- Image tag. Empty defaults to the chart's appVersion. tag: "" # -- Image pull policy. diff --git a/docs/src/content/docs/branding.md b/docs/src/content/docs/branding.md index d963c6e..1a65fae 100644 --- a/docs/src/content/docs/branding.md +++ b/docs/src/content/docs/branding.md @@ -142,7 +142,7 @@ Running the standalone `apps-ui` image without a chart, branding resolves from, -e API_URL=http://apps-api:8080 \ -e BRANDING_TITLE="Acme Apps" \ -e BRANDING_LOGO_URL=https://cdn.acme.example/logo.svg \ - ghcr.io/nebari-dev/apps-pack/apps-ui + ghcr.io/nebari-dev/apps-pack/ui ``` 3. **Built-in Nebari defaults** for any field still unset. diff --git a/docs/src/content/docs/ci-cd.md b/docs/src/content/docs/ci-cd.md index 2c59a38..b90d69f 100644 --- a/docs/src/content/docs/ci-cd.md +++ b/docs/src/content/docs/ci-cd.md @@ -13,7 +13,7 @@ description: What CI checks on every pull request, how images and charts are bui | | UI e2e + a11y | Playwright on Chromium against the UI job's build (apps-api mocked): a user flow or keyboard path that breaks, or a `critical`/`serious` axe violation (WCAG 2.0/2.1 A and AA) on any page in light or dark, at desktop width and at 390px (where a page that scrolls sideways also fails); uploads the HTML report every run | | | Helm chart | `helm lint`/`helm template` errors, `values.schema.json`, a stale chart README, chart RBAC assertions, the alert rules (promtool), kubeconform on the examples and rendered chart | | | Repo invariants | a path in `AGENTS.md` or the skill that does not exist, version strings that disagree with the release manifest, a pin file that disagrees with its Dockerfile, actionlint, zizmor | -| **Images** (`images.yaml`) | one per image | the image does not build, or (`apps-ui`) serves the wrong security headers; on changes to the images' sources only | +| **Images** (`images.yaml`) | one per image | the image does not build, or (`ui`) serves the wrong security headers; on changes to the images' sources only | | **Docs** (`docs.yml`) | docs | the site's tests or build, or a broken external link (on docs changes only) | CI also runs weekly, so advisories published after a merge surface even when nothing @@ -29,8 +29,7 @@ Every action is pinned to a commit SHA, and every workflow starts from ## Images -`images.yaml` builds the five images (`apps-operator`, `apps-authz`, `apps-api`, -`apps-ui`, `apps-mcp`). Pull requests build each image once, on `linux/amd64`, without +`images.yaml` builds the five images (`operator`, `authz`, `api`, `ui`, `mcp`). Pull requests build each image once, on `linux/amd64`, without pushing. Every merge to `main` builds them for `linux/amd64` and `linux/arm64` through the org's reusable `pack-build-image` workflow, pushes `ghcr.io/nebari-dev/apps-pack/` and `quay.io/nebari/apps-pack-`, tagged `sha-` and `latest`, and signs diff --git a/docs/src/content/docs/configuration.md b/docs/src/content/docs/configuration.md index 65104d6..43871d7 100644 --- a/docs/src/content/docs/configuration.md +++ b/docs/src/content/docs/configuration.md @@ -35,7 +35,7 @@ only `clusterDomain` and `keycloak.url` need changing. | `staticImage` | `nginxinc/nginx-unprivileged:1.27-alpine` | Serves static app content; must listen on 8080 as non-root. The operator mounts its nginx server config (security headers, `try_files`) over `/etc/nginx/conf.d`, so a replacement image should be nginx-based or ignore that directory. | | `gitImage` | `alpine/git:v2.47.2` | Init-container image that clones `git` sources. | | `pythonImage` | `ghcr.io/prefix-dev/pixi:0.68.1-noble` | Runs Python/pixi apps (`runtime.pixiTask`); must provide `pixi` and run as non-root. | -| `authz.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-authz` / appVersion / `IfNotPresent` | The sidecar the operator adds to apps that can idle and to image apps: it takes port 8080, proxies every request to the app and records when the app last served one. It checks no tokens; `access.groups` are enforced at the gateway (see [Access control](/launching-apps/#access-control)). | +| `authz.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/authz` / appVersion / `IfNotPresent` | The sidecar the operator adds to apps that can idle and to image apps: it takes port 8080, proxies every request to the app and records when the app last served one. It checks no tokens; `access.groups` are enforced at the gateway (see [Access control](/launching-apps/#access-control)). | | `authz.resources` | `10m`/`32Mi` requests, `100m`/`64Mi` limits | The sidecar's requests/limits, on top of the app container's, in every pod that runs it. | ### Image registries @@ -82,7 +82,7 @@ apps: | Value | Default | Purpose | |---|---|---| -| `operator.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-operator` / `""` (chart `appVersion`) / `IfNotPresent` | Operator image. | +| `operator.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/operator` / `""` (chart `appVersion`) / `IfNotPresent` | Operator image. | | `operator.replicaCount` | `1` | Replicas; more than one needs `leaderElection`. | | `operator.leaderElection` | `true` | Lease-based leader election in the release namespace. | | `operator.resources` | `500m/256Mi` (requests = limits) | Pod resources. | @@ -112,7 +112,7 @@ namespace. | Value | Default | Purpose | |---|---|---| | `api.enabled` | `true` | Deploy the REST API (the UI and MCP are thin clients over it). | -| `api.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-api` / `""` / `IfNotPresent` | API image. | +| `api.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/api` / `""` / `IfNotPresent` | API image. | | `api.replicaCount` | `1` | The API is stateless; more replicas are safe. | | `api.auth.enabled` | `true` | Keycloak JWT bearer auth for the API and keycloak-js login in the UI. **Disable only for local development**; off, every caller is anonymous with full access. Rendering fails if it is off while `tls.enabled=true`, unless `api.auth.insecureAllowAnonymous` is set. | | `api.auth.insecureAllowAnonymous` | `false` | Explicit opt-in to an unauthenticated API on a TLS install. Leave off. | @@ -137,7 +137,7 @@ in). | Value | Default | Purpose | |---|---|---| | `ui.enabled` | `true` | Deploy the web UI (also the nginx front door that proxies `/api/` and `/mcp`). | -| `ui.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-ui` / `""` / `IfNotPresent` | UI image. | +| `ui.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/ui` / `""` / `IfNotPresent` | UI image. | | `ui.replicaCount` | `1` | Replicas. | | `ui.hostname` | `` | Hostname the UI is served at. | | `ui.title` | `Nebari Apps` | Browser-tab title. | @@ -154,7 +154,7 @@ in). | Value | Default | Purpose | |---|---|---| | `mcp.enabled` | `true` | Deploy the MCP server at `https:///mcp` (proxied by the UI's nginx; no separate hostname). When off, `/mcp` returns 404. | -| `mcp.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/apps-mcp` / `""` / `IfNotPresent` | MCP image. | +| `mcp.image.repository` / `tag` / `pullPolicy` | `ghcr.io/nebari-dev/apps-pack/mcp` / `""` / `IfNotPresent` | MCP image. | | `mcp.replicaCount` | `1` | **Keep at 1**: handshake-era MCP sessions are held in process memory (authentication is per request and does not depend on them). | | `mcp.oauth.clientId` | `""` | Public Keycloak client MCP clients sign in with ([MCP authentication](/mcp/#the-sign-in-client)); create it in Keycloak first. The chart adds it to the allowed clients of apps-api and apps-mcp, and the UI's `claude mcp add` command passes it as `--client-id`. Empty: MCP clients cannot sign in on their own and need a token from elsewhere. | | `mcp.oauth.callbackPort` | `33418` | The MCP client's OAuth callback port (`--callback-port`); the client's redirect URI must be `http://localhost:/callback`. | diff --git a/docs/src/content/docs/upgrading.md b/docs/src/content/docs/upgrading.md index dbad32b..fbd969f 100644 --- a/docs/src/content/docs/upgrading.md +++ b/docs/src/content/docs/upgrading.md @@ -84,6 +84,15 @@ Status fields in the backup are ignored; the operator recomputes them. ## Version notes +### Upgrading to the shorter image names + +The images dropped their `apps-` prefix: `ghcr.io/nebari-dev/apps-pack/apps-operator` is now +`ghcr.io/nebari-dev/apps-pack/operator`, and the same for `authz`, `api`, `ui` and `mcp` +(on Quay, `quay.io/nebari/apps-pack-operator` and so on). The chart's defaults follow, so +nothing changes unless you set `*.image.repository` yourself, for example to mirror the +images: point those values at the new names. Images built before the rename stay at the old +paths; new tags are published only under the new ones. + ### Upgrading to gateway group enforcement **Breaking:** `access.groups` are enforced at the gateway by nebari-operator diff --git a/operator/internal/controller/authz_test.go b/operator/internal/controller/authz_test.go index f0b2734..4fcdf44 100644 --- a/operator/internal/controller/authz_test.go +++ b/operator/internal/controller/authz_test.go @@ -14,7 +14,7 @@ import ( appsv1alpha1 "github.com/nebari-dev/nebari-apps-pack/operator/api/v1alpha1" ) -const testAuthzImage = "ghcr.io/nebari-dev/apps-pack/apps-authz:test" +const testAuthzImage = "ghcr.io/nebari-dev/apps-pack/authz:test" func restrictedApp(app *appsv1alpha1.App, groups []string) *appsv1alpha1.App { app.Spec.Access.Public = false