Validation of repository and org and other names is for the consumer of this call. Maybe this can change later.
289 lines
6.9 KiB
Go
289 lines
6.9 KiB
Go
package common
|
|
|
|
/*
|
|
* This file is part of Autogits.
|
|
*
|
|
* Copyright © 2024 SUSE LLC
|
|
*
|
|
* Autogits is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* Autogits is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* Foobar. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"src.opensuse.org/autogits/common/gitea-generated/models"
|
|
)
|
|
|
|
type NewRepos struct {
|
|
Repos []struct {
|
|
Organization, Repository, Branch string
|
|
PackageName string
|
|
}
|
|
IsMaintainer bool
|
|
}
|
|
|
|
const maintainership_line = "MAINTAINER"
|
|
|
|
var true_lines []string = []string{"1", "TRUE", "YES", "OK", "T"}
|
|
|
|
func HasSpace(s string) bool {
|
|
return strings.IndexFunc(s, unicode.IsSpace) >= 0
|
|
}
|
|
|
|
func FindNewReposInIssueBody(body string) *NewRepos {
|
|
Issues := &NewRepos{}
|
|
for _, line := range strings.Split(body, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if ul := strings.ToUpper(line); strings.HasPrefix(ul, "MAINTAINER") {
|
|
value := ""
|
|
if idx := strings.IndexRune(ul, ':'); idx > 0 && len(ul) > idx+2 {
|
|
value = ul[idx+1:]
|
|
} else if idx := strings.IndexRune(ul, ' '); idx > 0 && len(ul) > idx+2 {
|
|
value = ul[idx+1:]
|
|
}
|
|
|
|
if slices.Contains(true_lines, strings.TrimSpace(value)) {
|
|
Issues.IsMaintainer = true
|
|
}
|
|
}
|
|
// line = strings.TrimSpace(line)
|
|
issue := struct{ Organization, Repository, Branch, PackageName string }{}
|
|
|
|
branch := strings.Split(line, "#")
|
|
repo := strings.Split(branch[0], "/")
|
|
|
|
if len(branch) == 2 {
|
|
issue.Branch = strings.TrimSpace(branch[1])
|
|
}
|
|
if len(repo) == 2 {
|
|
issue.Organization = strings.TrimSpace(repo[0])
|
|
issue.Repository = strings.TrimSpace(repo[1])
|
|
issue.PackageName = issue.Repository
|
|
|
|
if idx := strings.Index(strings.ToUpper(issue.Branch), " AS "); idx > 0 && len(issue.Branch) > idx+5 {
|
|
issue.PackageName = strings.TrimSpace(issue.Branch[idx+3:])
|
|
issue.Branch = strings.TrimSpace(issue.Branch[0:idx])
|
|
}
|
|
|
|
if HasSpace(issue.Organization) || HasSpace(issue.Repository) || HasSpace(issue.PackageName) || HasSpace(issue.Branch) {
|
|
continue
|
|
}
|
|
} else {
|
|
continue
|
|
}
|
|
Issues.Repos = append(Issues.Repos, issue)
|
|
//PackageNameIdx := strings.Index(strings.ToUpper(line), " AS ")
|
|
//words := strings.Split(line)
|
|
}
|
|
|
|
if len(Issues.Repos) == 0 {
|
|
return nil
|
|
}
|
|
return Issues
|
|
}
|
|
|
|
func IssueToString(issue *models.Issue) string {
|
|
if issue == nil {
|
|
return "(nil)"
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s#%d", issue.Repository.Owner, issue.Repository.Name, issue.Index)
|
|
}
|
|
|
|
func SplitLines(str string) []string {
|
|
return SplitStringNoEmpty(str, "\n")
|
|
}
|
|
|
|
func SplitStringNoEmpty(str, sep string) []string {
|
|
ret := slices.DeleteFunc(strings.Split(str, sep), func(s string) bool {
|
|
return len(strings.TrimSpace(s)) == 0
|
|
})
|
|
for i := range ret {
|
|
ret[i] = strings.TrimSpace(ret[i])
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func TranslateHttpsToSshUrl(url string) (string, error) {
|
|
const (
|
|
url1 = "https://src.opensuse.org/"
|
|
url2 = "https://src.suse.de/"
|
|
|
|
url1_len = len(url1)
|
|
url2_len = len(url2)
|
|
)
|
|
|
|
if len(url) > 10 && (url[0:10] == "gitea@src." || url[0:10] == "ssh://gite") {
|
|
return url, nil
|
|
}
|
|
|
|
if len(url) > url1_len && url[0:url1_len] == url1 {
|
|
return "ssh://gitea@src.opensuse.org/" + url[url1_len:], nil
|
|
}
|
|
if len(url) > url2_len && url[0:url2_len] == url2 {
|
|
return "ssh://gitea@src.suse.de/" + url[url2_len:], nil
|
|
}
|
|
|
|
return "", fmt.Errorf("Unknown input url %s", url)
|
|
}
|
|
|
|
func TranslateSshNativeToUrl(urlString string) (string, error) {
|
|
rx := regexp.MustCompile("^([^:@]+)@?([^:]*):(.+)$")
|
|
m := rx.FindAllStringSubmatch(urlString, -1)
|
|
if m == nil {
|
|
return "", fmt.Errorf("Cannot match expected native SSH schema: %s", urlString)
|
|
}
|
|
|
|
if len(m[0][2]) > 0 {
|
|
// with user
|
|
return "ssh://" + m[0][1] + "@" + m[0][2] + "/" + m[0][3], nil
|
|
}
|
|
// without user
|
|
return "ssh://" + m[0][1] + "/" + m[0][3], nil
|
|
}
|
|
|
|
type GitUrl struct {
|
|
Org string
|
|
Repo string
|
|
Commit string
|
|
}
|
|
|
|
var valid_schemas []string = []string{"https", "ssh", "http", "file"}
|
|
|
|
func ParseGitRemoteUrl(urlString string) (*GitUrl, error) {
|
|
url, err := url.Parse(urlString)
|
|
|
|
if url != nil && url.Scheme == "file" && err == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if err != nil || !slices.Contains(valid_schemas, url.Scheme) {
|
|
u, err := TranslateSshNativeToUrl(urlString)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unable to parse url: %w", err)
|
|
}
|
|
return ParseGitRemoteUrl(u)
|
|
}
|
|
|
|
e := SplitStringNoEmpty(url.Path, "/")
|
|
if len(e) != 2 {
|
|
return nil, fmt.Errorf("Unexpected format for Gitea URL: %s", e)
|
|
}
|
|
|
|
org := e[0]
|
|
repo := strings.TrimSuffix(e[1], ".git")
|
|
|
|
u := GitUrl{
|
|
Org: org,
|
|
Repo: repo,
|
|
Commit: url.Fragment,
|
|
}
|
|
|
|
return &u, nil
|
|
}
|
|
|
|
func (giturl *GitUrl) RemoteName() string {
|
|
if giturl == nil || len(giturl.Org) == 0 || len(giturl.Repo) == 0 {
|
|
return "origin"
|
|
}
|
|
|
|
return strings.ToLower(giturl.Org) + "_" + strings.ToLower(giturl.Repo)
|
|
}
|
|
|
|
func PRtoString(pr *models.PullRequest) string {
|
|
if pr == nil {
|
|
return "(null)"
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s!%d", pr.Base.Repo.Owner.UserName, pr.Base.Repo.Name, pr.Index)
|
|
}
|
|
|
|
type DevelProject struct {
|
|
Project, Package string
|
|
}
|
|
|
|
type DevelProjects []*DevelProject
|
|
|
|
func FetchDevelProjects() (DevelProjects, error) {
|
|
res, err := http.Get("https://src.opensuse.org/openSUSE/Factory/raw/branch/main/pkgs/_meta/devel_packages")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
scanner := bufio.NewScanner(res.Body)
|
|
ret := []*DevelProject{}
|
|
for scanner.Scan() {
|
|
d := SplitStringNoEmpty(scanner.Text(), " ")
|
|
if len(d) == 2 {
|
|
ret = append(ret, &DevelProject{
|
|
Project: d[1],
|
|
Package: d[0],
|
|
})
|
|
}
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
var DevelProjectNotFound = errors.New("Devel project not found")
|
|
|
|
func (d DevelProjects) GetDevelProject(pkg string) (string, error) {
|
|
for _, item := range d {
|
|
if item.Package == pkg {
|
|
return item.Project, nil
|
|
}
|
|
}
|
|
|
|
return "", DevelProjectNotFound
|
|
}
|
|
|
|
var removedBranchNameSuffixes []string = []string{
|
|
"-rm",
|
|
"-removed",
|
|
"-deleted",
|
|
}
|
|
|
|
func findRemovedBranchSuffix(branchName string) string {
|
|
branchName = strings.ToLower(branchName)
|
|
|
|
for _, suffix := range removedBranchNameSuffixes {
|
|
if len(suffix) < len(branchName) && strings.HasSuffix(branchName, suffix) {
|
|
return suffix
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func IsRemovedBranch(branchName string) bool {
|
|
return len(findRemovedBranchSuffix(branchName)) > 0
|
|
}
|
|
|
|
func TrimRemovedBranchSuffix(branchName string) string {
|
|
suffix := findRemovedBranchSuffix(branchName)
|
|
if len(suffix) > 0 {
|
|
return branchName[0 : len(branchName)-len(suffix)]
|
|
}
|
|
|
|
return branchName
|
|
}
|