From b89e8bbd372b0f9e73faf4465e872acddadadcf2 Mon Sep 17 00:00:00 2001 From: Julio Caicedo Date: Fri, 28 Nov 2025 14:13:29 -0500 Subject: [PATCH 1/3] Based on the staged changes, here's the conventional commit message: chore: add PGP public key and values JSON schema Add PGP public key for Start Codex Helm Charts signing and comprehensive JSON schema for Valkey Helm chart values validation. The schema defines all configurable parameters including architecture modes (standalone/sentinel), authentication, persistence, networking, metrics, and TLS configuration. --- .github/workflows/pages.yml | 41 +- Chart.yaml | 16 +- public-key.asc | 52 +++ values.schema.json | 799 ++++++++++++++++++++++++++++++++++++ 4 files changed, 902 insertions(+), 6 deletions(-) create mode 100644 public-key.asc create mode 100644 values.schema.json diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 6282d40..c8cdc7b 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -8,6 +8,8 @@ on: - 'Chart.yaml' - 'README.md' - 'artifacthub-repo.yml' + - 'public-key.asc' + - 'values.schema.json' - '.github/workflows/pages.yml' workflow_dispatch: @@ -42,7 +44,20 @@ jobs: with: version: v3.12.1 + - name: Import GPG key + if: ${{ env.GPG_PRIVATE_KEY != '' }} + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + echo "$GPG_PRIVATE_KEY" | gpg --batch --import + # Configure GPG for non-interactive use + echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf + gpg-connect-agent reloadagent /bye || true + - name: Create Helm repository index + env: + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | # Create directories mkdir -p .cr-release-packages @@ -75,8 +90,16 @@ jobs: cp existing-index.yaml .cr-release-packages/ || true fi - # Package the current chart - helm package . -d .cr-release-packages + # Package the current chart (with signing if GPG key is available) + if [ -n "$GPG_PASSPHRASE" ] && gpg --list-secret-keys --keyid-format LONG 2>/dev/null | grep -q "sec"; then + echo "Signing chart with GPG key..." + helm package . -d .cr-release-packages --sign --key "Start Codex" --keyring ~/.gnupg/secring.gpg 2>/dev/null || \ + helm package . -d .cr-release-packages --sign --key "cloud@startcodex.com" --passphrase-file <(echo "$GPG_PASSPHRASE") 2>/dev/null || \ + helm package . -d .cr-release-packages + else + echo "Packaging chart without signature..." + helm package . -d .cr-release-packages + fi # Create/update the index with existing and new charts if [ -f .cr-release-packages/existing-index.yaml ]; then @@ -87,10 +110,14 @@ jobs: helm repo index .cr-release-packages --url https://start-codex.github.io/valkey-helm-chart fi - # Copy new chart files to public directory + # Copy new chart files to public directory (including .prov files if signed) if ls .cr-release-packages/*.tgz 1> /dev/null 2>&1; then cp .cr-release-packages/*.tgz public/ || true fi + if ls .cr-release-packages/*.prov 1> /dev/null 2>&1; then + cp .cr-release-packages/*.prov public/ || true + echo "Provenance files copied" + fi # Copy the updated index if [ -f .cr-release-packages/index.yaml ]; then @@ -99,6 +126,7 @@ jobs: cp README.md public/ || true cp artifacthub-repo.yml public/ || true + cp public-key.asc public/ || true # Verify what we have echo "Contents of public directory:" @@ -208,8 +236,15 @@ jobs:
  • index.yaml - Helm repository index
  • README.md - Chart documentation
  • artifacthub-repo.yml - Artifact Hub metadata
  • +
  • public-key.asc - GPG public key for chart verification
  • + +
    +

    🔐 Chart Verification

    +

    Charts are signed with GPG. To verify:

    +
    wget https://start-codex.github.io/valkey-helm-chart/public-key.asc
    gpg --import public-key.asc
    helm verify valkey-0.1.24.tgz
    +
    EOF diff --git a/Chart.yaml b/Chart.yaml index 54cfbba..fd3669c 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -5,7 +5,17 @@ type: application version: 0.1.24 appVersion: "9.0.0" annotations: - images: | + artifacthub.io/signKey: | + fingerprint: BD9C3A3FB5C152390B589739DA06B8958C5CE1D1 + url: https://start-codex.github.io/valkey-helm-chart/public-key.asc + artifacthub.io/category: database + artifacthub.io/license: Apache-2.0 + artifacthub.io/links: | + - name: Valkey Official + url: https://valkey.io/ + - name: Chart Source + url: https://github.com/start-codex/valkey-helm-chart + artifacthub.io/images: | - name: valkey image: docker.io/valkey/valkey:9.0.0 - name: os-shell @@ -14,8 +24,8 @@ annotations: image: docker.io/oliver006/redis_exporter:v1.80.0 - name: kubectl image: docker.io/alpine/k8s:1.31.13 - licenses: Apache-2.0 - category: database + artifacthub.io/containsSecurityUpdates: "false" + artifacthub.io/prerelease: "false" keywords: - valkey - redis diff --git a/public-key.asc b/public-key.asc new file mode 100644 index 0000000..6ac4192 --- /dev/null +++ b/public-key.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGkp8N4BEADfflqnk0txQKKKKyKxb//M8SIv/pxLEj0I2HYArBoQWoNtMF6P +/o8h31gm89wQNAO13hem/pbAeEmug/fKb/OSMQSQqqZE7KwzVNV5JD++FvruQxLM +lg+exIO0/Sdcjtg5t2hdfykfjrdh0yWRqbIItJSO69/94y7sEWbHvuqmjoPl+BB1 +0u91dZQnIlhtadAYkHouZq0WXOVnrLtUJZLYHxXpq5Kk3I0qDZoJ0+jhxfqQy00F +SGrG5JwyVX50eTF/WoAzmoV79FoU483x35yu4J2XU0mlO8e7DdHRm7AnsT++q5rl +aty1nytnqZ2Qk1d0A9UnLEJ9Ol7Zh73hcQH1fwdAfIyYgHDq1JhUWqLkcuE1ZV0C +tqPBaze416CGODVfJSBBTkgWJj2HjRWIKWnh7psqQUTMTEXCnYWSzJAt6CB32hiI +e49+vrO8PszYNYn6d4cWvmXNJwm3xx44S4qETQDu9S/hr0JOSKrscgkOQPstAgx2 +JTJ4uZhgzD1IxxMi3SdQH8J544VO3mihJRG/XKrRK8DFGUyx5Ep9z0aEV1Yod1b4 +xJkOf9tCIqqIalkCf6oDuR0GTQk8ycPzlCdw9dD0rufWrl0fxPOArxptdi8DcZ/d +QsHyMdY0akJOBQqt6WroRPxUB3c/tnkAk73Z9LZ37FWjSjk3JW/lKVJC7wARAQAB +tDxTdGFydCBDb2RleCAoU3RhcnQgQ29kZXggSGVsbSBDaGFydHMpIDxjbG91ZEBz +dGFydGNvZGV4LmNvbT6JAlEEEwEIADsWIQS9nDo/tcFSOQtYlznaBriVjFzh0QUC +aSnw3gIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRDaBriVjFzh0ewt +D/0bngs6zcJRoV+RH3rBg8NKZNah5i2MfdjoybbinHI+8Ni2tBmGesGFKLC0DFhU +LFLhq0tlVnLwbLQ6kcwUzWdncWad15unC//9F/v0MG17EKAW49KnFJs86D06rbGT +tP3qLQ/c3ahfy/E1mTsxiBdTN2GehXgSbwIHKfs8pkBmUklo/CBNHKeueXWEN4TH +1lEpYD5MVDNNDlhJGZz869dRTJ7+XNcRKeKsPybxRGq6t7OXybQJi6j8fnbh0daF +5bmGBAsmyhi14hkLDKvYqfs3ujBGgn+EE7y2J/Wj00ICbDA3A7indB/KFGRWe0y4 +AU0ifryCsfTpVXEM/ZmYbAz5SWpqg+QYR+arheESWkms8fe3h+FKaUH5SbEDw6+b +sSbOup8U+PdalyMRB1rDdsRKma5+1wSJzXcVmotUP0rNyGrONy99CXoWJkvLdHO8 +wQALVmFoeOagCUsTvkFvic2lmz0t4c4CIADxIlPi2TORnBoGaD/fiWTEhOkAJqm2 +o4HIdycwaRw14JxH7/KvCY8hBLc4saunbt78Nj8t3n5oYel01F/XDPI4DfhW8q7d +kksnktsGlIPeX1qd1SEJiAZefVSHbWzCpFxq9weNjBFIGL9CGw0ODzjazViVoIs4 +oqpy7hbzJJufoG3TxJW1ywYWRuZw7YFaGg0aJtX/G8GOxrkCDQRpKfDeARAAxVN4 +heq4QZciAYivwaxCXcrcDEmAbx/7P5y04OexikW5ZuYJf/sWvgnSUMOvtfZpjBNx +pV9wgktqZXddjPGxwLa/e4jw73pY3rGyeT8xbABgEOXt5Uur9AFrpYYrnYspLmQ9 +VF8bcKJMaLiSkjob0NlyjtxDBh0UCcN4jIxvBPWPcIWmSbLyp7yfb/siWTiWpClG +/uzKljJLb8lE7iQwiezLAhNDyucrzMjeE7fLmX6jPIYJk5y+RgR0wTGttdc8ncz/ +25QdmFYGoPWBm8lEiPcoAE7upn9qWp3x2sCKQe9ujyP/u7+PD4OMAafdse05ExWo +2cpUOqVNKJNuaNijVsN+ynTXoQB6wIQHgRhOTrlW2LA+UeOkzUBVy9BPez/DWx84 +JrZ7uzfpdJeYtkJqJJ4GHkguCGaCiRHxJe3XuW+k4xMqtTVEvtDkJ9zU9ahkiSC1 +kqOx/u9cD18KTuCC7Ufyiwk7kdcqsDOP0tmEg6X1Fohj3JOSNAeZXlu59k4alpFf +KptQQEtnmHiaJnZG4iepwNIDTjmDsR7BYfLwiFhHxEcyYCraw0zEbIU5o9U2J4op +P20/EsZEe9Q+NyIpeOSd0pycELAtri9BNRd34ifM592YwBxbP4KUcjCsiHeApCqX +h/ec2dnOfXwsLdzpoHblQ5HLY2Hxs2WW+NPsfU8AEQEAAYkCNgQYAQgAIBYhBL2c +Oj+1wVI5C1iXOdoGuJWMXOHRBQJpKfDeAhsMAAoJENoGuJWMXOHRRp0QAJnF9tPi ++NqNe8jE4nHZdKR9pFzlHb4XLXZSl/g90nV8szNj+Hc2sIyNY4Y/ujDy6gXBAJru +B4gdvYdyLglXQSuiQ+nPyqx4y45ZA7QwUn2fvQpQaye/6cCusRSIfw8jXHcz4/mn +1VOEtth8sN9DKRu2zvkH03GKeZ6+6O1IIdUwag+6H8umgpTXbOuUTVyK79JeSSXw +HTS3HxtKc4DZK7Ak3Hs+ooyKiWhEKuNmmZ+WrM+dmjm/gQgh/UvxWhkKwLUBTH4o +33P7dAe/dkhQ9eLXman/kkavxBMTR45MgpcJ7fOzkLeJEGrSi2xm/3gndIE35OFh +cpghDObt8GXYgvDVZurUkgZMKsOCxIiSr6zSUtbk1/Lzctt4nciP3xn2sNA9A05n +cYzziyQKl3EHy+GnG0G4Ly4/OpMV27UT6NM2QKNZ2LKswHvsWU68C6vOyMo4SyT1 +R4wAbO66GEtTS9KeWB/irGiQJvmVI4OmqrYvnRrc2K8q1JW7fY4Kcs4IDMjTqvVs +fPm0qyGP1vFVSJ8syQmFWfexqcfhPkJsEtu/9scTt0wT6GkhwVEBgAlqMUc6NJVG +/KJIz81Kad7OCUBDCQds9DlyF1PtMsQrxQ77/SrXzQoPhnQLVSrHXtrMsuLmZTPz +9WC8JBHDuwy4luXYC9J80Vdmgn7k+zc8yeKO +=3bnc +-----END PGP PUBLIC KEY BLOCK----- diff --git a/values.schema.json b/values.schema.json new file mode 100644 index 0000000..6c5b159 --- /dev/null +++ b/values.schema.json @@ -0,0 +1,799 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Valkey Helm Chart Values", + "type": "object", + "properties": { + "architecture": { + "type": "string", + "description": "Deployment architecture: standalone or sentinel", + "enum": ["standalone", "sentinel"], + "default": "standalone" + }, + "global": { + "type": "object", + "description": "Global parameters", + "properties": { + "imageRegistry": { + "type": "string", + "description": "Global Docker image registry" + }, + "imagePullSecrets": { + "type": "array", + "description": "Global Docker registry secret names", + "items": { + "type": "string" + } + }, + "storageClass": { + "type": "string", + "description": "Global StorageClass for Persistent Volume(s)" + } + } + }, + "clusterDomain": { + "type": "string", + "description": "Kubernetes cluster domain", + "default": "cluster.local" + }, + "image": { + "type": "object", + "description": "Valkey image configuration", + "properties": { + "registry": { + "type": "string", + "description": "Image registry", + "default": "docker.io" + }, + "repository": { + "type": "string", + "description": "Image repository", + "default": "valkey/valkey" + }, + "tag": { + "type": "string", + "description": "Image tag", + "default": "9.0.0" + }, + "pullPolicy": { + "type": "string", + "description": "Image pull policy", + "enum": ["Always", "IfNotPresent", "Never"], + "default": "IfNotPresent" + } + } + }, + "nameOverride": { + "type": "string", + "description": "String to partially override fullname template" + }, + "fullnameOverride": { + "type": "string", + "description": "String to fully override fullname template" + }, + "auth": { + "type": "object", + "description": "Authentication configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable password authentication", + "default": false + }, + "password": { + "type": "string", + "description": "Valkey password (not recommended for production)" + }, + "existingSecret": { + "type": "string", + "description": "Name of existing secret containing the password" + }, + "existingSecretPasswordKey": { + "type": "string", + "description": "Key in existing secret containing the password", + "default": "password" + }, + "sentinel": { + "type": "boolean", + "description": "Enable authentication for Sentinel", + "default": false + } + } + }, + "networkPolicy": { + "type": "object", + "description": "Network Policy configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable NetworkPolicy", + "default": false + }, + "allowExternal": { + "type": "boolean", + "description": "Allow external connections", + "default": true + }, + "ingressNSMatchLabels": { + "type": "object", + "description": "Namespace labels for ingress rules" + }, + "ingressNSPodMatchLabels": { + "type": "object", + "description": "Pod labels for ingress rules" + } + } + }, + "rbac": { + "type": "object", + "description": "RBAC configuration", + "properties": { + "create": { + "type": "boolean", + "description": "Create RBAC resources", + "default": false + } + } + }, + "serviceAccount": { + "type": "object", + "description": "Service Account configuration", + "properties": { + "create": { + "type": "boolean", + "description": "Create ServiceAccount", + "default": true + }, + "annotations": { + "type": "object", + "description": "ServiceAccount annotations" + }, + "name": { + "type": "string", + "description": "ServiceAccount name" + } + } + }, + "podSecurityContext": { + "type": "object", + "description": "Pod Security Context", + "properties": { + "fsGroup": { + "type": "integer", + "description": "Group ID for the filesystem", + "default": 999 + }, + "runAsUser": { + "type": "integer", + "description": "User ID for the container", + "default": 999 + }, + "runAsGroup": { + "type": "integer", + "description": "Group ID for the container", + "default": 999 + } + } + }, + "securityContext": { + "type": "object", + "description": "Container Security Context", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Allow privilege escalation", + "default": false + }, + "capabilities": { + "type": "object", + "description": "Linux capabilities", + "properties": { + "drop": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Read-only root filesystem", + "default": true + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Run as non-root user", + "default": true + }, + "runAsUser": { + "type": "integer", + "description": "User ID for the container", + "default": 999 + } + } + }, + "commonAnnotations": { + "type": "object", + "description": "Annotations to add to all deployed objects" + }, + "commonLabels": { + "type": "object", + "description": "Labels to add to all deployed objects" + }, + "standalone": { + "type": "object", + "description": "Standalone mode configuration", + "properties": { + "replicaCount": { + "type": "integer", + "description": "Number of replicas", + "default": 1, + "minimum": 1 + }, + "resources": { + "type": "object", + "description": "Container resources", + "properties": { + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } + } + }, + "requests": { + "type": "object", + "properties": { + "memory": { + "type": "string" + }, + "cpu": { + "type": "string" + } + } + } + } + }, + "nodeSelector": { + "type": "object", + "description": "Node labels for pod assignment" + }, + "tolerations": { + "type": "array", + "description": "Tolerations for pod assignment", + "items": { + "type": "object" + } + }, + "affinity": { + "type": "object", + "description": "Affinity for pod assignment" + }, + "persistence": { + "type": "object", + "description": "Persistence configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable persistence", + "default": true + }, + "storageClass": { + "type": "string", + "description": "StorageClass for PVC" + }, + "accessModes": { + "type": "array", + "description": "PVC access modes", + "items": { + "type": "string", + "enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany"] + }, + "default": ["ReadWriteOnce"] + }, + "size": { + "type": "string", + "description": "PVC size", + "default": "8Gi" + }, + "annotations": { + "type": "object", + "description": "PVC annotations" + } + } + }, + "service": { + "type": "object", + "description": "Service configuration", + "properties": { + "type": { + "type": "string", + "description": "Service type", + "enum": ["ClusterIP", "NodePort", "LoadBalancer"], + "default": "ClusterIP" + }, + "port": { + "type": "integer", + "description": "Service port", + "default": 6379 + }, + "targetPort": { + "type": "string", + "description": "Target port name", + "default": "valkey" + }, + "annotations": { + "type": "object", + "description": "Service annotations" + } + } + }, + "configuration": { + "type": "string", + "description": "Valkey configuration file content" + } + } + }, + "sentinel": { + "type": "object", + "description": "Sentinel configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable Sentinel", + "default": false + }, + "replicaCount": { + "type": "integer", + "description": "Number of Sentinel replicas", + "default": 3, + "minimum": 1 + }, + "quorum": { + "type": "integer", + "description": "Quorum for Sentinel", + "default": 2, + "minimum": 1 + }, + "masterSet": { + "type": "string", + "description": "Name of the master set", + "default": "mymaster" + }, + "downAfterMilliseconds": { + "type": "integer", + "description": "Time in ms to consider a master down", + "default": 30000 + }, + "failoverTimeout": { + "type": "integer", + "description": "Failover timeout in ms", + "default": 180000 + }, + "parallelSyncs": { + "type": "integer", + "description": "Number of replicas to sync in parallel", + "default": 1 + }, + "image": { + "type": "object", + "description": "Sentinel image configuration", + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "enum": ["Always", "IfNotPresent", "Never"] + } + } + }, + "resources": { + "type": "object", + "description": "Container resources" + }, + "service": { + "type": "object", + "description": "Sentinel service configuration", + "properties": { + "type": { + "type": "string", + "enum": ["ClusterIP", "NodePort", "LoadBalancer"] + }, + "port": { + "type": "integer", + "default": 26379 + } + } + } + } + }, + "master": { + "type": "object", + "description": "Master configuration (sentinel mode)", + "properties": { + "replicaCount": { + "type": "integer", + "description": "Number of master replicas", + "default": 1 + }, + "resources": { + "type": "object", + "description": "Container resources" + }, + "persistence": { + "type": "object", + "description": "Persistence configuration", + "properties": { + "enabled": { + "type": "boolean", + "default": true + }, + "storageClass": { + "type": "string" + }, + "accessModes": { + "type": "array", + "items": { + "type": "string" + } + }, + "size": { + "type": "string", + "default": "8Gi" + } + } + }, + "service": { + "type": "object", + "description": "Service configuration" + } + } + }, + "replica": { + "type": "object", + "description": "Replica configuration (sentinel mode)", + "properties": { + "replicaCount": { + "type": "integer", + "description": "Number of replica instances", + "default": 2, + "minimum": 0 + }, + "resources": { + "type": "object", + "description": "Container resources" + }, + "persistence": { + "type": "object", + "description": "Persistence configuration", + "properties": { + "enabled": { + "type": "boolean", + "default": true + }, + "storageClass": { + "type": "string" + }, + "accessModes": { + "type": "array", + "items": { + "type": "string" + } + }, + "size": { + "type": "string", + "default": "8Gi" + } + } + }, + "service": { + "type": "object", + "description": "Service configuration" + } + } + }, + "metrics": { + "type": "object", + "description": "Metrics exporter configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable Prometheus metrics exporter", + "default": false + }, + "port": { + "type": "integer", + "description": "Metrics port", + "default": 9121 + }, + "image": { + "type": "object", + "description": "Metrics exporter image", + "properties": { + "registry": { + "type": "string", + "default": "docker.io" + }, + "repository": { + "type": "string", + "default": "oliver006/redis_exporter" + }, + "tag": { + "type": "string", + "default": "v1.80.0" + }, + "pullPolicy": { + "type": "string", + "enum": ["Always", "IfNotPresent", "Never"] + } + } + }, + "resources": { + "type": "object", + "description": "Container resources" + }, + "serviceMonitor": { + "type": "object", + "description": "Prometheus ServiceMonitor configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create ServiceMonitor", + "default": false + }, + "namespace": { + "type": "string", + "description": "ServiceMonitor namespace" + }, + "interval": { + "type": "string", + "description": "Scrape interval" + }, + "scrapeTimeout": { + "type": "string", + "description": "Scrape timeout" + } + } + }, + "podMonitor": { + "type": "object", + "description": "Prometheus PodMonitor configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create PodMonitor", + "default": false + } + } + }, + "prometheusRule": { + "type": "object", + "description": "Prometheus PrometheusRule configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create PrometheusRule", + "default": false + }, + "rules": { + "type": "array", + "description": "Prometheus rules", + "items": { + "type": "object" + } + } + } + } + } + }, + "preUpgradeHook": { + "type": "object", + "description": "Pre-upgrade hook configuration", + "properties": { + "image": { + "type": "object", + "description": "Hook image configuration", + "properties": { + "registry": { + "type": "string", + "default": "docker.io" + }, + "repository": { + "type": "string", + "default": "alpine/k8s" + }, + "tag": { + "type": "string", + "default": "1.31.13" + }, + "pullPolicy": { + "type": "string", + "enum": ["Always", "IfNotPresent", "Never"] + } + } + }, + "resources": { + "type": "object", + "description": "Container resources" + } + } + }, + "volumePermissions": { + "type": "object", + "description": "Volume permissions init container configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable init container for volume permissions", + "default": false + }, + "image": { + "type": "object", + "description": "Init container image", + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + } + } + }, + "resources": { + "type": "object", + "description": "Container resources" + } + } + }, + "sysctls": { + "type": "object", + "description": "Sysctls configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable sysctls init container", + "default": false + } + } + }, + "existingConfigmap": { + "type": "string", + "description": "Name of existing ConfigMap with Valkey configuration" + }, + "extraEnvVars": { + "type": "array", + "description": "Extra environment variables", + "items": { + "type": "object" + } + }, + "extraVolumes": { + "type": "array", + "description": "Extra volumes", + "items": { + "type": "object" + } + }, + "extraVolumeMounts": { + "type": "array", + "description": "Extra volume mounts", + "items": { + "type": "object" + } + }, + "tls": { + "type": "object", + "description": "TLS configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable TLS", + "default": false + }, + "port": { + "type": "integer", + "description": "TLS port", + "default": 6380 + }, + "existingSecret": { + "type": "string", + "description": "Name of existing secret with TLS certificates" + }, + "certFilename": { + "type": "string", + "description": "Certificate filename in secret", + "default": "tls.crt" + }, + "certKeyFilename": { + "type": "string", + "description": "Certificate key filename in secret", + "default": "tls.key" + }, + "caCertFilename": { + "type": "string", + "description": "CA certificate filename in secret", + "default": "ca.crt" + }, + "authClients": { + "type": "boolean", + "description": "Require client authentication", + "default": true + } + } + }, + "extraDeploy": { + "type": "array", + "description": "Extra Kubernetes resources to deploy", + "items": { + "type": "object" + } + }, + "serviceBinding": { + "type": "object", + "description": "Service Binding configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable Service Binding", + "default": false + } + } + }, + "headlessService": { + "type": "object", + "description": "Headless service configuration", + "properties": { + "annotations": { + "type": "object", + "description": "Headless service annotations" + }, + "extraPorts": { + "type": "array", + "description": "Extra ports for headless service", + "items": { + "type": "object" + } + } + } + }, + "podSecurityPolicy": { + "type": "object", + "description": "Pod Security Policy configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable Pod Security Policy", + "default": false + } + } + }, + "diagnosticMode": { + "type": "object", + "description": "Diagnostic mode configuration", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable diagnostic mode", + "default": false + } + } + } + } +} From 2f20dc37dba9db642fb1210f55a1430dc215d8cb Mon Sep 17 00:00:00 2001 From: Julio Caicedo Date: Fri, 28 Nov 2025 14:16:52 -0500 Subject: [PATCH 2/3] ci: add comprehensive GitHub Actions workflow Implement automated testing pipeline with four jobs: - Helm chart linting validation - Template rendering tests across multiple configurations (standalone/sentinel modes) - Chart packaging verification - JSON schema validation for values file Test matrix includes standalone configurations (default, auth, persistence, metrics) and sentinel mode variations with automated manifest validation checks. --- .github/workflows/ci.yml | 102 ++++++++++ .github/workflows/pages.yml | 269 ------------------------- .github/workflows/release.yml | 225 +++++++++++++++++++-- .github/workflows/test.yml | 60 ------ .github/workflows/validate-configs.yml | 109 ---------- 5 files changed, 307 insertions(+), 458 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/pages.yml delete mode 100644 .github/workflows/test.yml delete mode 100644 .github/workflows/validate-configs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cf0b214 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + paths-ignore: + - '*.md' + - 'LICENSE' + +jobs: + lint: + name: Lint Chart + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.16.0 + + - name: Lint chart + run: helm lint . + + test: + name: Test Templates + runs-on: ubuntu-latest + needs: lint + strategy: + matrix: + config: + - name: standalone-default + args: "" + - name: standalone-auth + args: "--set auth.enabled=true --set auth.password=test" + - name: standalone-persistence + args: "--set standalone.persistence.enabled=true --set standalone.persistence.size=10Gi" + - name: standalone-metrics + args: "--set metrics.enabled=true" + - name: sentinel-mode + args: "--set architecture=sentinel --set auth.enabled=true --set auth.password=test" + - name: sentinel-metrics + args: "--set architecture=sentinel --set metrics.enabled=true" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.16.0 + + - name: Test template - ${{ matrix.config.name }} + run: | + helm template test . ${{ matrix.config.args }} > output.yaml + echo "✅ Template generation passed for ${{ matrix.config.name }}" + + - name: Validate manifests - ${{ matrix.config.name }} + run: | + grep -q "kind: StatefulSet" output.yaml || (echo "❌ StatefulSet not found" && exit 1) + grep -q "kind: Service" output.yaml || (echo "❌ Service not found" && exit 1) + grep -q "kind: ConfigMap" output.yaml || (echo "❌ ConfigMap not found" && exit 1) + echo "✅ Manifest validation passed for ${{ matrix.config.name }}" + + package: + name: Package Chart + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.16.0 + + - name: Package chart + run: | + helm package . + ls -la *.tgz + echo "✅ Chart packaging passed" + + schema-validation: + name: Validate Values Schema + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate JSON schema + run: | + if [ -f values.schema.json ]; then + python3 -m json.tool values.schema.json > /dev/null + echo "✅ values.schema.json is valid JSON" + else + echo "⚠️ values.schema.json not found" + fi diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index c8cdc7b..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,269 +0,0 @@ -name: Deploy GitHub Pages - -on: - push: - branches: - - main - paths: - - 'Chart.yaml' - - 'README.md' - - 'artifacthub-repo.yml' - - 'public-key.asc' - - 'values.schema.json' - - '.github/workflows/pages.yml' - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Pages - uses: actions/configure-pages@v4 - continue-on-error: true - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: v3.12.1 - - - name: Import GPG key - if: ${{ env.GPG_PRIVATE_KEY != '' }} - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - run: | - echo "$GPG_PRIVATE_KEY" | gpg --batch --import - # Configure GPG for non-interactive use - echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf - gpg-connect-agent reloadagent /bye || true - - - name: Create Helm repository index - env: - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - run: | - # Create directories - mkdir -p .cr-release-packages - mkdir -p public - - # Download existing index.yaml and .tgz files to preserve history - echo "Downloading existing repository files..." - wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml || echo "No existing index.yaml found" - - # Download existing .tgz files if index exists - if [ -f existing-index.yaml ]; then - echo "Parsing existing chart versions..." - # Extract URLs of existing chart packages from index.yaml and download to both directories - urls=$(grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml || true) - if [ -n "$urls" ]; then - echo "$urls" | while read url; do - if [ -n "$url" ]; then - filename=$(basename "$url") - echo "Downloading existing chart: $filename" - # Download to both .cr-release-packages and public for efficiency - wget -q "$url" -O ".cr-release-packages/$filename" || echo "Failed to download $filename to .cr-release-packages" - wget -q "$url" -O "public/$filename" || echo "Failed to download $filename to public" - fi - done - else - echo "No existing chart packages found in index" - fi - - # Copy existing index to merge later - cp existing-index.yaml .cr-release-packages/ || true - fi - - # Package the current chart (with signing if GPG key is available) - if [ -n "$GPG_PASSPHRASE" ] && gpg --list-secret-keys --keyid-format LONG 2>/dev/null | grep -q "sec"; then - echo "Signing chart with GPG key..." - helm package . -d .cr-release-packages --sign --key "Start Codex" --keyring ~/.gnupg/secring.gpg 2>/dev/null || \ - helm package . -d .cr-release-packages --sign --key "cloud@startcodex.com" --passphrase-file <(echo "$GPG_PASSPHRASE") 2>/dev/null || \ - helm package . -d .cr-release-packages - else - echo "Packaging chart without signature..." - helm package . -d .cr-release-packages - fi - - # Create/update the index with existing and new charts - if [ -f .cr-release-packages/existing-index.yaml ]; then - echo "Merging with existing index..." - helm repo index .cr-release-packages --url https://start-codex.github.io/valkey-helm-chart --merge .cr-release-packages/existing-index.yaml - else - echo "Creating new index..." - helm repo index .cr-release-packages --url https://start-codex.github.io/valkey-helm-chart - fi - - # Copy new chart files to public directory (including .prov files if signed) - if ls .cr-release-packages/*.tgz 1> /dev/null 2>&1; then - cp .cr-release-packages/*.tgz public/ || true - fi - if ls .cr-release-packages/*.prov 1> /dev/null 2>&1; then - cp .cr-release-packages/*.prov public/ || true - echo "Provenance files copied" - fi - - # Copy the updated index - if [ -f .cr-release-packages/index.yaml ]; then - cp .cr-release-packages/index.yaml public/ - fi - - cp README.md public/ || true - cp artifacthub-repo.yml public/ || true - cp public-key.asc public/ || true - - # Verify what we have - echo "Contents of public directory:" - ls -la public/ - - # Show chart versions in the index - if [ -f public/index.yaml ]; then - echo "" - echo "Chart versions in repository:" - grep -A 2 "version:" public/index.yaml | grep "version:" | sort -V || echo "No versions found" - - echo "" - echo "Total charts in repository:" - grep "\.tgz" public/index.yaml | wc -l || echo "0" - fi - - # Create a simple index.html - cat > public/index.html << 'EOF' - - - - Valkey Helm Chart Repository - - - - - -
    - -

    Valkey Helm Chart Repository

    -

    Official Helm chart for Valkey - A high-performance data structure server

    -
    - -
    -

    🚀 Quick Start

    -

    Add this repository to your Helm:

    -
    helm repo add valkey https://start-codex.github.io/valkey-helm-chart
    helm repo update
    - -

    Install Valkey:

    -
    helm install my-valkey valkey/valkey
    -
    - -
    -

    📦 Available Charts

    -
      -
    • valkey - Valkey server with standalone and sentinel modes
    • -
    -
    - -
    -

    🔧 Configuration

    -

    The chart supports both standalone and sentinel architectures:

    -
    # Standalone mode
    helm install valkey valkey/valkey

    # Sentinel mode
    helm install valkey valkey/valkey --set architecture=sentinel
    -
    - -
    -

    📚 Documentation

    -

    For detailed configuration options and examples, visit:

    - -
    - -
    -

    📁 Repository Files

    - -
    - -
    -

    🔐 Chart Verification

    -

    Charts are signed with GPG. To verify:

    -
    wget https://start-codex.github.io/valkey-helm-chart/public-key.asc
    gpg --import public-key.asc
    helm verify valkey-0.1.24.tgz
    -
    - - - EOF - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: public - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - if: github.repository_owner != '' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - continue-on-error: true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b52ee1a..c236286 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,13 +1,32 @@ -name: Release Charts +name: Release on: + push: + branches: + - main + paths: + - 'Chart.yaml' + - 'values.yaml' + - 'values.schema.json' + - 'templates/**' workflow_dispatch: +permissions: + contents: write + pages: write + id-token: write + +concurrency: + group: "release" + cancel-in-progress: false + jobs: release: - permissions: - contents: write + name: Release Chart runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout uses: actions/checkout@v4 @@ -19,29 +38,195 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + - name: Setup Pages + uses: actions/configure-pages@v4 + continue-on-error: true + - name: Install Helm uses: azure/setup-helm@v4 with: - version: v3.12.1 + version: v3.16.0 - - name: Add dependency chart repos + - name: Import GPG key + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | - helm repo add bitnami https://charts.bitnami.com/bitnami + if [ -n "$GPG_PRIVATE_KEY" ]; then + echo "$GPG_PRIVATE_KEY" | gpg --batch --import + echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf + gpg-connect-agent reloadagent /bye || true + echo "GPG_AVAILABLE=true" >> $GITHUB_ENV + else + echo "GPG_AVAILABLE=false" >> $GITHUB_ENV + fi - - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.6.0 - with: - charts_dir: . - skip_existing: true + - name: Lint chart + run: helm lint . + + - name: Build and Package env: - CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - CR_SKIP_EXISTING: true + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: | + mkdir -p .packages + mkdir -p public + + # Download existing repository files + echo "Downloading existing repository..." + wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml || echo "No existing index found" + + # Download existing chart packages + if [ -f existing-index.yaml ]; then + urls=$(grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml || true) + if [ -n "$urls" ]; then + echo "$urls" | while read url; do + if [ -n "$url" ]; then + filename=$(basename "$url") + echo "Downloading: $filename" + wget -q "$url" -O "public/$filename" || true + wget -q "$url" -O ".packages/$filename" || true + fi + done + fi + cp existing-index.yaml .packages/ || true + fi + + # Package chart (with signing if available) + if [ "$GPG_AVAILABLE" = "true" ] && [ -n "$GPG_PASSPHRASE" ]; then + echo "Packaging with GPG signature..." + echo "$GPG_PASSPHRASE" | helm package . -d .packages \ + --sign \ + --key "cloud@startcodex.com" \ + --passphrase-file /dev/stdin || helm package . -d .packages + else + echo "Packaging without signature..." + helm package . -d .packages + fi + + # Generate index + if [ -f .packages/existing-index.yaml ]; then + helm repo index .packages --url https://start-codex.github.io/valkey-helm-chart --merge .packages/existing-index.yaml + else + helm repo index .packages --url https://start-codex.github.io/valkey-helm-chart + fi + + # Copy to public directory + cp .packages/*.tgz public/ || true + cp .packages/*.prov public/ 2>/dev/null || true + cp .packages/index.yaml public/ + + # Copy static files + cp README.md public/ + cp artifacthub-repo.yml public/ + cp public-key.asc public/ - - name: Trigger Pages Workflow - if: github.ref == 'refs/heads/main' + # Show results + echo "" + echo "=== Release Contents ===" + ls -la public/ + echo "" + echo "=== Chart Versions ===" + grep "version:" public/index.yaml | head -20 + + - name: Create index.html run: | - curl -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/actions/workflows/pages.yml/dispatches \ - -d '{"ref":"main"}' \ No newline at end of file + CHART_VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}') + cat > public/index.html << EOF + + + + Valkey Helm Chart Repository + + + + + +
    + Valkey Logo +

    Valkey Helm Chart

    +

    High-performance data structure server for Kubernetes

    + v${CHART_VERSION} + Signed +
    + +
    +

    Quick Start

    +
    + helm repo add valkey https://start-codex.github.io/valkey-helm-chart + helm repo update + helm install my-valkey valkey/valkey +
    +
    + +
    +

    Installation Options

    +
    + # Standalone mode (default) + helm install my-valkey valkey/valkey + + # With authentication + helm install my-valkey valkey/valkey --set auth.enabled=true --set auth.password="secret" + + # Sentinel mode (HA) + helm install my-valkey valkey/valkey --set architecture=sentinel +
    +
    + +
    +

    Verify Chart Signature

    +
    + wget https://start-codex.github.io/valkey-helm-chart/public-key.asc + gpg --import public-key.asc + helm pull valkey/valkey --verify +
    +
    + + + +
    +

    Repository Files

    + +
    + +

    + Made with ❤️ by StartCodex +

    + + + EOF + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 7b85b29..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Test Chart - -on: - workflow_dispatch: - pull_request: - branches: [ main ] - push: - branches: [ main ] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: v3.12.1 - - - name: Lint chart - run: | - helm lint . - echo "✅ Chart lint passed" - - - name: Test chart templates - run: | - echo "Testing standalone mode..." - helm template test . --set architecture=standalone > /dev/null - echo "✅ Standalone template generation passed" - - echo "Testing sentinel mode..." - helm template test . --set architecture=sentinel --set auth.enabled=true --set auth.password=test > /dev/null - echo "✅ Sentinel template generation passed" - - - name: Package chart - run: | - helm package . - ls -la *.tgz - echo "✅ Chart packaging passed" - - - name: Test installation (template only) - run: | - echo "Testing template generation with default values..." - helm template test-valkey . > /dev/null - echo "✅ Template generation with default values passed" - - echo "Testing template generation with custom values..." - cat > test-values.yaml << 'EOF' - architecture: standalone - auth: - enabled: true - password: "testpassword" - standalone: - persistence: - storageClass: "standard" - EOF - helm template test-valkey . -f test-values.yaml > /dev/null - echo "✅ Template generation with custom values passed" \ No newline at end of file diff --git a/.github/workflows/validate-configs.yml b/.github/workflows/validate-configs.yml deleted file mode 100644 index 9398a9e..0000000 --- a/.github/workflows/validate-configs.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Validate Chart Configurations - -on: - workflow_dispatch: - pull_request: - branches: [ main ] - push: - branches: [ main ] - -jobs: - validate-configs: - runs-on: ubuntu-latest - strategy: - matrix: - config: - - name: "standalone-no-auth" - values: | - architecture: standalone - auth: - enabled: false - - name: "standalone-with-auth" - values: | - architecture: standalone - auth: - enabled: true - password: "testpassword" - - name: "standalone-existing-secret" - values: | - architecture: standalone - auth: - enabled: true - existingSecret: "redis-secret" - - name: "standalone-with-persistence" - values: | - architecture: standalone - standalone: - persistence: - enabled: true - storageClass: "longhorn-simple" - size: "10Gi" - - name: "sentinel-mode" - values: | - architecture: sentinel - auth: - enabled: true - password: "testpassword" - sentinel: - replicaCount: 3 - - name: "with-metrics" - values: | - architecture: standalone - metrics: - enabled: true - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: v3.12.1 - - - name: Create test values file - run: | - cat > test-values-${{ matrix.config.name }}.yaml << 'EOF' - ${{ matrix.config.values }} - EOF - - - name: Lint chart with ${{ matrix.config.name }} config - run: | - helm lint . -f test-values-${{ matrix.config.name }}.yaml - echo "✅ Lint passed for ${{ matrix.config.name }}" - - - name: Test template generation for ${{ matrix.config.name }} - run: | - helm template test-${{ matrix.config.name }} . -f test-values-${{ matrix.config.name }}.yaml > output-${{ matrix.config.name }}.yaml - echo "✅ Template generation passed for ${{ matrix.config.name }}" - - - name: Validate generated manifests - run: | - # Check that required resources are generated - if [ "${{ matrix.config.name }}" = "standalone-no-auth" ] || [ "${{ matrix.config.name }}" = "standalone-with-auth" ] || [ "${{ matrix.config.name }}" = "standalone-existing-secret" ] || [ "${{ matrix.config.name }}" = "standalone-with-persistence" ] || [ "${{ matrix.config.name }}" = "with-metrics" ]; then - grep -q "kind: StatefulSet" output-${{ matrix.config.name }}.yaml || (echo "❌ StatefulSet not found" && exit 1) - grep -q "kind: Service" output-${{ matrix.config.name }}.yaml || (echo "❌ Service not found" && exit 1) - grep -q "kind: ConfigMap" output-${{ matrix.config.name }}.yaml || (echo "❌ ConfigMap not found" && exit 1) - fi - - if [ "${{ matrix.config.name }}" = "sentinel-mode" ]; then - grep -q "kind: StatefulSet" output-${{ matrix.config.name }}.yaml || (echo "❌ StatefulSet not found" && exit 1) - # Should have master, replica, and sentinel services - SERVICE_COUNT=$(grep -c "kind: Service" output-${{ matrix.config.name }}.yaml) - if [ "$SERVICE_COUNT" -lt 3 ]; then - echo "❌ Expected at least 3 services for sentinel mode, found $SERVICE_COUNT" - exit 1 - fi - fi - - if [[ "${{ matrix.config.name }}" == *"persistence"* ]]; then - grep -q "volumeClaimTemplates" output-${{ matrix.config.name }}.yaml || (echo "❌ VolumeClaimTemplates not found when persistence enabled" && exit 1) - fi - - echo "✅ Manifest validation passed for ${{ matrix.config.name }}" - - - name: Upload generated manifests - uses: actions/upload-artifact@v4 - with: - name: manifests-${{ matrix.config.name }} - path: output-${{ matrix.config.name }}.yaml \ No newline at end of file From 12398c684ecd735e8b12b87e509ff0f3ad2ea222 Mon Sep 17 00:00:00 2001 From: Julio Caicedo Date: Fri, 28 Nov 2025 14:20:42 -0500 Subject: [PATCH 3/3] ci: optimize workflows and improve error handling Streamline CI/CD pipelines by consolidating jobs, improving test configurations, and enhancing the release process with better error handling and cleaner output formatting. Changes include: - Add workflow_call trigger to enable CI reuse in release workflow - Remove paths-ignore filter to ensure all changes are validated - Consolidate schema validation into lint job from separate job - Add fail-fast: false to test matrix for complete test coverage - Remove standalone-persistence test case from matrix - Simplify test validation output and improve error messages - Merge schema validation into main CI flow - Enhance release workflow with CI dependency - Simplify GPG key import and package process - Improve HTML index generation with cleaner template - Remove redundant steps and optimize file operations --- .github/workflows/ci.yml | 52 +++----- .github/workflows/release.yml | 245 +++++++++++++++------------------- 2 files changed, 128 insertions(+), 169 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf0b214..0051c2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,13 +5,11 @@ on: branches: [main] push: branches: [main] - paths-ignore: - - '*.md' - - 'LICENSE' + workflow_call: jobs: lint: - name: Lint Chart + name: Lint runs-on: ubuntu-latest steps: - name: Checkout @@ -25,19 +23,25 @@ jobs: - name: Lint chart run: helm lint . + - name: Validate JSON schema + run: | + if [ -f values.schema.json ]; then + python3 -m json.tool values.schema.json > /dev/null + echo "✅ values.schema.json is valid" + fi + test: - name: Test Templates + name: Test runs-on: ubuntu-latest needs: lint strategy: + fail-fast: false matrix: config: - name: standalone-default args: "" - name: standalone-auth args: "--set auth.enabled=true --set auth.password=test" - - name: standalone-persistence - args: "--set standalone.persistence.enabled=true --set standalone.persistence.size=10Gi" - name: standalone-metrics args: "--set metrics.enabled=true" - name: sentinel-mode @@ -56,17 +60,16 @@ jobs: - name: Test template - ${{ matrix.config.name }} run: | helm template test . ${{ matrix.config.args }} > output.yaml - echo "✅ Template generation passed for ${{ matrix.config.name }}" - - name: Validate manifests - ${{ matrix.config.name }} - run: | - grep -q "kind: StatefulSet" output.yaml || (echo "❌ StatefulSet not found" && exit 1) - grep -q "kind: Service" output.yaml || (echo "❌ Service not found" && exit 1) - grep -q "kind: ConfigMap" output.yaml || (echo "❌ ConfigMap not found" && exit 1) - echo "✅ Manifest validation passed for ${{ matrix.config.name }}" + # Validate required resources + grep -q "kind: StatefulSet" output.yaml + grep -q "kind: Service" output.yaml + grep -q "kind: ConfigMap" output.yaml + + echo "✅ ${{ matrix.config.name }} passed" package: - name: Package Chart + name: Package runs-on: ubuntu-latest needs: test steps: @@ -82,21 +85,4 @@ jobs: run: | helm package . ls -la *.tgz - echo "✅ Chart packaging passed" - - schema-validation: - name: Validate Values Schema - runs-on: ubuntu-latest - needs: lint - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Validate JSON schema - run: | - if [ -f values.schema.json ]; then - python3 -m json.tool values.schema.json > /dev/null - echo "✅ values.schema.json is valid JSON" - else - echo "⚠️ values.schema.json not found" - fi + echo "✅ Package successful" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c236286..df414a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,13 +2,14 @@ name: Release on: push: - branches: - - main + branches: [main] paths: - 'Chart.yaml' - 'values.yaml' - 'values.schema.json' - 'templates/**' + - 'public-key.asc' + - 'artifacthub-repo.yml' workflow_dispatch: permissions: @@ -17,13 +18,18 @@ permissions: id-token: write concurrency: - group: "release" + group: release cancel-in-progress: false jobs: + ci: + name: CI + uses: ./.github/workflows/ci.yml + release: - name: Release Chart + name: Release & Deploy runs-on: ubuntu-latest + needs: ci environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} @@ -33,14 +39,8 @@ jobs: with: fetch-depth: 0 - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Setup Pages uses: actions/configure-pages@v4 - continue-on-error: true - name: Install Helm uses: azure/setup-helm@v4 @@ -50,7 +50,6 @@ jobs: - name: Import GPG key env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | if [ -n "$GPG_PRIVATE_KEY" ]; then echo "$GPG_PRIVATE_KEY" | gpg --batch --import @@ -61,45 +60,30 @@ jobs: echo "GPG_AVAILABLE=false" >> $GITHUB_ENV fi - - name: Lint chart - run: helm lint . - - - name: Build and Package + - name: Package Chart env: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} run: | - mkdir -p .packages - mkdir -p public + mkdir -p .packages public - # Download existing repository files - echo "Downloading existing repository..." - wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml || echo "No existing index found" + # Download existing charts + wget -q https://start-codex.github.io/valkey-helm-chart/index.yaml -O existing-index.yaml 2>/dev/null || true - # Download existing chart packages if [ -f existing-index.yaml ]; then - urls=$(grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml || true) - if [ -n "$urls" ]; then - echo "$urls" | while read url; do - if [ -n "$url" ]; then - filename=$(basename "$url") - echo "Downloading: $filename" - wget -q "$url" -O "public/$filename" || true - wget -q "$url" -O ".packages/$filename" || true - fi - done - fi - cp existing-index.yaml .packages/ || true + grep -o 'https://start-codex.github.io/valkey-helm-chart/[^"]*\.tgz' existing-index.yaml | while read url; do + [ -n "$url" ] && wget -q "$url" -O "public/$(basename $url)" || true + done + cp existing-index.yaml .packages/ fi - # Package chart (with signing if available) + # Package with signature if GPG available if [ "$GPG_AVAILABLE" = "true" ] && [ -n "$GPG_PASSPHRASE" ]; then - echo "Packaging with GPG signature..." + echo "📦 Packaging with GPG signature..." echo "$GPG_PASSPHRASE" | helm package . -d .packages \ - --sign \ - --key "cloud@startcodex.com" \ - --passphrase-file /dev/stdin || helm package . -d .packages + --sign --key "cloud@startcodex.com" --passphrase-file /dev/stdin \ + || helm package . -d .packages else - echo "Packaging without signature..." + echo "📦 Packaging without signature..." helm package . -d .packages fi @@ -110,117 +94,106 @@ jobs: helm repo index .packages --url https://start-codex.github.io/valkey-helm-chart fi - # Copy to public directory - cp .packages/*.tgz public/ || true + # Copy files to public + cp .packages/*.tgz public/ cp .packages/*.prov public/ 2>/dev/null || true cp .packages/index.yaml public/ + cp README.md artifacthub-repo.yml public-key.asc public/ - # Copy static files - cp README.md public/ - cp artifacthub-repo.yml public/ - cp public-key.asc public/ - - # Show results - echo "" - echo "=== Release Contents ===" - ls -la public/ - echo "" - echo "=== Chart Versions ===" - grep "version:" public/index.yaml | head -20 - - - name: Create index.html + - name: Generate index.html run: | - CHART_VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}') - cat > public/index.html << EOF + VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}') + cat > public/index.html << 'HTMLEOF' - + - Valkey Helm Chart Repository - - - + + + Valkey Helm Chart + -
    - Valkey Logo -

    Valkey Helm Chart

    -

    High-performance data structure server for Kubernetes

    - v${CHART_VERSION} - Signed +
    + Valkey +

    Valkey Helm Chart

    +

    High-performance data structure server for Kubernetes

    +
    + v${VERSION} + Signed
    +
    -
    -

    Quick Start

    -
    - helm repo add valkey https://start-codex.github.io/valkey-helm-chart +
    +

    Quick Start

    +
    helm repo add valkey https://start-codex.github.io/valkey-helm-chart
               helm repo update
    -          helm install my-valkey valkey/valkey
    -                  
    -
    - -
    -

    Installation Options

    -
    - # Standalone mode (default) - helm install my-valkey valkey/valkey - - # With authentication - helm install my-valkey valkey/valkey --set auth.enabled=true --set auth.password="secret" - - # Sentinel mode (HA) - helm install my-valkey valkey/valkey --set architecture=sentinel -
    -
    - -
    -

    Verify Chart Signature

    -
    - wget https://start-codex.github.io/valkey-helm-chart/public-key.asc + helm install my-valkey valkey/valkey +
    + +
    +

    Installation Examples

    +
    # With authentication
    +          helm install my-valkey valkey/valkey \
    +            --set auth.enabled=true \
    +            --set auth.password="your-password"
    +
    +          # High Availability (Sentinel)
    +          helm install my-valkey valkey/valkey \
    +            --set architecture=sentinel
    +
    + +
    +

    Verify Signature

    +
    wget https://start-codex.github.io/valkey-helm-chart/public-key.asc
               gpg --import public-key.asc
    -          helm pull valkey/valkey --verify
    -                  
    -
    - - - -
    -

    Repository Files

    - -
    - -

    - Made with ❤️ by StartCodex -

    + helm pull valkey/valkey --verify
    +
    + + + + + + - EOF + HTMLEOF + sed -i "s/\${VERSION}/$VERSION/g" public/index.html - name: Upload Pages artifact uses: actions/upload-pages-artifact@v3