package main import ( "code.gitea.io/sdk/gitea" "errors" "fmt" "github.com/sethvargo/go-githubactions" "os" "slices" "strconv" "strings" ) func ParseActionConfig(ctx githubactions.GitHubContext) (*CreatePrConfig, error) { base := githubactions.GetInput("base") if base == "" { return nil, errors.New("base branch name cannot be empty") } repo := strings.Split(ctx.Repository, "/")[1] fmt.Printf("The repository is %v\n", repo) title := githubactions.GetInput("title") if title == "" { return nil, errors.New("title cannot be empty") } body := githubactions.GetInput("body") assigneesRaw := githubactions.GetInput("assignees") assignees := []string{} for _, a := range strings.Split(assigneesRaw, ",") { assignees = append(assignees, strings.TrimSpace(a)) } labelsRaw := githubactions.GetInput("labels") labels := []string{} for _, l := range strings.Split(labelsRaw, ",") { labels = append(labels, strings.TrimSpace(l)) } var head string headRaw := githubactions.GetInput("head") if headRaw == "" { if os.Getenv("GITHUB_REF_TYPE") != "branch" { return nil, fmt.Errorf("set the \"head\" parameter or work from a branch: only branch can create a pull request: %v given as parameter GITHUB_REF_TYPE", githubactions.GetInput("GITHUB_REF_TYPE")) } head = os.Getenv("GITHUB_REF_NAME") } else { head = headRaw } return &CreatePrConfig{ Org: ctx.RepositoryOwner, Repo: repo, HeadBranch: head, BaseBranch: base, Title: title, Body: body, Assignees: assignees, Labels: labels, }, nil } func main() { fmt.Println("Starting result CreatePullRequest, main") ctx, err := githubactions.Context() if err != nil { githubactions.Fatalf("could not get context: %v", err.Error()) } token := githubactions.GetInput("token") fmt.Printf("Api url is %v\n", ctx.ServerURL) fmt.Printf("Debug: token length is %d characters\n", len(token)) config, err := ParseActionConfig(*ctx) if err != nil { githubactions.Fatalf("%v", err.Error()) } pr, result, err := createPullRequest(ctx.ServerURL, token, *config) if err != nil { githubactions.Fatalf("Error while creating pr: %v", err.Error()) } fmt.Printf("Created PR with id %d\n", pr.ID) githubactions.SetOutput("pull-request-number", strconv.FormatInt(pr.Index, 10)) githubactions.SetOutput("pull-request-result", result) githubactions.SetOutput("pull-request-url", pr.URL) fmt.Println("Ending result CreatePullRequest, main") } // Agent which will perform changes on gitea type Agent struct { client *gitea.Client } // CreatePrConfig represents the configuration for creating a pull request type CreatePrConfig struct { // the organization where the PR is created Org string // the repository where the PR is created Repo string // the branch where the changes are made HeadBranch string // the branch where the changes will be added BaseBranch string // the title of the pull requests Title string // the body of the pull requests Body string // the list of assignees Assignees []string // the list of requested labels Labels []string } // ExistingOpenPullRequest checks if there is an existing pull request for the same head branch and return it func (a *Agent) ExistingOpenPullRequest(config CreatePrConfig) (bool, *gitea.PullRequest, error) { currentPage := 1 for currentPage != 0 { pulls, response, err := a.client.ListRepoPullRequests(config.Org, config.Repo, gitea.ListPullRequestsOptions{State: gitea.StateOpen, ListOptions: gitea.ListOptions{Page: currentPage}}) if err != nil { return false, nil, fmt.Errorf("error while listing all exisiting open pull requests: %v", err.Error()) } for _, pull := range pulls { if pull.Head.Name == config.HeadBranch { return true, pull, nil } } currentPage = response.NextPage } return false, nil, nil } // labelsFromString retrieves a list of labels from a repository based on the provided configuration. // It uses the Gitea client to list all labels in the repository and filters them based on the provided label names. // The method takes a CreatePrConfig struct as a parameter which contains the organization, repository, and label details. // It returns a slice of gitea.Label that matches the provided label names and an error if any occurred. func (a *Agent) labelsFromString(config CreatePrConfig) ([]gitea.Label, error) { foundLabels := []gitea.Label{} currentPage := 1 for currentPage != 0 { labels, response, err := a.client.ListRepoLabels(config.Org, config.Repo, gitea.ListLabelsOptions{ListOptions: gitea.ListOptions{Page: currentPage}}) if err != nil { return nil, err } for _, label := range labels { if slices.Contains(config.Labels, label.Name) { foundLabels = append(foundLabels, *label) } } currentPage = response.NextPage } return foundLabels, nil } // createPullRequestGitea creates a pull request in a Gitea repository based on the provided configuration. // It retrieves the label IDs for the given labels and uses them when creating the pull request. // The method takes a CreatePrConfig struct as a parameter which contains the organization, repository, and pull request details. // It returns the created pull request and any error encountered during the process. func (a *Agent) createPullRequestGitea(config CreatePrConfig) (*gitea.PullRequest, error) { labelIds := []int64{} labels, err := a.labelsFromString(config) if err != nil { return nil, err } for _, label := range labels { labelIds = append(labelIds, label.ID) } if err != nil { return nil, err } pr, _, err := a.client.CreatePullRequest(config.Org, config.Repo, gitea.CreatePullRequestOption{ Head: config.HeadBranch, Base: config.BaseBranch, Title: config.Title, Body: config.Body, Assignees: config.Assignees, Labels: labelIds, Deadline: nil, }) if err != nil { return nil, err } return pr, nil } // createPullRequest takes in the API URL, access token, and configuration for creating a pull request. // It initializes a Gitea client using the API URL and access token. // It creates an instance of the Agent struct using the Gitea client. // It checks if there is an open pull request with the given branch name in the repository using the ExistingOpenPullRequest method of Agent. // If an open pull request already exists, it returns nil as the pull request and nil error. // If no open pull request exists, it creates a new pull request using the createPullRequestGitea method of Agent. // It returns the created pull request and nil error if successful. // If there is an error while checking for existing pull requests or creating a new pull request, it returns nil as the pull request and the error. // The function takes in the following parameters: // - apiUrl: The URL of the Gitea API. // - token: The access token for authenticating with the Gitea API. // - config: The configuration for creating the pull request, including the organization, repository, branch details, title, body, assignees, and labels. // It returns a pointer to the created pull request and an error. func createPullRequest(apiUrl string, token string, config CreatePrConfig) (*gitea.PullRequest, string, error) { client, _ := gitea.NewClient(apiUrl, gitea.SetToken(token)) agent := &Agent{client: client} has, pull, err := agent.ExistingOpenPullRequest(config) if err != nil { return nil, "error", err } if has { return pull, "existing", nil } pr, err := agent.createPullRequestGitea(config) if err != nil { return nil, "error", err } return pr, "created", nil }