221 lines
6 KiB
Go
221 lines
6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
// Version information
|
|
var (
|
|
Version = "v3.1.0"
|
|
BuildTime = "unknown"
|
|
GitCommit = "unknown"
|
|
)
|
|
|
|
func main() {
|
|
// Print version information
|
|
log.Printf("[MAIN] Jenkins-Cron %s (built: %s, commit: %s)", Version, BuildTime, GitCommit)
|
|
|
|
// Get configuration path
|
|
configPath := GetConfigPath()
|
|
log.Printf("[MAIN] Loading configuration from: %s", configPath)
|
|
|
|
// Load configuration with proper error handling
|
|
cfg, err := LoadConfig(configPath)
|
|
if err != nil {
|
|
log.Fatalf("[MAIN] Failed to load configuration: %v", err)
|
|
}
|
|
|
|
// Validate configuration
|
|
if len(cfg.Jobs) == 0 {
|
|
log.Fatalf("[MAIN] No jobs configured")
|
|
}
|
|
if cfg.Jenkins.URL == "" {
|
|
log.Fatalf("[MAIN] No Jenkins URL configured")
|
|
}
|
|
|
|
// Create a cancelable context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Setup signal handling for graceful shutdown
|
|
setupSignalHandler(cancel)
|
|
|
|
// Clean Gradle caches
|
|
if err := cleanGradleCaches(ctx, cfg); err != nil {
|
|
log.Printf("[MAIN] Warning: Error cleaning Gradle caches: %v", err)
|
|
}
|
|
|
|
// Create Jenkins client
|
|
var jenkinsSvc JenkinsService = NewJenkinsClient(&cfg.Jenkins)
|
|
|
|
// Trigger Jenkins jobs
|
|
if err := triggerJobs(ctx, jenkinsSvc, cfg); err != nil {
|
|
log.Printf("[MAIN] Error triggering jobs: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
log.Printf("[MAIN] All operations completed successfully")
|
|
}
|
|
|
|
// setupSignalHandler sets up handling of OS signals for graceful shutdown
|
|
func setupSignalHandler(cancel context.CancelFunc) {
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
sig := <-c
|
|
log.Printf("[MAIN] Received signal %v, shutting down gracefully", sig)
|
|
cancel()
|
|
|
|
// Force exit after 5 seconds if graceful shutdown is taking too long
|
|
time.Sleep(5 * time.Second)
|
|
log.Printf("[MAIN] Forced shutdown after timeout")
|
|
os.Exit(1)
|
|
}()
|
|
}
|
|
|
|
// cleanGradleCaches cleans Gradle caches in parallel
|
|
func cleanGradleCaches(ctx context.Context, cfg *Config) error {
|
|
if len(cfg.Gradle.Caches) == 0 {
|
|
log.Printf("[MAIN] No Gradle caches configured, skipping cleanup")
|
|
return nil
|
|
}
|
|
|
|
log.Printf("[MAIN] Starting Gradle cache cleanup")
|
|
stats := &RemoveStats{}
|
|
|
|
var wg sync.WaitGroup
|
|
var errs []error
|
|
var mu sync.Mutex
|
|
|
|
for _, cache := range cfg.Gradle.Caches {
|
|
wg.Add(1)
|
|
go func(cachePath string) {
|
|
defer wg.Done()
|
|
|
|
log.Printf("[MAIN] Cleaning cache: %s", cachePath)
|
|
err := Remove(ctx, cachePath, RemoveOptions{
|
|
Workers: 4,
|
|
Retries: 3,
|
|
Stats: stats,
|
|
Logger: log.Printf,
|
|
Progress: func(current int64) {
|
|
if current > 0 && current%100 == 0 {
|
|
log.Printf("[MAIN] Removed %d files from %s", current, cachePath)
|
|
}
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("[MAIN] Error removing cache %s: %v", cachePath, err)
|
|
mu.Lock()
|
|
errs = append(errs, fmt.Errorf("cache %s: %w", cachePath, err))
|
|
mu.Unlock()
|
|
}
|
|
}(cache)
|
|
}
|
|
|
|
wg.Wait()
|
|
log.Printf("[MAIN] Cache cleanup summary: Total=%d Success=%d Failed=%d",
|
|
stats.Total, stats.Success, stats.Failed)
|
|
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("encountered %d errors during cache cleanup", len(errs))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// triggerJobs triggers all configured Jenkins jobs
|
|
func triggerJobs(ctx context.Context, js JenkinsService, cfg *Config) error {
|
|
log.Printf("[MAIN] Starting to trigger %d Jenkins jobs", len(cfg.Jobs))
|
|
|
|
var wg sync.WaitGroup
|
|
errChan := make(chan error, len(cfg.Jobs))
|
|
|
|
// Create a timeout context for the entire operation
|
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
|
|
defer cancel()
|
|
|
|
for _, job := range cfg.Jobs {
|
|
wg.Add(1)
|
|
go func(jobName string) {
|
|
defer wg.Done()
|
|
|
|
if err := processJob(ctx, js, cfg, jobName); err != nil {
|
|
log.Printf("[MAIN] Error processing job %s: %v", jobName, err)
|
|
errChan <- fmt.Errorf("job %s: %w", jobName, err)
|
|
}
|
|
}(job)
|
|
}
|
|
|
|
// Wait for all jobs to complete
|
|
wg.Wait()
|
|
close(errChan)
|
|
|
|
// Collect errors
|
|
var errs []error
|
|
for err := range errChan {
|
|
errs = append(errs, err)
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("encountered %d errors while triggering jobs", len(errs))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// processJob handles the processing of a single Jenkins job
|
|
func processJob(ctx context.Context, js JenkinsService, cfg *Config, job string) error {
|
|
log.Printf("[MAIN] Processing job: %s", job)
|
|
|
|
// Fetch build with retry
|
|
build, err := js.FetchBuildWithRetry(ctx, job, cfg.Retries)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to fetch build: %w", err)
|
|
}
|
|
|
|
log.Printf("[MAIN] Successfully fetched build #%d for job: %s", build.Number, job)
|
|
|
|
// Extract and merge parameters
|
|
latestParams := ExtractBuildParams(build)
|
|
merged := MergeParams(latestParams, cfg.Jenkins.DefaultParameters)
|
|
|
|
// Trigger build with default parameters
|
|
log.Printf("[MAIN] Triggering build with default parameters for job: %s", job)
|
|
if err := js.TriggerBuildWithRetry(ctx, build, StringMapToInterfaceMap(merged), cfg.Retries); err != nil {
|
|
log.Printf("[MAIN] Failed to trigger build with default parameters for job %s: %v", job, err)
|
|
// Continue with special parameters even if default build fails
|
|
}
|
|
|
|
// Process special parameters
|
|
for _, special := range cfg.Jenkins.SpecialParameters {
|
|
if IsSubset(latestParams, special) {
|
|
log.Printf("[MAIN] Skipping special parameters build (subset of latest) for %s: %+v", job, special)
|
|
continue
|
|
}
|
|
|
|
log.Printf("[MAIN] Triggering build with special parameters for job %s: %+v", job, special)
|
|
specialLatestParams := make(map[string]string)
|
|
for k, v := range latestParams {
|
|
specialLatestParams[k] = v
|
|
}
|
|
for k, v := range special {
|
|
specialLatestParams[k] = fmt.Sprintf("%v", v)
|
|
}
|
|
|
|
speciaMerged := MergeParams(specialLatestParams, cfg.Jenkins.DefaultParameters)
|
|
if err := js.TriggerBuildWithRetry(ctx, build, StringMapToInterfaceMap(speciaMerged), cfg.Retries); err != nil {
|
|
log.Printf("[MAIN] Failed to trigger build with special parameters for job %s: %v", job, err)
|
|
// Continue with other special parameters even if one fails
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|