summaryrefslogtreecommitdiff
path: root/internal/github
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-11 18:24:16 +0200
committerPaul Buetow <paul@buetow.org>2026-03-11 18:24:16 +0200
commit1c12325c64bb734dd0ae95a0c803de1ff45d2b4c (patch)
treee57c97d5b77e9b542dea887176bb11b533501b23 /internal/github
parent224d9059e5c51eaab9295ea5266c64c4dd7fa218 (diff)
fix(github): stop logging token diagnostics
Diffstat (limited to 'internal/github')
-rw-r--r--internal/github/github.go60
-rw-r--r--internal/github/github_test.go111
2 files changed, 135 insertions, 36 deletions
diff --git a/internal/github/github.go b/internal/github/github.go
index 238b486..c886e43 100644
--- a/internal/github/github.go
+++ b/internal/github/github.go
@@ -19,45 +19,35 @@ type Client struct {
// NewClient creates a new GitHub API client
func NewClient(token, org string) Client {
- // If no token provided, try other sources
- if token == "" {
- fmt.Println(" No token in config, trying environment variable...")
- // Try environment variable
- token = os.Getenv("GITHUB_TOKEN")
-
- // If still no token, try reading from file
- if token == "" {
- fmt.Println(" No GITHUB_TOKEN env var, trying ~/.gitsyncer_github_token file...")
- home, err := os.UserHomeDir()
- if err == nil {
- tokenFile := filepath.Join(home, ".gitsyncer_github_token")
- data, err := os.ReadFile(tokenFile)
- if err == nil {
- token = strings.TrimSpace(string(data))
- fmt.Printf(" Loaded token from file (length: %d)\n", len(token))
- // Check for common issues
- if strings.Contains(token, "\n") || strings.Contains(token, "\r") {
- fmt.Println(" Warning: Token contains newline characters")
- }
- if strings.HasPrefix(token, " ") || strings.HasSuffix(token, " ") {
- fmt.Println(" Warning: Token has leading/trailing spaces")
- }
- } else {
- fmt.Printf(" Could not read token file: %v\n", err)
- }
- }
- } else {
- fmt.Printf(" Loaded token from env var (length: %d)\n", len(token))
- }
- } else {
- fmt.Printf(" Using token from config (length: %d)\n", len(token))
- }
return Client{
- token: token,
+ token: loadToken(token),
org: org,
}
}
+func loadToken(token string) string {
+ if token != "" {
+ return strings.TrimSpace(token)
+ }
+
+ if envToken := os.Getenv("GITHUB_TOKEN"); envToken != "" {
+ return strings.TrimSpace(envToken)
+ }
+
+ home, err := os.UserHomeDir()
+ if err != nil {
+ return ""
+ }
+
+ tokenFile := filepath.Join(home, ".gitsyncer_github_token")
+ data, err := os.ReadFile(tokenFile)
+ if err != nil {
+ return ""
+ }
+
+ return strings.TrimSpace(string(data))
+}
+
// CreateRepoRequest represents the request to create a repository
type CreateRepoRequest struct {
Name string `json:"name"`
@@ -94,7 +84,6 @@ func (c *Client) RepoExists(repoName string) (bool, error) {
url := fmt.Sprintf("https://api.github.com/repos/%s/%s", c.org, repoName)
fmt.Printf(" Checking URL: %s\n", url)
- fmt.Printf(" Token present: %v (length: %d)\n", c.token != "", len(c.token))
req, err := http.NewRequest("GET", url, nil)
if err != nil {
@@ -118,7 +107,6 @@ func (c *Client) RepoExists(repoName string) (bool, error) {
// Read the response body for 401 errors
body, _ := io.ReadAll(resp.Body)
fmt.Printf(" 401 Unauthorized - Response: %s\n", string(body))
- fmt.Printf(" Authorization header: %s\n", req.Header.Get("Authorization"))
return false, fmt.Errorf("authentication failed (401): %s", string(body))
}
diff --git a/internal/github/github_test.go b/internal/github/github_test.go
new file mode 100644
index 0000000..a65e00a
--- /dev/null
+++ b/internal/github/github_test.go
@@ -0,0 +1,111 @@
+package github
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+ "os"
+ "strings"
+ "testing"
+)
+
+type roundTripFunc func(*http.Request) (*http.Response, error)
+
+func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
+ return f(req)
+}
+
+func TestNewClient_DoesNotLogTokenDetails(t *testing.T) {
+ t.Setenv("GITHUB_TOKEN", " test-token \n")
+
+ output := captureStdout(t, func() {
+ client := NewClient("", "snonux")
+ if !client.HasToken() {
+ t.Fatal("expected token to be loaded")
+ }
+ if client.token != "test-token" {
+ t.Fatalf("expected trimmed token, got %q", client.token)
+ }
+ })
+
+ if output != "" {
+ t.Fatalf("expected no stdout output, got %q", output)
+ }
+}
+
+func TestClientRepoExists_DoesNotLogAuthorizationHeaderOnUnauthorized(t *testing.T) {
+ const token = "super-secret-token"
+
+ originalTransport := http.DefaultTransport
+ http.DefaultTransport = roundTripFunc(func(req *http.Request) (*http.Response, error) {
+ if got := req.Header.Get("Authorization"); got != "Bearer "+token {
+ t.Fatalf("expected bearer token header, got %q", got)
+ }
+
+ return &http.Response{
+ StatusCode: http.StatusUnauthorized,
+ Body: io.NopCloser(strings.NewReader(`{"message":"bad credentials"}`)),
+ Header: make(http.Header),
+ }, nil
+ })
+ t.Cleanup(func() {
+ http.DefaultTransport = originalTransport
+ })
+
+ client := NewClient(token, "snonux")
+
+ output := captureStdout(t, func() {
+ exists, err := client.RepoExists("gitsyncer")
+ if err == nil {
+ t.Fatal("expected unauthorized error")
+ }
+ if exists {
+ t.Fatal("expected repo existence check to fail")
+ }
+ if !strings.Contains(err.Error(), "authentication failed (401)") {
+ t.Fatalf("expected 401 error, got %v", err)
+ }
+ })
+
+ if strings.Contains(output, token) {
+ t.Fatalf("expected output to omit token, got %q", output)
+ }
+ if strings.Contains(output, "Authorization header") {
+ t.Fatalf("expected output to omit authorization header log, got %q", output)
+ }
+}
+
+func captureStdout(t *testing.T, fn func()) string {
+ t.Helper()
+
+ originalStdout := os.Stdout
+ reader, writer, err := os.Pipe()
+ if err != nil {
+ t.Fatalf("failed to create pipe: %v", err)
+ }
+
+ os.Stdout = writer
+ defer func() {
+ os.Stdout = originalStdout
+ }()
+
+ outputCh := make(chan string, 1)
+ go func() {
+ var buffer bytes.Buffer
+ _, _ = io.Copy(&buffer, reader)
+ outputCh <- buffer.String()
+ }()
+
+ fn()
+
+ if err := writer.Close(); err != nil {
+ t.Fatalf("failed to close stdout writer: %v", err)
+ }
+
+ output := <-outputCh
+ if err := reader.Close(); err != nil {
+ t.Fatalf("failed to close stdout reader: %v", err)
+ }
+
+ return output
+}