Record reviewed owner implementation and consumer compatibility (5)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 9s

This commit is contained in:
codex 2026-10-17 23:18:33 +02:00
parent 14670441cd
commit 11a485f1dd
8 changed files with 3069 additions and 0 deletions

View file

@ -0,0 +1,228 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gitea.fullname" . }}-init
namespace: {{ .Values.namespace | default .Release.Namespace }}
labels:
{{- include "gitea.labels" . | nindent 4 }}
type: Opaque
stringData:
{{ (.Files.Glob "scripts/init-containers/init/*.sh").AsConfig | indent 2 }}
init_directory_structure.sh: |-
#!/usr/bin/env bash
set -euo pipefail
{{- if .Values.initPreScript }}
# BEGIN: initPreScript
{{- with .Values.initPreScript -}}
{{ . | nindent 4}}
{{- end -}}
# END: initPreScript
{{- end }}
{{- if not .Values.image.rootless }}
chown -v 1000:1000 /data
{{- end }}
mkdir -pv /data/git/.ssh
chmod -Rv 700 /data/git/.ssh
[ ! -d /data/gitea/conf ] && mkdir -pv /data/gitea/conf
# prepare temp directory structure
mkdir -pv "${GITEA_TEMP}"
{{- if not .Values.image.rootless }}
chown -v 1000:1000 "${GITEA_TEMP}"
{{- end }}
chmod -v ug+rwx "${GITEA_TEMP}"
{{ if .Values.signing.enabled -}}
if [ ! -d "${GNUPGHOME}" ]; then
mkdir -pv "${GNUPGHOME}"
chmod -v 700 "${GNUPGHOME}"
chown -v 1000:1000 "${GNUPGHOME}"
fi
{{- end }}
configure_gitea.sh: |-
#!/usr/bin/env bash
set -euo pipefail
echo '==== BEGIN GITEA CONFIGURATION ===='
{ # try
gitea migrate
} || { # catch
echo "Gitea migrate might fail due to database connection...This init-container will try again in a few seconds"
exit 1
}
{{- if include "valkey.servicename" . }}
function test_valkey_connection() {
local RETRY=0
local MAX=30
echo 'Wait for valkey to become avialable...'
until [ "${RETRY}" -ge "${MAX}" ]; do
nc -vz -w2 {{ include "valkey.servicename" . }} {{ include "valkey.port" . }} && break
RETRY=$[${RETRY}+1]
echo "...not ready yet (${RETRY}/${MAX})"
done
if [ "${RETRY}" -ge "${MAX}" ]; then
echo "Valkey not reachable after '${MAX}' attempts!"
exit 1
fi
}
test_valkey_connection
{{- end }}
{{- if or .Values.gitea.admin.existingSecret (and .Values.gitea.admin.username .Values.gitea.admin.password) }}
function configure_admin_user() {
local full_admin_list=$(gitea admin user list --admin)
local actual_user_table=''
# We might have distorted output due to warning logs, so we have to detect the actual user table by its headline and trim output above that line
local regex="(.*)(ID\s+Username\s+Email\s+IsActive.*)"
if [[ "${full_admin_list}" =~ $regex ]]; then
actual_user_table=$(echo "${BASH_REMATCH[2]}" | tail -n+2) # tail'ing to drop the table headline
else
# This code block should never be reached, as long as the output table header remains the same.
# If this code block is reached, the regex doesn't match anymore and we probably have to adjust this script.
echo "ERROR: 'configure_admin_user' was not able to determine the current list of admin users."
echo " Please review the output of 'gitea admin user list --admin' shown below."
echo " If you think it is an issue with the Helm Chart provisioning, file an issue at https://gitea.com/gitea/helm-gitea/issues."
echo "DEBUG: Output of 'gitea admin user list --admin'"
echo "--"
echo "${full_admin_list}"
echo "--"
exit 1
fi
local ACCOUNT_ID=$(echo "${actual_user_table}" | grep -E "\s+${GITEA_ADMIN_USERNAME}\s+" | awk -F " " "{printf \$1}")
if [[ -z "${ACCOUNT_ID}" ]]; then
local -a create_args
create_args=(--admin --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" --email {{ .Values.gitea.admin.email | quote }})
if [[ "${GITEA_ADMIN_PASSWORD_MODE}" = initialOnlyRequireReset ]]; then
create_args+=(--must-change-password=true)
else
create_args+=(--must-change-password=false)
fi
echo "No admin user '${GITEA_ADMIN_USERNAME}' found. Creating now..."
gitea admin user create "${create_args[@]}"
echo '...created.'
else
if [[ "${GITEA_ADMIN_PASSWORD_MODE}" = keepUpdated ]]; then
echo "Admin account '${GITEA_ADMIN_USERNAME}' already exist. Running update to sync password..."
# See https://gitea.com/gitea/helm-gitea/issues/673
# --must-change-password argument was added to change-password, defaulting to true, counter to the previous behavior
# which acted as if it were provided with =false. If the argument is present in this version of gitea, then we
# should add it to prevent requiring frequent admin password resets.
local -a change_args
change_args=(--username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}")
if gitea admin user change-password --help | grep -qF -- '--must-change-password'; then
change_args+=(--must-change-password=false)
fi
gitea admin user change-password "${change_args[@]}"
echo '...password sync done.'
else
echo "Admin account '${GITEA_ADMIN_USERNAME}' already exist, but update mode is set to '${GITEA_ADMIN_PASSWORD_MODE}'. Skipping."
fi
fi
}
configure_admin_user
{{- end }}
function configure_ldap() {
{{- if .Values.gitea.ldap }}
{{- range $idx, $value := .Values.gitea.ldap }}
local LDAP_NAME={{ (printf "%s" $value.name) | squote }}
local full_auth_list=$(gitea admin auth list --vertical-bars)
local actual_auth_table=''
# We might have distorted output due to warning logs, so we have to detect the actual user table by its headline and trim output above that line
local regex="(.*)(ID\s+\|Name\s+\|Type\s+\|Enabled.*)"
if [[ "${full_auth_list}" =~ $regex ]]; then
actual_auth_table=$(echo "${BASH_REMATCH[2]}" | tail -n+2) # tail'ing to drop the table headline
else
# This code block should never be reached, as long as the output table header remains the same.
# If this code block is reached, the regex doesn't match anymore and we probably have to adjust this script.
echo "ERROR: 'configure_ldap' was not able to determine the current list of authentication sources."
echo " Please review the output of 'gitea admin auth list --vertical-bars' shown below."
echo " If you think it is an issue with the Helm Chart provisioning, file an issue at https://gitea.com/gitea/helm-gitea/issues."
echo "DEBUG: Output of 'gitea admin auth list --vertical-bars'"
echo "--"
echo "${full_auth_list}"
echo "--"
exit 1
fi
local GITEA_AUTH_ID=$(echo "${actual_auth_table}" | grep -E "\|${LDAP_NAME}\s+\|" | grep -iE '\|LDAP \(via BindDN\)\s+\|' | awk -F " " "{print \$1}")
if [[ -z "${GITEA_AUTH_ID}" ]]; then
echo "No ldap configuration found with name '${LDAP_NAME}'. Installing it now..."
gitea admin auth add-ldap {{- include "gitea.ldap_settings" (list $idx $value) | indent 1 }}
echo '...installed.'
else
echo "Existing ldap configuration with name '${LDAP_NAME}': '${GITEA_AUTH_ID}'. Running update to sync settings..."
gitea admin auth update-ldap --id "${GITEA_AUTH_ID}" {{- include "gitea.ldap_settings" (list $idx $value) | indent 1 }}
echo '...sync settings done.'
fi
{{- end }}
{{- else }}
echo 'no ldap configuration... skipping.'
{{- end }}
}
configure_ldap
function configure_oauth() {
{{- if .Values.gitea.oauth }}
{{- range $idx, $value := .Values.gitea.oauth }}
local OAUTH_NAME={{ (printf "%s" $value.name) | squote }}
local full_auth_list=$(gitea admin auth list --vertical-bars)
local actual_auth_table=''
# We might have distorted output due to warning logs, so we have to detect the actual user table by its headline and trim output above that line
local regex="(.*)(ID\s+\|Name\s+\|Type\s+\|Enabled.*)"
if [[ "${full_auth_list}" =~ $regex ]]; then
actual_auth_table=$(echo "${BASH_REMATCH[2]}" | tail -n+2) # tail'ing to drop the table headline
else
# This code block should never be reached, as long as the output table header remains the same.
# If this code block is reached, the regex doesn't match anymore and we probably have to adjust this script.
echo "ERROR: 'configure_oauth' was not able to determine the current list of authentication sources."
echo " Please review the output of 'gitea admin auth list --vertical-bars' shown below."
echo " If you think it is an issue with the Helm Chart provisioning, file an issue at https://gitea.com/gitea/helm-gitea/issues."
echo "DEBUG: Output of 'gitea admin auth list --vertical-bars'"
echo "--"
echo "${full_auth_list}"
echo "--"
exit 1
fi
local AUTH_ID=$(echo "${actual_auth_table}" | grep -E "\|${OAUTH_NAME}\s+\|" | grep -iE '\|OAuth2\s+\|' | awk -F " " "{print \$1}")
if [[ -z "${AUTH_ID}" ]]; then
echo "No oauth configuration found with name '${OAUTH_NAME}'. Installing it now..."
gitea admin auth add-oauth {{- include "gitea.oauth_settings" (list $idx $value) | indent 1 }}
echo '...installed.'
else
echo "Existing oauth configuration with name '${OAUTH_NAME}': '${AUTH_ID}'. Running update to sync settings..."
gitea admin auth update-oauth --id "${AUTH_ID}" {{- include "gitea.oauth_settings" (list $idx $value) | indent 1 }}
echo '...sync settings done.'
fi
{{- end }}
{{- else }}
echo 'no oauth configuration... skipping.'
{{- end }}
}
configure_oauth
echo '==== END GITEA CONFIGURATION ===='

View file

@ -0,0 +1,717 @@
# Default values for gitea.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
## @section Global
#
## @param global.imageRegistry global image registry override
## @param global.imagePullSecrets global image pull secrets override; can be extended by `imagePullSecrets`
## @param global.storageClass global storage class override
## @param global.hostAliases global hostAliases which will be added to the pod's hosts files
global:
imageRegistry: ""
## E.g.
## imagePullSecrets:
## - myRegistryKeySecretName
##
imagePullSecrets: []
storageClass: ""
hostAliases: []
# - ip: 192.168.137.2
# hostnames:
# - example.com
## @param namespace An explicit namespace to deploy gitea into. Defaults to the release namespace if not specified
namespace: ""
## @param replicaCount number of replicas for the deployment
replicaCount: 1
## @section strategy
## @param strategy.type strategy type
## @param strategy.rollingUpdate.maxSurge maxSurge
## @param strategy.rollingUpdate.maxUnavailable maxUnavailable
strategy:
type: "RollingUpdate"
rollingUpdate:
maxSurge: "100%"
maxUnavailable: 0
## @param clusterDomain cluster domain
clusterDomain: cluster.local
## @section Image
## @param image.registry image registry, e.g. gcr.io,docker.io
## @param image.repository Image to start for this pod
## @param image.tag Visit: [Image tag](https://hub.docker.com/r/gitea/gitea/tags?page=1&ordering=last_updated). Defaults to `appVersion` within Chart.yaml.
## @param image.digest Image digest. Allows to pin the given image tag. Useful for having control over mutable tags like `latest`
## @param image.pullPolicy Image pull policy
## @param image.rootless Wether or not to pull the rootless version of Gitea, only works on Gitea 1.14.x or higher
## @param image.fullOverride Completely overrides the image registry, path/image, tag and digest. **Adjust `image.rootless` accordingly and review [Rootless defaults](#rootless-defaults).**
image:
registry: "docker.gitea.com"
repository: gitea
# Overrides the image tag whose default is the chart appVersion.
tag: ""
digest: ""
pullPolicy: IfNotPresent
rootless: true
fullOverride: ""
## @param imagePullSecrets Secret to use for pulling the image
imagePullSecrets: []
## @section Security
# Security context is only usable with rootless image due to image design
## @param podSecurityContext.fsGroup Set the shared file system group for all containers in the pod.
podSecurityContext:
fsGroup: 1000
## @param containerSecurityContext Security context
containerSecurityContext: {}
# allowPrivilegeEscalation: false
# capabilities:
# drop:
# - ALL
# # Add the SYS_CHROOT capability for root and rootless images if you intend to
# # run pods on nodes that use the container runtime cri-o. Otherwise, you will
# # get an error message from the SSH server that it is not possible to read from
# # the repository.
# # https://gitea.com/gitea/helm-gitea/issues/161
# add:
# - SYS_CHROOT
# privileged: false
# readOnlyRootFilesystem: true
# runAsGroup: 1000
# runAsNonRoot: true
# runAsUser: 1000
## @deprecated The securityContext variable has been split two:
## - containerSecurityContext
## - podSecurityContext.
## @param securityContext Run init and Gitea containers as a specific securityContext
securityContext: {}
## @param podDisruptionBudget Pod disruption budget
podDisruptionBudget: {}
# maxUnavailable: 1
# minAvailable: 1
## @section Service
service:
## @param service.http.type Kubernetes service type for web traffic
## @param service.http.port Port number for web traffic
## @param service.http.clusterIP ClusterIP setting for http autosetup for deployment is None
## @param service.http.loadBalancerIP LoadBalancer IP setting
## @param service.http.nodePort NodePort for http service
## @param service.http.externalTrafficPolicy If `service.http.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation
## @param service.http.externalIPs External IPs for service
## @param service.http.ipFamilyPolicy HTTP service dual-stack policy
## @param service.http.ipFamilies HTTP service dual-stack familiy selection,for dual-stack parameters see official kubernetes [dual-stack concept documentation](https://kubernetes.io/docs/concepts/services-networking/dual-stack/).
## @param service.http.loadBalancerSourceRanges Source range filter for http loadbalancer
## @param service.http.annotations HTTP service annotations
## @param service.http.labels HTTP service additional labels
## @param service.http.loadBalancerClass Loadbalancer class
http:
type: ClusterIP
port: 3000
clusterIP: None
loadBalancerIP:
nodePort:
externalTrafficPolicy:
externalIPs:
ipFamilyPolicy:
ipFamilies:
loadBalancerSourceRanges: []
annotations: {}
labels: {}
loadBalancerClass:
## @param service.ssh.type Kubernetes service type for ssh traffic
## @param service.ssh.port Port number for ssh traffic
## @param service.ssh.clusterIP ClusterIP setting for ssh autosetup for deployment is None
## @param service.ssh.loadBalancerIP LoadBalancer IP setting
## @param service.ssh.nodePort NodePort for ssh service
## @param service.ssh.externalTrafficPolicy If `service.ssh.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation
## @param service.ssh.externalIPs External IPs for service
## @param service.ssh.ipFamilyPolicy SSH service dual-stack policy
## @param service.ssh.ipFamilies SSH service dual-stack familiy selection,for dual-stack parameters see official kubernetes [dual-stack concept documentation](https://kubernetes.io/docs/concepts/services-networking/dual-stack/).
## @param service.ssh.hostPort HostPort for ssh service
## @param service.ssh.loadBalancerSourceRanges Source range filter for ssh loadbalancer
## @param service.ssh.annotations SSH service annotations
## @param service.ssh.labels SSH service additional labels
## @param service.ssh.loadBalancerClass Loadbalancer class
ssh:
type: ClusterIP
port: 22
clusterIP: None
loadBalancerIP:
nodePort:
externalTrafficPolicy:
externalIPs:
ipFamilyPolicy:
ipFamilies:
hostPort:
loadBalancerSourceRanges: []
annotations: {}
labels: {}
loadBalancerClass:
## @section Ingress
## @param ingress.enabled Enable ingress
## @param ingress.className DEPRECATED: Ingress class name.
## @param ingress.pathType Ingress Path Type
## @param ingress.annotations Ingress annotations
## @param ingress.hosts[0].host Default Ingress host
## @param ingress.hosts[0].paths[0].path Default Ingress path
## @param ingress.tls Ingress tls settings
ingress:
enabled: false
className: ""
pathType: Prefix
annotations: {}
hosts:
- host: git.example.com
paths:
- path: /
tls: []
# - secretName: chart-example-tls
# hosts:
# - git.example.com
## @section deployment
#
## @param resources Kubernetes resources
resources:
{}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
## Use an alternate scheduler, e.g. "stork".
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
##
## @param schedulerName Use an alternate scheduler, e.g. "stork"
schedulerName: ""
## @param nodeSelector NodeSelector for the deployment
nodeSelector: {}
## @param tolerations Tolerations for the deployment
tolerations: []
## @param affinity Affinity for the deployment
affinity: {}
## @param topologySpreadConstraints TopologySpreadConstraints for the deployment
topologySpreadConstraints: []
## @param dnsConfig dnsConfig for the deployment
dnsConfig: {}
## @param priorityClassName priorityClassName for the deployment
priorityClassName: ""
## @param deployment.env Additional environment variables to pass to containers
## @param deployment.terminationGracePeriodSeconds How long to wait until forcefully kill the pod
## @param deployment.labels Labels for the deployment
## @param deployment.annotations Annotations for the Gitea deployment to be created
deployment:
env:
[]
# - name: VARIABLE
# value: my-value
terminationGracePeriodSeconds: 60
labels: {}
annotations: {}
## @section ServiceAccount
## @param serviceAccount.create Enable the creation of a ServiceAccount
## @param serviceAccount.name Name of the created ServiceAccount, defaults to release name. Can also link to an externally provided ServiceAccount that should be used.
## @param serviceAccount.automountServiceAccountToken Enable/disable auto mounting of the service account token
## @param serviceAccount.imagePullSecrets Image pull secrets, available to the ServiceAccount
## @param serviceAccount.annotations Custom annotations for the ServiceAccount
## @param serviceAccount.labels Custom labels for the ServiceAccount
serviceAccount:
create: false
name: ""
automountServiceAccountToken: false
imagePullSecrets: []
# - name: private-registry-access
annotations: {}
labels: {}
## @section Persistence
#
## @param persistence.enabled Enable persistent storage
## @param persistence.create Whether to create the persistentVolumeClaim for shared storage
## @param persistence.mount Whether the persistentVolumeClaim should be mounted (even if not created)
## @param persistence.claimName Use an existing claim to store repository information
## @param persistence.size Size for persistence to store repo information
## @param persistence.accessModes AccessMode for persistence
## @param persistence.labels Labels for the persistence volume claim to be created
## @param persistence.annotations.helm.sh/resource-policy Resource policy for the persistence volume claim
## @param persistence.storageClass Name of the storage class to use
## @param persistence.subPath Subdirectory of the volume to mount at
## @param persistence.volumeName Name of persistent volume in PVC
persistence:
enabled: true
create: true
mount: true
claimName: gitea-shared-storage
size: 10Gi
accessModes:
- ReadWriteOnce
labels: {}
storageClass:
subPath:
volumeName: ""
annotations:
helm.sh/resource-policy: keep
## @param extraContainers Additional sidecar containers to run in the pod
extraContainers: []
# - name: sidecar-bob
# image: busybox
# command: [/bin/sh, -c, 'echo "Hello world"']
## @param preExtraInitContainers Additional init containers to run in the pod before gitea runs it owns init containers.
preExtraInitContainers: []
# - name: pre-init-container
# image: docker.io/library/busybox
# command: [ /bin/sh, -c, 'echo "Hello world! I am a pre init container."' ]
## @param postExtraInitContainers Additional init containers to run in the pod after gitea runs it owns init containers.
postExtraInitContainers: []
# - name: post-init-container
# image: docker.io/library/busybox
# command: [ /bin/sh, -c, 'echo "Hello world! I am a post init container."' ]
## @param extraVolumes Additional volumes to mount to the Gitea deployment
extraVolumes: []
# - name: postgres-ssl-vol
# secret:
# secretName: gitea-postgres-ssl
## @param extraContainerVolumeMounts Mounts that are only mapped into the Gitea runtime/main container, to e.g. override custom templates.
extraContainerVolumeMounts: []
## @param extraInitVolumeMounts Mounts that are only mapped into the init-containers. Can be used for additional preconfiguration.
extraInitVolumeMounts: []
## @deprecated The extraVolumeMounts variable has been split two:
## - extraContainerVolumeMounts
## - extraInitVolumeMounts
## As an example, can be used to mount a client cert when connecting to an external Postgres server.
## @param extraVolumeMounts **DEPRECATED** Additional volume mounts for init containers and the Gitea main container
extraVolumeMounts: []
# - name: postgres-ssl-vol
# readOnly: true
# mountPath: "/pg-ssl"
## @section Init
## @param initPreScript Bash shell script copied verbatim to the start of the init-container.
initPreScript: ""
## @param initContainersScriptsVolumeMountPath Path to mount the scripts consumed from the Secrets
initContainersScriptsVolumeMountPath: "/usr/sbinx"
#
# initPreScript: |
# mkdir -p /data/git/.postgresql
# cp /pg-ssl/* /data/git/.postgresql/
# chown -R git:git /data/git/.postgresql/
# chmod 400 /data/git/.postgresql/postgresql.key
## @param initContainers.resources.limits initContainers.limits Kubernetes resource limits for init containers
## @param initContainers.resources.requests.cpu initContainers.requests.cpu Kubernetes cpu resource limits for init containers
## @param initContainers.resources.requests.memory initContainers.requests.memory Kubernetes memory resource limits for init containers
initContainers:
resources:
limits: {}
requests:
cpu: 100m
memory: 128Mi
# Configure commit/action signing prerequisites
## @section Signing
#
## @param signing.enabled Enable commit/action signing
## @param signing.gpgHome GPG home directory
## @param signing.privateKey Inline private gpg key for signed internal Git activity
## @param signing.existingSecret Use an existing secret to store the value of `signing.privateKey`
signing:
enabled: false
gpgHome: /data/git/.gnupg
privateKey: ""
# privateKey: |-
# -----BEGIN PGP PRIVATE KEY BLOCK-----
# ...
# -----END PGP PRIVATE KEY BLOCK-----
existingSecret: ""
## @section Gitea
#
gitea:
## @param gitea.admin.username Username for the Gitea admin user
## @param gitea.admin.existingSecret Use an existing secret to store admin user credentials
## @param gitea.admin.password Password for the Gitea admin user
## @param gitea.admin.email Email for the Gitea admin user
## @param gitea.admin.passwordMode Mode for how to set/update the admin user password. Options are: initialOnlyNoReset, initialOnlyRequireReset, and keepUpdated
admin:
# existingSecret: gitea-admin-secret
existingSecret:
username: gitea_admin
password: r8sA8CPHD9!bt6d
email: "gitea@local.domain"
passwordMode: keepUpdated
## @param gitea.metrics.enabled Enable Gitea metrics
## @param gitea.metrics.token used for `bearer` token authentication on metrics endpoint. If not specified or empty metrics endpoint is public.
## @param gitea.metrics.serviceMonitor.enabled Enable Gitea metrics service monitor. Requires, that `gitea.metrics.enabled` is also set to true, to enable metrics generally.
## @param gitea.metrics.serviceMonitor.interval Interval at which metrics should be scraped. If not specified Prometheus' global scrape interval is used.
## @param gitea.metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping.
## @param gitea.metrics.serviceMonitor.scheme HTTP scheme to use for scraping. For example `http` or `https`. Default is http.
## @param gitea.metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended. If not specified, global Prometheus scrape timeout is used.
## @param gitea.metrics.serviceMonitor.tlsConfig TLS configuration to use when scraping the metric endpoint by Prometheus.
metrics:
enabled: false
token:
serviceMonitor:
enabled: false
# additionalLabels:
# prometheus-release: prom1
interval: ""
relabelings: []
scheme: ""
scrapeTimeout: ""
tlsConfig: {}
## @param gitea.ldap LDAP configuration
ldap:
[]
# - name: "LDAP 1"
# existingSecret:
# securityProtocol:
# host:
# port:
# userSearchBase:
# userFilter:
# adminFilter:
# emailAttribute:
# bindDn:
# bindPassword:
# usernameAttribute:
# publicSSHKeyAttribute:
# Either specify inline `key` and `secret` or refer to them via `existingSecret`
## @param gitea.oauth OAuth configuration
oauth:
[]
# - name: 'OAuth 1'
# provider:
# key:
# secret:
# existingSecret:
# autoDiscoverUrl:
# useCustomUrls:
# customAuthUrl:
# customTokenUrl:
# customProfileUrl:
# customEmailUrl:
## @param gitea.config.server.SSH_PORT SSH port for rootlful Gitea image
## @param gitea.config.server.SSH_LISTEN_PORT SSH port for rootless Gitea image
config:
# APP_NAME: "Gitea: Git with a cup of tea"
# RUN_MODE: dev
server:
SSH_PORT: 22 # rootful image
SSH_LISTEN_PORT: 2222 # rootless image
#
# security:
# PASSWORD_COMPLEXITY: spec
## @param gitea.additionalConfigSources Additional configuration from secret or configmap
additionalConfigSources: []
# - secret:
# secretName: gitea-app-ini-oauth
# - configMap:
# name: gitea-app-ini-plaintext
## @param gitea.additionalConfigFromEnvs Additional configuration sources from environment variables
additionalConfigFromEnvs: []
## @param gitea.podAnnotations Annotations for the Gitea pod
podAnnotations: {}
## @param gitea.ssh.logLevel Configure OpenSSH's log level. Only available for root-based Gitea image.
ssh:
logLevel: "INFO"
## @section LivenessProbe
#
## @param gitea.livenessProbe.enabled Enable liveness probe
## @param gitea.livenessProbe.tcpSocket.port Port to probe for liveness
## @param gitea.livenessProbe.initialDelaySeconds Initial delay before liveness probe is initiated
## @param gitea.livenessProbe.timeoutSeconds Timeout for liveness probe
## @param gitea.livenessProbe.periodSeconds Period for liveness probe
## @param gitea.livenessProbe.successThreshold Success threshold for liveness probe
## @param gitea.livenessProbe.failureThreshold Failure threshold for liveness probe
# Modify the liveness probe for your needs or completely disable it by commenting out.
livenessProbe:
enabled: true
tcpSocket:
port: http
initialDelaySeconds: 200
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 10
## @section ReadinessProbe
#
## @param gitea.readinessProbe.enabled Enable readiness probe
## @param gitea.readinessProbe.tcpSocket.port Port to probe for readiness
## @param gitea.readinessProbe.initialDelaySeconds Initial delay before readiness probe is initiated
## @param gitea.readinessProbe.timeoutSeconds Timeout for readiness probe
## @param gitea.readinessProbe.periodSeconds Period for readiness probe
## @param gitea.readinessProbe.successThreshold Success threshold for readiness probe
## @param gitea.readinessProbe.failureThreshold Failure threshold for readiness probe
# Modify the readiness probe for your needs or completely disable it by commenting out.
readinessProbe:
enabled: true
tcpSocket:
port: http
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
# # Uncomment the startup probe to enable and modify it for your needs.
## @section StartupProbe
#
## @param gitea.startupProbe.enabled Enable startup probe
## @param gitea.startupProbe.tcpSocket.port Port to probe for startup
## @param gitea.startupProbe.initialDelaySeconds Initial delay before startup probe is initiated
## @param gitea.startupProbe.timeoutSeconds Timeout for startup probe
## @param gitea.startupProbe.periodSeconds Period for startup probe
## @param gitea.startupProbe.successThreshold Success threshold for startup probe
## @param gitea.startupProbe.failureThreshold Failure threshold for startup probe
startupProbe:
enabled: false
tcpSocket:
port: http
initialDelaySeconds: 60
timeoutSeconds: 1
periodSeconds: 10
successThreshold: 1
failureThreshold: 10
## @section valkey-cluster
## @descriptionStart
## Valkey cluster and [Valkey](#valkey) cannot be enabled at the same time.
## @descriptionEnd
valkey-cluster:
## @param valkey-cluster.enabled Enable valkey cluster
# ⚠️ The valkey charts do not work well with special characters in the password (<https://gitea.com/gitea/helm-chart/issues/690>).
# Consider omitting such or open an issue in the Bitnami repo and let us know once this got fixed.
## @param valkey-cluster.usePassword Whether to use password authentication.
## @param valkey-cluster.usePasswordFiles Whether to mount passwords as files instead of environment variables.
enabled: true
usePassword: false
usePasswordFiles: false
## @param valkey-cluster.image.repository Image repository, eg. `bitnamilegacy/valkey-cluster`.
image:
repository: bitnamilegacy/valkey-cluster
## @param valkey-cluster.cluster.nodes Number of valkey cluster master nodes
## @param valkey-cluster.cluster.replicas Number of valkey cluster master node replicas
cluster:
nodes: 3 # default: 6
replicas: 0 # default: 1
## @param valkey-cluster.metrics.image.repository Image repository, eg. `bitnamilegacy/redis-exporter`.
metrics:
image:
repository: bitnamilegacy/redis-exporter
## @param valkey-cluster.service.ports.valkey Port of Valkey service
service:
ports:
valkey: 6379
## @param valkey-cluster.sysctlImage.repository Image repository, eg. `bitnamilegacy/os-shell`.
sysctlImage:
repository: bitnamilegacy/os-shell
## @param valkey-cluster.volumePermissions.image.repository Image repository, eg. `bitnamilegacy/os-shell`.
volumePermissions:
image:
repository: bitnamilegacy/os-shell
## @section valkey
## @descriptionStart
## Valkey and [Valkey cluster](#valkey-cluster) cannot be enabled at the same time.
## @descriptionEnd
valkey:
## @param valkey.enabled Enable valkey standalone or replicated
## @param valkey.architecture Whether to use standalone or replication
enabled: false
architecture: standalone
## @param valkey.kubectl.image.repository Image repository, eg. `bitnamilegacy/kubectl`.
kubectl:
image:
repository: bitnamilegacy/kubectl
## @param valkey.image.repository Image repository, eg. `bitnamilegacy/valkey`.
image:
repository: bitnamilegacy/valkey
# ⚠️ The valkey charts do not work well with special characters in the password (<https://gitea.com/gitea/helm-chart/issues/690>).
# Consider omitting such or open an issue in the Bitnami repo and let us know once this got fixed.
## @param valkey.global.valkey.password Required password
global:
valkey:
password: changeme
## @param valkey.master.count Number of Valkey master instances to deploy
## @param valkey.master.service.ports.valkey Port of Valkey service
master:
count: 1
service:
ports:
valkey: 6379
## @param valkey.metrics.image.repository Image repository, eg. `bitnamilegacy/redis-exporter`.
metrics:
image:
repository: bitnamilegacy/redis-exporter
## @param valkey.sentinel.image.repository Image repository, eg. `bitnamilegacy/sentinel`.
sentinel:
image:
repository: bitnamilegacy/valkey-sentinel
## @param valkey.volumePermissions.image.repository Image repository, eg. `bitnamilegacy/os-shell`.
volumePermissions:
image:
repository: bitnamilegacy/os-shell
## @section PostgreSQL HA
postgresql-ha:
## @param postgresql-ha.enabled Enable PostgreSQL HA
enabled: true
## @param postgresql-ha.global.postgresql.database Name for a custom database to create (overrides `auth.database`)
## @param postgresql-ha.global.postgresql.username Name for a custom user to create (overrides `auth.username`)
## @param postgresql-ha.global.postgresql.password Name for a custom password to create (overrides `auth.password`)
global:
postgresql:
database: gitea
password: gitea
username: gitea
## @param postgresql-ha.metrics.image.repository Image repository, eg. `bitnamilegacy/postgres-exporter`.
metrics:
image:
repository: bitnamilegacy/postgres-exporter
## @param postgresql-ha.postgresql.image.repository Image repository, eg. `bitnamilegacy/postgresql-repmgr`.
## @param postgresql-ha.postgresql.repmgrPassword Repmgr Password
## @param postgresql-ha.postgresql.postgresPassword postgres Password
## @param postgresql-ha.postgresql.password Password for the `gitea` user (overrides `auth.password`)
postgresql:
image:
repository: bitnamilegacy/postgresql-repmgr
repmgrPassword: changeme2
postgresPassword: changeme1
password: changeme4
## @param postgresql-ha.pgpool.adminPassword pgpool adminPassword
## @param postgresql-ha.pgpool.image.repository Image repository, eg. `bitnamilegacy/pgpool`.
## @param postgresql-ha.pgpool.srCheckPassword pgpool srCheckPassword
pgpool:
adminPassword: changeme3
image:
repository: bitnamilegacy/pgpool
srCheckPassword: changeme4
## @param postgresql-ha.service.ports.postgresql PostgreSQL service port (overrides `service.ports.postgresql`)
service:
ports:
postgresql: 5432
## @param postgresql-ha.persistence.size PVC Storage Request for PostgreSQL HA volume
persistence:
size: 10Gi
## @param postgresql-ha.volumePermissions.image.repository Image repository, eg. `bitnamilegacy/os-shell`.
volumePermissions:
image:
repository: bitnamilegacy/os-shell
## @section PostgreSQL
postgresql:
## @param postgresql.enabled Enable PostgreSQL
enabled: false
## @param postgresql.global.postgresql.auth.password Password for the `gitea` user (overrides `auth.password`)
## @param postgresql.global.postgresql.auth.database Name for a custom database to create (overrides `auth.database`)
## @param postgresql.global.postgresql.auth.username Name for a custom user to create (overrides `auth.username`)
## @param postgresql.global.postgresql.service.ports.postgresql PostgreSQL service port (overrides `service.ports.postgresql`)
global:
postgresql:
auth:
password: gitea
database: gitea
username: gitea
service:
ports:
postgresql: 5432
## @param postgresql.image.repository Image repository, eg. `bitnamilegacy/postgresql`.
image:
repository: bitnamilegacy/postgresql
## @param postgresql.primary.persistence.size PVC Storage Request for PostgreSQL volume
primary:
persistence:
size: 10Gi
## @param postgresql.metrics.image.repository Image repository, eg. `bitnamilegacy/postgres-exporter`.
metrics:
image:
repository: bitnamilegacy/postgres-exporter
## @param postgresql.volumePermissions.image.repository Image repository, eg. `bitnamilegacy/os-shell`.
volumePermissions:
image:
repository: bitnamilegacy/os-shell
# By default, removed or moved settings that still remain in a user defined values.yaml will cause Helm to fail running the install/update.
# Set it to false to skip this basic validation check.
## @section Advanced
## @param checkDeprecation Set it to false to skip this basic validation check.
## @param test.enabled Set it to false to disable test-connection Pod.
## @param test.image.name Image name for the wget container used in the test-connection Pod.
## @param test.image.tag Image tag for the wget container used in the test-connection Pod.
checkDeprecation: true
test:
enabled: true
image:
name: busybox
tag: latest
## @param extraDeploy Array of extra objects to deploy with the release
##
extraDeploy: []

View file

@ -0,0 +1,75 @@
{
"annotations": {
"artifacthub.io/changes": "- kind: changed\n description: update dependency go-gitea/gitea to v1.25.4 (#1012)\n- kind: fixed\n description: remove deployment.labels from selector.matchLabels (#1011)\n- kind: changed\n description: update lockfiles (#1009)\n- kind: changed\n description: update lockfiles (#1008)\n- kind: changed\n description: update lockfiles (#1006)\n- kind: changed\n description: update unittests/bash/test_helper/bats-mock digest to db2acdc (#1002)\n- kind: changed\n description: update unittests/bash/test_helper/bats-mock digest to c5674ff (#1000)\n- kind: changed\n description: update dependency go-gitea/gitea to v1.25.3 (#999)\n- kind: changed\n description: update dependency markdownlint-cli to ^0.47.0 (#998)\n- kind: changed\n description: update unittests/bash/test_helper/bats-mock digest to 1ce6b03 (#997)\n- kind: changed\n description: update lockfiles (#996)\n- kind: changed\n description: update workflow dependencies (minor & patch) (#995)\n- kind: changed\n description: update actions/checkout action to v6 (#989)\n- kind: changed\n description: update lockfiles (#994)\n- kind: changed\n description: update bats testing framework (#992)\n- kind: changed\n description: update dependency go-gitea/gitea to v1.25.2 (#990)\n- kind: changed\n description: update dependency markdownlint-cli to ^0.46.0 (#988)\n- kind: changed\n description: update unittests/bash/test_helper/bats-mock digest to 7839917 (#987)\n- kind: changed\n description: update lockfiles (#986)\n- kind: changed\n description: update lockfiles (#985)\n- kind: changed\n description: update docker.io/thegeeklab/git-sv docker tag to v2.0.7 (#984)\n- kind: changed\n description: update bats testing framework (#983)\n- kind: changed\n description: update dependency go-gitea/gitea to v1.25.1 (#981)\n- kind: fixed\n description: add yamllint as dedicated target\n- kind: changed\n description: add missing new line\n- kind: fixed\n description: 'avoid duplicated nodeSelector #980'\n- kind: changed\n description: update unittests/bash/bats digest to f423e84 (#978)\n- kind: changed\n description: update dependency go-gitea/gitea to v1.25.0 (#976)\n- kind: changed\n description: update dependency go-gitea/gitea to v1.24.7 (#975)\n- kind: changed\n description: update docker.io/thegeeklab/git-sv docker tag to v2.0.6 (#974)\n- kind: changed\n description: update bats testing framework (#967)\n- kind: changed\n description: update lockfiles (#970)\n- kind: changed\n description: update lockfiles (#969)\n- kind: fixed\n description: add annotation 'artifacthub.io/links'\n- kind: changed\n description: update dependency helm-unittest/helm-unittest to v1.0.3 (#964)\n- kind: changed\n description: update lockfiles (#961)\n",
"artifacthub.io/links": "- name: support\n url: https://gitea.com/gitea/helm-gitea/issues\n"
},
"apiVersion": "v2",
"appVersion": "1.25.4",
"created": "2026-01-23T20:13:36.765450965Z",
"dependencies": [
{
"condition": "postgresql.enabled",
"name": "postgresql",
"repository": "oci://registry-1.docker.io/bitnamicharts",
"version": "16.7.27"
},
{
"condition": "postgresql-ha.enabled",
"name": "postgresql-ha",
"repository": "oci://registry-1.docker.io/bitnamicharts",
"version": "16.3.2"
},
{
"condition": "valkey-cluster.enabled",
"name": "valkey-cluster",
"repository": "oci://registry-1.docker.io/bitnamicharts",
"version": "3.0.24"
},
{
"condition": "valkey.enabled",
"name": "valkey",
"repository": "oci://registry-1.docker.io/bitnamicharts",
"version": "3.0.31"
}
],
"description": "Gitea Helm chart for Kubernetes",
"digest": "2d6f35f3d1ff0cfcab0f22142d20a1d6405fa974a79d7a18000904d233131823",
"icon": "https://gitea.com/assets/img/logo.svg",
"keywords": [
"git",
"issue tracker",
"code review",
"wiki",
"gitea",
"gogs"
],
"maintainers": [
{
"email": "ross@golder.org",
"name": "Ross Golder"
},
{
"email": "markus.pesch+apps@cryptic.systems",
"name": "Markus Pesch"
},
{
"email": "dselen@nerthus.nl",
"name": "Daan Selen"
},
{
"email": "christopher.homberger@web.de",
"name": "Christopher Homberger"
}
],
"name": "gitea",
"sources": [
"https://gitea.com/gitea/helm-gitea",
"https://github.com/go-gitea/gitea",
"https://docker.gitea.com/gitea"
],
"type": "application",
"urls": [
"https://dl.gitea.com/charts/gitea-12.5.0.tgz"
],
"version": "12.5.0"
}

View file

@ -0,0 +1,299 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"errors"
"fmt"
"net/url"
auth_model "forgejo.org/models/auth"
"forgejo.org/services/auth/source/oauth2"
"github.com/urfave/cli/v2"
)
var (
oauthCLIFlags = []cli.Flag{
&cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
&cli.StringFlag{
Name: "provider",
Value: "",
Usage: "OAuth2 Provider",
},
&cli.StringFlag{
Name: "key",
Value: "",
Usage: "Client ID (Key)",
},
&cli.StringFlag{
Name: "secret",
Value: "",
Usage: "Client Secret",
},
&cli.StringFlag{
Name: "auto-discover-url",
Value: "",
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
},
&cli.StringFlag{
Name: "use-custom-urls",
Value: "false",
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-tenant-id",
Value: "",
Usage: "Use custom Tenant ID for OAuth endpoints",
},
&cli.StringFlag{
Name: "custom-auth-url",
Value: "",
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-token-url",
Value: "",
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-profile-url",
Value: "",
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
},
&cli.StringFlag{
Name: "custom-email-url",
Value: "",
Usage: "Use a custom Email URL (option for GitHub)",
},
&cli.StringFlag{
Name: "icon-url",
Value: "",
Usage: "Custom icon URL for OAuth2 login source",
},
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Set to true to skip local 2fa for users authenticated by this source",
},
&cli.StringSliceFlag{
Name: "scopes",
Value: nil,
Usage: "Scopes to request when to authenticate against this OAuth2 source",
},
&cli.StringFlag{
Name: "required-claim-name",
Value: "",
Usage: "Claim name that has to be set to allow users to login with this source",
},
&cli.StringFlag{
Name: "required-claim-value",
Value: "",
Usage: "Claim value that has to be set to allow users to login with this source",
},
&cli.StringFlag{
Name: "group-claim-name",
Value: "",
Usage: "Claim name providing group names for this source",
},
&cli.StringFlag{
Name: "admin-group",
Value: "",
Usage: "Group Claim value for administrator users",
},
&cli.StringFlag{
Name: "restricted-group",
Value: "",
Usage: "Group Claim value for restricted users",
},
&cli.StringFlag{
Name: "group-team-map",
Value: "",
Usage: "JSON mapping between groups and org teams",
},
&cli.BoolFlag{
Name: "group-team-map-removal",
Usage: "Activate automatic team membership removal depending on groups",
},
}
microcmdAuthAddOauth = &cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
}
microcmdAuthUpdateOauth = &cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
}
)
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
TokenURL: c.String("custom-token-url"),
AuthURL: c.String("custom-auth-url"),
ProfileURL: c.String("custom-profile-url"),
EmailURL: c.String("custom-email-url"),
Tenant: c.String("custom-tenant-id"),
}
} else {
customURLMapping = nil
}
return &oauth2.Source{
Provider: c.String("provider"),
ClientID: c.String("key"),
ClientSecret: c.String("secret"),
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"),
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"),
GroupClaimName: c.String("group-claim-name"),
AdminGroup: c.String("admin-group"),
RestrictedGroup: c.String("restricted-group"),
GroupTeamMap: c.String("group-team-map"),
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
}
}
func runAddOauth(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
config := parseOAuth2Config(c)
if config.Provider == "openidConnect" {
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
}
}
return auth_model.CreateSource(ctx, &auth_model.Source{
Type: auth_model.OAuth2,
Name: c.String("name"),
IsActive: true,
Cfg: config,
})
}
func runUpdateOauth(c *cli.Context) error {
if !c.IsSet("id") {
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil {
return err
}
oAuth2Config := source.Cfg.(*oauth2.Source)
if c.IsSet("name") {
source.Name = c.String("name")
}
if c.IsSet("provider") {
oAuth2Config.Provider = c.String("provider")
}
if c.IsSet("key") {
oAuth2Config.ClientID = c.String("key")
}
if c.IsSet("secret") {
oAuth2Config.ClientSecret = c.String("secret")
}
if c.IsSet("auto-discover-url") {
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
}
if c.IsSet("icon-url") {
oAuth2Config.IconURL = c.String("icon-url")
}
if c.IsSet("scopes") {
oAuth2Config.Scopes = c.StringSlice("scopes")
}
if c.IsSet("required-claim-name") {
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
}
if c.IsSet("required-claim-value") {
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
}
if c.IsSet("group-claim-name") {
oAuth2Config.GroupClaimName = c.String("group-claim-name")
}
if c.IsSet("admin-group") {
oAuth2Config.AdminGroup = c.String("admin-group")
}
if c.IsSet("restricted-group") {
oAuth2Config.RestrictedGroup = c.String("restricted-group")
}
if c.IsSet("group-team-map") {
oAuth2Config.GroupTeamMap = c.String("group-team-map")
}
if c.IsSet("group-team-map-removal") {
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
}
// update custom URL mapping
customURLMapping := &oauth2.CustomURLMapping{}
if oAuth2Config.CustomURLMapping != nil {
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
customURLMapping.TokenURL = c.String("custom-token-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
customURLMapping.AuthURL = c.String("custom-auth-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
customURLMapping.ProfileURL = c.String("custom-profile-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
customURLMapping.EmailURL = c.String("custom-email-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
customURLMapping.Tenant = c.String("custom-tenant-id")
}
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
return auth_model.UpdateSource(ctx, source)
}

View file

@ -0,0 +1,59 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package oauth2
import (
"strings"
"forgejo.org/models/auth"
"forgejo.org/modules/json"
)
// Source holds configuration for the OAuth2 login source.
type Source struct {
Provider string
ClientID string
ClientSecret string
OpenIDConnectAutoDiscoveryURL string
CustomURLMapping *CustomURLMapping
IconURL string
Scopes []string
AttributeSSHPublicKey string
RequiredClaimName string
RequiredClaimValue string
GroupClaimName string
AdminGroup string
GroupTeamMap string
GroupTeamMapRemoval bool
RestrictedGroup string
SkipLocalTwoFA bool `json:",omitempty"`
// reference to the authSource
authSource *auth.Source
}
// FromDB fills up an OAuth2Config from serialized format.
func (source *Source) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &source)
}
// ToDB exports an SMTPConfig to a serialized format.
func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
// ProvidesSSHKeys returns if this source provides SSH Keys
func (source *Source) ProvidesSSHKeys() bool {
return len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0
}
// SetAuthSource sets the related AuthSource
func (source *Source) SetAuthSource(authSource *auth.Source) {
source.authSource = authSource
}
func init() {
auth.RegisterTypeConfig(auth.OAuth2, &Source{})
}

View file

@ -0,0 +1,59 @@
{
"scope": "offline-source-and-public-chart-only",
"canonical_edits": false,
"owner_acknowledgment": false,
"case_source_accepted": false,
"full_first_case_configuration": "refused: unsupported exact any-of gate",
"tests": {
"gate_evidence": {
"joined_or_regex_value_matches_any_individual_case_group": false,
"running_forgejo_callback_test": false,
"singular_literal_operand": true
},
"reference_render": {
"actual_case_gate": "refused",
"ambient_environment": false,
"chart": "12.5.0",
"cli_flags_match_pinned_forgejo": true,
"forgejo_image": "11.0.3",
"native_calls": false,
"private_helper_executed": false,
"secret_reference": {
"key": "CLIENT_SECRET",
"name": "forgejo-netkingdom-oidc-client"
},
"secret_values_used": false
},
"rendered_negative_controls": {
"actual_delivery_keys": [
"CLIENT_SECRET"
],
"existingSecret_requests": [
"key",
"secret"
],
"helm_schema_does_not_reject_unknown_oauth_keys": true,
"invented_list_field_renders_unknown_cli_flag": "--required-claim-values"
},
"successful": true,
"tests_run": 9
},
"validation_history": [
{
"outcome": "failed",
"tests_run": 7,
"reason": "review fixture selected Deployment named forgejo, chart actually uses forgejo-gitea",
"correction": "select actual Deployment by app.kubernetes.io/instance=forgejo; no chart/policy weakening"
},
{
"outcome": "passed",
"tests_run": 7
},
{
"outcome": "passed",
"tests_run": 9,
"environment": "empty HOME, allowlisted PATH/PYTHONDONTWRITEBYTECODE; Helm child receives own isolated paths"
}
],
"isolated_patch_apply_and_exact_postimages": true
}

View file

@ -0,0 +1,151 @@
# Independent Platform delivery mechanics review r1
Disposition: **changes required before canonical integration**. This review is
of source preparation, not owner acceptance, native execution, an authority
grant, authenticated delivery evidence, or completion of RPF-1.3-T4.
Reviewed `platform-delivery-backend-r1` against Platform
`104ed90f42a9a86bf9fda343b49b65a85c9ee4b9` (also actual local HEAD), its
AGENTS/INTENT, fixed template/helper and actual command runner. Original
candidate and canonical repositories are unchanged. No credentials, network,
model calls, production operations, commits or dispatch occurred.
## Findings
1. **Stale Ready can become a success receipt.** `_ready` tests only a
Ready=True condition. A matching spec at generation 2 with old
`syncedResourceVersion=1-old-spec` and old `refreshTime` is accepted, as is
Ready=True without either freshness field. The Store gate likewise accepts
an old Ready condition after a prior spec change. Metadata RV records an
observation; it does not establish which spec ESO successfully reconciled.
A later observation with the same UID cannot prove that either.
The locally cached chart `external-secrets-0.16.1.tgz` declares appVersion
v0.16.1 and is the version pinned by Platform's Argo source declaration.
Its actual v1 ExternalSecret schema has `status.syncedResourceVersion` and
`status.refreshTime`, and no `status.observedGeneration` or condition
observedGeneration. A Deployment-style observedGeneration gate would not
implement this contract. The controller's encoding of syncedResourceVersion
was not available in the inspected local source; no encoding is invented
here. Pin/review actual controller semantics and prove freshness against
the current spec before returning success. Source chart evidence is not a
live installed-version observation.
2. **Normal API defaulting breaks the supposedly exact match.** The actual v1
CRD adds `remoteRef.conversionStrategy=Default`, `decodingStrategy=None`,
and `metadataPolicy=None`. The fixed template omits them. Applying the
cached schema's defaults to a synthetic create response makes `_match`
reject it: delivery becomes Uncertain and loses the returned UID as deletion
provenance because assignment occurs only after `_match` succeeds. This is
an ordinary API-shaped response, not an attack or conflicting resource.
The existing fixed helper also has the same exact-spec limitation.
`partial-defaults-fix.patch` separately fixes this narrowly in the helper
and candidate: absent and explicit values of exactly these defaults compare
equally, while all other fields remain exact. The source template and its
digest remain unchanged. This patch does **not** repair the other findings.
3. **Store identity/continuity is unproven.** Only one Store GET occurs, before
the ExternalSecret absence check and create. Its UID/RV are never required
or retained. Replacing or changing the Store between that response and
create is undetected; the ExternalSecret references only kind/name. No
subsequent Store observation precedes the success receipt. Rechecking
before create and success would detect some races, but cannot atomically
bind a cross-resource create to the Store UID/RV. An actual existing owner
enforcement mechanism or an explicitly limited guarantee is needed; a
fictional admission policy is not supplied by this review.
4. **Output capture is unbounded; timeout claims need qualification.** Actual
`state_hub_preflight_lane.command` calls
`subprocess.run(capture_output=True, timeout=60)`. The 262144-byte stdout
limit runs after all stdout and stderr have been collected. Stderr has no
limit at all. A socket denial and mocked run function cannot prove resource
bounds. Require a transport that bounds bytes during collection, includes
stderr, terminates/reaps on overflow and timeout, and returns fixed errors.
Kubectl receives `--request-timeout=5s`, but the enclosing process timeout
is 60 seconds. Ten polls bound command count, not delivery to ten seconds:
the nominal worst-case create route is 13 commands times 60 seconds plus
9 seconds of sleeps (789 seconds); remove is 12 times 60 plus 9 (729).
These are configured nominal bounds, not a tested global deadline or a
guarantee about descendant process cleanup.
5. **Read API version depends on discovery.** GET uses the fully qualified
plural/group, which correctly restricts the resource kind, but does not pin
v1 in the request. DELETE explicitly uses
`/apis/external-secrets.io/v1/namespaces/sso/externalsecrets/<fixed-name>`.
Matching a returned apiVersion rejects a different response version; it
does not prove the request used the intended API version. Explicitly pin
reads when that is part of the owner contract. The cached chart advertises
v1 served/storage and v1beta1 served; no live discovery was performed.
6. **Malformed object errors can escape the claimed fixed envelope.** A create
response with `metadata: null` raises AttributeError outside `_call` rather
than Refused/Uncertain. The pre-dispatch pending flag remains set, which
prevents a retry, but the fixed reconciliation/error contract is incomplete.
Validate every nested metadata shape and preserve fixed uncertainty at all
post-mutation boundaries. No input values should enter error strings.
## Guarantees actually supported by the tests and inspected source
- Template-derived fixed client name, namespace `sso`, custody path, key names,
exact Store spec and existing Store Ready gate; no caller store/namespace/
remoteRef overrides. Only ExternalSecret is created. No Kubernetes Secret
read/list/create/delete or Store mutation is dispatched.
- Fetch errors cannot become absence. Create is `create`, not apply/upsert.
Conflicting existing ExternalSecrets are refused, and matching existing
objects yield unchanged receipts without acquiring deletion provenance.
- A verified instance-created object's UID is kept across polling; replacement
is refused. RV is refreshed on successful readiness observation.
- DELETE has **both** UID and resourceVersion API preconditions, in one
DeleteOptions body. An intervening status/spec update or replacement is
therefore expected to conflict at the API rather than delete another
revision. Tests prove generated argv/body and conservative handling of
errors; actual API-server enforcement was not exercised.
- Lost create/delete responses, a replaced UID and unconfirmed deletion keep
pending reconciliation obligations and prevent blind retry in that instance.
A supplied revision or fresh process cannot manufacture deletion provenance.
Successful absence means absence of the fixed ExternalSecret at observation
time; it does not prove target Secret garbage collection or credential revoke.
- Production factory unconditionally refuses before any command. Empty env and
`/dev/null` kubeconfig remain deliberate preparation restrictions. Private
mechanics and caller-supplied Python objects establish no authority.
This is not one-shot durable admission: a successful unchanged delivery can be
called again, and instance state is lost on restart. The candidate correctly
does not claim that its private state replaces the missing owner journal.
## Validation and exact partial correction
`runs.json` and logs retain all final results:
| Run | Result | Meaning |
| --- | --- | --- |
| Original command regressions | 17/17 pass | Reproduces candidate's suite |
| Existing Platform fixed-contract suite | 8/8 pass | Reproduces base contract checks |
| Independent review tests | 12/12 pass | Includes explicit **defect reproductions**, not acceptance tests |
| Partial defaults fix, original suites plus new adversarial checks | 29/29 pass | Three exact defaults accepted; alternate strategies, extra fields, wrong identity, unready metadata refused |
| Incremental patch check against original r1 scratch tree | pass | `git apply --check` only |
All tests run with an empty inherited environment, empty scratch HOME, no
credential variables and no live command dispatch. Independent tests and the
17 command tests deny socket construction and intercept subprocess.run.
Contract tests are pure metadata operations. The public chart was rendered
locally from cache by Helm; no chart download or cluster access occurred.
Rendered v1 schemas are retained for reproducible defaults/status evidence.
The incremental partial patch applies **after** the original candidate patch,
not directly to bare Platform 104ed90. `partial-fix-source/scripts/` contains
its exact resulting helper and delivery script. Stage those in a scratch
Platform-shaped tree with the pinned template/command helper, then run the
original two suites plus `test_partial_defaults_fix.py`. Source and artifact
digests are in `source-pins.json`. No canonical integration is approved by a
passing partial-fix suite. Stale Ready, Store continuity, bounded transport,
exact-version reads and malformed-response handling remain unresolved.
Production integration additionally still requires actual owner-authenticated
case/client binding, current scoped caller/session, durable one-shot admission
and recovery journal, authenticated evidence publication/verification and
per-attempt revocation. Keep `production_bindings()` refusing until those real
facilities exist and have been independently verified. Neither this review,
the partial patch, source pins, mock receipts, nor clearing transport
restrictions supplies those facilities or makes dispatch operational.