@@ -15,6 +15,8 @@ import (
15
15
"errors"
16
16
"fmt"
17
17
"io"
18
+ "iter"
19
+ "maps"
18
20
"net/url"
19
21
"os"
20
22
"os/exec"
@@ -568,125 +570,120 @@ func (a *Actions) MigrateTest(ctx context.Context) error {
568
570
569
571
// MigrateAutoRebase runs the Action for "ariga/atlas-action/migrate/autorebase"
570
572
func (a * Actions ) MigrateAutoRebase (ctx context.Context ) error {
571
- gitVer , err := a .CmdExecutor (ctx , "git" , "--version" ).Output ()
572
- switch err := err .(type ) {
573
- case nil :
574
- a .Infof ("running with git version: %s" , string (gitVer ))
575
- case * exec.ExitError :
576
- return fmt .Errorf ("failed to get git version: stderr %s" , string (err .Stderr ))
577
- default :
578
- return fmt .Errorf ("failed to get git version: %w" , err )
579
- }
580
- dirpath := strings .TrimPrefix (a .GetInput ("dir" ), "file://" )
581
- if dirpath == "" {
582
- dirpath = "migrations"
583
- }
584
- sumpath := filepath .Join (a .WorkingDir (), dirpath , migrate .HashFileName )
585
573
tc , err := a .GetTriggerContext (ctx )
586
574
if err != nil {
587
575
return err
588
576
}
589
577
var (
578
+ remote = a .GetInputDefault ("remote" , "origin" )
579
+ baseBranch = a .GetInputDefault ("base-branch" , tc .DefaultBranch )
590
580
currBranch = tc .Branch
591
- baseBranch = a .GetInput ("base-branch" )
592
- remote = a .GetInput ("remote" )
593
581
)
594
- if baseBranch == "" {
595
- baseBranch = tc .DefaultBranch
596
- }
597
- if remote == "" {
598
- remote = "origin"
582
+ if v , err := a .exec (ctx , "git" , "--version" ); err != nil {
583
+ return fmt .Errorf ("failed to get git version: %w" , err )
584
+ } else {
585
+ a .Infof ("auto-rebase with %s" , v )
599
586
}
600
- if out , err := a .CmdExecutor (ctx , "git" , "fetch" , remote , baseBranch ).Output (); err != nil {
601
- a .Errorf (string (out ))
587
+ if _ , err := a .exec (ctx , "git" , "fetch" , remote , baseBranch ); err != nil {
602
588
return fmt .Errorf ("failed to fetch the branch %s: %w" , baseBranch , err )
603
589
}
604
590
// Since running in detached HEAD, we need to switch to the branch.
605
- if out , err := a .CmdExecutor (ctx , "git" , "checkout" , currBranch ).Output (); err != nil {
606
- a .Errorf (string (out ))
591
+ if _ , err := a .exec (ctx , "git" , "checkout" , currBranch ); err != nil {
607
592
return fmt .Errorf ("failed to checkout to the branch: %w" , err )
608
593
}
609
- incoming , err := a .CmdExecutor (ctx , "git" , "show" , fmt .Sprintf ("%s/%s:%s" , remote , baseBranch , sumpath )).Output ()
594
+ dirURL := a .GetInputDefault ("dir" , "file://migrations" )
595
+ u , err := url .Parse (dirURL )
610
596
if err != nil {
611
- a .Errorf (string (incoming ))
612
- return fmt .Errorf ("failed to get the atlas.sum file from the rebase branch: %w" , err )
597
+ return fmt .Errorf ("failed to parse dir URL: %w" , err )
613
598
}
614
- base , err := a .CmdExecutor (ctx , "git" , "show" , fmt .Sprintf ("%s/%s:%s" , remote , currBranch , sumpath )).Output ()
599
+ dirPath := filepath .Join (u .Host , u .Path )
600
+ sumPath := filepath .Join (a .WorkingDir (), dirPath , migrate .HashFileName )
601
+ baseHash , err := a .hashFileFrom (ctx , remote , baseBranch , sumPath )
615
602
if err != nil {
616
- a .Errorf (string (base ))
617
- return fmt .Errorf ("failed to get the atlas.sum file from current branch: %w" , err )
618
- }
619
- var incomingHash , baseHash migrate.HashFile
620
- if err := incomingHash .UnmarshalText (incoming ); err != nil {
621
- return fmt .Errorf ("failed to unmarshal incoming atlas.sum: %w" , err )
622
- }
623
- if err := baseHash .UnmarshalText (base ); err != nil {
624
- return fmt .Errorf ("failed to unmarshal base atlas.sum: %w" , err )
625
- }
626
- incomingFilesSet := make (map [string ]struct {})
627
- for _ , v := range incomingHash {
628
- incomingFilesSet [v .N ] = struct {}{}
603
+ return fmt .Errorf ("failed to get the atlas.sum file from the base branch: %w" , err )
629
604
}
630
- baseNames := make ([]string , len (baseHash ))
631
- for i , v := range baseHash {
632
- baseNames [i ] = v .N
633
- }
634
- // Get all the file names the exists only in the base branch atlas.sum file.
635
- var onlyInBase []string
636
- for _ , file := range baseNames {
637
- if _ , ok := incomingFilesSet [file ]; ! ok {
638
- onlyInBase = append (onlyInBase , file )
639
- }
605
+ currHash , err := a .hashFileFrom (ctx , remote , currBranch , sumPath )
606
+ if err != nil {
607
+ return fmt .Errorf ("failed to get the atlas.sum file from the current branch: %w" , err )
640
608
}
641
- if len (onlyInBase ) == 0 {
642
- a .Infof ("No files to rebase" )
609
+ files := newFiles (baseHash , currHash )
610
+ if len (files ) == 0 {
611
+ a .Infof ("No new migration files to rebase" )
643
612
return nil
644
613
}
645
614
// Try to merge the base branch into the current branch.
646
- out , err := a .CmdExecutor (ctx , "git" , "merge" , "--no-ff" , fmt .Sprintf ("%s/%s" , remote , baseBranch )).Output ()
647
- switch err := err .(type ) {
648
- case nil :
615
+ if _ , err := a .exec (ctx , "git" , "merge" , "--no-ff" ,
616
+ fmt .Sprintf ("%s/%s" , remote , baseBranch )); err == nil {
649
617
a .Infof ("No conflict found when merging %s into %s" , baseBranch , currBranch )
650
618
return nil
651
- case * exec.ExitError :
652
- a .Infof ("Running `git merge` got following error: %s" , string (err .Stderr ))
653
- a .Infof ("git merge output: %s" , string (out ))
654
- default :
655
- return fmt .Errorf ("receive unexpected error %w" , err )
656
619
}
657
620
// If merge failed due to conflict, check that the conflict is only in atlas.sum file.
658
- diff , err := a .CmdExecutor (ctx , "git" , "diff" , "--name-only" , "--diff-filter=U" ).Output ()
659
- if err != nil {
660
- a .Errorf (string (diff ))
621
+ switch out , err := a .exec (ctx , "git" , "diff" , "--name-only" , "--diff-filter=U" ); {
622
+ case err != nil :
661
623
return fmt .Errorf ("failed to get conflicting files: %w" , err )
662
- }
663
- conflictFiles := strings .Split (strings .TrimSpace (string (diff )), "\n " )
664
- if len (conflictFiles ) != 1 || conflictFiles [0 ] != sumpath {
665
- return fmt .Errorf ("conflict found in files other than %s, conflict files: %v" , sumpath , conflictFiles )
624
+ case len (out ) == 0 :
625
+ return errors .New ("conflict found but no conflicting files found" )
626
+ case strings .TrimSpace (string (out )) != sumPath :
627
+ a .Infof ("Conflict files are:\n %s" , out )
628
+ return fmt .Errorf ("conflict found in files other than %s" , sumPath )
666
629
}
667
630
// Re-hash the migrations and rebase the migrations.
668
- if err = a .Atlas .MigrateHash (ctx , & atlasexec.MigrateHashParams {DirURL : a .GetInput ("dir" )}); err != nil {
631
+ if err = a .Atlas .MigrateHash (ctx , & atlasexec.MigrateHashParams {
632
+ DirURL : dirURL ,
633
+ }); err != nil {
669
634
return fmt .Errorf ("failed to run `atlas migrate hash`: %w" , err )
670
635
}
671
- if err = a .Atlas .MigrateRebase (ctx , & atlasexec.MigrateRebaseParams {DirURL : a .GetInput ("dir" ), Files : onlyInBase }); err != nil {
636
+ if err = a .Atlas .MigrateRebase (ctx , & atlasexec.MigrateRebaseParams {
637
+ DirURL : dirURL ,
638
+ Files : files ,
639
+ }); err != nil {
672
640
return fmt .Errorf ("failed to rebase migrations: %w" , err )
673
641
}
674
- if out , err = a .CmdExecutor (ctx , "git" , "add" , dirpath ).CombinedOutput (); err != nil {
675
- a .Errorf (string (out ))
642
+ if _ , err = a .exec (ctx , "git" , "add" , dirPath ); err != nil {
676
643
return fmt .Errorf ("failed to stage changes: %w" , err )
677
644
}
678
- if out , err = a .CmdExecutor (ctx , "git" , "commit" , "-m" , fmt . Sprintf ( "Rebase migrations in %s" , dirpath )). CombinedOutput (); err != nil {
679
- a . Errorf ( string ( out ))
645
+ if _ , err = a .exec (ctx , "git" , "commit" , "--message" ,
646
+ fmt . Sprintf ( "%s: rebase migration files" , dirPath )); err != nil {
680
647
return fmt .Errorf ("failed to commit changes: %w" , err )
681
648
}
682
- if out , err = a .CmdExecutor (ctx , "git" , "push" , remote , currBranch ).CombinedOutput (); err != nil {
683
- a .Errorf (string (out ))
649
+ if _ , err = a .exec (ctx , "git" , "push" , remote , currBranch ); err != nil {
684
650
return fmt .Errorf ("failed to push changes: %w" , err )
685
651
}
686
652
a .Infof ("Migrations rebased successfully" )
687
653
return nil
688
654
}
689
655
656
+ // hashFileFrom returns the hash file from the remote branch.
657
+ func (a * Actions ) hashFileFrom (ctx context.Context , remote , branch , path string ) (migrate.HashFile , error ) {
658
+ data , err := a .exec (ctx , "git" , "show" ,
659
+ fmt .Sprintf ("%s/%s:%s" , remote , branch , path ))
660
+ if err != nil {
661
+ return nil , err
662
+ }
663
+ var hf migrate.HashFile
664
+ if err := hf .UnmarshalText (data ); err != nil {
665
+ return nil , fmt .Errorf ("failed to unmarshal atlas.sum: %w" , err )
666
+ }
667
+ return hf , nil
668
+ }
669
+
670
+ // exec runs the command and returns the output.
671
+ func (a * Actions ) exec (ctx context.Context , name string , args ... string ) ([]byte , error ) {
672
+ cmd := a .CmdExecutor (ctx , name , args ... )
673
+ out , err := cmd .Output ()
674
+ switch err := err .(type ) {
675
+ case nil :
676
+ return out , nil
677
+ case * exec.ExitError :
678
+ if err .Stderr != nil {
679
+ a .Infof ("Running %q got following error: %s" , cmd .String (), string (err .Stderr ))
680
+ }
681
+ return nil , fmt .Errorf ("failed to run %s: %w" , name , err )
682
+ default :
683
+ return nil , fmt .Errorf ("failed to run %s: %w" , name , err )
684
+ }
685
+ }
686
+
690
687
// SchemaPush runs the GitHub Action for "ariga/atlas-action/schema/push"
691
688
func (a * Actions ) SchemaPush (ctx context.Context ) error {
692
689
tc , err := a .GetTriggerContext (ctx )
@@ -1315,6 +1312,15 @@ func (a *Actions) GetArrayInput(name string) []string {
1315
1312
})
1316
1313
}
1317
1314
1315
+ // GetInputDefault returns the input with the given name.
1316
+ // If the input is empty, it returns the default value.
1317
+ func (a * Actions ) GetInputDefault (name , def string ) string {
1318
+ if v := a .GetInput (name ); v != "" {
1319
+ return v
1320
+ }
1321
+ return def
1322
+ }
1323
+
1318
1324
// DeployRunContext returns the run context for the `migrate/apply`, and `migrate/down` actions.
1319
1325
func (a * Actions ) DeployRunContext () * atlasexec.DeployRunContext {
1320
1326
return & atlasexec.DeployRunContext {
@@ -1447,6 +1453,25 @@ func (tc *TriggerContext) GetRunContext() *atlasexec.RunContext {
1447
1453
return rc
1448
1454
}
1449
1455
1456
+ // newFiles returns the files that only exists in the current hash.
1457
+ func newFiles (base , current migrate.HashFile ) []string {
1458
+ m := maps .Collect (hashIter (current ))
1459
+ for k := range hashIter (base ) {
1460
+ delete (m , k )
1461
+ }
1462
+ return slices .Collect (maps .Keys (m ))
1463
+ }
1464
+
1465
+ func hashIter (hf migrate.HashFile ) iter.Seq2 [string , string ] {
1466
+ return func (yield func (string , string ) bool ) {
1467
+ for _ , v := range hf {
1468
+ if ! yield (v .N , v .H ) {
1469
+ return
1470
+ }
1471
+ }
1472
+ }
1473
+ }
1474
+
1450
1475
func execTime (start , end time.Time ) string {
1451
1476
return end .Sub (start ).String ()
1452
1477
}
0 commit comments