Skip to content

Commit f346536

Browse files
authored
Add Comprehensive Backend Unit Test Suite with Ginkgo Framework (#460)
## Summary This PR introduces a comprehensive unit testing infrastructure for the ambient-code backend using the Ginkgo/Gomega testing framework. The addition includes **13,000+ lines of test coverage** across all major handler components, along with critical **security hardening** and **architectural improvements** to support robust testing. ## 🧪 Test Coverage Added - **Complete handler test coverage**: 15+ test files covering all major API endpoints - **Test framework infrastructure**: Comprehensive test utilities, fake clients, and helper functions - **GitHub Actions integration**: New CI workflow for automated unit test execution - **Developer tooling**: Enhanced Makefile with test commands and documentation ### Key Test Areas Covered - **Authentication & Authorization**: Token validation, RBAC, service account extraction - **Session Management**: CRUD operations, lifecycle management, status updates - **Project Operations**: Creation, listing, validation, multi-tenant isolation - **Repository Management**: Git operations, seeding, content handling - **Secret Management**: API key storage, token minting, secure handling - **GitHub/GitLab Integration**: OAuth flows, webhook handling, API interactions - **Health Endpoints**: Service readiness and liveness checks ## 🔒 Security Hardening (Non-Test Changes) ### Critical Security Fixes **Removed authentication bypass vulnerability** (`handlers/middleware.go:359-410`): - **REMOVED**: `isLocalDevEnvironment()` function that checked environment variables (`DISABLE_AUTH`, `ENVIRONMENT`) - **REMOVED**: `getLocalDevK8sClients()` function that bypassed user authentication - **SECURITY IMPACT**: Eliminates risk of accidental authentication bypass in production environments **Enhanced token validation** (`handlers/middleware.go:57-108`): - **BUG FIX**: Improved token parsing logic to handle malformed Authorization headers - **ENHANCED**: Added support for `X-Remote-User` header (OpenShift OAuth proxy format) - **SECURITY**: Enforced "token required" semantics with no fallback mechanisms ### Architecture Improvements **Build tag separation for production vs test code**: - **NEW**: `k8s_clients_for_request_prod.go` - Immutable production authentication path - **NEW**: `k8s_clients_for_request_testtag.go` - Secure test-only client injection - **SECURITY**: Prevents function-pointer override vulnerabilities in production builds **Type safety improvements**: - **ENHANCED**: Changed `K8sClientMw` from `*kubernetes.Clientset` to `kubernetes.Interface` - **BENEFIT**: Enables proper dependency injection for testing without compromising production type safety ## 🛠 Development Infrastructure ### New GitHub Actions Workflow - **File**: `.github/workflows/backend-unit-tests.yml` - **Features**: - Automatic test execution on PRs affecting backend code - JUnit XML report generation with HTML visualization - Test result summaries in PR comments - Configurable test filtering and namespaces ### Enhanced Build System - **Updated**: `components/backend/Makefile` with comprehensive test commands - **Added**: `install-tools` target for Ginkgo CLI installation - **Enhanced**: Test execution with coverage reporting and filtering ### Documentation - **NEW**: `TEST_GUIDE.md` - Comprehensive 877-line testing guide - **UPDATED**: `README.md` - Removed DISABLE_AUTH documentation, added secure local dev instructions ## 🔧 Why These Non-Test Changes Were Required ### 1. Authentication Bypass Removal **Problem**: The original `DISABLE_AUTH` mechanism created a production security vulnerability **Solution**: Complete removal of environment-variable-based authentication bypass **Impact**: All requests now require valid user tokens - no exceptions ### 2. Testability Architecture **Problem**: Original code tightly coupled to production Kubernetes clients **Solution**: Interface-based dependency injection with build tag separation **Impact**: Unit tests can inject fake clients without affecting production behavior ### 3. Type Safety for Testing **Problem**: Direct type assertions (`*kubernetes.Clientset`) prevented mock/fake client injection **Solution**: Interface-based typing (`kubernetes.Interface`) supporting both real and fake implementations **Impact**: Robust testing with proper type safety maintained ### 4. Token Parsing Robustness **Problem**: Fragile token extraction logic that failed on edge cases **Solution**: Enhanced parsing with support for multiple header formats **Impact**: Better compatibility with various authentication proxy configurations ## 📊 Test Metrics - **Files Added**: 25+ test files - **Test Coverage**: 13,000+ lines of test code - **Dependencies**: Ginkgo v2.27.3, Gomega v1.38.3 - **CI Integration**: Automated execution on all backend changes - **Test Categories**: Unit, integration, handlers, auth, git operations ## 🚀 Benefits 1. **Confidence**: Comprehensive test coverage enables safe refactoring and feature development 2. **Security**: Elimination of authentication bypass vulnerabilities 3. **Maintainability**: Well-structured test framework supports long-term development 4. **CI/CD**: Automated testing prevents regressions 5. **Documentation**: Extensive testing guide enables team onboarding --- ## Breaking Changes ⚠️ **BREAKING**: Removed `DISABLE_AUTH` environment variable support - **Migration**: Use proper authentication tokens for local development (see updated README.md) - **Security Rationale**: Prevents accidental authentication bypass in production ## Testing Instructions ```bash cd components/backend # Install test tools make install-tools # Run all unit tests make test-unit # Run specific test categories make test-handlers make test-auth ``` # Local Development Experience Change Harden local-dev auth checks + enable real ServiceAccount token flow for Test 26 ### Summary This PR improves the **local developer experience test suite** by replacing the old “token minting in backend code” expectation with a **real Kubernetes TokenRequest workflow**. It also adds a small helper target to mint a local-dev token from the cluster. ### What changed - **`tests/local-dev-test.sh`** - **Test 26** now validates the secured local-dev workflow: - Confirms `local-dev-user` **ServiceAccount** exists - Confirms `local-dev-user` **RoleBinding** exists - Mints a **real token** via `kubectl -n <ns> create token local-dev-user` - Uses that token to call `GET /api/projects/<ns>/agentic-sessions` and expects **HTTP 200** - Added retry logic around the API call to reduce startup flakiness. - **Root `Makefile`** - Added `make local-dev-token` to print a fresh TokenRequest token for `local-dev-user` (after `make local-up`). ### Why - Keeps backend code **free of local-dev auth bypass/token fabrication logic**. - Ensures local dev and CI validate a **real auth path** (token + RBAC) instead of relying on insecure shortcuts. - Makes it easy for developers to get a working token for manual API calls. ### How to test ```bash make local-up CONTAINER_ENGINE=docker ./tests/local-dev-test.sh --skip-setup --ci # optional: get a token for manual curl testing make local-dev-token ``` ### Notes / Requirements - Requires **kubectl v1.24+** and a cluster that supports **TokenRequest** (minikube does). - `local-dev-user` RBAC is provided by `components/manifests/minikube/local-dev-rbac.yaml` and is applied during `make local-up`. This PR establishes the foundation for reliable, secure backend testing while significantly improving the overall security posture of the authentication system. --------- Signed-off-by: Nelesh Singla <117123879+nsingla@users.noreply.github.com>
1 parent 3fb014e commit f346536

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+13199
-635
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# This workflow runs tests to verify all the API Server REST Endpoints
2+
name: Backend Unit Tests
3+
env:
4+
TESTS_DIR: "./components/backend/handlers"
5+
TESTS_LABEL: "unit"
6+
JUNIT_FILENAME: "junit.xml"
7+
8+
on:
9+
push:
10+
branches: [main]
11+
12+
workflow_dispatch:
13+
inputs:
14+
test_label:
15+
description: "Test label that you want to filter on and run"
16+
default: 'unit'
17+
required: true
18+
type: string
19+
default_namespace:
20+
description: "Default namespace for testing"
21+
default: 'test-namespace'
22+
required: false
23+
type: string
24+
25+
pull_request:
26+
paths:
27+
- '.github/workflows/backend-unit-tests.yml'
28+
- './components/backend/**'
29+
- '!**/*.md'
30+
31+
concurrency:
32+
group: backend-unit-tests-${{ github.event.pull_request.number || github.ref }}
33+
cancel-in-progress: true
34+
35+
jobs:
36+
backend-unit-test:
37+
runs-on: ubuntu-latest
38+
name: Ambient Code Backend Unit Tests
39+
40+
steps:
41+
- name: Checkout code
42+
uses: actions/checkout@v4
43+
44+
- name: Create reports directory
45+
shell: bash
46+
working-directory: ${{ env.TESTS_DIR }}
47+
run: |
48+
mkdir -p reports
49+
50+
- name: Configure Input Variables
51+
shell: bash
52+
id: configure
53+
run: |
54+
TEST_LABEL=${{ env.TESTS_LABEL }}
55+
DEFAULT_NAMESPACE="test-namespace"
56+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
57+
TEST_LABEL=${{ inputs.test_label }}
58+
DEFAULT_NAMESPACE=${{ inputs.default_namespace }}
59+
fi
60+
61+
{
62+
echo "TEST_LABEL=$TEST_LABEL"
63+
echo "DEFAULT_NAMESPACE=$DEFAULT_NAMESPACE"
64+
} >> "$GITHUB_OUTPUT"
65+
66+
- name: Run Tests
67+
id: run-tests
68+
shell: bash
69+
working-directory: ${{ env.TESTS_DIR }}
70+
run: |
71+
go run github.com/onsi/ginkgo/v2/ginkgo -r -v --cover --keep-going --github-output=true --tags=test --label-filter=${{ steps.configure.outputs.TEST_LABEL }} --junit-report=${{ env.JUNIT_FILENAME }} --output-dir=reports -- -testNamespace=${{ steps.configure.outputs.DEFAULT_NAMESPACE }}
72+
continue-on-error: true
73+
74+
- name: Install Junit2Html plugin and generate report
75+
if: (!cancelled())
76+
shell: bash
77+
run: |
78+
pip install junit2html
79+
junit2html ${{ env.TESTS_DIR }}/reports/${{ env.JUNIT_FILENAME }} ${{ env.TESTS_DIR }}/reports/test-report.html
80+
continue-on-error: true
81+
82+
- name: Configure report name
83+
id: name_gen
84+
shell: bash
85+
run: |
86+
uuid=$(uuidgen)
87+
REPORT_NAME="Backend Unit Tests HTML Report - ${{ github.run_id }}_${{ github.job }}_$uuid"
88+
echo "REPORT_NAME=$REPORT_NAME" >> "$GITHUB_OUTPUT"
89+
90+
- name: Upload HTML Report
91+
id: upload
92+
uses: actions/upload-artifact@v4
93+
if: (!cancelled())
94+
with:
95+
name: ${{ steps.name_gen.outputs.REPORT_NAME }}
96+
path: ${{ env.TESTS_DIR }}/reports/test-report.html
97+
retention-days: 7
98+
continue-on-error: true
99+
100+
- name: Publish Test Summary With HTML Report
101+
id: publish
102+
uses: kubeflow/pipelines/.github/actions/junit-summary@master
103+
if: (!cancelled()) && steps.upload.outcome != 'failure'
104+
with:
105+
xml_files: '${{ env.TESTS_DIR }}/reports'
106+
custom_data: '{\"HTML Report\": \"${{ steps.upload.outputs.artifact-url }}\"}'
107+
continue-on-error: true
108+
109+
- name: Publish Test Summary
110+
id: summary
111+
uses: kubeflow/pipelines/.github/actions/junit-summary@master
112+
if: (!cancelled()) && steps.upload.outcome == 'failure'
113+
with:
114+
xml_files: '${{ env.TESTS_DIR }}/reports'
115+
continue-on-error: true
116+
117+
- name: Mark Workflow failure if test step failed
118+
if: steps.run-tests.outcome != 'success' && !cancelled()
119+
shell: bash
120+
run: exit 1
121+
122+
- name: Mark Workflow failure if test reporting failed
123+
if: (steps.publish.outcome == 'failure' || steps.summary.outcome == 'failure' || steps.upload.outcome != 'success') && !cancelled()
124+
shell: bash
125+
run: exit 1

.github/workflows/go-lint.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ jobs:
8989
working-directory: components/backend
9090
args: --timeout=5m
9191

92+
# The backend unit tests require -tags=test (to compile test-only hooks used by handlers tests).
93+
# We lint both production build (default) and test build (with tags) to avoid hiding issues.
94+
- name: Run golangci-lint (test build tags)
95+
uses: golangci/golangci-lint-action@v9
96+
with:
97+
version: latest
98+
working-directory: components/backend
99+
args: --timeout=5m --build-tags=test
100+
92101
lint-operator:
93102
runs-on: ubuntu-latest
94103
needs: detect-go-changes

.github/workflows/test-local-dev.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ jobs:
1111
steps:
1212
- name: Checkout code
1313
uses: actions/checkout@v5
14+
15+
- name: Set up Go
16+
uses: actions/setup-go@v6
17+
with:
18+
go-version-file: 'components/backend/go.mod'
19+
cache-dependency-path: 'components/backend/go.sum'
1420

1521
- name: Install minikube and kubectl
1622
run: |
@@ -35,7 +41,7 @@ jobs:
3541
- name: Deploy using Makefile
3642
run: |
3743
echo "Using Makefile to deploy complete stack..."
38-
make local-up CONTAINER_ENGINE=docker
44+
make local-up CONTAINER_ENGINE=docker CI_MODE=true
3945
4046
- name: Wait for deployments
4147
run: |
@@ -58,6 +64,29 @@ jobs:
5864
kubectl describe deployment agentic-operator -n ambient-code | tail -50
5965
exit 1
6066
}
67+
68+
- name: Run backend integration tests (real k8s auth path)
69+
run: |
70+
set -euo pipefail
71+
72+
echo "Setting up ServiceAccount + RBAC for backend integration tests..."
73+
kubectl -n ambient-code create serviceaccount backend-integration-test 2>/dev/null || true
74+
kubectl -n ambient-code create role backend-integration-test \
75+
--verb=get,list \
76+
--resource=configmaps 2>/dev/null || true
77+
kubectl -n ambient-code create rolebinding backend-integration-test \
78+
--role=backend-integration-test \
79+
--serviceaccount=ambient-code:backend-integration-test 2>/dev/null || true
80+
81+
TEST_TOKEN="$(kubectl -n ambient-code create token backend-integration-test)"
82+
echo "::add-mask::$TEST_TOKEN"
83+
84+
echo "Running Go integration tests (skips any external-provider tests without env)..."
85+
cd components/backend
86+
INTEGRATION_TESTS=true \
87+
K8S_TEST_TOKEN="$TEST_TOKEN" \
88+
K8S_TEST_NAMESPACE="ambient-code" \
89+
go test ./tests/integration/... -count=1 -timeout=10m
6190
6291
- name: Run Makefile smoke tests
6392
run: |

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,7 @@ e2e/langfuse/.env.langfuse-keys
132132
# AI assistant configuration
133133
.cursor/
134134
.tessl/
135+
136+
# Test Reporting
137+
logs/
138+
reports/

Makefile

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.PHONY: help setup build-all build-frontend build-backend build-operator build-runner deploy clean
22
.PHONY: local-up local-down local-clean local-status local-rebuild local-reload-backend local-reload-frontend local-reload-operator local-sync-version
3+
.PHONY: local-dev-token
34
.PHONY: local-logs local-logs-backend local-logs-frontend local-logs-operator local-shell local-shell-frontend
45
.PHONY: local-test local-test-dev local-test-quick test-all local-url local-troubleshoot local-port-forward local-stop-port-forward
56
.PHONY: push-all registry-login setup-hooks remove-hooks check-minikube check-kubectl
@@ -16,6 +17,19 @@ PLATFORM ?= linux/amd64
1617
BUILD_FLAGS ?=
1718
NAMESPACE ?= ambient-code
1819
REGISTRY ?= quay.io/your-org
20+
CI_MODE ?= false
21+
22+
# In CI we want full command output to diagnose failures. Locally we keep the Makefile quieter.
23+
# GitHub Actions sets CI=true by default; the workflow can also pass CI_MODE=true explicitly.
24+
ifeq ($(CI),true)
25+
CI_MODE := true
26+
endif
27+
28+
ifeq ($(CI_MODE),true)
29+
QUIET_REDIRECT :=
30+
else
31+
QUIET_REDIRECT := >/dev/null 2>&1
32+
endif
1933

2034
# Image tags
2135
FRONTEND_IMAGE ?= vteam-frontend:latest
@@ -122,40 +136,40 @@ local-up: check-minikube check-kubectl ## Start local development environment (m
122136
@echo ""
123137
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 1/8: Starting minikube..."
124138
@if [ "$(CONTAINER_ENGINE)" = "docker" ]; then \
125-
minikube start --driver=docker --memory=4096 --cpus=2 2>/dev/null || \
139+
minikube start --driver=docker --memory=4096 --cpus=2 $(QUIET_REDIRECT) || \
126140
(minikube status >/dev/null 2>&1 && echo "$(COLOR_GREEN)$(COLOR_RESET) Minikube already running") || \
127141
(echo "$(COLOR_RED)$(COLOR_RESET) Failed to start minikube" && exit 1); \
128142
else \
129-
minikube start --driver=podman --memory=4096 --cpus=2 --kubernetes-version=v1.28.3 --container-runtime=cri-o 2>/dev/null || \
143+
minikube start --driver=podman --memory=4096 --cpus=2 --kubernetes-version=v1.28.3 --container-runtime=cri-o $(QUIET_REDIRECT) || \
130144
(minikube status >/dev/null 2>&1 && echo "$(COLOR_GREEN)$(COLOR_RESET) Minikube already running") || \
131145
(echo "$(COLOR_RED)$(COLOR_RESET) Failed to start minikube" && exit 1); \
132146
fi
133147
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 2/8: Enabling addons..."
134-
@minikube addons enable ingress >/dev/null 2>&1 || true
135-
@minikube addons enable storage-provisioner >/dev/null 2>&1 || true
148+
@minikube addons enable ingress $(QUIET_REDIRECT) || true
149+
@minikube addons enable storage-provisioner $(QUIET_REDIRECT) || true
136150
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 3/8: Building images..."
137151
@$(MAKE) --no-print-directory _build-and-load
138152
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 4/8: Creating namespace..."
139-
@kubectl create namespace $(NAMESPACE) --dry-run=client -o yaml | kubectl apply -f - >/dev/null 2>&1
153+
@kubectl create namespace $(NAMESPACE) --dry-run=client -o yaml | kubectl apply -f - $(QUIET_REDIRECT)
140154
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 5/8: Applying CRDs and RBAC..."
141-
@kubectl apply -f components/manifests/base/crds/ >/dev/null 2>&1 || true
142-
@kubectl apply -f components/manifests/base/rbac/ >/dev/null 2>&1 || true
143-
@kubectl apply -f components/manifests/minikube/local-dev-rbac.yaml >/dev/null 2>&1 || true
155+
@kubectl apply -f components/manifests/base/crds/ $(QUIET_REDIRECT) || true
156+
@kubectl apply -f components/manifests/base/rbac/ $(QUIET_REDIRECT) || true
157+
@kubectl apply -f components/manifests/minikube/local-dev-rbac.yaml $(QUIET_REDIRECT) || true
144158
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 6/8: Creating storage..."
145-
@kubectl apply -f components/manifests/base/workspace-pvc.yaml -n $(NAMESPACE) >/dev/null 2>&1 || true
159+
@kubectl apply -f components/manifests/base/workspace-pvc.yaml -n $(NAMESPACE) $(QUIET_REDIRECT) || true
146160
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 6.5/8: Configuring operator..."
147161
@$(MAKE) --no-print-directory _create-operator-config
148162
@$(MAKE) --no-print-directory local-sync-version
149163
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 7/8: Deploying services..."
150-
@kubectl apply -f components/manifests/minikube/backend-deployment.yaml >/dev/null 2>&1
151-
@kubectl apply -f components/manifests/minikube/backend-service.yaml >/dev/null 2>&1
152-
@kubectl apply -f components/manifests/minikube/frontend-deployment.yaml >/dev/null 2>&1
153-
@kubectl apply -f components/manifests/minikube/frontend-service.yaml >/dev/null 2>&1
154-
@kubectl apply -f components/manifests/minikube/operator-deployment.yaml >/dev/null 2>&1
164+
@kubectl apply -f components/manifests/minikube/backend-deployment.yaml $(QUIET_REDIRECT)
165+
@kubectl apply -f components/manifests/minikube/backend-service.yaml $(QUIET_REDIRECT)
166+
@kubectl apply -f components/manifests/minikube/frontend-deployment.yaml $(QUIET_REDIRECT)
167+
@kubectl apply -f components/manifests/minikube/frontend-service.yaml $(QUIET_REDIRECT)
168+
@kubectl apply -f components/manifests/minikube/operator-deployment.yaml $(QUIET_REDIRECT)
155169
@echo "$(COLOR_BLUE)$(COLOR_RESET) Step 8/8: Setting up ingress..."
156170
@kubectl wait --namespace ingress-nginx --for=condition=ready pod \
157171
--selector=app.kubernetes.io/component=controller --timeout=90s >/dev/null 2>&1 || true
158-
@kubectl apply -f components/manifests/minikube/ingress.yaml >/dev/null 2>&1 || true
172+
@kubectl apply -f components/manifests/minikube/ingress.yaml $(QUIET_REDIRECT) || true
159173
@echo ""
160174
@echo "$(COLOR_GREEN)✓ Ambient Code Platform is starting up!$(COLOR_RESET)"
161175
@echo ""
@@ -356,7 +370,7 @@ local-test-quick: check-kubectl check-minikube ## Quick smoke test of local envi
356370
@echo "$(COLOR_BLUE)$(COLOR_RESET) Testing namespace..."
357371
@kubectl get namespace $(NAMESPACE) >/dev/null 2>&1 && echo "$(COLOR_GREEN)$(COLOR_RESET) Namespace exists" || (echo "$(COLOR_RED)$(COLOR_RESET) Namespace missing" && exit 1)
358372
@echo "$(COLOR_BLUE)$(COLOR_RESET) Waiting for pods to be ready..."
359-
@kubectl wait --for=condition=ready pod -l app=backend -n $(NAMESPACE) --timeout=60s >/dev/null 2>&1 && \
373+
@kubectl wait --for=condition=ready pod -l app=backend-api -n $(NAMESPACE) --timeout=60s >/dev/null 2>&1 && \
360374
kubectl wait --for=condition=ready pod -l app=frontend -n $(NAMESPACE) --timeout=60s >/dev/null 2>&1 && \
361375
echo "$(COLOR_GREEN)$(COLOR_RESET) Pods ready" || (echo "$(COLOR_RED)$(COLOR_RESET) Pods not ready" && exit 1)
362376
@echo "$(COLOR_BLUE)$(COLOR_RESET) Testing backend health..."
@@ -492,13 +506,13 @@ check-kubectl: ## Check if kubectl is installed
492506

493507
_build-and-load: ## Internal: Build and load images
494508
@echo " Building backend..."
495-
@$(CONTAINER_ENGINE) build -t $(BACKEND_IMAGE) components/backend >/dev/null 2>&1
509+
@$(CONTAINER_ENGINE) build -t $(BACKEND_IMAGE) components/backend $(QUIET_REDIRECT)
496510
@echo " Building frontend..."
497-
@$(CONTAINER_ENGINE) build -t $(FRONTEND_IMAGE) components/frontend >/dev/null 2>&1
511+
@$(CONTAINER_ENGINE) build -t $(FRONTEND_IMAGE) components/frontend $(QUIET_REDIRECT)
498512
@echo " Building operator..."
499-
@$(CONTAINER_ENGINE) build -t $(OPERATOR_IMAGE) components/operator >/dev/null 2>&1
513+
@$(CONTAINER_ENGINE) build -t $(OPERATOR_IMAGE) components/operator $(QUIET_REDIRECT)
500514
@echo " Building runner..."
501-
@$(CONTAINER_ENGINE) build -t $(RUNNER_IMAGE) -f components/runners/claude-code-runner/Dockerfile components/runners >/dev/null 2>&1
515+
@$(CONTAINER_ENGINE) build -t $(RUNNER_IMAGE) -f components/runners/claude-code-runner/Dockerfile components/runners $(QUIET_REDIRECT)
502516
@echo " Tagging images with localhost prefix..."
503517
@$(CONTAINER_ENGINE) tag $(BACKEND_IMAGE) localhost/$(BACKEND_IMAGE) 2>/dev/null || true
504518
@$(CONTAINER_ENGINE) tag $(FRONTEND_IMAGE) localhost/$(FRONTEND_IMAGE) 2>/dev/null || true
@@ -510,10 +524,10 @@ _build-and-load: ## Internal: Build and load images
510524
@$(CONTAINER_ENGINE) save -o /tmp/minikube-images/frontend.tar localhost/$(FRONTEND_IMAGE)
511525
@$(CONTAINER_ENGINE) save -o /tmp/minikube-images/operator.tar localhost/$(OPERATOR_IMAGE)
512526
@$(CONTAINER_ENGINE) save -o /tmp/minikube-images/runner.tar localhost/$(RUNNER_IMAGE)
513-
@minikube image load /tmp/minikube-images/backend.tar >/dev/null 2>&1
514-
@minikube image load /tmp/minikube-images/frontend.tar >/dev/null 2>&1
515-
@minikube image load /tmp/minikube-images/operator.tar >/dev/null 2>&1
516-
@minikube image load /tmp/minikube-images/runner.tar >/dev/null 2>&1
527+
@minikube image load /tmp/minikube-images/backend.tar $(QUIET_REDIRECT)
528+
@minikube image load /tmp/minikube-images/frontend.tar $(QUIET_REDIRECT)
529+
@minikube image load /tmp/minikube-images/operator.tar $(QUIET_REDIRECT)
530+
@minikube image load /tmp/minikube-images/runner.tar $(QUIET_REDIRECT)
517531
@rm -rf /tmp/minikube-images
518532
@echo "$(COLOR_GREEN)$(COLOR_RESET) Images built and loaded"
519533

@@ -549,6 +563,16 @@ _show-access-info: ## Internal: Show access information
549563
@echo ""
550564
@echo "$(COLOR_YELLOW)⚠ SECURITY NOTE:$(COLOR_RESET) Authentication is DISABLED for local development."
551565

566+
local-dev-token: check-kubectl ## Print a TokenRequest token for local-dev-user (for local dev API calls)
567+
@kubectl get serviceaccount local-dev-user -n $(NAMESPACE) >/dev/null 2>&1 || \
568+
(echo "$(COLOR_RED)$(COLOR_RESET) local-dev-user ServiceAccount not found in namespace $(NAMESPACE). Run 'make local-up' first." && exit 1)
569+
@TOKEN=$$(kubectl -n $(NAMESPACE) create token local-dev-user 2>/dev/null); \
570+
if [ -z "$$TOKEN" ]; then \
571+
echo "$(COLOR_RED)$(COLOR_RESET) Failed to mint token (kubectl create token). Ensure TokenRequest is supported and kubectl is v1.24+"; \
572+
exit 1; \
573+
fi; \
574+
echo "$$TOKEN"
575+
552576
_create-operator-config: ## Internal: Create operator config from environment variables
553577
@VERTEX_PROJECT_ID=$${ANTHROPIC_VERTEX_PROJECT_ID:-""}; \
554578
VERTEX_KEY_FILE=$${GOOGLE_APPLICATION_CREDENTIALS:-""}; \

components/backend/.golangci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ linters:
3131
- staticcheck
3232
- govet
3333

34+
# Exclude style checks from test utilities (common patterns in test code)
35+
- path: tests/.*\.go
36+
linters:
37+
- staticcheck
38+
text: "(should not use|should not use dot imports|should not use ALL_CAPS|at least one file)"
39+
3440
# Allow type assertions in K8s unstructured object parsing (intentional pattern)
3541
- path: (handlers|jira)/.*\.go
3642
text: "type assertion"

0 commit comments

Comments
 (0)