Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/workflows/chart-tooling.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Chart tooling

on: pull_request

permissions:
contents: read

env:
HELM_DOCS_VERSION: 1.14.2
SCHEMA_PLUGIN_VERSION: v2.2.0

jobs:
lint:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
helm: ["v3.17.0", "v4.3.0"]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
with:
version: ${{ matrix.helm }}

- run: hooks/assert-helm-version.sh ${{ matrix.helm }}

- run: hooks/helm-lint.sh

schema:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
helm: ["v3.17.0", "v4.3.0"]
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
with:
version: ${{ matrix.helm }}

- run: hooks/assert-helm-version.sh ${{ matrix.helm }}

- run: hooks/install-schema-plugin.sh ${{ env.SCHEMA_PLUGIN_VERSION }}

- run: hooks/helm-schema.sh

# Derived the same way the Makefile derives $(CHARTS), not hardcoded:
# a chart's values.schema.json only gets checked here if this step
# actually finds it.
- id: charts
run: |
shopt -s nullglob
paths=()
for f in */Chart.yaml; do
paths+=("${f%/Chart.yaml}/values.schema.json")
done
echo "paths=${paths[*]}" >> "$GITHUB_OUTPUT"

- run: |
hooks/check-drift.sh "values.schema.json" "make schema" \
--tool "helm schema $(helm plugin list | awk '$1=="schema"{print $2}') (repo pins ${SCHEMA_PLUGIN_VERSION#v})" \
${{ steps.charts.outputs.paths }}

docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- run: |
asset="helm-docs_${HELM_DOCS_VERSION}_Linux_x86_64.tar.gz"
expected="$(awk -v f="$asset" '$2==f{print $1}' hooks/helm-docs-checksums.txt)"
[ -n "$expected" ] || { echo "no pinned checksum for $asset in hooks/helm-docs-checksums.txt" >&2; exit 1; }

curl -fsSL "https://github.com/norwoodj/helm-docs/releases/download/v${HELM_DOCS_VERSION}/${asset}" -o "/tmp/$asset"
actual="$(sha256sum "/tmp/$asset" | awk '{print $1}')"
if [ "$actual" != "$expected" ]; then
echo "helm-docs checksum mismatch: expected $expected, got $actual" >&2
exit 1
fi

sudo tar -xz -C /usr/local/bin -f "/tmp/$asset" helm-docs

- run: hooks/helm-docs.sh

# Derived the same way the Makefile derives $(CHARTS), not hardcoded:
# a chart's README.md only gets checked here if this step actually
# finds it.
- id: charts
run: |
shopt -s nullglob
paths=()
for f in */Chart.yaml; do
paths+=("${f%/Chart.yaml}/README.md")
done
echo "paths=${paths[*]}" >> "$GITHUB_OUTPUT"

- run: |
hooks/check-drift.sh "README.md" "make docs" \
--tool "helm-docs $(helm-docs --version | awk '{print $NF}') (repo pins ${HELM_DOCS_VERSION})" \
${{ steps.charts.outputs.paths }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@

.serena/cache/
reviews/

# Contract working files are local-only in this repository — see CLAUDE.md.
.claude/contracts/
tests/contract/

__pycache__/
23 changes: 23 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
repos:
- repo: local
hooks:
- id: helm-schema
name: Helm values schema
entry: hooks/helm-schema.sh
language: script
pass_filenames: false
files: ^[^/]+/(values\.yaml|\.schema\.yaml|schemas/.*\.json)$

- id: helm-docs
name: Helm docs
entry: hooks/helm-docs.sh
language: script
pass_filenames: false
files: ^[^/]+/(values\.yaml|Chart\.yaml|README\.md\.gotmpl)$

- id: helm-lint
name: Helm lint
entry: hooks/helm-lint.sh
language: script
pass_filenames: false
files: ^[^/]+/(templates/.*|values\.yaml|values\.schema\.json|Chart\.yaml)$|^ci/linting/
27 changes: 27 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,30 @@ all.
this file already does above for the `extraEnv` exemption). Ticket numbers and links to
in-progress design artifacts belong in the commit message and PR description, not in
repo-committed files.

## Contract working files stay out of this repository

Agent workflows that produce a contract, design document, requirements document or validation
records under `.claude/contracts/` must keep those files **local and untracked here**.
`.gitignore` excludes the directory; do not re-include it, and do not commit its contents.

This overrides the default those workflows ship with, which is to commit the contract as a
shared audit trail. That default is right for a private repository and wrong for this one.

**Why:** those documents are dense with exactly what the section above forbids — Jira keys,
internal tooling paths, and design rationale written for people inside the team. Committing
them here publishes all of it, and the audit-trail argument for doing so does not outweigh
that when the repository is public. The evidence that matters to an outside reader is the
tests in `tests/`, which stand on their own.

**How to apply:** let the contract live at `.claude/contracts/<slug>/` on disk so the tooling
keeps working unchanged, and carry any reasoning a future reader needs into the commit message,
the pull request description, or this file — restated in prose, never as a pointer to a path
that does not exist in the repository.

The same applies to the executable probes those workflows generate, which live under
`tests/contract/` and are gitignored alongside the contract. They are machinery for grading one
piece of work against one agreement, not a suite this repository runs: nothing invokes them, they
need two Helm majors on `PATH` to run at all, and their headers are written in the contract's own
vocabulary. `tests/` otherwise remains the right place for tests this repository does run —
`helm unittest` suites under `<chart>/tests/` are committed and are what CI executes.
37 changes: 37 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Charts are derived, not listed, so a third chart is picked up without
# anyone remembering to edit this file.
CHARTS := $(patsubst %/Chart.yaml,%,$(wildcard */Chart.yaml))

export HELM_DOCS_VERSION := 1.14.2
export SCHEMA_PLUGIN_VERSION := v2.2.0
export PRE_COMMIT_VERSION := 4.6.2

.PHONY: setup lint schema docs test check

## Install the pinned toolchain and register the pre-commit hooks. Idempotent.
setup:
@hooks/setup.sh

## helm lint every chart, with defaults and with its ci/linting overrides.
lint:
@hooks/helm-lint.sh

## Regenerate every chart's values.schema.json from its .schema.yaml.
schema:
@hooks/helm-schema.sh

## Regenerate every chart's README.md from its README.md.gotmpl + values.yaml.
docs:
@hooks/helm-docs.sh

## Run the helm unittest suites (same suites .github/workflows/unittest.yml runs).
test:
@set -e; $(foreach c,$(CHARTS),helm unittest $(c);)

## schema, docs and lint all read and (re)write the same chart directories;
## running them concurrently under `make -j` would race on that shared state.
.NOTPARALLEL: check

## Everything a PR will be checked for, in the order CI runs it.
check: schema docs lint
@git diff --exit-code -- $(addsuffix /values.schema.json,$(CHARTS)) $(addsuffix /README.md,$(CHARTS))
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,49 @@ helm install gremlin-integrations gremlin/gremlin-integrations \
For more detailed instructions, see the chart's
documentation [here](https://github.com/gremlin/helm/blob/master/gremlin-integrations/README.md).

## Contributing

This repo lints, schema-validates, and generates docs for its charts via a `Makefile`. To get set
up:

```shell
make setup
```

This installs the pinned toolchain (`helm-docs`, the `helm schema` plugin, `pre-commit`) and
registers the pre-commit hooks - each idempotently, and it never replaces or upgrades a tool
you've already installed at a different version (you'll get a warning instead, naming the
version found and the version this repo pins).

The `helm schema` plugin install disables Helm 4's plugin signature verification
(`--verify=false`): its upstream, [losisin/helm-values-schema-json](https://github.com/losisin/helm-values-schema-json),
publishes no signed release for Helm to verify against, so this is the only way to install it at
all, not a shortcut taken instead of a safer option. `make setup` prints this at install time too.
The `helm-docs` download is checksummed against a pinned SHA-256 before it's extracted, so that
one isn't in the same position.

Before committing, or at any time:

```shell
make check
```

runs the same schema, docs and lint checks the `chart-tooling` workflow runs, in that order:
regenerating each chart's `values.schema.json` and `README.md`, then linting. It does not run the
unit test suites - CI checks those separately (`.github/workflows/unittest.yml`), so run
`make test` too before pushing. The pre-commit hooks run `check`'s steps automatically on
`git commit`.

Individual targets:

| Target | What it does |
|---|---|
| `make lint` | `helm lint --strict` on every chart, with defaults and with its `ci/linting` override |
| `make schema` | Regenerates every chart's `values.schema.json` from its `.schema.yaml` |
| `make docs` | Regenerates every chart's `README.md` from its `README.md.gotmpl` |
| `make test` | Runs the `helm unittest` suites |
| `make check` | `schema`, `docs`, `lint`, then fails if regeneration produced an uncommitted diff |

## Reporting Issues

Please report all issues [here](https://support-site.gremlin.com/).
28 changes: 28 additions & 0 deletions ci/linting/gremlin-integrations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# hooks/helm-lint.sh's second pass for the gremlin-integrations chart.
# Exercises the paths stock defaults leave dark: a managed secret, the
# client mTLS identity Secret, and the ssl.certFile Secret.
#
# The cert/key values below are deliberately NOT real PEM. The chart only
# base64-encodes them into a Secret without parsing them, so a placeholder
# exercises the "material supplied" render path identically - and keeps a
# private key, throwaway or not, out of a public repository.
gremlin:
secret:
managed: true
type: secret
teamID: "11111111-1111-1111-1111-111111111111"
teamSecret: "00000000-0000-0000-0000-000000000000"

tls:
identity:
enabled: true
createSecret:
name: gremlin-tls-identity
cert: |
lint-placeholder-certificate-not-real-pem
key: |
lint-placeholder-key-not-real-pem

ssl:
certFile: |
lint-placeholder-certificate-not-real-pem
62 changes: 62 additions & 0 deletions ci/linting/gremlin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# hooks/helm-lint.sh's second pass for the gremlin chart. Exercises the
# paths stock defaults leave dark: a managed secret, the PodSecurityPolicy
# and SecurityContextConstraints objects, the seccomp profile ConfigMap, the
# client mTLS identity Secrets, the apparmor ConfigMap, and the per-vendor
# GPU DaemonSets.
#
# The cert/key values below are deliberately NOT real PEM. The chart only
# base64-encodes them into a Secret without parsing them, so a placeholder
# exercises the "material supplied" render path identically - and keeps a
# private key, throwaway or not, out of a public repository.
gremlin:
secret:
managed: true
type: secret
teamID: "11111111-1111-1111-1111-111111111111"
clusterID: "lint-cluster"
teamSecret: "00000000-0000-0000-0000-000000000000"

# installApparmorProfile is deliberately left at its default (false) here:
# gremlin/templates/_helpers.tpl's pspApiVersion helper hard-fails a
# render that combines it with podSecurity.podSecurityPolicy.create
# (verified - helm template exits 1, though helm lint does not surface
# that failure). apparmor alone still exercises the blank-sentinel
# hazard without tripping that combination.
apparmor: unconfined

podSecurity:
podSecurityPolicy:
create: true
securityContextConstraints:
create: true
seccomp:
enabled: true
profile: "localhost/gremlin"

tls:
identity:
enabled: true
createSecret:
name: gremlin-tls-identity
cert: |
lint-placeholder-certificate-not-real-pem
key: |
lint-placeholder-key-not-real-pem

gpu:
enabled: true

chao:
tls:
identity:
enabled: true
createSecret:
name: chao-tls-identity
cert: |
lint-placeholder-certificate-not-real-pem
key: |
lint-placeholder-key-not-real-pem

ssl:
certFile: |
lint-placeholder-certificate-not-real-pem
17 changes: 17 additions & 0 deletions gremlin-integrations/.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
input:
- values.yaml

draft: 2020
indent: 2
output: values.schema.json

useHelmDocs: true

bundle: true
bundleRoot: "."
bundleWithoutID: false

schemaRoot:
id: gremlin-integrations
title: Gremlin Integrations Chart Schema
description: Schema for the Gremlin integrations Helm values
Loading
Loading