Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-08-23 04:14:57 -04:00

Compare commits

...
Author SHA1 Message Date
Andrew Nesbitt
116f763bd8 Add Helm chart 2026-08-13 10:36:34 +01:00
14 changed files with 507 additions and 2 deletions

View file

@ -46,3 +46,31 @@ jobs:
- name: golangci-lint
run: go tool golangci-lint run ./...
helm:
name: Helm chart
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
with:
version: v3.18.6
- name: Lint chart
run: helm lint deploy/charts/proxy
- name: Render chart variants
run: |
set -euo pipefail
helm template proxy deploy/charts/proxy >/dev/null
helm template proxy deploy/charts/proxy \
--set persistence.enabled=false \
--set config.existingConfigMap=proxy-config \
--set ingress.enabled=true \
--set 'ingress.hosts[0].host=proxy.example.com' \
--set 'ingress.hosts[0].paths[0].path=/' \
--set 'ingress.hosts[0].paths[0].pathType=Prefix' \
>/dev/null

View file

@ -96,3 +96,66 @@ jobs:
for predicate in sbom-linux-amd64.spdx.json sbom-linux-arm64.spdx.json; do
cosign attest --yes --type spdxjson --predicate "$predicate" "$reference"
done
publish_chart:
name: Push Helm chart to GHCR
if: github.ref_type == 'tag'
needs: push_to_registry
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Check out the repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
ref: ${{ github.sha }}
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
with:
version: v3.18.6
- name: Validate and normalize release version
id: version
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
semver='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?$'
[[ "$TAG" =~ $semver ]] || {
echo "Tag must be strict SemVer of the form vMAJOR.MINOR.PATCH[-PRERELEASE]: $TAG" >&2
exit 1
}
version="${TAG#v}"
[[ "$version" != "0.0.0" ]] || {
echo "0.0.0 is a development placeholder and must not be published" >&2
exit 1
}
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Log in to GHCR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: printf '%s' "$GH_TOKEN" | helm registry login ghcr.io --username "$GITHUB_ACTOR" --password-stdin
- name: Lint and package chart
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
helm lint deploy/charts/proxy
mkdir -p build
helm package \
--destination build \
--version "$VERSION" \
--app-version "$VERSION" \
deploy/charts/proxy
metadata="$(helm show chart "build/proxy-${VERSION}.tgz")"
[[ "$(awk '$1 == "version:" {print $2}' <<<"$metadata")" == "$VERSION" ]]
[[ "$(awk '$1 == "appVersion:" {gsub(/\"/, "", $2); print $2}' <<<"$metadata")" == "$VERSION" ]]
- name: Push chart
env:
VERSION: ${{ steps.version.outputs.version }}
run: helm push "build/proxy-${VERSION}.tgz" oci://ghcr.io/git-pkgs/charts

4
.gitignore vendored
View file

@ -4,7 +4,7 @@
*.dll
*.so
*.dylib
proxy
/proxy
# Test binary, built with `go test -c`
*.test
@ -43,4 +43,4 @@ cache/*
# Debug files
__debug_bin
debug
debug

View file

@ -62,6 +62,21 @@ brew install git-pkgs/git-pkgs/proxy
Or download a binary from the [releases page](https://github.com/git-pkgs/proxy/releases).
### Helm
Install the chart from GHCR, setting the public URL that package-manager clients
will use to reach the proxy:
```bash
helm install proxy oci://ghcr.io/git-pkgs/charts/proxy \
--set config.data.base_url=https://proxy.example.com
```
The default chart deploys one replica backed by a 10 GiB persistent volume,
using SQLite and filesystem artifact storage under `/data`. See
[`deploy/charts/proxy/values.yaml`](deploy/charts/proxy/values.yaml) for ingress,
external database and object-storage configuration options.
## Quick Start
```bash

View file

@ -0,0 +1,6 @@
.DS_Store
.git/
.github/
*.swp
*.tmp
*.tgz

View file

@ -0,0 +1,11 @@
apiVersion: v2
name: proxy
description: A caching proxy for package registries
type: application
version: 0.0.0
appVersion: "0.0.0"
home: https://github.com/git-pkgs/proxy
sources:
- https://github.com/git-pkgs/proxy
annotations:
artifacthub.io/license: MIT

View file

@ -0,0 +1,15 @@
git-pkgs proxy has been installed.
The default base URL is intended for local port forwarding. Before exposing the
proxy, set config.data.base_url to the URL used by package-manager clients.
To access the proxy locally:
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "proxy.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }}
Then visit http://localhost:{{ .Values.service.port }}/.
{{- if not .Values.persistence.enabled }}
WARNING: persistence is disabled. Cached artifacts and the default SQLite
database will be lost when the pod is replaced.
{{- end }}

View file

@ -0,0 +1,46 @@
{{/* Expand the chart name. */}}
{{- define "proxy.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/* Create a release-specific, DNS-safe resource name. */}}
{{- define "proxy.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := include "proxy.name" . }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "proxy.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{ include "proxy.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "proxy.selectorLabels" -}}
app.kubernetes.io/name: {{ include "proxy.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- define "proxy.configMapName" -}}
{{- default (include "proxy.fullname" .) .Values.config.existingConfigMap }}
{{- end }}
{{- define "proxy.claimName" -}}
{{- default (include "proxy.fullname" .) .Values.persistence.existingClaim }}
{{- end }}
{{- define "proxy.image" -}}
{{- if .Values.image.digest -}}
{{- printf "%s@%s" .Values.image.repository .Values.image.digest -}}
{{- else -}}
{{- printf "%s:%s" .Values.image.repository (default .Chart.AppVersion .Values.image.tag) -}}
{{- end -}}
{{- end }}

View file

@ -0,0 +1,12 @@
{{- if not .Values.config.existingConfigMap }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "proxy.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proxy.labels" . | nindent 4 }}
data:
{{ required "config.existingConfigMapKey is required" .Values.config.existingConfigMapKey }}: |
{{- toYaml .Values.config.data | nindent 4 }}
{{- end }}

View file

@ -0,0 +1,102 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "proxy.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proxy.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
{{- toYaml .Values.deploymentStrategy | nindent 4 }}
selector:
matchLabels:
{{- include "proxy.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "proxy.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
automountServiceAccountToken: false
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: {{ include "proxy.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 12 }}
args:
- serve
- -config
- /etc/proxy/{{ .Values.config.existingConfigMapKey }}
{{- with .Values.extraEnv }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.extraEnvFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.containerPort }}
protocol: TCP
startupProbe:
{{- toYaml .Values.startupProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/proxy/{{ .Values.config.existingConfigMapKey }}
subPath: {{ .Values.config.existingConfigMapKey }}
readOnly: true
- name: data
mountPath: {{ .Values.persistence.mountPath }}
volumes:
- name: config
configMap:
name: {{ include "proxy.configMapName" . }}
items:
- key: {{ required "config.existingConfigMapKey is required" .Values.config.existingConfigMapKey }}
path: {{ .Values.config.existingConfigMapKey }}
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "proxy.claimName" . }}
{{- else }}
emptyDir: {}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -0,0 +1,36 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "proxy.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proxy.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . | quote }}
{{- end }}
{{- with .Values.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "proxy.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,22 @@
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "proxy.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proxy.labels" . | nindent 4 }}
{{- with .Values.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
{{- toYaml .Values.persistence.accessModes | nindent 4 }}
{{- with .Values.persistence.storageClass }}
storageClassName: {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- end }}

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "proxy.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "proxy.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
selector:
{{- include "proxy.selectorLabels" . | nindent 4 }}

View file

@ -0,0 +1,133 @@
replicaCount: 1
nameOverride: ""
fullnameOverride: ""
image:
repository: ghcr.io/git-pkgs/proxy
# An empty tag uses the chart appVersion.
tag: ""
# When set, digest takes precedence over tag.
digest: ""
pullPolicy: IfNotPresent
imagePullSecrets: []
service:
type: ClusterIP
port: 8080
# Keep this aligned with config.data.listen (or the listen address in an
# existing ConfigMap).
containerPort: 8080
# The generated configuration is ignored when existingConfigMap is set.
config:
existingConfigMap: ""
existingConfigMapKey: config.yaml
data:
listen: ":8080"
# Set this to the URL package-manager clients use to reach the proxy.
base_url: "http://localhost:8080"
storage:
url: "file:///data/artifacts"
database:
driver: sqlite
path: "/data/proxy.db"
log:
level: info
format: json
# Environment variables override values from the configuration file. This is
# also the recommended way to supply secret values such as database passwords
# and object-storage credentials.
extraEnv: []
# - name: PROXY_DATABASE_URL
# valueFrom:
# secretKeyRef:
# name: proxy-database
# key: url
extraEnvFrom: []
# - secretRef:
# name: proxy-object-storage
persistence:
enabled: true
mountPath: /data
existingClaim: ""
annotations: {}
accessModes:
- ReadWriteOnce
size: 10Gi
storageClass: ""
deploymentStrategy:
type: Recreate
ingress:
enabled: false
className: ""
annotations: {}
hosts: []
# - host: proxy.example.com
# paths:
# - path: /
# pathType: Prefix
tls: []
# - secretName: proxy-tls
# hosts:
# - proxy.example.com
podAnnotations: {}
podLabels: {}
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources: {}
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# memory: 512Mi
startupProbe:
tcpSocket:
port: http
failureThreshold: 30
periodSeconds: 2
# /health checks both the database and storage backends and can take up to ten
# seconds. It is intentionally a readiness check rather than a liveness check.
readinessProbe:
httpGet:
path: /health
port: http
timeoutSeconds: 11
periodSeconds: 15
failureThreshold: 2
livenessProbe:
tcpSocket:
port: http
periodSeconds: 20
failureThreshold: 3
terminationGracePeriodSeconds: 30
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []