package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "net/http" "os" "strconv" ) func getEnv(key, defaultVal string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultVal } func main() { // Define command-line flags with environment variable defaults var forgejoAPIURL string flag.StringVar(&forgejoAPIURL, "forgejo-api-url", getEnv("FORGEJO_API_URL", "https://code.252.no/api/v1"), "Forgejo API URL (default: https://code.252.no/api/v1 or $FORGEJO_API_URL)") var token string flag.StringVar(&token, "token", os.Getenv("FORGEJO_TOKEN"), "Forgejo API token (required, or set $FORGEJO_TOKEN)") var repoOwner string flag.StringVar(&repoOwner, "repo-owner", os.Getenv("REPO_OWNER"), "Repository owner (required, or set $REPO_OWNER)") var repoName string flag.StringVar(&repoName, "repo-name", os.Getenv("REPO_NAME"), "Repository name (required, or set $REPO_NAME)") var issueId int issueIdStr := os.Getenv("ISSUE_ID") if issueIdStr != "" { var err error issueId, err = strconv.Atoi(issueIdStr) if err != nil { fmt.Println("Error: Invalid ISSUE_ID") os.Exit(1) } } flag.IntVar(&issueId, "issue-id", issueId, "Issue ID to comment on (cannot be used with --pull-id, or set $ISSUE_ID)") var pullId int pullIdStr := os.Getenv("PULL_ID") if pullIdStr != "" { var err error pullId, err = strconv.Atoi(pullIdStr) if err != nil { fmt.Println("Error: Invalid PULL_ID") os.Exit(1) } } flag.IntVar(&pullId, "pull-id", pullId, "Pull Request ID to comment on (cannot be used with --issue-id, or set $PULL_ID)") // Override the default usage message to be more helpful flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] \n", os.Args[0]) fmt.Fprintln(flag.CommandLine.Output(), "\nOptions:") flag.PrintDefaults() fmt.Fprintln(flag.CommandLine.Output(), "\nExample:") fmt.Fprintf(flag.CommandLine.Output(), " %s --pull-id=123 --token=YOUR_TOKEN --repo-owner=owner --repo-name=repo diff.txt\n", os.Args[0]) } flag.Parse() // Get the diff file from the positional arguments args := flag.Args() if len(args) < 1 { fmt.Println("Error: Missing diff file.") flag.Usage() os.Exit(1) } diffFile := args[0] // Check for required parameters if token == "" { fmt.Println("Error: Forgejo API token is not set. Use the --token flag or set FORGEJO_TOKEN environment variable.") flag.Usage() os.Exit(1) } if repoOwner == "" { fmt.Println("Error: Repository owner is not set. Use the --repo-owner flag or set REPO_OWNER environment variable.") flag.Usage() os.Exit(1) } if repoName == "" { fmt.Println("Error: Repository name is not set. Use the --repo-name flag or set REPO_NAME environment variable.") flag.Usage() os.Exit(1) } // Ensure that only one of issueId or pullId is set if (issueId == 0 && pullId == 0) || (issueId != 0 && pullId != 0) { fmt.Println("Error: You must specify either --issue-id or --pull-id, but not both.") flag.Usage() os.Exit(1) } // Determine which ID to use var itemId int var itemType string if issueId != 0 { itemId = issueId itemType = "issue" } else { itemId = pullId itemType = "pull" } // Read the diff file diffContentBytes, err := os.ReadFile(diffFile) if err != nil { fmt.Printf("Error reading diff file '%s': %v\n", diffFile, err) os.Exit(1) } // Prepare the comment content diffContent := string(diffContentBytes) commentBody := fmt.Sprintf("```diff\n%s\n```", diffContent) // Prepare the request payload payload := map[string]string{ "body": commentBody, } payloadBytes, err := json.Marshal(payload) if err != nil { fmt.Printf("Error marshaling payload: %v\n", err) os.Exit(1) } // Construct the API URL apiURL := fmt.Sprintf("%s/repos/%s/%s/issues/%d/comments", forgejoAPIURL, repoOwner, repoName, itemId) // Create the HTTP request req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes)) if err != nil { fmt.Printf("Error creating request: %v\n", err) os.Exit(1) } req.Header.Set("Authorization", fmt.Sprintf("token %s", token)) req.Header.Set("Content-Type", "application/json") // Send the request client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Printf("Error sending request: %v\n", err) os.Exit(1) } defer resp.Body.Close() // Read the response body respBody, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("Error reading response: %v\n", err) os.Exit(1) } // Check the response status code if resp.StatusCode != 201 { fmt.Printf("Failed to post comment. HTTP status: %d\n", resp.StatusCode) fmt.Printf("Response body: %s\n", string(respBody)) os.Exit(1) } else { fmt.Printf("Posted diff to Forgejo %s #%d\n", itemType, itemId) } }