Files
woodpecker/test/integration/utils/api_client.go
Anbraten ac43237b4e next
2026-02-24 16:52:59 +01:00

267 lines
7.3 KiB
Go

package utils
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// WoodpeckerClient provides methods to interact with Woodpecker API
type WoodpeckerClient struct {
baseURL string
token string
client *http.Client
}
// NewWoodpeckerClient creates a new API client
func NewWoodpeckerClient(baseURL, token string) *WoodpeckerClient {
return &WoodpeckerClient{
baseURL: baseURL,
token: token,
client: &http.Client{
Timeout: 10 * time.Second,
},
}
}
// doRequest performs an HTTP request with authentication
func (c *WoodpeckerClient) doRequest(method, path string, body any) (*http.Response, error) {
var bodyReader io.Reader
if body != nil {
jsonData, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %w", err)
}
bodyReader = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequest(method, c.baseURL+path, bodyReader)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
if c.token != "" {
req.Header.Set("Authorization", "Bearer "+c.token)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
return c.client.Do(req)
}
// GetRepos fetches the list of repositories
func (c *WoodpeckerClient) GetRepos() ([]map[string]any, error) {
resp, err := c.doRequest("GET", "/api/repos", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
}
var repos []map[string]any
if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return repos, nil
}
// ActivateRepo activates a repository
func (c *WoodpeckerClient) ActivateRepo(owner, name string) error {
path := fmt.Sprintf("/api/repos/%s/%s", owner, name)
resp, err := c.doRequest("POST", path, nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to activate repo: status %d, body: %s", resp.StatusCode, string(body))
}
return nil
}
// GetPipeline fetches a specific pipeline
func (c *WoodpeckerClient) GetPipeline(owner, name string, pipelineID int) (map[string]any, error) {
path := fmt.Sprintf("/api/repos/%s/%s/pipelines/%d", owner, name, pipelineID)
resp, err := c.doRequest("GET", path, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
}
var pipeline map[string]any
if err := json.NewDecoder(resp.Body).Decode(&pipeline); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return pipeline, nil
}
// TriggerPipeline manually triggers a pipeline
func (c *WoodpeckerClient) TriggerPipeline(owner, name, branch string) (map[string]any, error) {
path := fmt.Sprintf("/api/repos/%s/%s/pipelines", owner, name)
body := map[string]string{
"branch": branch,
}
resp, err := c.doRequest("POST", path, body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to trigger pipeline: status %d, body: %s", resp.StatusCode, string(respBody))
}
var pipeline map[string]any
if err := json.NewDecoder(resp.Body).Decode(&pipeline); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return pipeline, nil
}
// CancelPipeline cancels a running pipeline
func (c *WoodpeckerClient) CancelPipeline(owner, name string, pipelineID int) error {
path := fmt.Sprintf("/api/repos/%s/%s/pipelines/%d/cancel", owner, name, pipelineID)
resp, err := c.doRequest("POST", path, nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to cancel pipeline: status %d, body: %s", resp.StatusCode, string(body))
}
return nil
}
// GetPipelines fetches all pipelines for a repository
func (c *WoodpeckerClient) GetPipelines(owner, name string) ([]map[string]any, error) {
path := fmt.Sprintf("/api/repos/%s/%s/pipelines", owner, name)
resp, err := c.doRequest("GET", path, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
}
var pipelines []map[string]any
if err := json.NewDecoder(resp.Body).Decode(&pipelines); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return pipelines, nil
}
// GetPipelineSteps fetches all steps for a pipeline
func (c *WoodpeckerClient) GetPipelineSteps(owner, name string, pipelineID int) ([]map[string]any, error) {
path := fmt.Sprintf("/api/repos/%s/%s/pipelines/%d", owner, name, pipelineID)
resp, err := c.doRequest("GET", path, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
}
var pipeline map[string]any
if err := json.NewDecoder(resp.Body).Decode(&pipeline); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
// Extract steps/workflows from pipeline
// TODO: Adjust based on actual API response structure
steps, ok := pipeline["steps"].([]any)
if !ok {
return nil, fmt.Errorf("steps not found in pipeline response")
}
result := make([]map[string]any, len(steps))
for i, step := range steps {
result[i] = step.(map[string]any)
}
return result, nil
}
// GetPipelineLogs fetches logs for a pipeline
func (c *WoodpeckerClient) GetPipelineLogs(owner, name string, pipelineID int) (string, error) {
// TODO: Implement based on actual Woodpecker API
// This might require iterating through workflow steps and fetching logs for each
path := fmt.Sprintf("/api/repos/%s/%s/pipelines/%d/logs", owner, name, pipelineID)
resp, err := c.doRequest("GET", path, nil)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("failed to get logs: status %d, body: %s", resp.StatusCode, string(body))
}
logs, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read logs: %w", err)
}
return string(logs), nil
}
// WaitForPipelineComplete waits for a pipeline to complete (success or failure)
func (c *WoodpeckerClient) WaitForPipelineComplete(owner, name string, pipelineID int, timeout time.Duration) (string, error) {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
pipeline, err := c.GetPipeline(owner, name, pipelineID)
if err != nil {
return "", err
}
status, ok := pipeline["status"].(string)
if !ok {
return "", fmt.Errorf("pipeline status not found")
}
// Check if pipeline is in a terminal state
switch status {
case "success":
return "success", nil
case "failure", "error", "killed":
return status, nil
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("timeout waiting for pipeline to complete")
}