Git URL Parsing in kubectl-kyverno#
Overview#
The kubectl-kyverno CLI supports loading policies and test cases directly from git repositories over HTTP/HTTPS. Git URL detection and parsing is used in the apply and test commands to distinguish remote git URLs from local filesystem paths, clone the repository into an in-memory filesystem, and extract YAML policy files from it.
Known limitation: The current implementation assumes all git repository URLs follow the flat https://<host>/<owner>/<repo> model and fails for GitLab repositories nested under groups/subgroups (e.g., https://gitlab.example.com/group/subgroup/team/project.git). See issue #16925.
Git Detection#
source.IsGit() delegates to source.IsHttp(), which matches any http:// or https:// URL. There is no hostname check β any HTTPS URL is treated as a git source.
URL Parsing Logic#
Both apply and test commands share the same multi-step parsing pattern. The relevant call sites are loadPolicies() in the apply command and loadTest() in the test command.
Step 1 β Parse and split: The URL is parsed with url.Parse() and the path (minus leading /) is split on / into pathElems .
Step 2 β Validate minimum length: If len(pathElems) <= 1 the path cannot encode both an owner and a repository, so an error is returned .
Step 3 β Reconstruct repository URL (the bug-prone step): Only pathElems[0] and pathElems[1] are kept:
gitSourceURL.Path = strings.Join([]string{pathElems[0], pathElems[1]}, "/")
repoURL := gitSourceURL.String()
Additional segments are silently discarded. This works for GitHub (owner/repo) but truncates GitLab URLs with nested groups.
Step 4 β Resolve branch and in-repo path:
GetGitBranchOrPolicyPaths(gitBranch, repoURL, path) determines:
- If
--git-branchis set: the remaining path afterrepoURLbecomes the in-repo directory to scan for YAMLs. - If
--git-branchis not set: the first path segment afterrepoURLis treated as the branch name, and the in-repo scan starts at/. - Falls back to
"main"if the branch cannot be extracted.
Repository Cloning#
gitutils.Clone() uses the go-git library with:
- In-memory storage (
memory.NewStorage()) and abilly.Filesystemβ no disk writes. - Shallow clone (
Depth: 1,SingleBranch: true) for efficiency. - Optional HTTP Basic Auth (username + password), enabled only when both are non-empty.
The Cloner field on ApplyCommandConfig is a gitutils.CloneFunc that can be replaced in tests to avoid real network calls.
After cloning, gitutils.ListYamls(fs, gitPathToYamls) recursively walks the in-memory filesystem and returns all .yaml/.yml files under the target path.
CLI Flags#
| Flag | Command | Description |
|---|---|---|
--git-branch / -b | apply, test | Explicitly set the branch; remaining path becomes in-repo directory |
--username | apply | HTTP Basic Auth username |
--password | apply | HTTP Basic Auth password |
Known Issue: GitLab Nested Groups#
Bug: #16925 (also previously #12513)
For a URL like https://gitlab.example.com/group/subgroup/team/project.git, the parser:
- Splits path β
["group", "subgroup", "team", "project.git"] - Keeps only
["group", "subgroup"] - Attempts to clone
https://gitlab.example.com/group/subgroupβ repository not found
The fix requires preserving enough path segments to identify the actual repository. For GitLab, this means detecting where the repo ends and the branch/directory begins, rather than always assuming position [0]/[1].
Key Source Files#
| File | Purpose |
|---|---|
cmd/cli/kubectl-kyverno/source/git.go | IsGit() detection |
cmd/cli/kubectl-kyverno/commands/apply/command.go | Git URL parsing + clone in apply |
cmd/cli/kubectl-kyverno/commands/test/load.go | Git URL parsing + clone in test |
cmd/cli/kubectl-kyverno/utils/common/common.go | GetGitBranchOrPolicyPaths() |
pkg/utils/git/git.go | Clone(), ListYamls() |