Select Git revision
main.go 12.24 KiB
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"github.com/niveus/cloud-build-go/cloudbuild"
"github.com/niveus/cloud-build-go/config"
)
func main() {
// Define command-line flags
configPath := flag.String("config", "config/config.json", "Path to config file")
action := flag.String("action", "list-connections", "Action to perform: list-connections, create-gitlab, delete-connection, list-linkable-repos, inspect")
connectionName := flag.String("name", "", "Name for the connection")
flag.Parse()
// Project ID, location, connection type will come from config file
var projectIDValue, locationValue, connTypeValue string
// Load config file
cfg, err := config.LoadConfig(*configPath)
if err != nil {
log.Fatalf("Failed to load config file: %v", err)
}
// Set values from config file
projectIDValue = cfg.ProjectID
locationValue = cfg.Location
connTypeValue = cfg.DefaultConnType
// Log the values that will be used
log.Printf("Config values set to: Project=%s, Location=%s, Type=%s",
projectIDValue, locationValue, connTypeValue)
// Check for required values after loading from config
if projectIDValue == "" {
log.Fatal("project ID is required in the config file")
}
// We no longer need this validation because each case will handle missing connection names by
// automatically using values from the config file
// Validation still happens in each case for more specific handling
ctx := context.Background()
// Create a Cloud Build client using our package
var client *cloudbuild.Client
// Authentication approach Application Default Credentials
client, err = cloudbuild.NewClient(ctx)
if err != nil {
log.Fatalf("Failed to create Cloud Build client: %v", err)
}
// Perform the requested action
switch *action {
case "create-gitlab":
// Find the connection config for the given name
var connConfig *config.ConnectionConfig
for _, conn := range cfg.Connections {
if conn.Name == *connectionName || *connectionName == "" && len(cfg.Connections) == 1 {
connConfig = &conn
break
}
}
if connConfig == nil {
if *connectionName == "" {
log.Fatalf("No connection configuration found in config file. Please add connections to your config file.")
} else {
log.Fatalf("No connection configuration found for name '%s' in config file", *connectionName)
}
}
// Check if the connection config has GitLab configuration
if connConfig.GitlabConfig == nil {
log.Fatalf("Connection '%s' does not have GitLab configuration", connConfig.Name)
}
log.Printf("Creating GitLab connection using config: ProjectID=%s, Location=%s, Name=%s",
projectIDValue, locationValue, connConfig.Name)
fmt.Printf("Creating GitLab connection '%s' in project '%s' (location: '%s')\n",
connConfig.Name, projectIDValue, locationValue)
// Create the GitLab connection using the Cloud Build SDK
err = client.CreateGitLabConnection(
projectIDValue,
locationValue,
connConfig.Name,
connConfig.GitlabConfig,
connConfig.Annotations,
)
if err != nil {
log.Fatalf("Failed to create GitLab connection: %v", err)
}
fmt.Printf("Successfully created GitLab connection: %s\n",
fmt.Sprintf("projects/%s/locations/%s/connections/%s", projectIDValue, locationValue, connConfig.Name))
case "list-connections":
// Use project ID and location from config
log.Printf("Listing connections in project %s (location: %s)", projectIDValue, locationValue)
connections, err := client.ListConnections(projectIDValue, locationValue)
if err != nil {
log.Fatalf("Failed to list connections: %v", err)
}
fmt.Printf("Found %d connections in project %s (location: %s):\n",
len(connections), projectIDValue, locationValue)
for i, conn := range connections {
// Determine connection type based on config
connType := "Unknown"
if conn.GithubConfig != nil {
connType = "GitHub"
} else if conn.GitlabConfig != nil {
connType = "GitLab"
}
fmt.Printf("%d. %s (Type: %s)\n", i+1, conn.Name, connType)
}
case "list-linkable-repos":
// If connection name not provided via command line, get it from config
connNameToList := *connectionName
if connNameToList == "" {
// If there's only one connection in config, use that
if len(cfg.Connections) == 1 {
connNameToList = cfg.Connections[0].Name
fmt.Printf("Using connection name from config: %s\n", connNameToList)
} else if len(cfg.Connections) > 1 {
// If multiple connections, show the available options
fmt.Println("Multiple connections found in config. Please specify which one to use with --name flag:")
for i, conn := range cfg.Connections {
fmt.Printf("%d. %s\n", i+1, conn.Name)
}
os.Exit(1)
} else {
log.Fatalf("No connections found in config file")
}
}
log.Printf("Listing linkable repositories for connection %s in project %s (location: %s)",
connNameToList, projectIDValue, locationValue)
repositories, err := client.ListLinkableRepositories(projectIDValue, locationValue, connNameToList)
if err != nil {
log.Fatalf("Failed to list linkable repositories: %v", err)
}
fmt.Printf("Found %d linkable repositories for connection %s:\n",
len(repositories), connNameToList)
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 "link-repository":
// If connection name not provided via command line, get it from config
connNameToLink := *connectionName
if connNameToLink == "" {
// If there's only one connection in config, use that
if len(cfg.Connections) == 1 {
connNameToLink = cfg.Connections[0].Name
fmt.Printf("Using connection name from config: %s\n", connNameToLink)
} else if len(cfg.Connections) > 1 {
// If multiple connections, show the available options
fmt.Println("Multiple connections found in config. Please specify which one to use with --name flag:")
for i, conn := range cfg.Connections {
fmt.Printf("%d. %s\n", i+1, conn.Name)
}
os.Exit(1)
} else {
log.Fatalf("No connections found in config file")
}
}
// Get the linkable repositories for this connection
log.Printf("Finding linkable repositories for connection %s in project %s (location: %s)",
connNameToLink, projectIDValue, locationValue)
linkableRepos, err := client.ListLinkableRepositories(projectIDValue, locationValue, connNameToLink)
if err != nil {
log.Fatalf("Failed to list linkable repositories: %v", err)
}
if len(linkableRepos) == 0 {
log.Fatalf("No linkable repositories found for connection %s", connNameToLink)
}
// Use the first repository in the list
repoToLink := linkableRepos[0]
// Extract and use a valid repository name
// First check if the repository has a RemoteUri field which usually contains the repo info
var repoName string
if repoToLink.RemoteUri != "" {
// Extract name from the remote URI (e.g., https://gitlab.com/namespace/repo-name)
uriParts := strings.Split(repoToLink.RemoteUri, "/")
if len(uriParts) > 0 {
repoName = uriParts[len(uriParts)-1]
// Remove .git suffix if present
repoName = strings.TrimSuffix(repoName, ".git")
}
}
// If we still don't have a name, use attributes (which is a more reliable way)
if repoName == "" {
// For GitLab repositories, the name is in the attributes
printRepositoryInfo(repoToLink) // Print debug info
// Try common attribute keys that might contain the name
if repoToLink.Annotations != nil {
if name, ok := repoToLink.Annotations["name"]; ok && name != "" {
repoName = name
} else if name, ok := repoToLink.Annotations["repository_name"]; ok && name != "" {
repoName = name
}
}
}
// If we still don't have a name, ask the user to provide one manually
if repoName == "" {
fmt.Println("Unable to automatically determine repository name. Please provide one manually:")
fmt.Print("Repository name: ")
fmt.Scanln(&repoName)
}
// Make sure the repository name is valid
if repoName == "" {
log.Fatalf("Cannot link repository: No valid repository name provided")
}
fmt.Printf("Linking repository: %s\n", repoName)
// We need to get the remote URI for the repository
var remoteURI string
if repoToLink.RemoteUri != "" {
// Use the remote URI from the repository object if available
remoteURI = repoToLink.RemoteUri
fmt.Printf("Using remote URI from repository data: %s\n", remoteURI)
} else {
// Otherwise construct a likely URI based on the connection and repository name
// This is a best guess and may need adjustment for different Git providers
for _, conn := range cfg.Connections {
if conn.Name == connNameToLink && conn.GitlabConfig != nil {
// If we have GitLab config, use the host URI to construct a repo URL
remoteURI = fmt.Sprintf("%s/%s.git", strings.TrimSuffix(conn.GitlabConfig.HostURI, "/"), repoName)
break
}
}
// If we still don't have a remote URI, prompt the user
if remoteURI == "" {
fmt.Println("Please enter the remote URI for the repository (e.g., https://gitlab.com/namespace/repo.git):")
fmt.Print("Remote URI: ")
fmt.Scanln(&remoteURI)
if remoteURI == "" {
log.Fatalf("Remote URI is required to link a repository")
}
} else {
fmt.Printf("Constructed remote URI: %s\n", remoteURI)
}
}
// Link the repository with the remote URI
err = client.LinkRepository(projectIDValue, locationValue, connNameToLink, repoName, remoteURI)
if err != nil {
log.Fatalf("Failed to link repository: %v", err)
}
fmt.Printf("Successfully linked repository %s to connection %s\n", repoName, connNameToLink)
case "delete-connection":
// If connection name not provided via command line, get it from config
connNameToDelete := *connectionName
if connNameToDelete == "" {
// If there's only one connection in config, use that
if len(cfg.Connections) == 1 {
connNameToDelete = cfg.Connections[0].Name
fmt.Printf("Using connection name from config: %s\n", connNameToDelete)
} else if len(cfg.Connections) > 1 {
// If multiple connections, show the available options
fmt.Println("Multiple connections found in config. Please specify which one to delete with --name flag:")
for i, conn := range cfg.Connections {
fmt.Printf("%d. %s\n", i+1, conn.Name)
}
os.Exit(1)
} else {
log.Fatalf("No connections found in config file")
}
}
log.Printf("Deleting connection %s in project %s (location: %s)",
connNameToDelete, projectIDValue, locationValue)
err := client.DeleteConnection(projectIDValue, locationValue, connNameToDelete)
if err != nil {
log.Fatalf("Failed to delete connection: %v", err)
}
fmt.Printf("Successfully deleted connection: %s\n", connNameToDelete)
case "inspect":
// This action provides detailed inspection of a connection's configuration and status
// If connection name not provided via command line, get it from config
connNameToInspect := *connectionName
if connNameToInspect == "" {
// If there's only one connection in config, use that
if len(cfg.Connections) == 1 {
connNameToInspect = cfg.Connections[0].Name
fmt.Printf("Using connection name from config: %s\n", connNameToInspect)
} else if len(cfg.Connections) > 1 {
// If multiple connections, show the available options
fmt.Println("Multiple connections found in config. Please specify which one to inspect with --name flag:")
for i, conn := range cfg.Connections {
fmt.Printf("%d. %s\n", i+1, conn.Name)
}
os.Exit(1)
} else {
log.Fatalf("No connections found in config file")
}
}
log.Printf("Inspecting connection %s in project %s (location: %s)",
connNameToInspect, projectIDValue, locationValue)
fmt.Printf("Inspecting connection %s in project %s (location: %s)...\n\n",
connNameToInspect, projectIDValue, locationValue)
err := client.InspectConnection(projectIDValue, locationValue, connNameToInspect)
if err != nil {
log.Fatalf("Failed to inspect connection: %v", err)
}
default:
log.Fatalf("Unknown action: %s. Supported actions are: list-connections, create-gitlab, delete-connection, list-linkable-repos, inspect", *action)
}
}