feat: improve logging and added verbose output

This commit is contained in:
Simon Cornet 2025-03-05 08:45:55 +01:00
commit e0f87d7ef9
6 changed files with 104 additions and 46 deletions

View file

@ -2,7 +2,6 @@ package main
import (
"fmt"
"log"
"os/exec"
"strings"
"sync"
@ -54,10 +53,6 @@ func checkoutRepositories(repositories []Repository, concurrency int) {
switch {
case strings.Contains(string(repoStatus), "No such file or directory"):
// update the progress bar
descriptionPrefixPre := "Cloning repository "
descriptionPrefix := descriptionPrefixPre + repoName + " ..."
// clone the repo
cloneRepository := func(repoDestination string, url string) (string, error) {
cloneCmd := exec.Command("git", "clone", url, repoDestination)
@ -67,29 +62,41 @@ func checkoutRepositories(repositories []Repository, concurrency int) {
}
_, err := cloneRepository(repoDestination, url)
if err != nil {
log.Printf("ERROR: %v\n", err)
logPrint("ERROR: %v\n", err)
}
// set a lock, increment counters and unlock
// set a lock, increment counters, update progressbar and unlock
mu.Lock()
clonedCount++
bar.Describe(descriptionPrefix)
progressBarAdd(1)
if !verbose {
// update the progress bar
descriptionPrefixPre := "Cloning repository "
descriptionPrefix := descriptionPrefixPre + repoName + " ..."
bar.Describe(descriptionPrefix)
progressBarAdd(1)
}
mu.Unlock()
// pull the latest
case strings.Contains(string(repoStatus), url):
pullRepository(repoName, repoDestination)
progressBarAdd(1)
if !verbose {
descriptionPrefixPre := "Pulling repository "
descriptionPrefix := descriptionPrefixPre + repoName + " ..."
bar.Describe(descriptionPrefix)
progressBarAdd(1)
}
default:
log.Printf("ERROR: decided not to clone or pull repository %v\n", repoName)
log.Printf("ERROR: this is why: %v\n", repoStatus)
logPrint("ERROR: decided not to clone or pull repository" + repoName, nil)
logPrint("ERROR: this is why: " + repoStatus, nil)
// set a lock, increment counters and unlock
mu.Lock()
errorCount++
progressBarAdd(1)
if !verbose {
progressBarAdd(1)
}
mu.Unlock()
}
}(repo)
@ -101,10 +108,6 @@ func checkoutRepositories(repositories []Repository, concurrency int) {
func pullRepository(repoName string, repoDestination string) {
// update the progress bar
descriptionPrefixPre := "Pulling repository "
descriptionPrefix := descriptionPrefixPre + repoName + " ..."
// find remote
findRemote := func(repoDestination string) (string, error) {
remoteCmd := exec.Command("git", "-C", repoDestination, "remote", "show")
@ -124,7 +127,6 @@ func pullRepository(repoName string, repoDestination string) {
// set a lock, increment counters and unlock
mu.Lock()
bar.Describe(descriptionPrefix)
pulledCount++
mu.Unlock()
@ -141,7 +143,7 @@ func pullRepository(repoName string, repoDestination string) {
pullErrorMsg = append(pullErrorMsg, repoDestination)
default:
log.Printf("ERROR: pulling %v\n", err)
logPrint("ERROR: pulling " + repoName, nil)
}
}
}

View file

@ -29,7 +29,7 @@ func fetchRepositoriesGitlab() ([]Repository, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("creating request: %v\n", err)
return nil, fmt.Errorf("ERROR: creating request: %v\n", err)
}
req.Header.Set("PRIVATE-TOKEN", gitlabToken)
@ -37,21 +37,21 @@ func fetchRepositoriesGitlab() ([]Repository, error) {
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("making request: %v\n", err)
return nil, fmt.Errorf("ERROR: making request: %v\n", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API request failed with status: %d\n", resp.StatusCode)
return nil, fmt.Errorf("ERROR: API request failed with status: %d\n", resp.StatusCode)
}
var repositories []Repository
if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil {
return nil, fmt.Errorf("decoding response: %v\n", err)
return nil, fmt.Errorf("ERROR: decoding response: %v\n", err)
}
if len(repositories) < 1 {
return repositories, fmt.Errorf("no repositories found\n")
return repositories, fmt.Errorf("ERROR: no repositories found\n")
}
return repositories, nil

View file

@ -16,6 +16,7 @@ func manageArguments() {
var destinationFlag = flag.String("destination", "$HOME/Documents", "Specify where to check the repositories out\n example: -destination=$HOME/repos\nenv = GOGITLABBER_DESTINATION\n")
var hostFlag = flag.String("gitlab-url", "gitlab.com", "Specify GitLab host\n example: -gitlab-url=gitlab.com\nenv = GITLAB_URL\n")
var tokenFlag = flag.String("gitlab-api-token", "", "Specify GitLab API token\n example: -gitlab-api=glpat-xxxx\nenv = GITLAB_API_TOKEN\n")
var verboseFlag = flag.Bool("verbose", false, "Specify verbosity\n example: -verbose=true\nenv = GOGITLABBER_VERBOSE\n")
flag.Parse()
@ -25,32 +26,50 @@ func manageArguments() {
gitlabToken = *tokenFlag
includeArchived = *archivedFlag
repoDestinationPre = *destinationFlag
verbose = *verboseFlag
// manage verbosity option
switch envVerbose := os.Getenv("GOGITLABBER_VERBOSE"); {
case envVerbose != "":
var err error
verbose, err = strconv.ParseBool(envVerbose)
logPrint("CONFIG: verbose option found", nil)
if err != nil {
logFatal("FATAL: config; not a valid bool", nil)
}
default:
flag.Usage()
logFatal("FATAL: config; no verbose option found", nil)
}
// manage gitlab api option
switch envToken := os.Getenv("GITLAB_API_TOKEN"); {
case envToken != "":
gitlabToken = envToken
logPrint("CONFIG: Gitlab API Token found", nil)
default:
flag.Usage()
log.Printf("FATAL: config; gitlab api token not found\n")
logFatal("CONFIG: Giltab API Token not found", nil)
}
// manage gitlab url option
switch envHost := os.Getenv("GITLAB_URL"); {
case envHost != "":
gitlabHost = envHost
logPrint("CONFIG: Gitlab host found", nil)
default:
flag.Usage()
log.Fatalf("FATAL: config; gitlab host not found\n")
logFatal("CONFIG: Gitlab host not found", nil)
}
// manage destination option
switch envRepoDest := os.Getenv("GOGITLABBER_DESTINATION"); {
case envRepoDest != "":
repoDestinationPre = envRepoDest
logPrint("CONFIG: destination found", nil)
default:
flag.Usage()
log.Fatalf("FATAL: config; destination not found\n")
logFatal("CONFIG: destination not found", nil)
}
// add slash 🎩🎸 if not provided
@ -60,36 +79,41 @@ func manageArguments() {
}
// manage concurrency option
switch envConcurrency := os.Getenv("GOGITLABBER_CONCURRENCY"); {
case envConcurrency == "":
concurrency = 15
case envConcurrency != "":
switch envConcurrency := os.Getenv("GOGITLABBER_CONCURRENCY"); {
case envConcurrency == "":
concurrency = 15
case envConcurrency != "":
concurrencyValue, err := strconv.Atoi(envConcurrency)
if err != nil {
log.Fatalf("FATAL: invalid concurrency value in environment: %v", err)
logFatal("invalid concurrency value in environment: %v", err)
}
concurrency = concurrencyValue
default:
flag.Usage()
log.Fatalf("FATAL: config; concurrency not found\n")
}
logPrint("CONFIG: concurrency option found", nil)
default:
flag.Usage()
log.Fatalln("FATAL: config; concurrency not found")
}
// manage archived option
switch envArchived := os.Getenv("GOGITLABBER_ARCHIVED"); {
case envArchived == "":
includeArchived = "excluded"
logPrint("CONFIG: archive option found", nil)
case envArchived == "any":
includeArchived = envArchived
logPrint("CONFIG: archive option found", nil)
case envArchived == "exclusive":
includeArchived = envArchived
logPrint("CONFIG: archive option found", nil)
case envArchived == "excluded":
includeArchived = envArchived
logPrint("CONFIG: archive option found", nil)
default:
flag.Usage()
log.Fatalf("FATAL: config; no or wrong archive option found\n")
logFatal("FATAL: config; no or wrong archive option found", nil)
}
}

View file

@ -1,6 +1,9 @@
package main
import "log"
import (
"io"
"log"
)
// userdata
var concurrency int
@ -8,6 +11,7 @@ var gitlabHost string
var gitlabToken string
var includeArchived string
var repoDestinationPre string
var verbose bool
// keep count 🧛
var clonedCount int
@ -27,16 +31,26 @@ func main() {
manageArguments()
// check for git
verifyGitAvailable()
err := verifyGitAvailable()
if err != nil {
logFatal("FATAL: git not found in path: %v", err)
}
logPrint("Git is available. Proceeding with the program.", nil)
// fetch repository information from gitlab
repositories, err := fetchRepositoriesGitlab()
if err != nil {
log.Fatalf("FATAL: %v", err)
if err != nil {
logFatal("FATAL: %v", err)
}
logPrint("Logged into GitLab, Repositories found. Proceeding with the program.", nil)
// print progressbar ony if not in verbose mode
if !verbose {
progressBar(repositories)
log.SetOutput(io.Discard)
}
// manage found repositories
progressBar(repositories)
checkoutRepositories(repositories, concurrency)
printSummary()
printPullError(pullErrorMsg)

View file

@ -41,7 +41,7 @@ func progressBar(repositories []Repository) {
func progressBarAdd(amount int) {
if err := bar.Add(amount); err != nil {
log.Printf("ERROR: Progress bar update error: %v\n", err)
logPrint("ERROR: Progress bar update error: %v\n", err)
}
}
@ -66,3 +66,21 @@ func printPullError(pullErrorMsg []string) {
}
}
}
func logPrint(message string, err error) {
if verbose == true {
if err != nil {
log.Printf("gogitlabber | %v error: %v\n", message, err)
}
if err == nil {
log.Printf("gogitlabber | %v\n", message)
}
}
}
func logFatal(message string, err error) {
if err != nil {
log.Fatalf("gogitlabber | FATAL: %v error: %v\n", message, err)
}
log.Fatalf("gogitlabber | FATAL: %v\n", message)
}

View file

@ -1,13 +1,13 @@
package main
import (
"log"
"os/exec"
)
func verifyGitAvailable() {
func verifyGitAvailable() error {
_, err := exec.LookPath("git")
if err != nil {
log.Fatal("could not find git in path")
return err
}
return nil
}