55 "errors"
66 "fmt"
77 "io"
8+ "net/url"
89 "os"
10+ "os/exec"
911 "path/filepath"
1012 "sort"
1113 "strings"
@@ -22,6 +24,7 @@ import (
2224type migrateOptions struct {
2325 orgID string
2426 token string
27+ repo string
2528 yes bool
2629 secrets []string
2730 variables []string
@@ -51,6 +54,7 @@ func NewCmdMigrate() *cobra.Command {
5154 flags .BoolVar (& opts .yes , "yes" , false , "Run in non-interactive mode" )
5255 flags .StringArrayVar (& opts .secrets , "secret" , nil , "CI secret assignment in KEY=VALUE format (repeatable)" )
5356 flags .StringArrayVar (& opts .variables , "var" , nil , "CI variable assignment in KEY=VALUE format (repeatable)" )
57+ flags .StringVar (& opts .repo , "repo" , "" , "Scope secrets and variables to a specific repository (e.g. owner/repo); auto-detected from git remote if not provided" )
5458 flags .BoolVar (& opts .overwrite , "overwrite" , false , "Overwrite existing .depot/ directory" )
5559
5660 return cmd
@@ -99,6 +103,16 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
99103
100104 fmt .Fprintf (out , "Found %d workflow(s) in .github/workflows\n " , len (workflows ))
101105
106+ repo := opts .repo
107+ if repo == "" {
108+ repo = detectRepoFromGitRemote (workDir )
109+ }
110+ if repo != "" {
111+ fmt .Fprintf (out , "Secrets and variables will be scoped to repo %s\n " , repo )
112+ } else {
113+ fmt .Fprintln (out , "Could not detect repository from git remote; secrets and variables will be org-scoped (use --repo owner/name to override)" )
114+ }
115+
102116 selectedWorkflows := workflows
103117 warnings := parseWarnings
104118 for _ , workflow := range workflows {
@@ -248,7 +262,7 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
248262 return err
249263 }
250264
251- if err := api .CIAddSecret (ctx , token , orgID , name , value ); err != nil {
265+ if err := api .CIAddSecretWithDescription (ctx , token , orgID , name , value , "" , repo ); err != nil {
252266 return fmt .Errorf ("failed to configure secret %s: %w" , name , err )
253267 }
254268 configuredSecrets = append (configuredSecrets , name )
@@ -279,7 +293,11 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
279293 }
280294 if len (missingSecrets ) > 0 {
281295 sort .Strings (missingSecrets )
282- warnings = append (warnings , fmt .Sprintf ("configure missing secrets with `depot ci secrets add <NAME> --value <VALUE>` (missing: %s)" , strings .Join (missingSecrets , ", " )))
296+ hint := "depot ci secrets add <NAME> --value <VALUE>"
297+ if repo != "" {
298+ hint = fmt .Sprintf ("depot ci secrets add <NAME> --repo %s --value <VALUE>" , repo )
299+ }
300+ warnings = append (warnings , fmt .Sprintf ("configure missing secrets with `%s` (missing: %s)" , hint , strings .Join (missingSecrets , ", " )))
283301 }
284302 } else if len (detectedSecrets ) > 0 {
285303 for _ , name := range detectedSecrets {
@@ -320,7 +338,7 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
320338 return err
321339 }
322340
323- if err := api .CIAddVariable (ctx , token , orgID , name , value , "" ); err != nil {
341+ if err := api .CIAddVariable (ctx , token , orgID , name , value , repo ); err != nil {
324342 return fmt .Errorf ("failed to configure variable %s: %w" , name , err )
325343 }
326344 configuredVariables = append (configuredVariables , name )
@@ -351,7 +369,11 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
351369 }
352370 if len (missingVariables ) > 0 {
353371 sort .Strings (missingVariables )
354- warnings = append (warnings , fmt .Sprintf ("configure missing variables with `depot ci vars add <NAME> --value <VALUE>` (missing: %s)" , strings .Join (missingVariables , ", " )))
372+ hint := "depot ci vars add <NAME> --value <VALUE>"
373+ if repo != "" {
374+ hint = fmt .Sprintf ("depot ci vars add <NAME> --repo %s --value <VALUE>" , repo )
375+ }
376+ warnings = append (warnings , fmt .Sprintf ("configure missing variables with `%s` (missing: %s)" , hint , strings .Join (missingVariables , ", " )))
355377 }
356378 } else if len (detectedVariables ) > 0 {
357379 for _ , name := range detectedVariables {
@@ -378,6 +400,11 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
378400 fmt .Fprintln (out , "Migration summary:" )
379401 fmt .Fprintf (out , "- Workflows selected: %d\n " , len (selectedWorkflows ))
380402 fmt .Fprintf (out , "- Files copied: %d\n " , len (copyResult .FilesCopied ))
403+ if repo != "" {
404+ fmt .Fprintf (out , "- Secret/variable scope: repo (%s)\n " , repo )
405+ } else {
406+ fmt .Fprintln (out , "- Secret/variable scope: org" )
407+ }
381408 fmt .Fprintf (out , "- Secrets detected: %d\n " , len (detectedSecrets ))
382409 fmt .Fprintf (out , "- Secrets configured: %d\n " , len (configuredSecrets ))
383410 fmt .Fprintf (out , "- Variables detected: %d\n " , len (detectedVariables ))
@@ -585,6 +612,47 @@ func detectVariablesFromWorkflows(workflows []*migrate.WorkflowFile) ([]string,
585612 return deduped , nil
586613}
587614
615+ // detectRepoFromGitRemote attempts to extract owner/repo from the origin remote URL.
616+ func detectRepoFromGitRemote (dir string ) string {
617+ cmd := exec .Command ("git" , "-C" , dir , "remote" , "get-url" , "origin" )
618+ out , err := cmd .Output ()
619+ if err != nil {
620+ return ""
621+ }
622+ return parseGitHubRepo (strings .TrimSpace (string (out )))
623+ }
624+
625+ func parseGitHubRepo (remoteURL string ) string {
626+ // SSH: git@github.com:owner/repo.git
627+ if strings .HasPrefix (remoteURL , "git@" ) {
628+ idx := strings .Index (remoteURL , ":" )
629+ if idx < 0 {
630+ return ""
631+ }
632+ path := remoteURL [idx + 1 :]
633+ path = strings .TrimSuffix (path , ".git" )
634+ parts := strings .SplitN (path , "/" , 3 )
635+ if len (parts ) != 2 || parts [0 ] == "" || parts [1 ] == "" {
636+ return ""
637+ }
638+ return parts [0 ] + "/" + parts [1 ]
639+ }
640+
641+ // HTTPS: https://github.com/owner/repo.git
642+ u , err := url .Parse (remoteURL )
643+ if err != nil {
644+ return ""
645+ }
646+ path := strings .TrimPrefix (u .Path , "/" )
647+ path = strings .TrimSuffix (path , ".git" )
648+ path = strings .TrimRight (path , "/" )
649+ parts := strings .SplitN (path , "/" , 3 )
650+ if len (parts ) != 2 || parts [0 ] == "" || parts [1 ] == "" {
651+ return ""
652+ }
653+ return parts [0 ] + "/" + parts [1 ]
654+ }
655+
588656func resolveMigrationAuth (ctx context.Context , opts migrateOptions ) (string , string , error ) {
589657 orgID := opts .orgID
590658 if orgID == "" {
0 commit comments