Files
plugin-drone-jenkins/plugin.go
T
Bo-Yi Wu cda84c122e feat: refactor plugin config validation and enhance job error handling
- Refactor plugin configuration validation into a dedicated method with improved error messages
- Add utility functions for trimming whitespace from slices and parsing key=value parameters
- Improve error handling for missing or invalid job names and parameters
- Enhance Exec to validate configuration, clean job list, and parse parameters before triggering jobs
- Update job triggering to provide more descriptive error reporting
- Replace deprecated and less precise test assertions with more explicit ones
- Add extensive unit tests for configuration validation, whitespace trimming, parameter parsing, and job triggering (including multiple jobs, parameters, remote token, and job lists with whitespace)
- Improve test coverage and reliability for plugin behavior and edge cases

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2025-12-01 17:39:36 +08:00

118 lines
3.2 KiB
Go

package main
import (
"errors"
"fmt"
"log"
"net/url"
"strings"
)
type (
// Plugin represents the configuration for the Jenkins plugin.
// It contains all necessary credentials and settings to trigger Jenkins jobs.
Plugin struct {
BaseURL string // Jenkins server base URL
Username string // Jenkins username for authentication
Token string // Jenkins API token for authentication
RemoteToken string // Optional remote trigger token for additional security
Job []string // List of Jenkins job names to trigger
Insecure bool // Whether to skip TLS certificate verification
Parameters []string // Job parameters in key=value format
}
)
// trimWhitespaceFromSlice removes empty and whitespace-only strings from a slice.
// It returns a new slice containing only non-empty trimmed strings.
func trimWhitespaceFromSlice(items []string) []string {
result := make([]string, 0, len(items))
for _, item := range items {
trimmed := strings.TrimSpace(item)
if trimmed != "" {
result = append(result, trimmed)
}
}
return result
}
// parseParameters converts a slice of key=value strings into url.Values.
// It logs a warning for any parameters that don't match the expected format.
func parseParameters(params []string) url.Values {
values := url.Values{}
for _, param := range params {
parts := strings.SplitN(param, "=", 2)
if len(parts) != 2 {
log.Printf("warning: skipping invalid parameter format (expected key=value): %q", param)
continue
}
key := strings.TrimSpace(parts[0])
value := parts[1] // Keep value as-is to preserve intentional spaces
if key == "" {
log.Printf("warning: skipping parameter with empty key: %q", param)
continue
}
values.Add(key, value)
}
return values
}
// validateConfig checks that all required plugin configuration is present.
// It returns a descriptive error if any required field is missing.
func (p Plugin) validateConfig() error {
if p.BaseURL == "" {
return errors.New("jenkins base URL is required")
}
if p.Username == "" {
return errors.New("jenkins username is required")
}
if p.Token == "" {
return errors.New("jenkins API token is required")
}
return nil
}
// Exec executes the plugin by triggering the configured Jenkins jobs.
// It validates the configuration, parses parameters, and triggers each job sequentially.
// Returns an error if validation fails or any job trigger fails.
func (p Plugin) Exec() error {
// Validate required configuration
if err := p.validateConfig(); err != nil {
return fmt.Errorf("configuration error: %w", err)
}
// Clean and validate job list
jobs := trimWhitespaceFromSlice(p.Job)
if len(jobs) == 0 {
return errors.New("at least one Jenkins job name is required")
}
// Set up authentication
auth := &Auth{
Username: p.Username,
Token: p.Token,
}
// Initialize Jenkins client
jenkins := NewJenkins(auth, p.BaseURL, p.RemoteToken, p.Insecure)
// Parse job parameters
params := parseParameters(p.Parameters)
// Trigger each job
for _, jobName := range jobs {
if err := jenkins.trigger(jobName, params); err != nil {
return fmt.Errorf("failed to trigger job %q: %w", jobName, err)
}
log.Printf("successfully triggered job: %s", jobName)
}
return nil
}