Skip to content

Commit c14c2e2

Browse files
committed
Phase 6 (continued): Port ado2gh simple commands + shared command extraction
Port all 8 ado2gh simple commands (Tasks 23a-f): - lock-ado-repo, disable-ado-repo - add-team-to-repo, configure-autolink - share-service-connection, integrate-boards - rewire-pipeline (with PipelineTriggerService ~557 lines) - test-pipelines (with PipelineTestService ~274 lines) Extract shared commands into internal/sharedcmd/ (Task 23g): - Created 8 files in internal/sharedcmd/ with exported interfaces, Run*, Validate*, types, and constants for all commands shared between gei and ado2gh (wait-for-migration, abort-migration, download-logs, grant/revoke-migrator-role, create-team, generate-mannequin-csv, reclaim-mannequin) - Updated cmd/gei/ files to be thin wrappers calling sharedcmd.* - Created cmd/ado2gh/wiring.go with 8 Live constructors - Wired all 18 commands into cmd/ado2gh/main.go Lint fixes (Task 23h): - Renamed AdoBranchPolicy* types to BranchPolicy* (revive stutter) - Extracted "unknown" constant (goconst) - Tagged switch and TrimPrefix (staticcheck) - File permissions 0o600 (gosec) - Removed dead code from cmd/gei/ after sharedcmd extraction - Fixed misspelling in test comment
1 parent aa40506 commit c14c2e2

46 files changed

Lines changed: 7150 additions & 631 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/ado2gh/add_team_to_repo.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package main
2+
3+
import (
4+
"context"
5+
6+
"github.com/github/gh-gei/internal/cmdutil"
7+
"github.com/github/gh-gei/pkg/env"
8+
"github.com/github/gh-gei/pkg/github"
9+
"github.com/github/gh-gei/pkg/logger"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
// ---------------------------------------------------------------------------
14+
// Consumer-defined interfaces
15+
// ---------------------------------------------------------------------------
16+
17+
// addTeamToRepoGitHub defines the GitHub API methods needed by add-team-to-repo.
18+
type addTeamToRepoGitHub interface {
19+
GetTeamSlug(ctx context.Context, org, teamName string) (string, error)
20+
AddTeamToRepo(ctx context.Context, org, teamSlug, repo, role string) error
21+
}
22+
23+
// addTeamToRepoEnvProvider provides environment variable fallbacks.
24+
type addTeamToRepoEnvProvider interface {
25+
TargetGitHubPAT() string
26+
}
27+
28+
// ---------------------------------------------------------------------------
29+
// Args struct
30+
// ---------------------------------------------------------------------------
31+
32+
type addTeamToRepoArgs struct {
33+
githubOrg string
34+
githubRepo string
35+
team string
36+
role string
37+
githubPAT string
38+
targetAPIURL string
39+
}
40+
41+
// ---------------------------------------------------------------------------
42+
// Command constructor (testable)
43+
// ---------------------------------------------------------------------------
44+
45+
func newAddTeamToRepoCmd(
46+
gh addTeamToRepoGitHub,
47+
envProv addTeamToRepoEnvProvider,
48+
log *logger.Logger,
49+
) *cobra.Command {
50+
var a addTeamToRepoArgs
51+
52+
cmd := &cobra.Command{
53+
Use: "add-team-to-repo",
54+
Short: "Adds a team to a repo with a specific role/permission",
55+
Long: "Adds a team to a repo with a specific role/permission\n" +
56+
"Note: Expects GH_PAT env variable or --github-pat option to be set.",
57+
RunE: func(cmd *cobra.Command, _ []string) error {
58+
return runAddTeamToRepo(cmd.Context(), gh, envProv, log, a)
59+
},
60+
}
61+
62+
cmd.Flags().StringVar(&a.githubOrg, "github-org", "", "GitHub organization name (REQUIRED)")
63+
cmd.Flags().StringVar(&a.githubRepo, "github-repo", "", "GitHub repository name (REQUIRED)")
64+
cmd.Flags().StringVar(&a.team, "team", "", "Team name (REQUIRED)")
65+
cmd.Flags().StringVar(&a.role, "role", "", "Role/permission: pull, push, admin, maintain, triage (REQUIRED)")
66+
cmd.Flags().StringVar(&a.githubPAT, "github-pat", "", "GitHub personal access token (falls back to GH_PAT env)")
67+
cmd.Flags().StringVar(&a.targetAPIURL, "target-api-url", "", "API URL for the target GitHub instance")
68+
69+
return cmd
70+
}
71+
72+
// ---------------------------------------------------------------------------
73+
// Production command constructor
74+
// ---------------------------------------------------------------------------
75+
76+
func newAddTeamToRepoCmdLive() *cobra.Command {
77+
var a addTeamToRepoArgs
78+
79+
cmd := &cobra.Command{
80+
Use: "add-team-to-repo",
81+
Short: "Adds a team to a repo with a specific role/permission",
82+
Long: "Adds a team to a repo with a specific role/permission\n" +
83+
"Note: Expects GH_PAT env variable or --github-pat option to be set.",
84+
RunE: func(cmd *cobra.Command, _ []string) error {
85+
log := getLogger(cmd)
86+
envProv := &addTeamToRepoEnvAdapter{prov: env.New()}
87+
88+
githubPAT := a.githubPAT
89+
if githubPAT == "" {
90+
githubPAT = envProv.TargetGitHubPAT()
91+
}
92+
93+
apiURL := a.targetAPIURL
94+
if apiURL == "" {
95+
apiURL = "https://github.com/_ext/api.github.com"
96+
}
97+
98+
gh := github.NewClient(githubPAT,
99+
github.WithAPIURL(apiURL),
100+
github.WithLogger(log),
101+
github.WithVersion(version),
102+
)
103+
104+
return runAddTeamToRepo(cmd.Context(), gh, envProv, log, a)
105+
},
106+
}
107+
108+
cmd.Flags().StringVar(&a.githubOrg, "github-org", "", "GitHub organization name (REQUIRED)")
109+
cmd.Flags().StringVar(&a.githubRepo, "github-repo", "", "GitHub repository name (REQUIRED)")
110+
cmd.Flags().StringVar(&a.team, "team", "", "Team name (REQUIRED)")
111+
cmd.Flags().StringVar(&a.role, "role", "", "Role/permission: pull, push, admin, maintain, triage (REQUIRED)")
112+
cmd.Flags().StringVar(&a.githubPAT, "github-pat", "", "GitHub personal access token (falls back to GH_PAT env)")
113+
cmd.Flags().StringVar(&a.targetAPIURL, "target-api-url", "", "API URL for the target GitHub instance")
114+
115+
return cmd
116+
}
117+
118+
type addTeamToRepoEnvAdapter struct {
119+
prov *env.Provider
120+
}
121+
122+
func (a *addTeamToRepoEnvAdapter) TargetGitHubPAT() string { return a.prov.TargetGitHubPAT() }
123+
124+
// ---------------------------------------------------------------------------
125+
// Validation
126+
// ---------------------------------------------------------------------------
127+
128+
func validateAddTeamToRepoArgs(a *addTeamToRepoArgs) error {
129+
if err := cmdutil.ValidateRequired(a.githubOrg, "--github-org"); err != nil {
130+
return err
131+
}
132+
if err := cmdutil.ValidateRequired(a.githubRepo, "--github-repo"); err != nil {
133+
return err
134+
}
135+
if err := cmdutil.ValidateRequired(a.team, "--team"); err != nil {
136+
return err
137+
}
138+
if err := cmdutil.ValidateRequired(a.role, "--role"); err != nil {
139+
return err
140+
}
141+
if err := cmdutil.ValidateOneOf(a.role, "--role", "pull", "push", "admin", "maintain", "triage"); err != nil {
142+
return err
143+
}
144+
return nil
145+
}
146+
147+
// ---------------------------------------------------------------------------
148+
// Runner
149+
// ---------------------------------------------------------------------------
150+
151+
func runAddTeamToRepo(
152+
ctx context.Context,
153+
gh addTeamToRepoGitHub,
154+
envProv addTeamToRepoEnvProvider,
155+
log *logger.Logger,
156+
a addTeamToRepoArgs,
157+
) error {
158+
if err := validateAddTeamToRepoArgs(&a); err != nil {
159+
return err
160+
}
161+
162+
log.Info("Adding team to repo...")
163+
164+
teamSlug, err := gh.GetTeamSlug(ctx, a.githubOrg, a.team)
165+
if err != nil {
166+
return err
167+
}
168+
169+
if err := gh.AddTeamToRepo(ctx, a.githubOrg, teamSlug, a.githubRepo, a.role); err != nil {
170+
return err
171+
}
172+
173+
log.Success("Successfully added team to repo")
174+
return nil
175+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"testing"
7+
8+
"github.com/github/gh-gei/pkg/logger"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// ---------------------------------------------------------------------------
14+
// Mock implementations
15+
// ---------------------------------------------------------------------------
16+
17+
type mockAddTeamToRepoGitHub struct {
18+
getTeamSlugFn func(ctx context.Context, org, teamName string) (string, error)
19+
addTeamToRepoFn func(ctx context.Context, org, teamSlug, repo, role string) error
20+
}
21+
22+
func (m *mockAddTeamToRepoGitHub) GetTeamSlug(ctx context.Context, org, teamName string) (string, error) {
23+
return m.getTeamSlugFn(ctx, org, teamName)
24+
}
25+
26+
func (m *mockAddTeamToRepoGitHub) AddTeamToRepo(ctx context.Context, org, teamSlug, repo, role string) error {
27+
return m.addTeamToRepoFn(ctx, org, teamSlug, repo, role)
28+
}
29+
30+
type mockAddTeamToRepoEnv struct {
31+
targetPAT string
32+
}
33+
34+
func (m *mockAddTeamToRepoEnv) TargetGitHubPAT() string { return m.targetPAT }
35+
36+
// ---------------------------------------------------------------------------
37+
// Tests
38+
// ---------------------------------------------------------------------------
39+
40+
func TestAddTeamToRepo_HappyPath(t *testing.T) {
41+
var buf bytes.Buffer
42+
log := logger.New(false, &buf)
43+
44+
var capturedOrg, capturedTeamSlug, capturedRepo, capturedRole string
45+
46+
gh := &mockAddTeamToRepoGitHub{
47+
getTeamSlugFn: func(_ context.Context, org, teamName string) (string, error) {
48+
assert.Equal(t, "my-org", org)
49+
assert.Equal(t, "my-team", teamName)
50+
return "foo-slug", nil
51+
},
52+
addTeamToRepoFn: func(_ context.Context, org, teamSlug, repo, role string) error {
53+
capturedOrg = org
54+
capturedTeamSlug = teamSlug
55+
capturedRepo = repo
56+
capturedRole = role
57+
return nil
58+
},
59+
}
60+
61+
cmd := newAddTeamToRepoCmd(gh, &mockAddTeamToRepoEnv{targetPAT: "gh-token"}, log)
62+
cmd.SetOut(&buf)
63+
cmd.SetErr(&buf)
64+
cmd.SetArgs([]string{
65+
"--github-org", "my-org",
66+
"--github-repo", "my-repo",
67+
"--team", "my-team",
68+
"--role", "push",
69+
})
70+
71+
err := cmd.Execute()
72+
require.NoError(t, err)
73+
74+
assert.Equal(t, "my-org", capturedOrg)
75+
assert.Equal(t, "foo-slug", capturedTeamSlug)
76+
assert.Equal(t, "my-repo", capturedRepo)
77+
assert.Equal(t, "push", capturedRole)
78+
79+
output := buf.String()
80+
assert.Contains(t, output, "Adding team to repo...")
81+
assert.Contains(t, output, "Successfully added team to repo")
82+
}

0 commit comments

Comments
 (0)