Skip to content

Commit ff08ccc

Browse files
committed
initialize the cloud functions repo with kustomize the functions fomr constructive
1 parent bef04df commit ff08ccc

99 files changed

Lines changed: 13121 additions & 1 deletion

File tree

Some content is hidden

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

.eslintrc.json

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es2021": true,
5+
"node": true,
6+
"jest": true
7+
},
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:@typescript-eslint/recommended",
11+
"prettier"
12+
],
13+
"overrides": [],
14+
"parser": "@typescript-eslint/parser",
15+
"parserOptions": {
16+
"ecmaVersion": "latest",
17+
"sourceType": "module"
18+
},
19+
"plugins": [
20+
"@typescript-eslint",
21+
"simple-import-sort",
22+
"unused-imports"
23+
],
24+
"rules": {
25+
"indent": [
26+
"error",
27+
2
28+
],
29+
"quotes": [
30+
"error",
31+
"single",
32+
{
33+
"avoidEscape": true,
34+
"allowTemplateLiterals": true
35+
}
36+
],
37+
"quote-props": [
38+
"error",
39+
"as-needed"
40+
],
41+
"semi": [
42+
"error",
43+
"always"
44+
],
45+
"comma-dangle": [
46+
"error",
47+
"never"
48+
],
49+
"simple-import-sort/imports": 1,
50+
"simple-import-sort/exports": 1,
51+
"unused-imports/no-unused-imports": 1,
52+
"@typescript-eslint/no-unused-vars": [
53+
1,
54+
{
55+
"argsIgnorePattern": "React|res|next|^_"
56+
}
57+
],
58+
"@typescript-eslint/no-explicit-any": 0,
59+
"@typescript-eslint/no-var-requires": 0,
60+
"no-console": 0,
61+
"@typescript-eslint/ban-ts-comment": 0,
62+
"prefer-const": 0,
63+
"no-case-declarations": 0,
64+
"no-implicit-globals": 0,
65+
"@typescript-eslint/no-unsafe-declaration-merging": 0
66+
}
67+
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
name: CI Test K8s
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
- release/*
8+
paths:
9+
- "k8s/**"
10+
- ".github/workflows/test-k8s-deployment.yaml"
11+
push:
12+
branches:
13+
- main
14+
- release/*
15+
paths:
16+
- "k8s/**"
17+
- ".github/workflows/test-k8s-deployment.yaml"
18+
workflow_dispatch: {}
19+
20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.ref }}-test-deployment
22+
cancel-in-progress: true
23+
24+
jobs:
25+
k8s-ci-test:
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 45
28+
29+
steps:
30+
- name: Checkout
31+
uses: actions/checkout@v4
32+
33+
- name: Setup kind cluster
34+
uses: helm/kind-action@v1
35+
with:
36+
cluster_name: local
37+
wait: 120s
38+
39+
- name: Install jq (for resource diagnostics)
40+
run: |
41+
sudo apt-get update
42+
sudo apt-get install -y jq
43+
44+
- name: Verify cluster
45+
run: |
46+
kubectl version
47+
kubectl get nodes -o wide
48+
49+
- name: Install Knative (operators-knative-only)
50+
run: |
51+
cd k8s/scripts/setup
52+
make operators-knative-only
53+
54+
- name: Trim Knative resources for CI
55+
run: |
56+
set -e
57+
58+
echo "=== Shrinking Knative control-plane resources ==="
59+
60+
echo "Patching knative-serving activator..."
61+
kubectl -n knative-serving set resources deploy/activator \
62+
--requests=cpu=50m,memory=80Mi --limits=cpu=200m,memory=256Mi \
63+
|| echo "WARN: failed to patch activator"
64+
65+
echo "Patching knative-serving autoscaler..."
66+
kubectl -n knative-serving set resources deploy/autoscaler \
67+
--requests=cpu=25m,memory=80Mi --limits=cpu=200m,memory=256Mi \
68+
|| echo "WARN: failed to patch autoscaler"
69+
70+
echo "Patching knative-serving controller..."
71+
kubectl -n knative-serving set resources deploy/controller \
72+
--requests=cpu=50m,memory=100Mi --limits=cpu=300m,memory=512Mi \
73+
|| echo "WARN: failed to patch controller"
74+
75+
echo "Patching knative-serving webhook..."
76+
kubectl -n knative-serving set resources deploy/webhook \
77+
--requests=cpu=25m,memory=80Mi --limits=cpu=200m,memory=256Mi \
78+
|| echo "WARN: failed to patch webhook"
79+
80+
echo "Patching knative net-kourier controller..."
81+
kubectl -n knative-serving set resources deploy/net-kourier-controller \
82+
--requests=cpu=25m,memory=80Mi --limits=cpu=200m,memory=256Mi \
83+
|| echo "WARN: failed to patch net-kourier-controller"
84+
85+
echo "Patching kourier gateway..."
86+
kubectl -n kourier-system set resources deploy/3scale-kourier-gateway \
87+
--requests=cpu=25m,memory=80Mi --limits=cpu=200m,memory=256Mi \
88+
|| echo "WARN: failed to patch 3scale-kourier-gateway"
89+
90+
echo "Optionally shrinking coredns..."
91+
kubectl -n kube-system scale deploy/coredns --replicas=1 || echo "WARN: failed to scale coredns"
92+
kubectl -n kube-system set resources deploy/coredns \
93+
--requests=cpu=50m,memory=70Mi --limits=cpu=200m,memory=170Mi \
94+
|| echo "WARN: failed to patch coredns"
95+
96+
echo "Restarting control-plane pods so resource changes take effect..."
97+
kubectl -n knative-serving rollout restart deploy/activator deploy/autoscaler deploy/controller deploy/webhook deploy/net-kourier-controller || true
98+
kubectl -n kourier-system rollout restart deploy/3scale-kourier-gateway || true
99+
kubectl -n kube-system rollout restart deploy/coredns || true
100+
101+
- name: Create ghcr-pull image pull secret (optional)
102+
run: |
103+
set -e
104+
if [ -n "${GHCR_USERNAME:-}" ] && [ -n "${GHCR_TOKEN:-}" ]; then
105+
echo "Creating ghcr-pull secret in default namespace"
106+
kubectl create namespace default --dry-run=client -o yaml | kubectl apply -f -
107+
kubectl create secret docker-registry ghcr-pull \
108+
--docker-server=ghcr.io \
109+
--docker-username="${GHCR_USERNAME}" \
110+
--docker-password="${GHCR_TOKEN}" \
111+
--docker-email="${GHCR_EMAIL:-devnull@example.com}" \
112+
--dry-run=client -o yaml | kubectl apply -n default -f -
113+
else
114+
echo "GHCR_USERNAME/GHCR_TOKEN not set; assuming dashboard/db-job images are public."
115+
fi
116+
env:
117+
GHCR_USERNAME: ${{ secrets.GH_USERNAME }}
118+
GHCR_TOKEN: ${{ secrets.GH_PAT_TOKEN }}
119+
GHCR_EMAIL: ${{ secrets.GH_EMAIL }}
120+
121+
- name: Sleep for a bit before install
122+
run: |
123+
sleep 10
124+
125+
- name: Apply CI overlay (on top of local)
126+
run: |
127+
cd k8s
128+
kubectl kustomize overlays/ci --load-restrictor=LoadRestrictionsNone | kubectl apply -f -
129+
130+
- name: Sleep for a bit
131+
run: |
132+
sleep 100
133+
134+
- name: Dump pod resource requests/limits (all namespaces)
135+
if: always()
136+
run: |
137+
echo "=== Per-pod resource requests/limits ==="
138+
kubectl get pods -A -o json \
139+
| jq -r '
140+
.items[]
141+
| .metadata.namespace as $ns
142+
| .metadata.name as $pod
143+
| .spec.containers[]
144+
| [$ns, $pod, .name,
145+
(.resources.requests.cpu // "0"),
146+
(.resources.requests.memory // "0"),
147+
(.resources.limits.cpu // "0"),
148+
(.resources.limits.memory // "0")]
149+
| @tsv
150+
' \
151+
| column -t
152+
153+
echo
154+
echo "=== Aggregated resource requests per namespace (CPU cores, MiB) ==="
155+
kubectl get pods -A -o json \
156+
| jq -r '
157+
[ .items[]
158+
| .metadata.namespace as $ns
159+
| {
160+
ns: $ns,
161+
cpu: ([.spec.containers[].resources.requests.cpu // "0"]
162+
| map(
163+
if test("m$") then (sub("m$";"") | tonumber / 1000)
164+
elif . == "0" then 0
165+
else tonumber
166+
end
167+
)
168+
| add),
169+
memMi: ([.spec.containers[].resources.requests.memory // "0"]
170+
| map(
171+
if test("Gi$") then (sub("Gi$";"") | tonumber * 1024)
172+
elif test("Mi$") then (sub("Mi$";"") | tonumber)
173+
elif . == "0" then 0
174+
else 0
175+
end
176+
)
177+
| add)
178+
}
179+
]
180+
| group_by(.ns)
181+
| map({ns: .[0].ns, cpu: (map(.cpu) | add), memMi: (map(.memMi) | add)})
182+
| sort_by(.ns)
183+
| .[]
184+
| [.ns, ( .cpu | tostring ), ( .memMi | tostring )]
185+
| @tsv
186+
' \
187+
| awk 'BEGIN{print "NAMESPACE\tCPU_CORES\tMEM_MIB"} {print}'
188+
189+
- name: Wait for local workloads
190+
continue-on-error: true
191+
run: |
192+
set -e
193+
echo "Namespaces:" && kubectl get ns
194+
echo "All pods (initial):" && kubectl get pods -A
195+
196+
echo "All pods (final):" && kubectl get pods -A
197+
echo "Knative services:" && kubectl get ksvc -A || true
198+
199+
- name: Dump diagnostics on failure
200+
if: always()
201+
run: |
202+
echo "=== Namespaces ==="
203+
kubectl get ns
204+
205+
echo "=== Pods (all namespaces) ==="
206+
kubectl get pods -A -o wide || true
207+
208+
echo "=== Events (default) ==="
209+
kubectl get events --sort-by=.lastTimestamp || true
210+
211+
echo "=== Deployments (default) ==="
212+
kubectl get deploy -o wide || true
213+
214+
echo "=== Logs for default pods ==="
215+
for pod in $(kubectl get pods -o jsonpath='{.items[*].metadata.name}'); do
216+
echo "--------------------------------------------------"
217+
echo "Logs for $pod"
218+
kubectl describe pod "$pod" || true
219+
kubectl logs "$pod" --all-containers --tail=200 || true
220+
done

.prettierrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"trailingComma": "es5",
3+
"tabWidth": 2,
4+
"semi": true,
5+
"useTabs": false,
6+
"singleQuote": true,
7+
"jsxSingleQuote": false
8+
}

AGENTS.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Constructive Functions Agent Guide
2+
3+
This guide helps AI agents quickly navigate the constructive-functions workspace.
4+
5+
## Quick Start
6+
7+
**Most important commands:**
8+
- `pnpm build` — Build all function packages (TypeScript → `dist/`)
9+
- `make docker-build` — Build Docker images for all functions
10+
- `make docker-build-simple-email` — Build just the simple-email image
11+
- `make docker-build-send-email-link` — Build just the send-email-link image
12+
- `cd k8s && make kustomize-local` — Deploy functions to a local Kubernetes cluster
13+
14+
**Entry points:**
15+
- Function HTTP handlers: `functions/*/src/index.ts`
16+
- Docker entrypoints: `functions/*/Dockerfile` (`CMD ["node", "dist/index.js"]`)
17+
- K8s manifests: `k8s/base/functions/*` and `k8s/overlays/local/functions/*`
18+
19+
## Monorepo Layout
20+
21+
- `functions/*` — Function packages (`send-email-link`, `simple-email`)
22+
- `k8s/` — Kubernetes manifests, overlays, and setup scripts
23+
- `types/` — Custom type definitions for `@launchql/*` packages
24+
25+
## Function Architecture
26+
27+
**Image naming:**
28+
29+
All Docker images use the registry prefix `ghcr.io/constructive-io/constructive-functions/`:
30+
31+
- `ghcr.io/constructive-io/constructive-functions/simple-email:latest`
32+
- `ghcr.io/constructive-io/constructive-functions/send-email-link:latest`
33+
34+
The `REGISTRY` variable in the Makefile controls this prefix.
35+
36+
Both functions follow the same pattern:
37+
38+
1. **Source** (`src/index.ts`):
39+
- Import `app` from `@constructive-io/knative-job-fn` (Express app)
40+
- Define HTTP handler with `app.post('/', ...)`
41+
- Export `app` and start server if `require.main === module`
42+
2. **Build** (`package.json``tsc`):
43+
- TypeScript compiles to `dist/index.js`
44+
3. **Docker** (`Dockerfile`):
45+
- Uses `node:22-alpine`
46+
- Installs production deps via `pnpm`
47+
- Copies `dist/` and runs `node dist/index.js`
48+
49+
## Common Workflows
50+
51+
**Build all functions locally:**
52+
53+
```bash
54+
pnpm install
55+
pnpm build
56+
```
57+
58+
**Build Docker images:**
59+
60+
```bash
61+
make docker-build # Build all functions
62+
make docker-build-simple-email # Build one function
63+
make docker-build-send-email-link
64+
```
65+
66+
**Deploy to Kubernetes (local):**
67+
68+
```bash
69+
cd k8s
70+
make operators-knative-only # Install Knative
71+
make kustomize-local # Apply manifests
72+
make proxy-server # Forward API to localhost:8080
73+
```
74+
75+
**Run locally with Docker (manual):**
76+
77+
```bash
78+
docker run -p 8080:8080 -e SIMPLE_EMAIL_DRY_RUN=true ghcr.io/constructive-io/constructive-functions/simple-email:latest
79+
```
80+
81+
## Type Definitions
82+
83+
The `types/` directory contains manual type definitions for packages without built-in types:
84+
85+
- `@launchql/mjml` — Styled-email generator (MJML-based email templates)
86+
- `@launchql/postmaster` — Mailgun email sender
87+
- `@launchql/styled-email` — Email template components
88+
89+
These are referenced in `tsconfig.json` via `"typeRoots": ["./types", "./node_modules/@types"]`.
90+
91+
## Tips
92+
93+
1. Build functions before building Docker images — `dist/` must exist
94+
2. Functions run on `PORT=8080` (Knative default)
95+
3. Email functions support dry-run mode via env vars (`SIMPLE_EMAIL_DRY_RUN`, `SEND_EMAIL_LINK_DRY_RUN`)
96+
4. K8s manifests reference Docker images; update image tags after pushing to registry

0 commit comments

Comments
 (0)