action-create-pr/main.go
Julien Fastré 3e47a24f8d Allow to set a token from input (#1)
Reviewed-on: #1
Co-authored-by: Julien Fastré <julien.fastre@champs-libres.coop>
Co-committed-by: Julien Fastré <julien.fastre@champs-libres.coop>
2024-03-11 11:26:05 +00:00

241 lines
7.4 KiB
Go

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
}