Skip to content

feat(hooks): add kustomize, kube:apply, and tekton:pipelinerun commands - #26

Merged
Xe merged 10 commits into
mainfrom
Xe/tekton-kubernetes-commands
Sep 30, 2026
Merged

Xe merged 10 commits into
mainfrom
Xe/tekton-kubernetes-commands

Conversation

@Xe

@Xe Xe commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Push hooks and the SSH sh shell get kustomize (an embedded WASI build, tracked with Git LFS), plus kube:apply (Server-Side Apply) and tekton:pipelinerun (a new run for each pushed commit, from a generateName template).
  • The new -allow-kubernetes flag is off by default. It gives the two Kubernetes commands the pod's in-cluster ServiceAccount, and manifest/tekton-rbac/ is an example Role that allows only Tekton kinds in namespace ci.

Details

The API client in internal/kube is small and hand-written instead of client-go, and the run's provenance (repo, ref, commit) comes from the daemon, not from OBJGIT_* variables that a script can change. CI and the Dockerfile now pull LFS objects and stop the build if the embedded module is a pointer file. See docs/usage/kubernetes-hooks.md for the hook recipe, the RBAC, and the security warning.

Test plan

  • Code compiles with go build ./...
  • Code formatted with npm run format (goimports and prettier --check on the changed files)
  • Manual testing: in a kind cluster, kubectl auth can-i checks, TestCluster, and two pushes through objgitd created the correct PipelineRuns

Xe added 10 commits September 25, 2026 14:10
Hooks need to render the Kustomize bundle a repository keeps its Tekton
Pipeline in. The module is tracked with Git LFS. Unlike the generic
Kefka adapter, this one sets the guest PWD so relative paths resolve
against the shell directory, and it closes a running instance when the
hook context ends.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
Hook commands need Server-Side Apply and create against the pod's
ServiceAccount. client-go would bring k8s.io/api and a large dependency
tree for three calls, so this client does per-apiVersion discovery,
apply with field manager objgitd and no forced conflicts, and create.
It rereads the ServiceAccount token for each request, because bound
tokens rotate. kubetest is a fake API server for the tests.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
kube:apply reads a YAML stream, checks every object, then applies each
one with Server-Side Apply. tekton:pipelinerun creates a new run from a
generateName template, with the pushed commit and branch in its params
and repo, ref, and commit metadata for a later lookup. Without a client
the commands are stubs that say how to turn them on.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
Push hooks and the SSH sh command now have kustomize, kube:apply, and
tekton:pipelinerun. The new -allow-kubernetes flag, off by default,
gives the two Kubernetes commands the pod's in-cluster ServiceAccount.
It needs -allow-hooks, and objgitd exits at startup when the in-cluster
config cannot load.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
kustomize.wasm is an LFS object. A checkout without LFS embeds the
pointer file, so CI checks out with lfs: true and the Docker build
stops when the file is not WebAssembly.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
The Role lets ServiceAccount objgit/objgitd create and patch Pipelines
and Tasks, and create PipelineRuns, in namespace ci. It lives outside
manifest/ because that kustomization's namespace field would move the
RoleBinding. TestCluster checks the client against a real API server
when a cluster is configured. A kind run showed that Server-Side Apply
needs patch even for a new object, so the fake API server now checks
that too.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
Add a usage page with the hook recipe, the flag, the RBAC, the command
reference, and the security warning. Link it from the hook, Kubernetes,
and architecture pages, and add ALLOW_KUBERNETES=false to the example
deployment.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
A script can change the OBJGIT_* variables, so a push to one repository
could make its cluster changes look like another's in the audit log and
in the PipelineRun annotations. The commands now take a kube.Origin
from the daemon. The branch parameter is only set for a branch, so the
SSH sh shell on a tag or commit keeps the template value.

Also refuse an apiVersion that is not a plain group and version, since
an escaped slash or a query string there reaches a different API path,
and reject a document separator with content after it instead of
dropping the documents that follow.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
The embedded kustomize.wasm added 24 MB to the objgitd binary, and each
new program would add more. Hook commands now come from .wasm files in
the directories of -wasm-path (default /app/wasm/bin and
/usr/libexec/objgit/bin), so an administrator can add a program to an
image or a volume without a new daemon build.

The directories are listed once at startup, and the first directory
with a name wins, as in PATH. Each program is read and compiled on its
first run. -wasm-cache-dir keeps the compiled code on disk, which drops
the first kustomize run after a restart from about 4 s to 0.15 s. The
manifest puts this cache on the existing PVC.

kustomize.wasm moves to bin/ (still Git LFS), and the image copies bin/
to /usr/libexec/objgit/bin. The stripped binary shrinks from 79.5 MB to
55.0 MB.

Assisted-by: Claude Opus 5.5 via Claude Code
Signed-off-by: Xe Iaso <xe@tigrisdata.com>
…commands

Signed-off-by: Xe Iaso <xe@tigrisdata.com>

# Conflicts:
#	AGENTS.md
@Xe
Xe merged commit b60f728 into main Sep 30, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant