diff --git a/cloud-build-go b/cloud-build-go index b50ac0293db637dca87d20350c293fda77324f48..f523b9633a3ed3ee132800bda68f581060b59dcc 100755 Binary files a/cloud-build-go and b/cloud-build-go differ diff --git a/cloudbuild/connection.go b/cloudbuild/connection.go index fb36bfdf7becb6d42189739367811c90bbf552b4..ba9f58407678689129644d705a4f1ef817569fbf 100644 --- a/cloudbuild/connection.go +++ b/cloudbuild/connection.go @@ -3,6 +3,8 @@ package cloudbuild import ( "context" "fmt" + "strings" + "time" "github.com/niveus/cloud-build-go/config" "golang.org/x/oauth2" @@ -161,6 +163,65 @@ func (c *Client) GetConnection(projectID, location, name string) (*cloudbuild.Co return resp, nil } +// InspectConnection retrieves detailed information about a connection and its status +func (c *Client) InspectConnection(projectID, location, name string) error { + // Get the connection details first + conn, err := c.GetConnection(projectID, location, name) + if err != nil { + return fmt.Errorf("error getting connection: %w", err) + } + + // Print basic connection details + fmt.Printf("Connection Name: %s\n", conn.Name) + fmt.Printf("Connection Status: %s\n", statusString(conn.Disabled)) + fmt.Printf("Installation State: %s\n", conn.InstallationState) + + // Print GitLab specific details if it's a GitLab connection + if conn.GitlabConfig != nil { + fmt.Printf("\nGitLab Connection Details:\n") + fmt.Printf("Host URI: %s\n", conn.GitlabConfig.HostUri) + fmt.Printf("Read Auth Username: %s\n", conn.GitlabConfig.ReadAuthorizerCredential.Username) + // Don't print the actual token versions for security reasons + fmt.Printf("Read Token Secret: %s\n", shortenSecretName(conn.GitlabConfig.ReadAuthorizerCredential.UserTokenSecretVersion)) + fmt.Printf("Auth Token Secret: %s\n", shortenSecretName(conn.GitlabConfig.AuthorizerCredential.UserTokenSecretVersion)) + + // Check if webhook is configured + if conn.GitlabConfig.WebhookSecretSecretVersion != "" { + fmt.Printf("Webhook Secret: %s\n", shortenSecretName(conn.GitlabConfig.WebhookSecretSecretVersion)) + } else { + fmt.Println("No webhook configuration found") + } + + // Print annotations if any + if len(conn.Annotations) > 0 { + fmt.Println("\nAnnotations:") + for k, v := range conn.Annotations { + fmt.Printf(" %s: %s\n", k, v) + } + } + } + + return nil +} + +// Helper function to get a status string +func statusString(disabled bool) string { + if disabled { + return "Disabled" + } + return "Enabled" +} + +// Helper function to shorten secret version names for display +func shortenSecretName(secretVersion string) string { + // Just show the last part of the secret name for brevity + parts := strings.Split(secretVersion, "/") + if len(parts) > 2 { + return "..." + parts[len(parts)-2] + "/" + parts[len(parts)-1] + } + return secretVersion +} + // ListConnections lists all connections in the specified project and location using v2 API func (c *Client) ListConnections(projectID, location string) ([]*cloudbuild.Connection, error) { // Format the parent string as required by the API @@ -190,3 +251,59 @@ func (c *Client) DeleteConnection(projectID, location, name string) error { return nil } + +// ListLinkableRepositories lists all repositories that can be linked to the specified connection +// This returns repositories from the source provider (GitHub, GitLab, etc.) that are available +// for linking to Cloud Build +func (c *Client) ListLinkableRepositories(projectID, location, connectionName string) ([]*cloudbuild.Repository, error) { + // Format the connection name as required by the API + fullConnectionName := fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectID, location, connectionName) + + // Add debug output to show connection being used + fmt.Printf("Fetching linkable repositories for connection: %s\n", fullConnectionName) + + // Create a list of all repositories from all pages + var allRepositories []*cloudbuild.Repository + + // Use the correct API method: FetchLinkableRepositories instead of List + call := c.service.Projects.Locations.Connections.FetchLinkableRepositories(fullConnectionName) + + // Set a larger page size to ensure we get more results if available + call = call.PageSize(100) + + // Fetch all repositories using the Pages method for proper pagination handling + fmt.Println("Starting to fetch repositories - this may take a moment...") + + // Create a context with timeout to avoid hanging indefinitely + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + error := call.Pages(ctx, func(resp *cloudbuild.FetchLinkableRepositoriesResponse) error { + if resp.Repositories != nil { + allRepositories = append(allRepositories, resp.Repositories...) + fmt.Printf("Received %d repositories in this page\n", len(resp.Repositories)) + } + return nil + }) + + if error != nil { + return nil, fmt.Errorf("error fetching linkable repositories: %w", error) + } + + // Debug output for results + fmt.Printf("Total linkable repositories found: %d\n", len(allRepositories)) + + if len(allRepositories) == 0 { + fmt.Println("No linkable repositories returned from API. This could be due to:") + fmt.Println("- No repositories exist in the connected GitLab/GitHub account") + fmt.Println("- Authentication/permission issues with the access token") + fmt.Println("- Repository visibility settings in GitLab might be restricted") + fmt.Println("- The repositories haven't been properly indexed by Cloud Build yet") + fmt.Println("\nTry the following troubleshooting steps:") + fmt.Println("1. Verify the access token has appropriate permissions in GitLab") + fmt.Println("2. Check if you can see repositories in the Cloud Build console") + fmt.Println("3. Try re-creating the connection with a new token") + } + + return allRepositories, nil +} diff --git a/config/config.json b/config/config.json index e132f49095e4c41e59e53324b7e9ee73ccd3a9e4..11288616373cace3cfadc330057a3a46f2ab1de1 100644 --- a/config/config.json +++ b/config/config.json @@ -13,8 +13,8 @@ "gitlab_config": { "host_uri": "https://gitlab.niveussolutions.com", "webhook_secret_version": "projects/584597185739/secrets/secreat-key/versions/1", - "read_token_version": "projects/584597185739/secrets/gitlab-access-token/versions/1", - "auth_token_version": "projects/584597185739/secrets/gitlab-access-token/versions/1" + "read_token_version": "projects/584597185739/secrets/gitlab-access-token/versions/2", + "auth_token_version": "projects/584597185739/secrets/gitlab-access-token/versions/2" } } ] diff --git a/main b/main index 4a13deb4149622702aecc8f6e5c80763e2986b31..ded941c47b4fdff50b6a706ba95472d9edfbd893 100755 Binary files a/main and b/main differ diff --git a/main.go b/main.go index 4baaa7bf51e6ed82e13e0dd4c1f2ba4c1e3c8a20..fa1ccc81a595883d773d47b027478a03e45a39de 100644 --- a/main.go +++ b/main.go @@ -68,8 +68,8 @@ func main() { } // Additional validation based on the action - if (*action == "create" || *action == "get" || *action == "delete") && *connectionName == "" { - log.Fatal("name flag is required for create, get, and delete actions") + if (*action == "create" || *action == "get" || *action == "delete" || *action == "listrepos" || *action == "inspect") && *connectionName == "" { + log.Fatal("name flag is required for create, get, delete, listrepos, and inspect actions") } ctx := context.Background() @@ -220,7 +220,34 @@ func main() { } fmt.Printf("Successfully deleted connection: %s\n", *connectionName) + case "listrepos": + repositories, err := client.ListLinkableRepositories(projectIDValue, locationValue, *connectionName) + if err != nil { + log.Fatalf("Failed to list linkable repositories: %v", err) + } + fmt.Printf("Found %d linkable repositories for connection %s:\n", + len(repositories), *connectionName) + for i, repo := range repositories { + fmt.Printf("%d. %s\n", i+1, repo.Name) + if repo.RemoteUri != "" { + fmt.Printf(" URI: %s\n", repo.RemoteUri) + } + // Add a blank line between repositories for better readability + if i < len(repositories)-1 { + fmt.Println() + } + } + + case "inspect": + // This action provides detailed inspection of a connection's configuration and status + fmt.Printf("Inspecting connection %s in project %s (location: %s)...\n\n", + *connectionName, projectIDValue, locationValue) + err := client.InspectConnection(projectIDValue, locationValue, *connectionName) + if err != nil { + log.Fatalf("Failed to inspect connection: %v", err) + } + default: - log.Fatalf("Unknown action: %s. Supported actions are: create, get, list, delete", *action) + log.Fatalf("Unknown action: %s. Supported actions are: create, get, list, delete, listrepos, inspect", *action) } }