package src import ( "bytes" "context" "fmt" "strings" "code.gitea.io/sdk/gitea" "git.beisel.it/florian/gitea-release-drafter/src/config" "github.com/sethvargo/go-githubactions" ) type Action struct { config *config.DrafterConfig globalContext context.Context client *gitea.Client } // NewAction factory for a new action func NewAction(ctx *context.Context, cfg *config.DrafterConfig) (*Action, error) { gitea, err := gitea.NewClient(cfg.ApiUrl, gitea.SetToken(cfg.Token)) if err != nil { return nil, err } return &Action{ config: cfg, globalContext: *ctx, client: gitea, }, nil } // updateOrCreateDraftRelease func updateOrCreateDraftRelease(a *Action, cfg *config.RepoConfig) (*gitea.Release, error) { draft, last, err := FindReleases(a.client, a.config.RepoOwner, a.config.RepoName) if err != nil { return nil, err } changelog, err := GenerateChangelog(a.client, a.config.RepoOwner, a.config.RepoName, last) if err != nil { return nil, err } if len(*changelog) == 0 { githubactions.Infof("No updates found") return nil, nil } // Create a map to hold categorized PRs categorizedPRs := make(map[string][]*gitea.PullRequest) for _, prs := range *changelog { for _, pr := range prs { categorized := false for _, category := range cfg.Categories { if !categorized && prHasLabel(pr, category.Labels) { categorizedPRs[category.Title] = append(categorizedPRs[category.Title], pr) categorized = true break // Break out of the category loop } } if !categorized { // Add to a default category if not categorized categorizedPRs["Other Changes"] = append(categorizedPRs["Other Changes"], pr) } if categorized { break // Break out of the PR loop once categorized } } } // Build the changelog string var b strings.Builder b.WriteString("# What's Changed\n\n") for _, category := range cfg.Categories { prs, exists := categorizedPRs[category.Title] if exists && len(prs) > 0 { fmt.Fprintf(&b, "## %s\n\n", category.Title) for _, pr := range prs { fmt.Fprintf(&b, "* %s (#%d) @%s\n", pr.Title, pr.Index, pr.Poster.UserName) } b.WriteString("\n") } } nextVersion, err := ResolveVersion(cfg, last, changelog) if err != nil { return nil, err } title := FillVariables(cfg.NameTemplate, TemplateVariables{ ReleaseVersion: nextVersion.String(), }) // FIXME: require RESOLVED_VERSION to be set? tag := FillVariables(cfg.TagTemplate, TemplateVariables{ ReleaseVersion: nextVersion.String(), }) if draft != nil { updatedDraft, err := UpdateExistingDraft(a.client, a.config.RepoOwner, a.config.RepoName, draft, title, tag, b.String()) if err != nil { return nil, err } return updatedDraft, nil } newDraft, err := CreateDraftRelease(a.client, a.config.RepoOwner, a.config.RepoName, cfg.DefaultBranch, title, tag, b.String()) if err != nil { return nil, err } return newDraft, nil } // GetConfigFile reads the local configuration file in `.gitea/` in the ref branch (probably main/master) func (a *Action) GetConfigFile(ref string) (*bytes.Reader, error) { data, _, err := a.client.GetFile(a.config.RepoOwner, a.config.RepoName, ref, a.config.ConfigPath) if err != nil { return nil, err } return bytes.NewReader(data), err } // Run builds the configuration and executes the action logic func (a *Action) Run() error { // fetch the repo to retrieve the default branch to be set as the config default repo, err := GetRepo(a.client, a.config.RepoOwner, a.config.RepoName) if err != nil { return err } githubactions.Debugf("Found default branch %s", repo.DefaultBranch) // build repo config configReader, err := a.GetConfigFile(repo.DefaultBranch) if err != nil { if err.Error() != "404 Not Found" { return err } else { // no config file found githubactions.Warningf("No such config file: .gitea/%s", a.config.ConfigPath) configReader = bytes.NewReader([]byte{}) } } config, err := config.ReadRepoConfig(configReader, repo.DefaultBranch) if err != nil { return err } draft, err := updateOrCreateDraftRelease(a, config) if err != nil { return err } if draft != nil { githubactions.Infof("created draft release %s", draft.Title) } return nil } func prHasLabel(pr *gitea.PullRequest, labels []string) bool { for _, prLabel := range pr.Labels { for _, label := range labels { if prLabel.Name == label { return true } } } return false }