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: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
.git
.cache
node_modules
**/node_modules
dist
**/dist
build
coverage
.venv
venv
__pycache__
.pytest_cache
**/.pytest_cache
*.log
.DS_Store
.terraform
**/.terraform
*.tfstate
*.tfstate.*
*.tfvars
!*.tfvars.example
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,35 @@ jobs:
- name: Build frontend
run: npm run build

deployment-packaging:
name: Deployment packaging
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Helm
uses: azure/setup-helm@v4

- name: Lint Helm chart
run: helm lint deploy/helm/buildplane

- name: Render Helm chart
run: helm template buildplane deploy/helm/buildplane --namespace buildplane-system --include-crds --values deploy/helm/buildplane/values-gke.example.yaml

- name: Set up Terraform
uses: hashicorp/setup-terraform@v3

- name: Check Terraform formatting
run: terraform fmt -check -recursive infra/gke

- name: Initialize Terraform
run: terraform -chdir=infra/gke init -backend=false

- name: Validate Terraform
run: terraform -chdir=infra/gke validate

repository-checks:
name: Repository checks
runs-on: ubuntu-latest
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ coverage/

# Logs
*.log

# Terraform
.terraform/
*.tfstate
*.tfstate.*
*.tfvars
!.tfvars.example
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,38 @@ URL printed by Vite and use the console to create demo workflow runs, inspect
audit timelines, submit human decisions, and operate the synthetic
`issue_classifier` release flow.

## Phase 12: Cloud Deployment

Phase 12 adds a Helm chart and GKE Terraform scaffold.

Render the Helm chart locally:

```bash
helm lint deploy/helm/buildplane
helm template buildplane deploy/helm/buildplane \
--namespace buildplane-system \
--include-crds \
--values deploy/helm/buildplane/values-gke.example.yaml
```

Plan GKE infrastructure:

```bash
cd infra/gke
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform plan
```

Build the frontend web image:

```bash
docker build -f deploy/docker/web.Dockerfile -t buildplane/web:dev .
```

See [GKE deployment guide](docs/deployment/gke.md) and
[cloud operations runbook](docs/runbooks/cloud-operations.md).

## Phase 5: Python AI Service

Phase 5 inserts a bounded AI classification node into the local workflow:
Expand Down
16 changes: 16 additions & 0 deletions deploy/docker/web.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM node:22-alpine AS build

WORKDIR /src/web

COPY web/package.json web/package-lock.json ./
RUN npm ci

COPY web/ ./
RUN npm run build

FROM nginx:1.27-alpine

COPY web/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /src/web/dist /usr/share/nginx/html

EXPOSE 8080
6 changes: 6 additions & 0 deletions deploy/helm/buildplane/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: buildplane
description: Kubernetes-native AI workflow control plane
type: application
version: 0.12.0
appVersion: "0.12.0"
132 changes: 132 additions & 0 deletions deploy/helm/buildplane/crds/buildplaneruntimes.buildplane.io.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: buildplaneruntimes.buildplane.io
labels:
app.kubernetes.io/name: buildplaneruntimes.buildplane.io
app.kubernetes.io/part-of: buildplane
spec:
group: buildplane.io
scope: Namespaced
names:
plural: buildplaneruntimes
singular: buildplaneruntime
kind: BuildPlaneRuntime
shortNames:
- bpr
versions:
- name: v1alpha1
served: true
storage: true
subresources:
status: {}
additionalPrinterColumns:
- name: Ready
type: string
jsonPath: .status.conditions[?(@.type=="Ready")].status
- name: Scheduler
type: integer
jsonPath: .spec.schedulerReplicas
- name: General
type: integer
jsonPath: .spec.workerPools.general.replicas
- name: AI
type: integer
jsonPath: .spec.workerPools.ai.replicas
schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
type: object
properties:
schedulerReplicas:
type: integer
format: int32
minimum: 0
maximum: 5
default: 1
workerPools:
type: object
properties:
general:
type: object
properties:
replicas:
type: integer
format: int32
minimum: 0
maximum: 10
default: 2
ai:
type: object
properties:
replicas:
type: integer
format: int32
minimum: 0
maximum: 10
default: 1
status:
type: object
properties:
observedGeneration:
type: integer
format: int64
conditions:
type: array
items:
type: object
required:
- type
- status
- lastTransitionTime
- reason
- message
properties:
type:
type: string
status:
type: string
enum:
- "True"
- "False"
- Unknown
observedGeneration:
type: integer
format: int64
lastTransitionTime:
type: string
format: date-time
reason:
type: string
message:
type: string
deployments:
type: array
items:
type: object
required:
- name
- desired
- available
- ready
properties:
name:
type: string
desired:
type: integer
format: int32
available:
type: integer
format: int32
ready:
type: boolean
9 changes: 9 additions & 0 deletions deploy/helm/buildplane/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{- define "buildplane.labels" -}}
app.kubernetes.io/part-of: buildplane
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
{{- end -}}

{{- define "buildplane.image" -}}
{{- printf "%s:%s" .repository .tag -}}
{{- end -}}
69 changes: 69 additions & 0 deletions deploy/helm/buildplane/templates/ai-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: buildplane-ai-service
labels:
app.kubernetes.io/name: buildplane-ai-service
{{- include "buildplane.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.aiService.replicas }}
selector:
matchLabels:
app.kubernetes.io/name: buildplane-ai-service
template:
metadata:
labels:
app.kubernetes.io/name: buildplane-ai-service
app.kubernetes.io/part-of: buildplane
spec:
serviceAccountName: buildplane-ai-service
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: ai-service
image: {{ include "buildplane.image" .Values.aiService.image }}
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
ports:
- name: http
containerPort: 8090
env:
- name: BUILDPLANE_AI_PROVIDER
value: {{ .Values.aiService.provider | quote }}
- name: BUILDPLANE_AI_PROMPT_VERSION
value: {{ .Values.aiService.promptVersion | quote }}
readinessProbe:
httpGet:
path: /readyz
port: http
livenessProbe:
httpGet:
path: /healthz
port: http
resources:
{{- toYaml .Values.aiService.resources | nindent 12 }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: buildplane-ai-service
labels:
app.kubernetes.io/name: buildplane-ai-service
{{- include "buildplane.labels" . | nindent 4 }}
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: buildplane-ai-service
ports:
- name: http
port: 8090
targetPort: http
28 changes: 28 additions & 0 deletions deploy/helm/buildplane/templates/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: buildplane-runtime-config
labels:
app.kubernetes.io/name: buildplane-runtime-config
{{- include "buildplane.labels" . | nindent 4 }}
data:
redis-url: {{ .Values.runtimeConfig.redisURL | quote }}
migrations-dir: {{ .Values.runtimeConfig.migrationsDir | quote }}
scheduler-batch-size: {{ .Values.runtimeConfig.schedulerBatchSize | quote }}
scheduler-interval: {{ .Values.runtimeConfig.schedulerInterval | quote }}
worker-lease-duration: {{ .Values.runtimeConfig.workerLeaseDuration | quote }}
worker-drain-timeout: {{ .Values.runtimeConfig.workerDrainTimeout | quote }}
ai-service-url: {{ .Values.runtimeConfig.aiServiceURL | quote }}
{{- if .Values.database.url }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.database.existingSecret }}
labels:
app.kubernetes.io/name: buildplane-postgres
{{- include "buildplane.labels" . | nindent 4 }}
type: Opaque
stringData:
{{ .Values.database.urlKey }}: {{ .Values.database.url | quote }}
{{- end }}
Loading
Loading