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 } 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 } // 构建构建触发 URL(带参数) func (c *JenkinsClient) GenerateBuildURL(build *Build) string { values := url.Values{} // 取出构建参数 for _, action := range build.Actions { if action.Class == "hudson.model.ParametersAction" { for _, param := range action.Parameters { values.Set(param.Name, fmt.Sprintf("%v", param.Value)) } } } // 加入默认参数 for _, param := range c.Config.DefaultParameters { for k, v := range param { values.Set(k, fmt.Sprintf("%v", v)) } } // 构建新的构建路径 u, err := url.Parse(build.URL) 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) error { triggerURL := c.GenerateBuildURL(build) if triggerURL == "" { 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 }