Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions services/github_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,44 @@ func GetGraphQLClient() (*graphql.Client, error) {
return client, nil
}

// GetGraphQLClientForOrg returns a GitHub GraphQL API client authenticated for a specific organization
func GetGraphQLClientForOrg(org string) (*graphql.Client, error) {
// Check if we have a cached token for this org
if token, ok := installationTokenCache[org]; ok && token != "" {
client := graphql.NewClient("https://api.github.com/graphql", &http.Client{
Transport: &transport{token: token},
})
return client, nil
}

// Get installation ID for the organization
installationID, err := getInstallationIDForOrg(org)
if err != nil {
return nil, fmt.Errorf("failed to get installation ID for org %s: %w", org, err)
}

// Get JWT token
token, err := getOrRefreshJWT()
if err != nil {
return nil, fmt.Errorf("failed to get JWT: %w", err)
}

// Get installation access token
installationToken, err := getInstallationAccessToken(installationID, token, HTTPClient)
if err != nil {
return nil, fmt.Errorf("failed to get installation token for org %s: %w", org, err)
}

// Cache the token
installationTokenCache[org] = installationToken

// Create and return client
client := graphql.NewClient("https://api.github.com/graphql", &http.Client{
Transport: &transport{token: installationToken},
})
return client, nil
}

// getOrRefreshJWT returns a valid JWT token, generating a new one if expired
func getOrRefreshJWT() (string, error) {
// Check if we have a valid cached JWT
Expand Down
5 changes: 3 additions & 2 deletions services/github_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ func GetFilesChangedInPr(owner string, repo string, pr_number int) ([]ChangedFil
}
}

client, err := GetGraphQLClient()
// Use org-specific client to ensure we have the right installation token
client, err := GetGraphQLClientForOrg(owner)
if err != nil {
return nil, fmt.Errorf("failed to get GraphQL client: %w", err)
return nil, fmt.Errorf("failed to get GraphQL client for org %s: %w", owner, err)
}
ctx := context.Background()

Expand Down
28 changes: 26 additions & 2 deletions services/github_write_to_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ func normalizeRepoName(repoName string) string {
return repoOwner() + "/" + repoName
}

// normalizeRefPath ensures a ref path is in the correct format for different GitHub API calls.
// For GetRef: expects "heads/main" (no "refs/" prefix)
// For UpdateRef: expects "refs/heads/main" (full ref path)
func normalizeRefPath(branchPath string, fullPath bool) string {
// Strip "refs/" prefix if present
refPath := strings.TrimPrefix(branchPath, "refs/")

// Ensure "heads/" prefix exists (unless it's a tag)
if !strings.HasPrefix(refPath, "heads/") && !strings.HasPrefix(refPath, "tags/") {
refPath = "heads/" + refPath
}

// Add "refs/" prefix back if full path is needed
if fullPath {
return "refs/" + refPath
}
return refPath
}

// AddFilesToTargetRepoBranch uploads files to the target repository branch
// using the specified commit strategy (direct or via pull request).
func AddFilesToTargetRepoBranch() {
Expand Down Expand Up @@ -344,8 +363,11 @@ func createCommitTree(ctx context.Context, client *github.Client, targetBranch U

retryDelay := time.Duration(initialRetryDelay) * time.Millisecond

// GetRef expects "heads/main" format (no "refs/" prefix)
refPath := normalizeRefPath(targetBranch.BranchPath, false)

for attempt := 1; attempt <= maxRetries; attempt++ {
ref, _, err = client.Git.GetRef(ctx, owner, repoName, targetBranch.BranchPath)
ref, _, err = client.Git.GetRef(ctx, owner, repoName, refPath)
if err == nil && ref != nil {
break // Success
}
Expand Down Expand Up @@ -405,8 +427,10 @@ func createCommit(ctx context.Context, client *github.Client, targetBranch Uploa
}

// Update branch ref directly (no second GET)
// UpdateRef expects full ref path "refs/heads/main"
fullRefPath := normalizeRefPath(targetBranch.BranchPath, true)
ref := &github.Reference{
Ref: github.String(targetBranch.BranchPath), // e.g., "refs/heads/main"
Ref: github.String(fullRefPath),
Object: &github.GitObject{SHA: github.String(newCommit.GetSHA())},
}
if _, _, err := client.Git.UpdateRef(ctx, owner, repoName, ref, false); err != nil {
Expand Down
6 changes: 5 additions & 1 deletion services/webhook_handler_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ func simpleVerifySignature(sigHeader string, body, secret []byte) bool {

// RetrieveFileContentsWithConfigAndBranch fetches file contents from a specific branch
func RetrieveFileContentsWithConfigAndBranch(ctx context.Context, filePath string, branch string, repoOwner string, repoName string) (*github.RepositoryContent, error) {
client := GetRestClient()
// Use org-specific client to ensure we have the right installation token
client, err := GetRestClientForOrg(repoOwner)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client for org %s: %w", repoOwner, err)
}

fileContent, _, _, err := client.Repositories.GetContents(
ctx,
Expand Down