Migrate pipelines - Best Effort

This commit is contained in:
sonix 2026-03-22 13:27:32 +02:00
parent 4e7cf06abf
commit 111f06ddf6
6 changed files with 620 additions and 5 deletions

View file

@ -4,11 +4,36 @@ import (
"encoding/base64"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-resty/resty/v2"
)
type Pipeline struct {
ID int `json:"id"`
Name string `json:"name"`
Folder string `json:"folder"`
URL string `json:"url"`
}
type BuildDefinition struct {
ID int `json:"id"`
Name string `json:"name"`
Process struct {
Type int `json:"type"`
YamlFilename string `json:"yamlFilename"` // for YAML pipelines
} `json:"process"`
Variables map[string]struct {
Value string `json:"value"`
IsSecret bool `json:"isSecret"`
} `json:"variables"`
Repository struct {
URL string `json:"url"`
Name string `json:"name"`
} `json:"repository"`
}
type Client struct {
client *resty.Client
organization string
@ -79,3 +104,96 @@ func (c *Client) GetAuthenticatedURL(remoteURL, pat string) string {
}
return remoteURL
}
// GetBuildDefinitions gets pipeline definitions for a project
func (c *Client) GetBuildDefinitions(project string) ([]BuildDefinition, error) {
url := fmt.Sprintf("https://dev.azure.com/%s/%s/_apis/build/definitions", c.organization, project)
var result struct {
Count int `json:"count"`
Value []BuildDefinition `json:"value"`
}
resp, err := c.client.R().
SetQueryParam("api-version", "7.0").
SetQueryParam("includeAll", "true").
SetResult(&result).
Get(url)
if err != nil {
return nil, fmt.Errorf("failed to fetch build definitions: %w", err)
}
if resp.StatusCode() != 200 {
return nil, fmt.Errorf("Azure API returned %d: %s", resp.StatusCode(), resp.String())
}
return result.Value, nil
}
// GetPipelineContent fetches the azure-pipelines.yml content from repository
func (c *Client) GetPipelineContent(repoURL, branch, filePath string) (string, error) {
// Azure DevOps Repos API for file content
// Extract project and repo name from URL
// URL format: https://dev.azure.com/org/project/_git/repo
// For this example, construct the items API URL
parts := strings.Split(repoURL, "/_git/")
if len(parts) != 2 {
return "", fmt.Errorf("cannot parse repo URL: %s", repoURL)
}
base := parts[0]
repoName := parts[1]
url := fmt.Sprintf("%s/_apis/sourceProviders/TfsGit/filecontents?repository=%s&commitOrBranch=%s&path=%s&api-version=7.0",
base, repoName, branch, filePath)
resp, err := c.client.R().
Get(url)
if err != nil {
return "", err
}
if resp.StatusCode() != 200 {
// Try alternative: get via Git API (Items endpoint)
return "", fmt.Errorf("file not found or access denied")
}
return string(resp.Body()), nil
}
// GetPipelineVariables gets variables for a specific build definition
func (c *Client) GetPipelineVariables(project string, definitionID int) (map[string]string, error) {
url := fmt.Sprintf("https://dev.azure.com/%s/%s/_apis/build/definitions/%d/variables",
c.organization, project, definitionID)
var result struct {
Count int `json:"count"`
Value map[string]struct {
Value string `json:"value"`
IsSecret bool `json:"isSecret"`
} `json:"value"`
}
resp, err := c.client.R().
SetQueryParam("api-version", "7.0").
SetResult(&result).
Get(url)
if err != nil {
return nil, err
}
if resp.StatusCode() != 200 {
return nil, fmt.Errorf("failed to get variables: %d", resp.StatusCode())
}
vars := make(map[string]string)
for k, v := range result.Value {
vars[k] = v.Value // Note: secrets won't have values via API unless explicitly allowed
}
return vars, nil
}