package main import ( "encoding/json" "fmt" "io" "log" "os" "os/exec" "strconv" "strings" "code.gitea.io/sdk/gitea" ) func main() { // Create a logger to write error messages logFile, err := os.OpenFile("action.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { fmt.Printf("Error while creating the log file: %v\n", err) os.Exit(1) } defer logFile.Close() multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) // Retrieve environment variables issueOdooTicketString := os.Getenv("ODOO_TICKET_NUMBER") logger.Printf("ODOO_TICKET_NUMBER: %s\n", issueOdooTicketString) issueTitle := os.Getenv("ISSUE_TITLE") labelsEnv := os.Getenv("ISSUE_LABELS") logger.Printf("ISSUE_LABELS: %s\n", labelsEnv) issueNumberString := os.Getenv("ISSUE_NUMBER") giteaToken := os.Getenv("GITEA_TOKEN") giteaAPIURL := "https://gitea.ethumada.com" repoOwner := os.Getenv("REPO_OWNER") repoName := os.Getenv("REPO_NAME") if issueOdooTicketString == "" || issueNumberString == "" || issueTitle == "" || giteaToken == "" || repoOwner == "" || repoName == "" { logger.Println("Error: some environment variables are missing.") os.Exit(1) } // Convert the issue number to int64 issueNumber, err := strconv.ParseInt(issueNumberString, 10, 64) if err != nil { logger.Printf("Error while converting the issue number: %v\n", err) os.Exit(1) } // Convert the odoo ticket number to int64 issueOdooTicketID, err := strconv.ParseInt(issueOdooTicketString, 10, 64) if err != nil { logger.Printf("Error while converting the Odoo ticket number: %v\n", err) os.Exit(1) } logger.Printf("Odoo ticket ID: %d\n", issueOdooTicketID) // Map labels to Git Flow prefixes labels := parseLabels(labelsEnv, logger) if len(labels) > 0 { logger.Printf("Extracted labels: %s\n", strings.Join(labels, ", ")) } prefix := "feature" prefix, err = getPrefixFromLabels(labels) if err != nil { logger.Printf("Error while determining the prefix: %v\n", err) os.Exit(1) } logger.Printf("Chosen prefix: %s\n", prefix) // Form the branch name in the format "prefix/ticket-number" branchName := fmt.Sprintf("%s/ticket-%d", prefix, issueOdooTicketID) logger.Printf("Branch name set to: %s\n", branchName) // Configure Git logger.Println("Configuring Git...") err = runCommand(logger, "git", "config", "--global", "user.name", "gitea-actions") if err != nil { logger.Printf("Error while configuring Git: %v\n", err) os.Exit(1) } err = runCommand(logger, "git", "fetch", "origin") if err != nil { logger.Printf("Error while updating remote references") os.Exit(1) } err = runCommand(logger, "git", "config", "--global", "user.email", "actions@gitea.com") if err != nil { logger.Printf("Error while configuring Git: %v\n", err) os.Exit(1) } // Check if the branch already exists logger.Printf("Checking if branch '%s' exists...\n", branchName) output, err := exec.Command("git", "ls-remote", "--heads", "origin", branchName).CombinedOutput() if err != nil { logger.Printf("Error while checking the branch: %v\nOutput: %s\n", err, string(output)) os.Exit(1) } branchExists := false if strings.TrimSpace(string(output)) != "" { branchExists = true } if branchExists { logger.Printf("Branch '%s' already exists. No action taken.\n", branchName) } else { // Create and push the branch originBranch := "origin/develop" logger.Printf("Prefix is : %s", prefix) if prefix == "hotfix" { originBranch = "origin/main" } else { logger.Printf("hotfix != %s", prefix) } logger.Printf("Creating branch '%s' from '%s'.\n", branchName, originBranch) err = runCommand(logger, "git", "checkout", "-b", branchName, originBranch) if err != nil { logger.Printf("Error while creating the branch: %v\n", err) os.Exit(1) } err = runCommand(logger, "git", "push", "origin", branchName) if err != nil { logger.Printf("Error while pushing the branch: %v\n", err) os.Exit(1) } logger.Printf("Branch '%s' successfully created and pushed.\n", branchName) } // Assign the branch to the issue via the Gitea API logger.Printf("Assigning branch '%s' to issue #%d.\n", branchName, issueOdooTicketID) // Create the Gitea client client, err := gitea.NewClient(giteaAPIURL, gitea.SetToken(giteaToken)) if err != nil { logger.Printf("Error while creating the Gitea client: %v\n", err) os.Exit(1) } // Prepare options to edit the issue editIssueOption := gitea.EditIssueOption{ Ref: &branchName, } // Update the issue with the branch reference _, _, err = client.EditIssue(repoOwner, repoName, issueNumber, editIssueOption) if err != nil { logger.Printf("Error while assigning the branch to the issue: %v\n", err) os.Exit(1) } logger.Printf("Branch '%s' successfully assigned to issue #%d.\n", branchName, issueNumber) } // Function to map labels to prefixes func getPrefixFromLabels(labels []string) (string, error) { // Define the label ➔ prefix correspondence labelPrefixMap := map[string]string{ "enhancement": "feature", "invalid": "bugfix", "bug": "hotfix", } // Create a mapping with lower-case keys labelPrefixMapLower := make(map[string]string) for key, value := range labelPrefixMap { keyLower := strings.ToLower(key) labelPrefixMapLower[keyLower] = value } for _, label := range labels { trimmed := strings.TrimSpace(label) labelLower := strings.ToLower(trimmed) fmt.Printf("Current label: %s\n", labelLower) if prefix, exists := labelPrefixMapLower[labelLower]; exists { return prefix, nil } } return "feature", nil } // Parse ISSUE_LABELS env which can be: // - JSON array of strings: ["bug", "enhancement"] // - JSON array of objects: [{"name":"bug"},{"name":"ui"}] // - Comma-separated string: "bug, enhancement" func parseLabels(raw string, logger *log.Logger) []string { raw = strings.TrimSpace(raw) if raw == "" { return nil } // Try JSON array of strings var arrStr []string if err := json.Unmarshal([]byte(raw), &arrStr); err == nil { return arrStr } // Try JSON array of objects with name type lbl struct { Name string `json:"name"` } var arrObj []lbl if err := json.Unmarshal([]byte(raw), &arrObj); err == nil { out := make([]string, 0, len(arrObj)) for _, o := range arrObj { if o.Name != "" { out = append(out, o.Name) } } if len(out) > 0 { return out } } // Fallback: comma-separated values if strings.Contains(raw, ",") { parts := strings.Split(raw, ",") for i := range parts { parts[i] = strings.TrimSpace(parts[i]) } return parts } // Last resort: if it looks like JSON but couldn't parse, log and return nil if strings.HasPrefix(raw, "[") || strings.HasPrefix(raw, "{") { logger.Printf("Warning: could not parse ISSUE_LABELS JSON: %s\n", raw) return nil } // Otherwise single token return []string{raw} } func runCommand(logger *log.Logger, name string, arg ...string) error { cmd := exec.Command(name, arg...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr logger.Printf("Executing command: %s %s\n", name, strings.Join(arg, " ")) return cmd.Run() }