jenkins-cron/v3/jenkins.go
“xHuPo” 55f3e2b3c6 add v3
2025-05-22 11:43:23 +08:00

200 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"path"
"strings"
"time"
)
type Build struct {
Actions []Action `json:"actions"`
Number int `json:"number"`
URL string `json:"url"`
}
type Action struct {
Class string `json:"_class"`
Parameters []Parameter `json:"parameters,omitempty"`
}
type Parameter struct {
Name string `json:"name"`
Value interface{} `json:"value"`
}
type JenkinsClient struct {
Client *http.Client
Config *Jenkins
}
type JenkinsService interface {
FetchBuild(job string) (*Build, error)
TriggerBuild(build *Build, params map[string]interface{}) error
}
func NewJenkinsClient(cfg *Jenkins) *JenkinsClient {
return &JenkinsClient{
Client: &http.Client{Timeout: 10 * time.Second},
Config: cfg,
}
}
func (c *JenkinsClient) buildAPIURL(job string) string {
return fmt.Sprintf("%s://%s/job/%s/%s/api/json",
c.Config.Schema,
c.Config.URL,
job,
c.Config.Number,
)
}
func (c *JenkinsClient) FetchBuild(job string) (*Build, error) {
api := c.buildAPIURL(job)
req, err := http.NewRequest("GET", api, nil)
if err != nil {
log.Printf("[jenkins] Failed to create GET request %s: %v", job, err)
return nil, err
}
req.SetBasicAuth(c.Config.User, c.Config.Token)
resp, err := c.Client.Do(req)
if err != nil {
log.Printf("[jenkins] Failed to fetch build %s: %v", job, err)
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("[jenkins] Failed to fetch build %s: %s", job, resp.Status)
return nil, fmt.Errorf("unexpected status: %s", resp.Status)
}
// io限制1m
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
log.Printf("[jenkins] Failed to read response body: %v", err)
return nil, fmt.Errorf("failed to read response body: %w", err)
}
var build Build
if err := json.Unmarshal(body, &build); err != nil {
log.Printf("[jenkins] Failed to unmarshal build %s: %v", job, err)
return nil, fmt.Errorf("failed to unmarshal response body: %w", err)
}
return &build, nil
}
// 提取构建参数为 map[string]string
func ExtractBuildParams(build *Build) map[string]string {
params := make(map[string]string)
for _, action := range build.Actions {
if action.Class == "hudson.model.ParametersAction" {
for _, param := range action.Parameters {
params[param.Name] = fmt.Sprintf("%v", param.Value)
}
}
}
return params
}
// 合并两个 map[string]string 和 []map[string]interface{}
func MergeParams(base map[string]string, defaults []map[string]interface{}) map[string]string {
result := make(map[string]string)
for k, v := range base {
result[k] = v
}
for _, param := range defaults {
for k, v := range param {
result[k] = fmt.Sprintf("%v", v)
}
}
return result
}
// string map 转 interface map
func StringMapToInterfaceMap(m map[string]string) map[string]interface{} {
res := make(map[string]interface{})
for k, v := range m {
res[k] = v
}
return res
}
// 判断 b 是否为 a 的子集
func IsSubset(a map[string]string, b map[string]interface{}) bool {
for k, v := range b {
if a[k] != fmt.Sprintf("%v", v) {
return false
}
}
return true
}
// 构建构建触发 URL带参数
func (c *JenkinsClient) generateBuildURL(baseURL string, mergedParams map[string]string) string {
values := url.Values{}
for k, v := range mergedParams {
values.Set(k, v)
}
// 构建新的构建路径
u, err := url.Parse(baseURL)
if err != nil {
log.Printf("[jenkins] Failed to parse build URL: %v", err)
return ""
}
u.Path = getBuildWithParametersPath(u.Path)
u.RawQuery = values.Encode()
return u.String()
}
// 替换为 buildWithParameters 路径
func getBuildWithParametersPath(p string) string {
parts := strings.Split(strings.TrimSuffix(p, "/"), "/")
if len(parts) > 0 {
return path.Join(parts[:len(parts)-1]...) + "/buildWithParameters"
}
return p
}
// 触发构建
func (c *JenkinsClient) TriggerBuild(build *Build, params map[string]interface{}) error {
merged := make(map[string]string)
for k, v := range params {
merged[k] = fmt.Sprintf("%v", v)
}
triggerURL := c.generateBuildURL(build.URL, merged)
if triggerURL == "" {
log.Printf("[jenkins] Failed to generate trigger URL")
return fmt.Errorf("failed to generate trigger URL")
}
req, err := http.NewRequest("POST", triggerURL, nil)
if err != nil {
log.Printf("[jenkins] Failed to create POST request: %v", err)
return fmt.Errorf("created trigger request failed: %w", err)
}
req.SetBasicAuth(c.Config.User, c.Config.Token)
resp, err := c.Client.Do(req)
if err != nil {
log.Printf("[jenkins] Failed to trigger build %s: %v", triggerURL, err)
return fmt.Errorf("trigger request failed: %w", err)
}
defer resp.Body.Close()
log.Printf("[jenkins] Triggered build %s: %s", triggerURL, resp.Status)
return nil
}