Skip to content

Commit 65cbfc2

Browse files
committed
korg: add userinfo command for org and OWNERS lookup
Adds `korg userinfo <user>...` to answer the recurring question this repo exists for: which Kubernetes orgs is a user in, what role, and where do they appear in OWNERS files. Sources: - GitHub user API for company / profile - config/<org>/org.yaml (Members / Admins) for k8s org membership - cs.k8s.io (hound) for OWNERS file references Behavior: - Honors GITHUB_TOKEN / GH_TOKEN to avoid the 60/hr unauth limit - Per-request context with a 30s timeout; SIGINT cancels in flight - Multi-user batches run concurrently (errgroup, bounded to 4) with output preserved in input order - One bad username does not kill the batch; per-user errors render and the process exits non-zero - Hound failures degrade to a warning instead of dropping org info - OWNERS results render as an aligned REPO / PATH / URL table; URL uses /blob/HEAD/ so the link resolves to the default branch - `--output json` for scripting Dependencies: - google/go-github v17 (untagged) -> v88 - spf13/cobra v0.0.5 -> v1.10.2 (for ExecuteContext) - adds golang.org/x/sync for errgroup - go directive bumped to 1.22+ Review fixes: - OWNERS/OWNERS_ALIASES search now matches "- <user>" as a literal YAML list item and skips commented-out lines, cutting false positives from prose mentions or substring matches - Each hit is further confirmed against the file's real content via `gh api` to exclude emeritus-only entries; toggle with --verify-owners - Fixed a double error-print in main() and dropped the unused ctx param on newGitHubClient - JSON output no longer silently drops per-user failures (entries now carry username + error instead of vanishing) - Copyright headers normalized to match repo convention Review fixes (round 2): - The "- <user>" match now requires the YAML list item to equal the username exactly, closing a substring false-positive gap (e.g. "- alice-bot" matching a search for "alice") that only mattered with --verify-owners=false - JSON per-user error entries no longer assume errs[i] is non-nil - Added coverage for the default text-mode renderer and for a mixed-batch run (success + failure users together) to exercise output ordering Signed-off-by: Nabarun Pal <pal.nabarun95@gmail.com>
1 parent 3a5430e commit 65cbfc2

5 files changed

Lines changed: 1112 additions & 8 deletions

File tree

cmd/korg/korg.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"os"
23+
"os/signal"
2224
"path/filepath"
2325
"strings"
26+
"syscall"
2427

2528
"github.com/spf13/cobra"
2629
)
@@ -58,6 +61,27 @@ Note: Removing from teams is currently unsupported.
5861
`
5962

6063
auditHelpText = "Audit GitHub org members"
64+
65+
userinfoHelpText = `
66+
Gets k8s org membership, GitHub profile, and OWNERS file references for user(s).
67+
68+
Honors GITHUB_TOKEN / GH_TOKEN for authenticated GitHub API access.
69+
70+
A failed per-user lookup does not omit that user from the output: in text mode
71+
it renders an ERROR block, and in JSON mode it appears with only "username"
72+
and "error" populated. Check the exit code to detect any failures.
73+
74+
By default, each OWNERS/OWNERS_ALIASES hit is confirmed against the file's
75+
actual content (via the gh CLI) to exclude emeritus-only entries; this costs
76+
one "gh api" call per hit and requires the gh CLI to be installed and
77+
authenticated. Pass --verify-owners=false to skip this and use hound's raw
78+
search hits as-is.
79+
80+
korg userinfo <github username>
81+
korg userinfo <github username1> <github username2> <github username3> ...
82+
korg userinfo --output json <github username>
83+
korg userinfo --verify-owners=false <github username>
84+
`
6185
)
6286

6387
type Options struct {
@@ -222,7 +246,32 @@ func main() {
222246
rootCmd.AddCommand(removeCmd)
223247
rootCmd.AddCommand(auditCmd)
224248

225-
if err := rootCmd.Execute(); err != nil {
249+
var (
250+
outputFormat string
251+
verifyOwners bool
252+
)
253+
userInfoCmd := &cobra.Command{
254+
Use: "userinfo",
255+
Short: "Get information about user(s)",
256+
Long: userinfoHelpText,
257+
Args: cobra.MinimumNArgs(1),
258+
RunE: func(cmd *cobra.Command, args []string) error {
259+
switch outputFormat {
260+
case "text", "json":
261+
default:
262+
return fmt.Errorf("invalid --output %q (want: text, json)", outputFormat)
263+
}
264+
return runUserinfo(cmd.Context(), o.RepoRoot, args, outputFormat == "json", verifyOwners, cmd.OutOrStdout())
265+
},
266+
}
267+
userInfoCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "output format: text|json")
268+
userInfoCmd.Flags().BoolVar(&verifyOwners, "verify-owners", true, "confirm each OWNERS/OWNERS_ALIASES hit via the gh CLI and exclude emeritus-only entries; requires gh to be installed and authenticated")
269+
rootCmd.AddCommand(userInfoCmd)
270+
271+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
272+
defer stop()
273+
274+
if err := rootCmd.ExecuteContext(ctx); err != nil {
226275
os.Exit(1)
227276
}
228277
}

0 commit comments

Comments
 (0)