Skip to content

Commit eae7614

Browse files
wthrbtnclaude
andcommitted
feat: add Helm chart for Kubernetes deployment
Best-practice chart with PSS restricted security context, CA certificate injection for air-gapped environments, and all cooked flags exposed as values. Includes HPA, PDB, NetworkPolicy, Ingress, schema validation, and chart-testing CI config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e50437a commit eae7614

20 files changed

Lines changed: 1114 additions & 1 deletion

.github/workflows/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,38 @@ jobs:
148148
DELAY=$((DELAY * 2))
149149
done
150150
151+
helm-chart:
152+
needs: release-please
153+
if: needs.release-please.outputs.release_created
154+
runs-on: ubuntu-24.04
155+
permissions:
156+
contents: read
157+
packages: write
158+
id-token: write
159+
environment: release
160+
steps:
161+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
162+
163+
- name: Install cosign
164+
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
165+
166+
- name: Log in to GHCR
167+
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
168+
169+
- name: Package and push
170+
run: |
171+
helm package charts/cooked/
172+
PACKAGE=$(ls cooked-*.tgz)
173+
helm push "$PACKAGE" oci://ghcr.io/${{ github.repository_owner }}/charts
174+
175+
- name: Sign chart
176+
env:
177+
TAG: ${{ needs.release-please.outputs.tag_name }}
178+
OWNER: ${{ github.repository_owner }}
179+
run: |
180+
VERSION="${TAG#v}"
181+
cosign sign --yes "ghcr.io/${OWNER}/charts/cooked:${VERSION}"
182+
151183
upload-assets:
152184
needs: [release-please, build-binaries]
153185
if: needs.release-please.outputs.release_created

charts/cooked/.helmignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# VCS
2+
.git/
3+
.github/
4+
.gitignore
5+
6+
# IDE/OS
7+
.DS_Store
8+
.idea/
9+
.vscode/
10+
*.swp
11+
*.tmp
12+
13+
# CI
14+
Makefile
15+
Jenkinsfile
16+
17+
# Security
18+
.env
19+
*secret*
20+
*.key
21+
*.pem
22+
*.crt
23+
24+
# Development
25+
*.md.gotmpl
26+
ci/
27+
28+
# Snapshots
29+
*/__snapshot__/*

charts/cooked/Chart.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: v2
2+
name: cooked
3+
description: A rendering proxy for air-gapped environments — fetches raw documents and serves styled HTML
4+
type: application
5+
version: 1.3.2 # x-release-please-version
6+
appVersion: "1.3.2" # x-release-please-version
7+
kubeVersion: ">=1.22.0"
8+
home: https://github.com/air-gapped/cooked
9+
sources:
10+
- https://github.com/air-gapped/cooked
11+
keywords:
12+
- rendering
13+
- proxy
14+
- markdown
15+
- asciidoc
16+
- air-gapped
17+
maintainers:
18+
- name: air-gapped
19+
url: https://github.com/air-gapped

charts/cooked/ci/ca-values.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# CI test values — CA certificate injection
2+
replicaCount: 1
3+
4+
cooked:
5+
allowedUpstreams: "*.internal"
6+
tlsSkipVerify: false
7+
8+
caCertificates:
9+
enabled: true
10+
inline:
11+
internal-ca.crt: |
12+
-----BEGIN CERTIFICATE-----
13+
MIIBkTCB+wIUYz1234567890abcdef=
14+
-----END CERTIFICATE-----
15+
16+
resources:
17+
limits:
18+
cpu: 500m
19+
memory: 256Mi
20+
requests:
21+
cpu: 100m
22+
memory: 128Mi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# CI test values — default configuration
2+
replicaCount: 1
3+
4+
cooked:
5+
allowedUpstreams: "*.example.com,10.0.0.0/8"
6+
defaultTheme: "auto"
7+
8+
resources:
9+
limits:
10+
cpu: 500m
11+
memory: 256Mi
12+
requests:
13+
cpu: 100m
14+
memory: 128Mi

charts/cooked/templates/NOTES.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Thank you for installing {{ .Chart.Name }}!
2+
3+
{{- if .Values.ingress.enabled }}
4+
5+
Access cooked at:
6+
{{- range .Values.ingress.hosts }}
7+
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}{{ (first .paths).path }}
8+
{{- end }}
9+
{{- else if contains "NodePort" .Values.service.type }}
10+
11+
Get the application URL:
12+
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "cooked.fullname" . }})
13+
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
14+
echo http://$NODE_IP:$NODE_PORT
15+
{{- else if contains "ClusterIP" .Values.service.type }}
16+
17+
Port-forward to access cooked:
18+
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "cooked.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }}
19+
20+
Then open:
21+
http://localhost:{{ .Values.service.port }}/https://example.com/README.md
22+
{{- end }}
23+
24+
{{- if .Values.cooked.allowedUpstreams }}
25+
26+
Allowed upstreams: {{ .Values.cooked.allowedUpstreams }}
27+
{{- else }}
28+
29+
WARNING: No --allowed-upstreams configured. SSRF protection is active (private IPs blocked).
30+
For air-gapped environments, set cooked.allowedUpstreams to your internal hosts.
31+
{{- end }}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "cooked.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
Truncated to 63 characters (DNS label limit).
11+
*/}}
12+
{{- define "cooked.fullname" -}}
13+
{{- if .Values.fullnameOverride }}
14+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
15+
{{- else }}
16+
{{- $name := default .Chart.Name .Values.nameOverride }}
17+
{{- if contains $name .Release.Name }}
18+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
19+
{{- else }}
20+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
21+
{{- end }}
22+
{{- end }}
23+
{{- end }}
24+
25+
{{/*
26+
Create chart name and version for the helm.sh/chart label.
27+
*/}}
28+
{{- define "cooked.chart" -}}
29+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
30+
{{- end }}
31+
32+
{{/*
33+
Standard labels for all resources.
34+
*/}}
35+
{{- define "cooked.labels" -}}
36+
helm.sh/chart: {{ include "cooked.chart" . }}
37+
{{ include "cooked.selectorLabels" . }}
38+
{{- if .Chart.AppVersion }}
39+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
40+
{{- end }}
41+
app.kubernetes.io/managed-by: {{ .Release.Service }}
42+
{{- with .Values.commonLabels }}
43+
{{ toYaml . }}
44+
{{- end }}
45+
{{- end }}
46+
47+
{{/*
48+
Selector labels (immutable — never include version here).
49+
*/}}
50+
{{- define "cooked.selectorLabels" -}}
51+
app.kubernetes.io/name: {{ include "cooked.name" . }}
52+
app.kubernetes.io/instance: {{ .Release.Name }}
53+
{{- end }}
54+
55+
{{/*
56+
Common annotations for all resources.
57+
*/}}
58+
{{- define "cooked.annotations" -}}
59+
{{- with .Values.commonAnnotations }}
60+
{{- toYaml . }}
61+
{{- end }}
62+
{{- end }}
63+
64+
{{/*
65+
Service account name.
66+
*/}}
67+
{{- define "cooked.serviceAccountName" -}}
68+
{{- if .Values.serviceAccount.create }}
69+
{{- default (include "cooked.fullname" .) .Values.serviceAccount.name }}
70+
{{- else }}
71+
{{- default "default" .Values.serviceAccount.name }}
72+
{{- end }}
73+
{{- end }}
74+
75+
{{/*
76+
Container image string from registry/repository/tag/digest.
77+
When using appVersion as the default tag, prepends "v" to match GHCR tag format (v1.3.2).
78+
An explicit image.tag is used as-is.
79+
*/}}
80+
{{- define "cooked.image" -}}
81+
{{- if .Values.image.digest }}
82+
{{- printf "%s/%s@%s" .Values.image.registry .Values.image.repository .Values.image.digest }}
83+
{{- else if .Values.image.tag }}
84+
{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository .Values.image.tag }}
85+
{{- else }}
86+
{{- printf "%s/%s:v%s" .Values.image.registry .Values.image.repository .Chart.AppVersion }}
87+
{{- end }}
88+
{{- end }}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{- if and .Values.caCertificates.enabled .Values.caCertificates.inline (not .Values.caCertificates.existingConfigMap) (not .Values.caCertificates.existingSecret) }}
2+
apiVersion: v1
3+
kind: ConfigMap
4+
metadata:
5+
name: {{ include "cooked.fullname" . }}-ca-certs
6+
namespace: {{ .Release.Namespace }}
7+
labels:
8+
{{- include "cooked.labels" . | nindent 4 }}
9+
{{- with (include "cooked.annotations" .) }}
10+
annotations:
11+
{{- . | nindent 4 }}
12+
{{- end }}
13+
data:
14+
{{- range $name, $cert := .Values.caCertificates.inline }}
15+
{{ $name }}: |
16+
{{- $cert | nindent 4 }}
17+
{{- end }}
18+
{{- end }}

0 commit comments

Comments
 (0)