Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/prod-release-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:
type: boolean
default: true
components:
description: 'Components to build (comma-separated: frontend,backend,operator,ambient-runner,state-sync,public-api,ambient-api-server,ambient-control-plane,ambient-mcp) - leave empty for all'
description: 'Components to build (comma-separated: frontend,backend,operator,ambient-runner,state-sync,public-api,ambient-api-server,ambient-control-plane,ambient-mcp,ambient-ui) - leave empty for all'
required: false
type: string
default: ''
Expand Down Expand Up @@ -238,7 +238,8 @@ jobs:
{"name":"public-api","context":"./components/public-api","image":"quay.io/ambient_code/vteam_public_api","dockerfile":"./components/public-api/Dockerfile"},
{"name":"ambient-api-server","context":"./components/ambient-api-server","image":"quay.io/ambient_code/vteam_api_server","dockerfile":"./components/ambient-api-server/Dockerfile"},
{"name":"ambient-control-plane","context":"./components","image":"quay.io/ambient_code/vteam_control_plane","dockerfile":"./components/ambient-control-plane/Dockerfile"},
{"name":"ambient-mcp","context":"./components/ambient-mcp","image":"quay.io/ambient_code/vteam_mcp","dockerfile":"./components/ambient-mcp/Dockerfile"}
{"name":"ambient-mcp","context":"./components/ambient-mcp","image":"quay.io/ambient_code/vteam_mcp","dockerfile":"./components/ambient-mcp/Dockerfile"},
{"name":"ambient-ui","context":"./components","image":"quay.io/ambient_code/vteam_ambient_ui","dockerfile":"./components/ambient-ui/Dockerfile"}
]'

FORCE_ALL="${{ github.event.inputs.force_build_all }}"
Expand Down Expand Up @@ -624,6 +625,7 @@ jobs:
["public-api"]="public-api:public-api"
["ambient-api-server"]="ambient-api-server:ambient-api-server"
["ambient-control-plane"]="ambient-control-plane:ambient-control-plane"
["ambient-ui"]="ambient-ui:ambient-ui"
)


Expand All @@ -636,7 +638,8 @@ jobs:
"public-api:quay.io/ambient_code/vteam_public_api" \
"ambient-api-server:quay.io/ambient_code/vteam_api_server" \
"ambient-control-plane:quay.io/ambient_code/vteam_control_plane" \
"ambient-mcp:quay.io/ambient_code/vteam_mcp"; do
"ambient-mcp:quay.io/ambient_code/vteam_mcp" \
"ambient-ui:quay.io/ambient_code/vteam_ambient_ui"; do
COMP="${comp_image%%:*}"
IMAGE="${comp_image#*:}"

Expand Down
42 changes: 41 additions & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Unit Tests

permissions:
contents: read

on:
push:
branches: [main]
Expand All @@ -10,6 +13,7 @@ on:
- 'components/ambient-cli/**'
- 'components/ambient-sdk/go-sdk/**'
- 'components/frontend/**'
- 'components/ambient-ui/**'
- '.github/scripts/**'
- 'tests/**'
- '.github/workflows/unit-tests.yml'
Expand All @@ -23,6 +27,7 @@ on:
- 'components/ambient-cli/**'
- 'components/ambient-sdk/go-sdk/**'
- 'components/frontend/**'
- 'components/ambient-ui/**'
- '.github/scripts/**'
- 'tests/**'
- '.github/workflows/unit-tests.yml'
Expand Down Expand Up @@ -54,6 +59,7 @@ jobs:
runner: ${{ steps.filter.outputs.runner }}
cli: ${{ steps.filter.outputs.cli }}
frontend: ${{ steps.filter.outputs.frontend }}
ambient-ui: ${{ steps.filter.outputs.ambient-ui }}
scripts: ${{ steps.filter.outputs.scripts }}
steps:
- name: Checkout code
Expand All @@ -75,6 +81,8 @@ jobs:
- 'components/ambient-sdk/go-sdk/**'
frontend:
- 'components/frontend/**'
ambient-ui:
- 'components/ambient-ui/**'
scripts:
- '.github/scripts/**'
- 'tests/test_model_discovery.py'
Expand Down Expand Up @@ -307,6 +315,36 @@ jobs:
- name: Run unit tests with coverage
run: npx vitest run --coverage

ambient-ui:
runs-on: ubuntu-latest
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
needs: detect-changes
if: needs.detect-changes.outputs.ambient-ui == 'true' || github.event_name == 'workflow_dispatch'
name: Ambient UI Unit Tests (Vitest)
defaults:
run:
working-directory: components/ambient-ui

steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version-file: 'components/ambient-ui/package.json'
cache: 'npm'
cache-dependency-path: 'components/ambient-ui/package-lock.json'

- name: Build ambient-sdk (file: dependency)
working-directory: components/ambient-sdk/ts-sdk
run: npm ci && npm run build

- name: Install dependencies
run: npm ci

- name: Run unit tests with coverage
run: npx vitest run --coverage

scripts:
runs-on: ubuntu-latest
needs: detect-changes
Expand All @@ -326,7 +364,7 @@ jobs:

summary:
runs-on: ubuntu-latest
needs: [detect-changes, backend, api-server, runner, cli, frontend, scripts]
needs: [detect-changes, backend, api-server, runner, cli, frontend, ambient-ui, scripts]
if: always()
steps:
- name: Check overall status
Expand All @@ -338,6 +376,7 @@ jobs:
"${{ needs.runner.result }}" \
"${{ needs.cli.result }}" \
"${{ needs.frontend.result }}" \
"${{ needs.ambient-ui.result }}" \
"${{ needs.scripts.result }}"; do
if [ "$result" == "failure" ] || [ "$result" == "cancelled" ]; then
failed=true
Expand All @@ -350,6 +389,7 @@ jobs:
echo " runner: ${{ needs.runner.result }}"
echo " cli: ${{ needs.cli.result }}"
echo " frontend: ${{ needs.frontend.result }}"
echo " ambient-ui: ${{ needs.ambient-ui.result }}"
echo " scripts: ${{ needs.scripts.result }}"
exit 1
fi
Expand Down
33 changes: 29 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.PHONY: help setup build-all build-frontend build-backend build-operator build-runner build-state-sync build-public-api build-cli deploy clean check-architecture
.PHONY: help setup build-all build-frontend build-backend build-operator build-runner build-state-sync build-public-api build-cli build-ambient-ui deploy clean check-architecture
.PHONY: local-down local-status local-reload-api-server local-up local-clean local-rebuild local-reload-backend local-reload-frontend local-reload-operator
.PHONY: local-dev-token
.PHONY: local-logs local-logs-backend local-logs-frontend local-logs-operator local-shell local-shell-frontend
.PHONY: local-test local-test-dev local-test-quick test-all local-troubleshoot local-port-forward local-stop-port-forward
.PHONY: push-all registry-login setup-hooks remove-hooks lint check-minikube check-kind check-kubectl check-local-context dev-bootstrap kind-rebuild kind-reload-backend kind-reload-frontend kind-reload-operator kind-status kind-login kind-sso-toggle
.PHONY: push-all registry-login setup-hooks remove-hooks lint check-minikube check-kind check-kubectl check-local-context dev-bootstrap kind-rebuild kind-reload-backend kind-reload-frontend kind-reload-operator kind-reload-ambient-ui kind-status kind-login kind-sso-toggle
.PHONY: preflight-cluster preflight dev-env dev
.PHONY: e2e-test e2e-setup e2e-clean deploy-langfuse-openshift
.PHONY: unleash-port-forward unleash-status
Expand Down Expand Up @@ -72,6 +72,7 @@ GITHUB_MCP_IMAGE ?= vteam_credential_github:$(IMAGE_TAG)
JIRA_MCP_IMAGE ?= vteam_credential_jira:$(IMAGE_TAG)
K8S_MCP_IMAGE ?= vteam_credential_k8s:$(IMAGE_TAG)
GOOGLE_MCP_IMAGE ?= vteam_credential_google:$(IMAGE_TAG)
AMBIENT_UI_IMAGE ?= vteam_ambient_ui:$(IMAGE_TAG)

# kind-local overlay always references localhost/vteam_* images.
# Podman produces this prefix natively; for Docker we tag before loading.
Expand Down Expand Up @@ -168,7 +169,7 @@ help: ## Display this help message

##@ Building

build-all: build-frontend build-backend build-operator build-runner build-state-sync build-public-api build-api-server build-observability-dashboard ## Build all container images
build-all: build-frontend build-backend build-operator build-runner build-state-sync build-public-api build-api-server build-observability-dashboard build-ambient-ui ## Build all container images

build-frontend: ## Build frontend image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building frontend with $(CONTAINER_ENGINE)..."
Expand All @@ -177,6 +178,14 @@ build-frontend: ## Build frontend image
-t $(FRONTEND_IMAGE) .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Frontend built: $(FRONTEND_IMAGE)"

build-ambient-ui: ## Build ambient-ui image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building ambient-ui with $(CONTAINER_ENGINE)..."
@cd components && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
-f ambient-ui/Dockerfile \
--build-arg GIT_COMMIT=$(shell git rev-parse HEAD) \
-t $(AMBIENT_UI_IMAGE) .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Ambient UI built: $(AMBIENT_UI_IMAGE)"

build-backend: ## Build backend image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building backend with $(CONTAINER_ENGINE)..."
@cd components/backend && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
Expand Down Expand Up @@ -1085,6 +1094,22 @@ kind-reload-frontend: check-kind check-kubectl check-local-context ## Rebuild an
@kubectl rollout status deployment/frontend -n $(NAMESPACE) --timeout=60s
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Frontend reloaded"

kind-reload-ambient-ui: check-kind check-kubectl check-local-context ## Rebuild and reload ambient-ui only (kind)
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Rebuilding ambient-ui..."
@cd components && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) \
-f ambient-ui/Dockerfile \
--build-arg GIT_COMMIT=$(shell git rev-parse HEAD) \
-t $(AMBIENT_UI_IMAGE) . $(QUIET_REDIRECT)
@$(CONTAINER_ENGINE) tag $(AMBIENT_UI_IMAGE) localhost/$(AMBIENT_UI_IMAGE) 2>/dev/null || true
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Loading image into kind cluster ($(KIND_CLUSTER_NAME))..."
@$(CONTAINER_ENGINE) save localhost/$(AMBIENT_UI_IMAGE) | \
$(CONTAINER_ENGINE) exec -i $(KIND_CLUSTER_NAME)-control-plane \
ctr --namespace=k8s.io images import -
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Restarting ambient-ui..."
@kubectl rollout restart deployment/ambient-ui -n $(NAMESPACE) $(QUIET_REDIRECT)
@kubectl rollout status deployment/ambient-ui -n $(NAMESPACE) --timeout=60s
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Ambient UI reloaded"

kind-reload-operator: check-kind check-kubectl check-local-context ## Rebuild and reload operator only (kind)
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Rebuilding operator..."
@cd components/operator && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) \
Expand Down Expand Up @@ -1300,7 +1325,7 @@ check-architecture: ## Validate build architecture matches host

_kind-load-images: ## Internal: Load images into kind cluster
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Loading images into kind ($(KIND_CLUSTER_NAME))..."
@for img in $(BACKEND_IMAGE) $(FRONTEND_IMAGE) $(OPERATOR_IMAGE) $(RUNNER_IMAGE) $(STATE_SYNC_IMAGE) $(PUBLIC_API_IMAGE) $(API_SERVER_IMAGE) $(OBSERVABILITY_DASHBOARD_IMAGE); do \
@for img in $(BACKEND_IMAGE) $(FRONTEND_IMAGE) $(OPERATOR_IMAGE) $(RUNNER_IMAGE) $(STATE_SYNC_IMAGE) $(PUBLIC_API_IMAGE) $(API_SERVER_IMAGE) $(OBSERVABILITY_DASHBOARD_IMAGE) $(AMBIENT_UI_IMAGE); do \
echo " Loading $(KIND_IMAGE_PREFIX)$$img..."; \
if [ -n "$(KIND_HOST)" ] || [ "$(CONTAINER_ENGINE)" = "podman" ]; then \
$(CONTAINER_ENGINE) save $(KIND_IMAGE_PREFIX)$$img | \
Expand Down
35 changes: 35 additions & 0 deletions components/ambient-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# dependencies
/node_modules
/.pnp
.pnp.*
__pycache__

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
67 changes: 67 additions & 0 deletions components/ambient-ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Use Red Hat UBI Node.js 20 minimal image for dependencies
FROM registry.access.redhat.com/ubi9/nodejs-20-minimal AS deps

WORKDIR /app

USER 0

# Copy SDK dependency first (it's a file: dependency in package.json)
COPY ambient-sdk/ts-sdk ./ambient-sdk/ts-sdk

# Copy ambient-ui package files
COPY ambient-ui/package.json ambient-ui/package-lock.json* ./
RUN npm ci

# Rebuild the source code only when needed
# Use the full nodejs-20 image (not minimal) for the build stage because
# Next.js 16 Turbopack requires native SWC binaries that depend on glibc.
FROM registry.access.redhat.com/ubi9/nodejs-20 AS builder

USER 0

WORKDIR /app

# Copy node_modules from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/ambient-sdk ./ambient-sdk
COPY ambient-ui/ .

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
ENV NEXT_TELEMETRY_DISABLED=1

RUN npm run build

# Prepare standalone output with OpenShift-compatible permissions in the builder
# (the hardened runner image is distroless — no shell or chmod available)
RUN mkdir -p /app-output/public /app-output/.next/static && \
cp -r .next/standalone/. /app-output/ && \
cp -r .next/static/. /app-output/.next/static/ && \
cp -r public/. /app-output/public/ && \
chmod -R g=u /app-output && \
chgrp -R 0 /app-output

# Production image — Red Hat Hardened Image (distroless, ~48MB, 0 CVEs)
FROM registry.access.redhat.com/hi/nodejs:latest AS runner

ARG GIT_COMMIT=unknown

WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

COPY --from=builder /app-output/. ./

USER 1001

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
LABEL org.opencontainers.image.revision=$GIT_COMMIT

CMD ["node", "server.js"]
22 changes: 22 additions & 0 deletions components/ambient-ui/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
32 changes: 32 additions & 0 deletions components/ambient-ui/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
],
},
{
rules: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": "error",
"react-hooks/exhaustive-deps": "warn",
},
},
];

export default eslintConfig;
14 changes: 14 additions & 0 deletions components/ambient-ui/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// eslint-disable-next-line @typescript-eslint/no-require-imports
const path = require('path')

/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
outputFileTracingRoot: path.resolve(__dirname, '../..'),
transpilePackages: ['ambient-sdk'],
experimental: {
staticGenerationMinPagesPerWorker: 100,
},
}

module.exports = nextConfig
Loading
Loading