package provider import ( "context" "fmt" "os" "regexp" "strconv" "strings" "code.gitea.io/sdk/gitea" "github.com/Masterminds/semver/v3" "github.com/go-semantic-release/semantic-release/v2/pkg/provider" "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" ) var PVERSION = "dev" var zexlabServerURL = "https://zexlab.dev" type ZexlabRepository struct { client *gitea.Client repo string owner string stripVTagPrefix bool } // gocyclo:ignore func (repo *ZexlabRepository) Init(config map[string]string) error { serverURL := config["server_url"] if serverURL == "" { serverURL = zexlabServerURL } slug := config["slug"] if slug == "" { slug = os.Getenv("GITHUB_REPOSITORY") } // Maybe we are running in Gitea Actions if slug == "" { slug = os.Getenv("GITEA_REPOSITORY") } // Maybe we are running in WoodpeckerCI if slug == "" { slug = os.Getenv("CI_REPO_NAME") } token := config["token"] if token == "" { token = os.Getenv("GITHUB_TOKEN") } if token == "" { token = os.Getenv("GITEA_TOKEN") } if token == "" { return fmt.Errorf("zexlab token missing") } if !strings.Contains(slug, "/") { return fmt.Errorf("invalid slug") } split := strings.Split(slug, "/") // This could be due to act locally // We'll work backwards to get the values repo.owner = split[len(split)-2] repo.repo = split[len(split)-1] // Ensure no .git suffix remains repo.repo = strings.TrimSuffix(repo.repo, ".git") ctx := context.Background() client, err := gitea.NewClient(serverURL, gitea.SetToken(token), gitea.SetContext(ctx)) if err != nil { return err } repo.client = client stripVTagPrefix := config["strip_v_tag_prefix"] repo.stripVTagPrefix, err = strconv.ParseBool(stripVTagPrefix) if stripVTagPrefix != "" && err != nil { return fmt.Errorf("failed to set property strip_v_tag_prefix: %w", err) } return nil } func (repo *ZexlabRepository) GetInfo() (*provider.RepositoryInfo, error) { r, _, err := repo.client.GetRepo(repo.owner, repo.repo) if err != nil { return nil, err } return &provider.RepositoryInfo{ Owner: r.Owner.UserName, Repo: r.Name, DefaultBranch: r.DefaultBranch, Private: r.Private, }, nil } func (repo *ZexlabRepository) GetCommits(_, toSha string) ([]*semrel.RawCommit, error) { allCommits := make([]*semrel.RawCommit, 0) opts := &gitea.ListOptions{PageSize: 100} for { commits, resp, err := repo.client.ListRepoCommits(repo.owner, repo.repo, gitea.ListCommitOptions{ SHA: toSha, ListOptions: *opts, }) if err != nil { return nil, err } for _, commit := range commits { sha := commit.SHA // Resolve author info: prefer Gitea user, fallback to git commit data authorLogin := "" authorName := commit.RepoCommit.Author.Name authorEmail := commit.RepoCommit.Author.Email if commit.Author != nil { authorLogin = commit.Author.UserName authorName = commit.Author.FullName authorEmail = commit.Author.Email } // Resolve committer info: prefer Gitea user, fallback to git commit data committerLogin := "" committerName := commit.RepoCommit.Committer.Name committerEmail := commit.RepoCommit.Committer.Email if commit.Committer != nil { committerLogin = commit.Committer.UserName committerName = commit.Committer.FullName committerEmail = commit.Committer.Email } allCommits = append(allCommits, &semrel.RawCommit{ SHA: sha, RawMessage: commit.RepoCommit.Message, Annotations: map[string]string{ "author_login": authorLogin, "author_name": authorName, "author_email": authorEmail, "author_date": commit.RepoCommit.Author.Date, "committer_login": committerLogin, "committer_name": committerName, "committer_email": committerEmail, "committer_date": commit.RepoCommit.Committer.Date, }, }) } if resp.NextPage == 0 { break } opts.Page = resp.NextPage } return allCommits, nil } //gocyclo:ignore func (repo *ZexlabRepository) GetReleases(rawRe string) ([]*semrel.Release, error) { re := regexp.MustCompile(rawRe) allReleases := make([]*semrel.Release, 0) refs, resp, err := repo.client.GetRepoRefs(repo.owner, repo.repo, "") if resp != nil && resp.StatusCode == 404 { return allReleases, nil } if err != nil { return nil, err } for _, r := range refs { tag := strings.TrimPrefix(r.Ref, "refs/tags/") if rawRe != "" && !re.MatchString(tag) { continue } objType := r.Object.Type if objType != "commit" && objType != "tag" { continue } foundSha := r.Object.SHA // resolve annotated tag if objType == "tag" { resTag, _, err := repo.client.GetRepoRef(repo.owner, repo.repo, foundSha) if err != nil { continue } if resTag.Object.Type != "commit" { continue } foundSha = resTag.Object.SHA } version, err := semver.NewVersion(tag) if err != nil { continue } allReleases = append(allReleases, &semrel.Release{SHA: foundSha, Version: version.String()}) } return allReleases, nil } func (repo *ZexlabRepository) CreateRelease(release *provider.CreateReleaseConfig) error { prefix := "v" if repo.stripVTagPrefix { prefix = "" } tag := prefix + release.NewVersion isPrerelease := release.Prerelease || semver.MustParse(release.NewVersion).Prerelease() != "" opt := gitea.CreateReleaseOption{ TagName: tag, Target: release.Branch, Title: tag, Note: release.Changelog, IsPrerelease: isPrerelease, } _, _, err := repo.client.CreateRelease(repo.owner, repo.repo, opt) if err != nil { return fmt.Errorf("error returned %w, tag value [%s]", err, tag) } return nil } func (repo *ZexlabRepository) Name() string { return "Zexlab" } func (repo *ZexlabRepository) Version() string { return PVERSION }