containers/apps/ci-os/packages/forgejo-label/src/main.go

241 lines
7.3 KiB
Go

// src/main.go
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
// Define command-line flags
baseURL := flag.String("base-url", "https://code.252.no/api/v1", "Base URL of the Forgejo API")
token := flag.String("token", "", "API token for authentication")
action := flag.String("action", "", "Action to perform: get, create, get-single, update, delete, apply, sync")
owner := flag.String("owner", "", "Owner of the repository")
repo := flag.String("repo", "", "Name of the repository")
id := flag.Int64("id", 0, "ID of the issue or pull request")
name := flag.String("name", "", "Name of the label")
color := flag.String("color", "", "Color of the label")
description := flag.String("description", "", "Description of the label")
// Define exclusive and isArchived as regular boolean flags
exclusive := flag.Bool("exclusive", false, "Exclusive flag for the label (true/false)")
isArchived := flag.Bool("is-archived", false, "Archived flag for the label (true/false)")
// Define paths to configuration files
labelerConfigPath := flag.String("labeler-config", ".forgejo/labeler.yaml", "Path to labeler.yaml configuration file")
labelsConfigPath := flag.String("labels-config", ".forgejo/labels.yaml", "Path to labels.yaml configuration file")
// Define changed files (comma-separated list)
changedFilesInput := flag.String("changed-files", "", "Comma-separated list of changed file paths")
// Define deleteOtherLabels flag
deleteOtherLabels := flag.Bool("delete-other-labels", false, "Delete labels not defined in the config file")
// Define dryRun flag
dryRun := flag.Bool("dry-run", false, "Simulate the sync without making any changes")
page := flag.Int("page", 1, "Page number for listing labels")
limit := flag.Int("limit", 30, "Number of labels per page")
flag.Parse()
// Validate required flags
if *action == "" || *owner == "" || *repo == "" {
fmt.Println("Error: action, owner, and repo are required.")
flag.Usage()
os.Exit(1)
}
// Initialize the Forgejo client
client := NewForgejoClient(*baseURL, *token)
switch *action {
case "get":
labels, err := client.GetLabels(*owner, *repo, *page, *limit)
if err != nil {
log.Fatalf("Error getting labels: %v", err)
}
for _, label := range labels {
fmt.Printf("ID: %d, Name: %s, Color: %s, Description: %s\n", label.ID, label.Name, label.Color, label.Description)
}
case "create":
if *name == "" || *color == "" {
fmt.Println("Error: name and color are required for creating a label.")
flag.Usage()
os.Exit(1)
}
option := CreateLabelOption{
Name: *name,
Color: *color,
Description: *description,
Exclusive: *exclusive,
IsArchived: *isArchived,
}
label, err := client.CreateLabel(*owner, *repo, option)
if err != nil {
log.Fatalf("Error creating label: %v", err)
}
fmt.Printf("Created Label: ID=%d, Name=%s\n", label.ID, label.Name)
case "get-single":
if *id == 0 {
fmt.Println("Error: id is required for getting a single label.")
flag.Usage()
os.Exit(1)
}
label, err := client.GetLabel(*owner, *repo, *id)
if err != nil {
log.Fatalf("Error getting label: %v", err)
}
fmt.Printf("Label: ID=%d, Name=%s, Color=%s, Description: %s\n", label.ID, label.Name, label.Color, label.Description)
case "update":
if *id == 0 {
fmt.Println("Error: id is required for updating a label.")
flag.Usage()
os.Exit(1)
}
option := EditLabelOption{
Name: *name,
Color: *color,
Description: *description,
}
if *exclusive {
option.Exclusive = exclusive
}
if *isArchived {
option.IsArchived = isArchived
}
err := client.UpdateLabel(*owner, *repo, *id, option)
if err != nil {
log.Fatalf("Error updating label: %v", err)
}
fmt.Printf("Updated Label: Name=%s\n", option.Name)
case "delete":
if *id == 0 {
fmt.Println("Error: id is required for deleting a label.")
flag.Usage()
os.Exit(1)
}
err := client.DeleteLabel(*owner, *repo, *id)
if err != nil {
log.Fatalf("Error deleting label: %v", err)
}
fmt.Println("Label deleted successfully.")
case "apply":
// Action to apply labels based on configuration and changed files
if *changedFilesInput == "" {
fmt.Println("Error: changed-files are required for the apply action.")
flag.Usage()
os.Exit(1)
}
// Parse changed files
changedFiles := parseChangedFiles(*changedFilesInput)
// Load configuration files
labelerConfig, err := LoadLabelerConfig(*labelerConfigPath)
if err != nil {
log.Fatalf("Error loading labeler config: %v", err)
}
labelsConfig, err := LoadLabelsConfig(*labelsConfigPath)
if err != nil {
log.Fatalf("Error loading labels config: %v", err)
}
// Validate configurations
if err := ValidateLabelerConfig(labelerConfig); err != nil {
log.Fatalf("Invalid labeler config: %v", err)
}
if err := ValidateLabelsConfig(labelsConfig); err != nil {
log.Fatalf("Invalid labels config: %v", err)
}
// Ensure all labels exist, optionally deleting others, with dryRun
err = EnsureLabelsExist(client, *owner, *repo, labelsConfig, *deleteOtherLabels, *dryRun)
if err != nil {
log.Fatalf("Error ensuring labels exist: %v", err)
}
// Determine labels to apply based on changed files
labelsToApply, err := DetermineLabels(labelerConfig, changedFiles)
if err != nil {
log.Fatalf("Error determining labels to apply: %v", err)
}
if len(labelsToApply) == 0 {
fmt.Println("No labels to apply based on the changed files.")
return
}
// Apply labels to an issue or pull request
// For demonstration, assume applying to a specific issue ID passed via the `id` flag
if *id == 0 {
fmt.Println("Error: id (issue or PR ID) is required to apply labels.")
flag.Usage()
os.Exit(1)
}
// Apply labels using SetLabelsOnIssue
err = client.SetLabelsOnIssue(*owner, *repo, *id, labelsToApply)
if err != nil {
log.Fatalf("Error applying labels: %v", err)
}
fmt.Printf("Labels applied successfully: %v\n", labelsToApply)
case "sync":
// New "sync" action
// Sync labels based on labels.yaml, optionally deleting other labels
// This action doesn't require changed-files or issue ID
// Load configuration files
labelsConfig, err := LoadLabelsConfig(*labelsConfigPath)
if err != nil {
log.Fatalf("Error loading labels config: %v", err)
}
// Validate labels configuration
if err := ValidateLabelsConfig(labelsConfig); err != nil {
log.Fatalf("Invalid labels config: %v", err)
}
// Ensure all labels exist, optionally deleting others, with dryRun
err = EnsureLabelsExist(client, *owner, *repo, labelsConfig, *deleteOtherLabels, *dryRun)
if err != nil {
log.Fatalf("Error ensuring labels exist: %v", err)
}
if *dryRun {
fmt.Println("Dry-run completed successfully. No changes were made.")
} else {
fmt.Println("Label synchronization completed successfully.")
}
default:
fmt.Println("Error: Invalid action. Available actions: get, create, get-single, update, delete, apply, sync.")
flag.Usage()
os.Exit(1)
}
}
// parseChangedFiles parses a comma-separated list of file paths into a slice.
func parseChangedFiles(input string) []string {
var files []string
for _, file := range strings.Split(input, ",") {
trimmed := filepath.Clean(strings.TrimSpace(file))
if trimmed != "" {
files = append(files, trimmed)
}
}
return files
}