porchctl CLI Architecture#
porchctl is the CLI for Porch (Package Orchestration). Its commands live under pkg/cli/commands/rpkg/, organized as one directory per subcommand plus a shared docs/ package and utility package .
Directory Layout#
pkg/cli/commands/rpkg/
├── approve/ # porchctl rpkg approve
├── clone/ # porchctl rpkg clone
├── copy/ # porchctl rpkg copy
├── del/ # porchctl rpkg del
├── docs/ # shared documentation strings (docs.go)
├── get/ # porchctl rpkg get
├── init/ # porchctl rpkg init
├── propose/ # porchctl rpkg propose
├── proposedelete/ # porchctl rpkg propose-delete
├── pull/ # porchctl rpkg pull
├── push/ # porchctl rpkg push
├── reject/ # porchctl rpkg reject
├── upgrade/ # porchctl rpkg upgrade
├── util/ # shared CLI utilities
├── rpkgcmd.go # top-level rpkg command; wires all subcommands
└── rpkgcmd_test.go
The top-level rpkg command is assembled in rpkgcmd.go, which calls each subcommand's NewCommand() and adds it via cobra.Command.AddCommand() .
docs.go vs. command.go Separation#
Every subcommand splits its concerns across two files:
| File | Responsibility |
|---|---|
docs/docs.go | Human-readable help text: Short, Long, and Examples string variables for every subcommand and its flags |
<subcommand>/command.go | Cobra Command struct construction, flag registration via cmd.Flags(), validation logic, and execution |
docs/docs.go is a single shared file (349 lines, originally generated by mdtogo but now manually maintained) that exports named string constants — e.g. GetShort, GetLong, GetExamples, CloneShort, CloneLong, etc. . Each string contains the full prose description of the command, its arguments, flags with extended constraint text, and example invocations.
command.go imports the docs package and wires the strings into the Cobra command at construction time :
cmd := &cobra.Command{
Short: docs.GetShort,
Long: docs.GetShort + "\n" + docs.GetLong,
Example: docs.GetExamples,
...
}
Flag registration happens immediately after, also in command.go :
cmd.Flags().StringVar(&r.packageName, "name", "", "...")
cmd.Flags().BoolVar(&r.showKptfile, "show-kptfile", false, "...")
Note the two-layer flag description:
- The inline string in
cmd.Flags().BoolVar(...)provides a one-line hint shown in--help. - The full constraint text (e.g., "Cannot be combined with
--name,--revision,--workspace, or--all-namespaces") lives only indocs.go.
A concrete example is --show-kptfile on rpkg get, added in PR #513: the long description and constraint rules were added to docs/docs.go, while validation logic (preRunShowKptfile) and flag binding were added to get/command.go .
Similarly, --subpackage-dir on rpkg clone and rpkg upgrade documents flag constraints in docs.go while the flag registration and execution path split live in their respective command.go files .
Branch Management Implications#
Because docs.go and each command.go are separate files, cherry-picking between branches requires picking both files together. Omitting either half produces mismatches:
- Docs without implementation: A flag appears in
--helpoutput but calling it returns an error or is silently ignored. - Implementation without docs: A flag is functional but invisible in the help text, making it undiscoverable.
This played out concretely during PR #1027, which cherry-picked subpackage clone/upgrade support to the 1.5 branch . The cherry-pick had to pull in changes from five upstream PRs:
- PR #1022 —
porchctlCLI implementation (clone/command.go,upgrade/command.go) - PR #1038 — flag documentation in
docs/docs.go
Because PR #1038 was initially documentation-only (the implementation arrived separately in PR #1022), cherry-picking only one of these to a release branch would have left the feature incomplete .
Rule of thumb when cherry-picking or reviewing: treat docs/docs.go and the relevant command.go as a single logical unit for any flag addition or change.
Key Source Files#
| File | Link |
|---|---|
| Shared docs | pkg/cli/commands/rpkg/docs/docs.go |
| Top-level rpkg wiring | pkg/cli/commands/rpkg/rpkgcmd.go |
rpkg get implementation | pkg/cli/commands/rpkg/get/command.go |
rpkg clone implementation | pkg/cli/commands/rpkg/clone/command.go |
rpkg upgrade implementation | pkg/cli/commands/rpkg/upgrade/command.go |
Reference PRs: