Skip to content

Commit ab260a7

Browse files
authored
Fix support for --ref when adding git profile registries (#864)
* Fix support for --ref when adding git profile registries
1 parent 2ada60e commit ab260a7

File tree

9 files changed

+234
-5
lines changed

9 files changed

+234
-5
lines changed

pkg/git/clone.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import (
1111

1212
// Clone wraps the command 'git clone'.
1313
func Clone(repoURL string, repoDirPath string) error {
14+
return CloneWithRef(repoURL, repoDirPath, "")
15+
}
16+
17+
// CloneWithRef wraps the command 'git clone' and checks out a specific ref.
18+
func CloneWithRef(repoURL string, repoDirPath string, ref string) error {
1419
clio.Debugf("git clone %s\n", repoURL)
1520

1621
cmd := exec.Command("git", "clone", repoURL, repoDirPath)
@@ -30,5 +35,25 @@ func Clone(repoURL string, repoDirPath string) error {
3035
}
3136
clio.Debugf("Successfully cloned %s", repoURL)
3237

38+
// If a ref is specified, checkout that ref
39+
if ref != "" {
40+
clio.Debugf("git -C %s checkout %s\n", repoDirPath, ref)
41+
checkoutCmd := exec.Command("git", "-C", repoDirPath, "checkout", ref)
42+
43+
stderr, _ := checkoutCmd.StderrPipe()
44+
if err := checkoutCmd.Start(); err != nil {
45+
return err
46+
}
47+
48+
scanner := bufio.NewScanner(stderr)
49+
for scanner.Scan() {
50+
if strings.Contains(strings.ToLower(scanner.Text()), "error") || strings.Contains(strings.ToLower(scanner.Text()), "fatal") {
51+
return errors.New(scanner.Text())
52+
}
53+
clio.Info(scanner.Text())
54+
}
55+
clio.Debugf("Successfully checked out %s", ref)
56+
}
57+
3358
return nil
3459
}

pkg/git/clone_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package git
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestCloneWithRef(t *testing.T) {
10+
// Create a temporary directory for the test
11+
tempDir, err := os.MkdirTemp("", "git-clone-test")
12+
if err != nil {
13+
t.Fatalf("Failed to create temp dir: %v", err)
14+
}
15+
defer func() {
16+
_ = os.RemoveAll(tempDir)
17+
}()
18+
19+
// Define test cases
20+
tests := []struct {
21+
name string
22+
repoURL string
23+
ref string
24+
wantErr bool
25+
}{
26+
{
27+
name: "clone with main branch",
28+
repoURL: "https://github.com/octocat/Hello-World.git",
29+
ref: "master",
30+
wantErr: false,
31+
},
32+
{
33+
name: "clone with specific commit",
34+
repoURL: "https://github.com/octocat/Hello-World.git",
35+
ref: "7fd1a60", // First 7 chars of a commit hash
36+
wantErr: false,
37+
},
38+
{
39+
name: "clone with empty ref",
40+
repoURL: "https://github.com/octocat/Hello-World.git",
41+
ref: "",
42+
wantErr: false,
43+
},
44+
{
45+
name: "clone with non-existent ref",
46+
repoURL: "https://github.com/octocat/Hello-World.git",
47+
ref: "non-existent-branch",
48+
wantErr: true,
49+
},
50+
}
51+
52+
for _, tt := range tests {
53+
t.Run(tt.name, func(t *testing.T) {
54+
// Create a unique directory for each test
55+
cloneDir := filepath.Join(tempDir, tt.name)
56+
57+
err := CloneWithRef(tt.repoURL, cloneDir, tt.ref)
58+
if (err != nil) != tt.wantErr {
59+
t.Errorf("CloneWithRef() error = %v, wantErr %v", err, tt.wantErr)
60+
return
61+
}
62+
63+
// If we expected success and got it, verify the clone
64+
if !tt.wantErr && err == nil {
65+
// Check if the directory exists
66+
if _, err := os.Stat(cloneDir); os.IsNotExist(err) {
67+
t.Errorf("Clone directory does not exist: %s", cloneDir)
68+
}
69+
70+
// Check if .git directory exists
71+
gitDir := filepath.Join(cloneDir, ".git")
72+
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
73+
t.Errorf(".git directory does not exist in clone: %s", gitDir)
74+
}
75+
}
76+
})
77+
}
78+
}

pkg/git/pull.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,29 @@ import (
1111

1212
// Pull wraps the command 'git pull'.
1313
func Pull(repoDirPath string, shouldSilentLogs bool) error {
14+
return PullRef(repoDirPath, "", shouldSilentLogs)
15+
}
16+
17+
// PullRef wraps the command 'git pull' for a specific ref.
18+
func PullRef(repoDirPath string, ref string, shouldSilentLogs bool) error {
19+
// if ref is specified, ensure we're on the right branch first
20+
if ref != "" {
21+
clio.Debugf("git -C %s checkout %s\n", repoDirPath, ref)
22+
checkoutCmd := exec.Command("git", "-C", repoDirPath, "checkout", ref)
23+
if err := checkoutCmd.Run(); err != nil {
24+
return err
25+
}
26+
}
27+
28+
// determine what to pull
29+
pullRef := "HEAD"
30+
if ref != "" {
31+
pullRef = ref
32+
}
33+
1434
// pull the repo here.
15-
clio.Debugf("git -C %s pull %s %s\n", repoDirPath, "origin", "HEAD")
16-
cmd := exec.Command("git", "-C", repoDirPath, "pull", "origin", "HEAD")
35+
clio.Debugf("git -C %s pull %s %s\n", repoDirPath, "origin", pullRef)
36+
cmd := exec.Command("git", "-C", repoDirPath, "pull", "origin", pullRef)
1737

1838
// StderrPipe returns a pipe that will be connected to the command's
1939
// standard error when the command starts.

pkg/granted/registry/add.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ var AddCommand = cli.Command{
2929
&cli.StringFlag{Name: "path", Usage: "For git registries: provide path if only the subfolder needs to be synced", Aliases: []string{"p"}},
3030
&cli.StringFlag{Name: "filename", Aliases: []string{"f"}, Usage: "For git registries: provide filename if yml file is not granted.yml", DefaultText: "granted.yml"},
3131
&cli.IntFlag{Name: "priority", Usage: "The priority for the profile registry", Value: 0},
32-
&cli.StringFlag{Name: "ref", Hidden: true},
32+
&cli.StringFlag{Name: "ref", Usage: "Git ref (branch, tag, or commit) to checkout"},
3333
&cli.BoolFlag{Name: "prefix-all-profiles", Aliases: []string{"pap"}, Usage: "Provide this flag if you want to append registry name to all profiles"},
3434
&cli.BoolFlag{Name: "prefix-duplicate-profiles", Aliases: []string{"pdp"}, Usage: "Provide this flag if you want to append registry name to duplicate profiles"},
3535
&cli.BoolFlag{Name: "write-on-sync-failure", Aliases: []string{"wosf"}, Usage: "Always overwrite AWS config, even if sync fails (DEPRECATED)"},
@@ -93,6 +93,7 @@ var AddCommand = cli.Command{
9393
URL: URL,
9494
Path: pathFlag,
9595
Filename: configFileName,
96+
Ref: ref,
9697
RequiredKeys: requiredKey,
9798
Interactive: true,
9899
})

pkg/granted/registry/add_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package registry
2+
3+
import (
4+
"testing"
5+
6+
"github.com/common-fate/granted/pkg/granted/registry/gitregistry"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestRegistryBackwardCompatibility(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
ref string
14+
wantErr bool
15+
}{
16+
{
17+
name: "existing flow without ref works",
18+
ref: "",
19+
wantErr: false,
20+
},
21+
{
22+
name: "flow with ref works",
23+
ref: "master",
24+
wantErr: false,
25+
},
26+
{
27+
name: "flow with invalid ref fails",
28+
ref: "invalid-branch",
29+
wantErr: true,
30+
},
31+
}
32+
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
// Test that we can create a registry with the ref
36+
opts := gitregistry.Opts{
37+
Name: "test-registry",
38+
URL: "https://github.com/octocat/Hello-World.git",
39+
Filename: "README",
40+
Ref: tt.ref,
41+
}
42+
43+
registry, err := gitregistry.New(opts)
44+
assert.NoError(t, err)
45+
assert.NotNil(t, registry)
46+
47+
// We can't directly test pull() as it's private and uses internal paths
48+
// But we've verified that the registry can be created with or without ref
49+
})
50+
}
51+
}

pkg/granted/registry/gitregistry/gitregistry.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Opts struct {
2020
URL string
2121
Path string
2222
Filename string
23+
Ref string
2324
RequiredKeys []string
2425
Interactive bool
2526
}

pkg/granted/registry/gitregistry/pull.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010
func (r Registry) pull() error {
1111
if _, err := os.Stat(r.clonedTo); err != nil {
1212
// folder doesn't exist yet, so clone the repo and return early.
13-
return git.Clone(r.opts.URL, r.clonedTo)
13+
return git.CloneWithRef(r.opts.URL, r.clonedTo, r.opts.Ref)
1414
}
1515

1616
// if we get here, the folder exists, so pull any changes.
17-
err := git.Pull(r.clonedTo, false)
17+
err := git.PullRef(r.clonedTo, r.opts.Ref, false)
1818
if err != nil {
1919
return err
2020
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package gitregistry
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestRegistryWithRef(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
opts Opts
13+
}{
14+
{
15+
name: "create registry without ref (backward compatibility)",
16+
opts: Opts{
17+
Name: "test-registry",
18+
URL: "https://github.com/octocat/Hello-World.git",
19+
Filename: "README",
20+
// Ref is not set, testing backward compatibility
21+
},
22+
},
23+
{
24+
name: "create registry with empty ref",
25+
opts: Opts{
26+
Name: "test-registry",
27+
URL: "https://github.com/octocat/Hello-World.git",
28+
Filename: "README",
29+
Ref: "",
30+
},
31+
},
32+
{
33+
name: "create registry with ref",
34+
opts: Opts{
35+
Name: "test-registry",
36+
URL: "https://github.com/octocat/Hello-World.git",
37+
Filename: "README",
38+
Ref: "master",
39+
},
40+
},
41+
}
42+
43+
for _, tt := range tests {
44+
t.Run(tt.name, func(t *testing.T) {
45+
// Test that we can create a registry with or without ref
46+
registry, err := New(tt.opts)
47+
assert.NoError(t, err)
48+
assert.NotNil(t, registry)
49+
assert.Equal(t, tt.opts.Ref, registry.opts.Ref)
50+
})
51+
}
52+
}

pkg/granted/registry/registry.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func GetProfileRegistries(interactive bool) ([]loadedRegistry, error) {
3737
URL: r.URL,
3838
Path: r.Path,
3939
Filename: r.Filename,
40+
Ref: r.Ref,
4041
Interactive: interactive,
4142
})
4243

0 commit comments

Comments
 (0)