Skip to content

Commit f312ffd

Browse files
authored
Merge pull request #28 from webstackdev/feature/deployment-k8s-ci-cd
Feature/deployment k8s ci cd
2 parents 407d131 + 349f85f commit f312ffd

39 files changed

Lines changed: 1298 additions & 444 deletions
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: Build and Release
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
tags:
11+
- "v*"
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: read
16+
packages: write
17+
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
20+
cancel-in-progress: true
21+
22+
jobs:
23+
validate-helm:
24+
name: Validate Helm chart
25+
runs-on: ubuntu-latest
26+
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v6
30+
31+
- name: Set up Helm
32+
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
33+
34+
- name: Lint chart
35+
run: helm lint deploy/helm/newsletter-maker
36+
37+
- name: Render chart
38+
run: helm template newsletter-maker deploy/helm/newsletter-maker -f
39+
deploy/helm/newsletter-maker/values-minikube.yaml >
40+
/tmp/newsletter-maker-chart.yaml
41+
42+
build-frontend:
43+
name: Build frontend
44+
runs-on: ubuntu-latest
45+
46+
steps:
47+
- name: Checkout repository
48+
uses: actions/checkout@v6
49+
50+
- name: Set up Node.js
51+
uses: actions/setup-node@v6
52+
with:
53+
node-version: "22"
54+
cache: npm
55+
cache-dependency-path: frontend/package-lock.json
56+
57+
- name: Install frontend dependencies
58+
working-directory: frontend
59+
run: npm ci
60+
61+
- name: Prepare frontend env
62+
working-directory: frontend
63+
run: |
64+
cp .env.example .env.local
65+
echo "NEXTAUTH_SECRET=ci-build-secret" >> .env.local
66+
67+
- name: Build frontend
68+
working-directory: frontend
69+
env:
70+
NEXT_PUBLIC_API_URL: http://localhost:8000
71+
NEXTAUTH_URL: http://localhost:3000
72+
NEXTAUTH_SECRET: ci-build-secret
73+
run: npm run build
74+
75+
build-backend:
76+
name: Build and scan backend image
77+
runs-on: ubuntu-latest
78+
79+
steps:
80+
- name: Checkout repository
81+
uses: actions/checkout@v6
82+
83+
- name: Build backend image
84+
env:
85+
DOCKER_BUILDKIT: "1"
86+
run: docker build -t newsletter-maker-ci:${{ github.sha }} -f
87+
docker/web/Dockerfile .
88+
89+
- name: Scan backend image with Trivy
90+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
91+
with:
92+
image-ref: newsletter-maker-ci:${{ github.sha }}
93+
scan-type: image
94+
severity: HIGH,CRITICAL
95+
ignore-unfixed: true
96+
exit-code: "1"
97+
98+
- name: Log in to GHCR
99+
if: github.event_name == 'push'
100+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
101+
with:
102+
registry: ghcr.io
103+
username: ${{ github.actor }}
104+
password: ${{ secrets.GITHUB_TOKEN }}
105+
106+
- name: Publish backend image
107+
if: github.event_name == 'push'
108+
env:
109+
IMAGE_REPOSITORY: ghcr.io/${{ github.repository_owner }}/newsletter-maker
110+
run: |
111+
set -euo pipefail
112+
113+
docker tag newsletter-maker-ci:${GITHUB_SHA} ${IMAGE_REPOSITORY}:${GITHUB_SHA}
114+
docker push ${IMAGE_REPOSITORY}:${GITHUB_SHA}
115+
116+
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
117+
docker tag newsletter-maker-ci:${GITHUB_SHA} ${IMAGE_REPOSITORY}:main
118+
docker push ${IMAGE_REPOSITORY}:main
119+
fi
120+
121+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
122+
version_tag="${GITHUB_REF#refs/tags/}"
123+
docker tag newsletter-maker-ci:${GITHUB_SHA} ${IMAGE_REPOSITORY}:${version_tag}
124+
docker push ${IMAGE_REPOSITORY}:${version_tag}
125+
docker tag newsletter-maker-ci:${GITHUB_SHA} ${IMAGE_REPOSITORY}:latest
126+
docker push ${IMAGE_REPOSITORY}:latest
127+
fi

‎.github/workflows/lint.yml‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,19 @@ jobs:
3636
cache-dependency-path: frontend/package-lock.json
3737

3838
- name: Install just
39-
uses: extractions/setup-crate@v2
39+
uses: extractions/setup-crate@7577c1bdf2d95e6d65d532788f35ed79d4b1dda2 # v2
4040
with:
4141
repo: casey/just@1.50.0
4242
github-token: ${{ github.token }}
4343

44+
- name: Set up Helm
45+
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
46+
4447
- name: Install dependencies
4548
run: just install
4649

4750
- name: Install pre-commit hooks
4851
run: pre-commit install --install-hooks
4952

50-
- name: Run lint
53+
- name: Run lint and type checks
5154
run: just lint

‎.github/workflows/test.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
cache-dependency-path: frontend/package-lock.json
3737

3838
- name: Install just
39-
uses: extractions/setup-crate@v2
39+
uses: extractions/setup-crate@7577c1bdf2d95e6d65d532788f35ed79d4b1dda2 # v2
4040
with:
4141
repo: casey/just@1.50.0
4242
github-token: ${{ github.token }}

‎.pre-commit-config.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ repos:
33
rev: v5.0.0
44
hooks:
55
- id: check-yaml
6+
exclude: ^deploy/helm/.+/templates/.*\.(ya?ml)$
67
- id: end-of-file-fixer
78
- id: trailing-whitespace
89

‎.vscode/settings.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"upserted",
4747
"upvote",
4848
"uritemplate",
49+
"xrpc",
4950
"xxhash"
5051
]
5152
}

‎README.md‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,10 @@ The system is designed for graceful failure, not silent corruption. Unparseable
8181

8282
**Deployment:** Docker Compose (MVP) · Kubernetes-ready · 12-factor configuration
8383

84-
## Implementation Plan
85-
86-
| Phase | Focus | Key Deliverables |
87-
| ----- | ----- | ---------------- |
88-
| **1. MVP** | Content ingestion + basic surfacing | RSS and Reddit plugins · Entity model in Postgres · Qdrant for embeddings · Classification, relevance scoring, and summarization skills · Dashboard with upvote/downvote · Seed script for demo data |
89-
| **2. Authority** | Newsletter ingestion + authority signals | Resend email intake with LLM extraction · Subscription confirmation flow · Authority scoring from mention frequency · Bluesky plugin · Deduplication and entity extraction skills |
90-
| **3. Intelligence** | Expanded sources + trend analysis | Mastodon plugin · Trend velocity detection · Theme suggestions · Source diversity analysis · Original content idea generation |
91-
| **4. Polish** | Advanced features | LinkedIn integration · Automated entity discovery · Full multi-signal authority model · Newsletter draft generation |
92-
9384
## Project Documentation
9485

9586
- [Developer Guide](docs/DEVELOPER_GUIDE.md) gives a fast "where to look first" map for new contributors.
87+
- [Deployment Guide](docs/DEPLOYMENT.md) covers Docker Compose, Helm, Minikube, and deployment-aware CI.
9688
- [Implementation Overview](docs/IMPLEMENTATION_OVERVIEW.md) summarizes the main features and current architecture.
9789
- [Data Models](docs/MODELS.md) describes the purpose of each core model.
9890
- [Relevance Scoring](docs/RELEVANCE_SCORING.md) explains how similarity scoring and review thresholds work.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v2
2+
name: newsletter-maker
3+
description: Helm chart for the Newsletter Maker backend stack.
4+
type: application
5+
version: 0.1.0
6+
appVersion: "0.1.0"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1. Install the chart into minikube:
2+
helm upgrade --install newsletter-maker ./deploy/helm/newsletter-maker -f ./deploy/helm/newsletter-maker/values-minikube.yaml
3+
4+
2. Check the rollout state:
5+
kubectl get pods -l app.kubernetes.io/instance={{ .Release.Name }}
6+
7+
3. Reach the cluster service:
8+
kubectl port-forward svc/{{ include "newsletter-maker.fullname" . }}-nginx 8080:{{ .Values.nginx.service.port }}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{{- define "newsletter-maker.name" -}}
2+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
3+
{{- end -}}
4+
5+
{{- define "newsletter-maker.fullname" -}}
6+
{{- if .Values.fullnameOverride -}}
7+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
8+
{{- else -}}
9+
{{- printf "%s-%s" .Release.Name (include "newsletter-maker.name" .) | trunc 63 | trimSuffix "-" -}}
10+
{{- end -}}
11+
{{- end -}}
12+
13+
{{- define "newsletter-maker.labels" -}}
14+
app.kubernetes.io/name: {{ include "newsletter-maker.name" . }}
15+
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
16+
app.kubernetes.io/instance: {{ .Release.Name }}
17+
app.kubernetes.io/managed-by: {{ .Release.Service }}
18+
{{- end -}}
19+
20+
{{- define "newsletter-maker.selectorLabels" -}}
21+
app.kubernetes.io/name: {{ include "newsletter-maker.name" . }}
22+
app.kubernetes.io/instance: {{ .Release.Name }}
23+
{{- end -}}
24+
25+
{{- define "newsletter-maker.componentLabels" -}}
26+
{{ include "newsletter-maker.selectorLabels" . }}
27+
app.kubernetes.io/component: {{ .component }}
28+
{{- end -}}
29+
30+
{{- define "newsletter-maker.databaseHost" -}}
31+
{{- printf "%s-postgres" (include "newsletter-maker.fullname" .) -}}
32+
{{- end -}}
33+
34+
{{- define "newsletter-maker.redisHost" -}}
35+
{{- printf "%s-redis" (include "newsletter-maker.fullname" .) -}}
36+
{{- end -}}
37+
38+
{{- define "newsletter-maker.qdrantHost" -}}
39+
{{- printf "%s-qdrant" (include "newsletter-maker.fullname" .) -}}
40+
{{- end -}}
41+
42+
{{- define "newsletter-maker.djangoHost" -}}
43+
{{- printf "%s-django" (include "newsletter-maker.fullname" .) -}}
44+
{{- end -}}
45+
46+
{{- define "newsletter-maker.databaseUrl" -}}
47+
{{- printf "postgresql://%s:%s@%s:%v/%s" .Values.postgres.username .Values.postgres.password (include "newsletter-maker.databaseHost" .) .Values.postgres.service.port .Values.postgres.database -}}
48+
{{- end -}}
49+
50+
{{- define "newsletter-maker.redisUrl" -}}
51+
{{- printf "redis://%s:%v/0" (include "newsletter-maker.redisHost" .) .Values.redis.service.port -}}
52+
{{- end -}}
53+
54+
{{- define "newsletter-maker.qdrantUrl" -}}
55+
{{- printf "http://%s:%v" (include "newsletter-maker.qdrantHost" .) .Values.qdrant.service.port -}}
56+
{{- end -}}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "newsletter-maker.fullname" . }}-celery-beat
5+
labels:
6+
{{- include "newsletter-maker.labels" . | nindent 4 }}
7+
spec:
8+
replicas: {{ .Values.celeryBeat.replicaCount }}
9+
selector:
10+
matchLabels:
11+
{{- include "newsletter-maker.componentLabels" (dict "Release" .Release "Values" .Values "Chart" .Chart "component" "celery-beat") | nindent 6 }}
12+
template:
13+
metadata:
14+
labels:
15+
{{- include "newsletter-maker.componentLabels" (dict "Release" .Release "Values" .Values "Chart" .Chart "component" "celery-beat") | nindent 8 }}
16+
spec:
17+
containers:
18+
- name: celery-beat
19+
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
20+
imagePullPolicy: {{ .Values.image.pullPolicy }}
21+
command:
22+
{{- toYaml .Values.celeryBeat.command | nindent 12 }}
23+
env:
24+
- name: BOOTSTRAP_APP
25+
value: "false"
26+
envFrom:
27+
- configMapRef:
28+
name: {{ include "newsletter-maker.fullname" . }}-env
29+
- secretRef:
30+
name: {{ include "newsletter-maker.fullname" . }}-secret
31+
resources:
32+
{{- toYaml .Values.celeryBeat.resources | nindent 12 }}

0 commit comments

Comments
 (0)