Skip to content

Commit d1fad72

Browse files
authored
[WIP] Add helper to wrap 'gh' calls using Exec (#3952)
1 parent d0f554e commit d1fad72

6 files changed

Lines changed: 198 additions & 6 deletions

File tree

.github/workflows/ci-doctor.lock.yml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/daily-team-status.lock.yml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"entries": {}
3+
}

pkg/workflow/action_resolver.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package workflow
22

33
import (
44
"fmt"
5-
"os/exec"
65
"strings"
76

87
"github.com/githubnext/gh-aw/pkg/logger"
@@ -61,7 +60,7 @@ func (r *ActionResolver) resolveFromGitHub(repo, version string) (string, error)
6160
apiPath := fmt.Sprintf("/repos/%s/git/ref/tags/%s", baseRepo, version)
6261
resolverLog.Printf("Querying GitHub API: %s", apiPath)
6362

64-
cmd := exec.Command("gh", "api", apiPath, "--jq", ".object.sha")
63+
cmd := ExecGH("api", apiPath, "--jq", ".object.sha")
6564
output, err := cmd.Output()
6665
if err != nil {
6766
// Try without "refs/tags/" prefix in case version is already a ref

pkg/workflow/gh_helper.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package workflow
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
7+
"github.com/githubnext/gh-aw/pkg/logger"
8+
)
9+
10+
var ghHelperLog = logger.New("workflow:gh_helper")
11+
12+
// ExecGH wraps exec.Command for "gh" CLI calls and ensures proper token configuration.
13+
// It sets GH_TOKEN from GITHUB_TOKEN if GH_TOKEN is not already set.
14+
// This ensures gh CLI commands work in environments where GITHUB_TOKEN is set but GH_TOKEN is not.
15+
//
16+
// Usage:
17+
//
18+
// cmd := ExecGH("api", "/user")
19+
// output, err := cmd.Output()
20+
func ExecGH(args ...string) *exec.Cmd {
21+
cmd := exec.Command("gh", args...)
22+
23+
// Check if GH_TOKEN is already set
24+
ghToken := os.Getenv("GH_TOKEN")
25+
if ghToken != "" {
26+
ghHelperLog.Printf("GH_TOKEN is set, using it for gh CLI")
27+
return cmd
28+
}
29+
30+
// Fall back to GITHUB_TOKEN if available
31+
githubToken := os.Getenv("GITHUB_TOKEN")
32+
if githubToken != "" {
33+
ghHelperLog.Printf("GH_TOKEN not set, using GITHUB_TOKEN as fallback for gh CLI")
34+
// Set GH_TOKEN in the command's environment
35+
cmd.Env = append(os.Environ(), "GH_TOKEN="+githubToken)
36+
} else {
37+
ghHelperLog.Printf("Neither GH_TOKEN nor GITHUB_TOKEN is set, gh CLI will use default authentication")
38+
}
39+
40+
return cmd
41+
}

pkg/workflow/gh_helper_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package workflow
2+
3+
import (
4+
"os"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestExecGH(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
ghToken string
13+
githubToken string
14+
expectGHToken bool
15+
expectValue string
16+
}{
17+
{
18+
name: "GH_TOKEN is set",
19+
ghToken: "gh-token-123",
20+
githubToken: "",
21+
expectGHToken: false, // Should use existing GH_TOKEN from environment
22+
expectValue: "",
23+
},
24+
{
25+
name: "GITHUB_TOKEN is set, GH_TOKEN is not",
26+
ghToken: "",
27+
githubToken: "github-token-456",
28+
expectGHToken: true,
29+
expectValue: "github-token-456",
30+
},
31+
{
32+
name: "Both GH_TOKEN and GITHUB_TOKEN are set",
33+
ghToken: "gh-token-123",
34+
githubToken: "github-token-456",
35+
expectGHToken: false, // Should prefer existing GH_TOKEN
36+
expectValue: "",
37+
},
38+
{
39+
name: "Neither GH_TOKEN nor GITHUB_TOKEN is set",
40+
ghToken: "",
41+
githubToken: "",
42+
expectGHToken: false,
43+
expectValue: "",
44+
},
45+
}
46+
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
// Save original environment
50+
originalGHToken := os.Getenv("GH_TOKEN")
51+
originalGitHubToken := os.Getenv("GITHUB_TOKEN")
52+
defer func() {
53+
os.Setenv("GH_TOKEN", originalGHToken)
54+
os.Setenv("GITHUB_TOKEN", originalGitHubToken)
55+
}()
56+
57+
// Set up test environment
58+
if tt.ghToken != "" {
59+
os.Setenv("GH_TOKEN", tt.ghToken)
60+
} else {
61+
os.Unsetenv("GH_TOKEN")
62+
}
63+
if tt.githubToken != "" {
64+
os.Setenv("GITHUB_TOKEN", tt.githubToken)
65+
} else {
66+
os.Unsetenv("GITHUB_TOKEN")
67+
}
68+
69+
// Execute the helper
70+
cmd := ExecGH("api", "/user")
71+
72+
// Verify the command
73+
if cmd.Path != "gh" && !strings.HasSuffix(cmd.Path, "/gh") {
74+
t.Errorf("Expected command path to be 'gh', got: %s", cmd.Path)
75+
}
76+
77+
// Verify arguments
78+
if len(cmd.Args) != 3 || cmd.Args[1] != "api" || cmd.Args[2] != "/user" {
79+
t.Errorf("Expected args [gh api /user], got: %v", cmd.Args)
80+
}
81+
82+
// Verify environment
83+
if tt.expectGHToken {
84+
found := false
85+
expectedEnv := "GH_TOKEN=" + tt.expectValue
86+
for _, env := range cmd.Env {
87+
if env == expectedEnv {
88+
found = true
89+
break
90+
}
91+
}
92+
if !found {
93+
t.Errorf("Expected environment to contain %s, but it wasn't found", expectedEnv)
94+
}
95+
} else {
96+
// When GH_TOKEN is already set or neither token is set, cmd.Env should be nil (uses parent process env)
97+
if cmd.Env != nil {
98+
t.Errorf("Expected cmd.Env to be nil (inherit parent environment), got: %v", cmd.Env)
99+
}
100+
}
101+
})
102+
}
103+
}
104+
105+
func TestExecGHWithMultipleArgs(t *testing.T) {
106+
// Save original environment
107+
originalGHToken := os.Getenv("GH_TOKEN")
108+
originalGitHubToken := os.Getenv("GITHUB_TOKEN")
109+
defer func() {
110+
os.Setenv("GH_TOKEN", originalGHToken)
111+
os.Setenv("GITHUB_TOKEN", originalGitHubToken)
112+
}()
113+
114+
// Set up test environment
115+
os.Unsetenv("GH_TOKEN")
116+
os.Setenv("GITHUB_TOKEN", "test-token")
117+
118+
// Test with multiple arguments
119+
cmd := ExecGH("api", "repos/owner/repo/git/ref/tags/v1.0", "--jq", ".object.sha")
120+
121+
// Verify command
122+
if cmd.Path != "gh" && !strings.HasSuffix(cmd.Path, "/gh") {
123+
t.Errorf("Expected command path to be 'gh', got: %s", cmd.Path)
124+
}
125+
126+
// Verify all arguments are preserved
127+
expectedArgs := []string{"gh", "api", "repos/owner/repo/git/ref/tags/v1.0", "--jq", ".object.sha"}
128+
if len(cmd.Args) != len(expectedArgs) {
129+
t.Errorf("Expected %d args, got %d: %v", len(expectedArgs), len(cmd.Args), cmd.Args)
130+
}
131+
132+
for i, expected := range expectedArgs {
133+
if i >= len(cmd.Args) || cmd.Args[i] != expected {
134+
t.Errorf("Arg %d: expected %s, got %s", i, expected, cmd.Args[i])
135+
}
136+
}
137+
138+
// Verify environment contains GH_TOKEN
139+
found := false
140+
for _, env := range cmd.Env {
141+
if env == "GH_TOKEN=test-token" {
142+
found = true
143+
break
144+
}
145+
}
146+
if !found {
147+
t.Errorf("Expected environment to contain GH_TOKEN=test-token")
148+
}
149+
}

0 commit comments

Comments
 (0)