Skip to content

Commit 5ee2b0f

Browse files
committed
Phase 5 (continued): Port gei migrate-org + alert migration commands
migrate-org command (Task 18): - cmd/gei/migrate_org.go: 274-line command with queue-only and full migration modes - Polls org migration status using existing pkg/migration/status.go helpers - Iterates repo migrations within org migration, reporting per-repo progress - 13 tests covering validation, queue-only, full migration, failure, token resolution, URL escaping, context cancellation, multi-state polling - Consumer-defined interfaces: orgMigrator, migrateOrgEnvProvider Alert migration commands (Task 19): - New models in pkg/github/models.go: SecretScanningAlert, CodeScanningAlert, CodeScanningAnalysis, SarifProcessingStatus, and related types - ~11 new REST API methods in pkg/github/client.go for secret scanning and code scanning alert operations (get/update alerts, SARIF upload/status) - pkg/alerts/secret_scanning.go: Dictionary matching by (SecretType, Secret), location comparison, resolution comment truncation to 270 chars - pkg/alerts/code_scanning.go: SARIF download/upload with gzip+base64, processing status polling, alert matching by RuleId + instance equality - cmd/gei/migrate_secret_alerts.go + tests (11 tests) - cmd/gei/migrate_code_scanning.go + tests (11 tests) - All commands wired into cmd/gei/main.go
1 parent a159148 commit 5ee2b0f

14 files changed

Lines changed: 4054 additions & 4 deletions

cmd/gei/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ func newRootCmd() *cobra.Command {
5757
// Add commands
5858
rootCmd.AddCommand(newGenerateScriptCmd())
5959
rootCmd.AddCommand(newMigrateRepoCmdLive())
60+
rootCmd.AddCommand(newMigrateOrgCmdLive())
61+
62+
rootCmd.AddCommand(newMigrateSecretAlertsCmdLive())
63+
rootCmd.AddCommand(newMigrateCodeScanningCmdLive())
6064

6165
// Additional commands will be implemented in subsequent phases
62-
// rootCmd.AddCommand(newMigrateRepoCmd())
63-
// rootCmd.AddCommand(newMigrateOrgCmd())
6466
// rootCmd.AddCommand(newWaitForMigrationCmd())
6567
// rootCmd.AddCommand(newAbortMigrationCmd())
6668
// rootCmd.AddCommand(newDownloadLogsCmd())
67-
// rootCmd.AddCommand(newMigrateSecretAlertsCmd())
68-
// rootCmd.AddCommand(newMigrateCodeScanningAlertsCmd())
6969
// rootCmd.AddCommand(newGenerateMannequinCSVCmd())
7070
// rootCmd.AddCommand(newReclaimMannequinCmd())
7171
// rootCmd.AddCommand(newGrantMigratorRoleCmd())

cmd/gei/migrate_code_scanning.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/github/gh-gei/internal/cmdutil"
8+
"github.com/github/gh-gei/pkg/alerts"
9+
"github.com/github/gh-gei/pkg/env"
10+
"github.com/github/gh-gei/pkg/github"
11+
"github.com/github/gh-gei/pkg/logger"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
// codeScanningMigrator is the consumer-defined interface for migrating code scanning alerts.
16+
type codeScanningMigrator interface {
17+
MigrateCodeScanningAlerts(ctx context.Context, sourceOrg, sourceRepo, targetOrg, targetRepo string, dryRun bool) error
18+
}
19+
20+
// newMigrateCodeScanningCmd creates the migrate-code-scanning-alerts cobra command.
21+
func newMigrateCodeScanningCmd(svc codeScanningMigrator, log *logger.Logger) *cobra.Command {
22+
var (
23+
sourceOrg string
24+
sourceRepo string
25+
targetOrg string
26+
targetRepo string
27+
targetAPIURL string
28+
ghesAPIURL string
29+
noSSLVerify bool
30+
githubSrcPAT string
31+
githubTgtPAT string
32+
dryRun bool
33+
)
34+
35+
cmd := &cobra.Command{
36+
Use: "migrate-code-scanning-alerts",
37+
Short: "Migrates code-scanning analyses, alert states, and dismissed-reasons",
38+
Long: "Migrates all code-scanning analyses, alert states and possible dismissed-reasons for the default branch. This lets you migrate the history of code-scanning alerts to the target repository.",
39+
RunE: func(cmd *cobra.Command, _ []string) error {
40+
if err := validateCodeScanningArgs(sourceOrg, sourceRepo, targetOrg, targetRepo, log); err != nil {
41+
return err
42+
}
43+
return runMigrateCodeScanning(cmd.Context(), svc, log, sourceOrg, sourceRepo, targetOrg, targetRepo, dryRun)
44+
},
45+
}
46+
47+
cmd.Flags().StringVar(&sourceOrg, "source-org", "", "Source GitHub organization (REQUIRED)")
48+
cmd.Flags().StringVar(&sourceRepo, "source-repo", "", "Source repository name (REQUIRED)")
49+
cmd.Flags().StringVar(&targetOrg, "target-org", "", "Target GitHub organization (REQUIRED)")
50+
cmd.Flags().StringVar(&targetRepo, "target-repo", "", "Target repository name (defaults to source-repo)")
51+
cmd.Flags().StringVar(&targetAPIURL, "target-api-url", "", "API URL for the target GitHub instance (defaults to https://api.github.com)")
52+
cmd.Flags().StringVar(&ghesAPIURL, "ghes-api-url", "", "API endpoint for GHES instance")
53+
cmd.Flags().BoolVar(&noSSLVerify, "no-ssl-verify", false, "Disable SSL verification for GHES")
54+
cmd.Flags().StringVar(&githubSrcPAT, "github-source-pat", "", "Personal access token for the source GitHub instance")
55+
cmd.Flags().StringVar(&githubTgtPAT, "github-target-pat", "", "Personal access token for the target GitHub instance")
56+
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Execute in dry run mode without making actual changes")
57+
58+
return cmd
59+
}
60+
61+
func validateCodeScanningArgs(sourceOrg, sourceRepo, targetOrg, targetRepo string, log *logger.Logger) error {
62+
if err := cmdutil.ValidateRequired(sourceOrg, "--source-org"); err != nil {
63+
return err
64+
}
65+
if err := cmdutil.ValidateRequired(sourceRepo, "--source-repo"); err != nil {
66+
return err
67+
}
68+
if err := cmdutil.ValidateRequired(targetOrg, "--target-org"); err != nil {
69+
return err
70+
}
71+
if err := cmdutil.ValidateNoURL(sourceOrg, "--source-org"); err != nil {
72+
return err
73+
}
74+
if err := cmdutil.ValidateNoURL(targetOrg, "--target-org"); err != nil {
75+
return err
76+
}
77+
if err := cmdutil.ValidateNoURL(sourceRepo, "--source-repo"); err != nil {
78+
return err
79+
}
80+
if err := cmdutil.ValidateNoURL(targetRepo, "--target-repo"); err != nil {
81+
return err
82+
}
83+
return nil
84+
}
85+
86+
func runMigrateCodeScanning(ctx context.Context, svc codeScanningMigrator, log *logger.Logger, sourceOrg, sourceRepo, targetOrg, targetRepo string, dryRun bool) error {
87+
// Default target-repo to source-repo
88+
if strings.TrimSpace(targetRepo) == "" {
89+
targetRepo = sourceRepo
90+
log.Info("Since target-repo is not provided, source-repo value will be used for target-repo.")
91+
}
92+
93+
log.Info("Migrating Repo Code Scanning Alerts...")
94+
95+
if err := svc.MigrateCodeScanningAlerts(ctx, sourceOrg, sourceRepo, targetOrg, targetRepo, dryRun); err != nil {
96+
return err
97+
}
98+
99+
if !dryRun {
100+
log.Success("Code scanning alerts successfully migrated.")
101+
}
102+
return nil
103+
}
104+
105+
// newMigrateCodeScanningCmdLive creates the migrate-code-scanning-alerts command with real deps.
106+
func newMigrateCodeScanningCmdLive() *cobra.Command {
107+
var (
108+
sourceOrg string
109+
sourceRepo string
110+
targetOrg string
111+
targetRepo string
112+
targetAPIURL string
113+
ghesAPIURL string
114+
noSSLVerify bool
115+
githubSrcPAT string
116+
githubTgtPAT string
117+
dryRun bool
118+
)
119+
120+
cmd := &cobra.Command{
121+
Use: "migrate-code-scanning-alerts",
122+
Short: "Migrates code-scanning analyses, alert states, and dismissed-reasons",
123+
Long: "Migrates all code-scanning analyses, alert states and possible dismissed-reasons for the default branch. This lets you migrate the history of code-scanning alerts to the target repository.",
124+
RunE: func(cmd *cobra.Command, _ []string) error {
125+
log := getLogger(cmd)
126+
ctx := cmd.Context()
127+
envProv := env.New()
128+
129+
if err := validateCodeScanningArgs(sourceOrg, sourceRepo, targetOrg, targetRepo, log); err != nil {
130+
return err
131+
}
132+
133+
// Resolve tokens from flags or environment
134+
sourcePAT := resolveAlertSourceToken(githubSrcPAT, githubTgtPAT, envProv)
135+
targetPAT := resolveAlertTargetToken(githubTgtPAT, envProv)
136+
137+
// Build source client
138+
sourceAPIURL := ghesAPIURL
139+
if sourceAPIURL == "" {
140+
sourceAPIURL = defaultGitHubAPIURL
141+
}
142+
sourceOpts := []github.Option{
143+
github.WithAPIURL(sourceAPIURL),
144+
github.WithLogger(log),
145+
}
146+
if noSSLVerify {
147+
sourceOpts = append(sourceOpts, github.WithNoSSLVerify())
148+
}
149+
sourceGH := github.NewClient(sourcePAT, sourceOpts...)
150+
151+
// Build target client
152+
tgtAPI := targetAPIURL
153+
if tgtAPI == "" {
154+
tgtAPI = defaultGitHubAPIURL
155+
}
156+
targetGH := github.NewClient(targetPAT,
157+
github.WithAPIURL(tgtAPI),
158+
github.WithLogger(log),
159+
)
160+
161+
svc := alerts.NewCodeScanningService(sourceGH, targetGH, log)
162+
return runMigrateCodeScanning(ctx, svc, log, sourceOrg, sourceRepo, targetOrg, targetRepo, dryRun)
163+
},
164+
}
165+
166+
cmd.Flags().StringVar(&sourceOrg, "source-org", "", "Source GitHub organization (REQUIRED)")
167+
cmd.Flags().StringVar(&sourceRepo, "source-repo", "", "Source repository name (REQUIRED)")
168+
cmd.Flags().StringVar(&targetOrg, "target-org", "", "Target GitHub organization (REQUIRED)")
169+
cmd.Flags().StringVar(&targetRepo, "target-repo", "", "Target repository name (defaults to source-repo)")
170+
cmd.Flags().StringVar(&targetAPIURL, "target-api-url", "", "API URL for the target GitHub instance (defaults to https://api.github.com)")
171+
cmd.Flags().StringVar(&ghesAPIURL, "ghes-api-url", "", "API endpoint for GHES instance")
172+
cmd.Flags().BoolVar(&noSSLVerify, "no-ssl-verify", false, "Disable SSL verification for GHES")
173+
cmd.Flags().StringVar(&githubSrcPAT, "github-source-pat", "", "Personal access token for the source GitHub instance")
174+
cmd.Flags().StringVar(&githubTgtPAT, "github-target-pat", "", "Personal access token for the target GitHub instance")
175+
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Execute in dry run mode without making actual changes")
176+
177+
return cmd
178+
}

0 commit comments

Comments
 (0)