This commit is contained in:
“xHuPo” 2025-05-27 11:48:18 +08:00
parent 942a0bd14e
commit 94d71a1e12
8 changed files with 1014 additions and 0 deletions

221
v3.1/main.go Normal file
View file

@ -0,0 +1,221 @@
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
}