Compare commits
10 Commits
pr-pedanti
...
ibs_state
| Author | SHA256 | Date | |
|---|---|---|---|
| 4da7a62404 | |||
| 1f6ec2a1c3 | |||
| e43833c2ed | |||
| 98d701bfe8 | |||
| 1c63899e49 | |||
| 2c891ded6a | |||
| ef61736d70 | |||
| 57a4860d67 | |||
| 94d83ccf80 | |||
|
|
5440476d10 |
@@ -22,6 +22,8 @@ Release: 0
|
||||
Summary: GitWorkflow utilities
|
||||
License: GPL-2.0-or-later
|
||||
URL: https://src.opensuse.org/adamm/autogits
|
||||
#!RemoteAsset: git+https://src.suse.de/adrianSuSE/autogits#ibs_state
|
||||
Source0: %name-%version.tar.xz
|
||||
BuildRequires: git
|
||||
BuildRequires: systemd-rpm-macros
|
||||
BuildRequires: go
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -58,6 +59,7 @@ type QAConfig struct {
|
||||
Name string
|
||||
Origin string
|
||||
Label string // requires this gitea lable to be set or skipped
|
||||
Labels []string // requires any of the lables to be set
|
||||
BuildDisableRepos []string // which repos to build disable in the new project
|
||||
}
|
||||
|
||||
@@ -204,21 +206,16 @@ func ReadWorkflowConfig(gitea GiteaFileContentAndRepoFetcher, git_project string
|
||||
|
||||
func ResolveWorkflowConfigs(gitea GiteaFileContentAndRepoFetcher, config *ConfigFile) (AutogitConfigs, error) {
|
||||
configs := make([]*AutogitConfig, 0, len(config.GitProjectNames))
|
||||
var errs []error
|
||||
for _, git_project := range config.GitProjectNames {
|
||||
c, err := ReadWorkflowConfig(gitea, git_project)
|
||||
if err != nil {
|
||||
// can't sync, so ignore for now
|
||||
errs = append(errs, err)
|
||||
log.Println(err)
|
||||
} else {
|
||||
configs = append(configs, c)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return configs, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
@@ -346,6 +343,13 @@ func ParseStagingConfig(data []byte) (*StagingConfig, error) {
|
||||
if err := json.Unmarshal(data, &staging); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// backward compability, transfer legacy Label to new Labels array
|
||||
for _, setup := range staging.QA {
|
||||
if len(setup.Labels) == 0 && len(setup.Label) > 0 {
|
||||
setup.Labels = []string{setup.Label}
|
||||
setup.Label = ""
|
||||
}
|
||||
}
|
||||
|
||||
return &staging, nil
|
||||
}
|
||||
|
||||
@@ -327,7 +327,6 @@ func main() {
|
||||
interval := flag.Int64("interval", 10, "Notification polling interval in minutes (min 1 min)")
|
||||
configFile := flag.String("config", "", "PrjGit listing config file")
|
||||
logging := flag.String("logging", "info", "Logging level: [none, error, info, debug]")
|
||||
exitOnConfigError := flag.Bool("exit-on-config-error", false, "Exit if any repository in configuration cannot be resolved")
|
||||
flag.BoolVar(&common.IsDryRun, "dry", false, "Dry run, no effect. For debugging")
|
||||
flag.Parse()
|
||||
|
||||
@@ -383,10 +382,8 @@ func main() {
|
||||
giteaTransport := common.AllocateGiteaTransport(*giteaUrl)
|
||||
configs, err := common.ResolveWorkflowConfigs(giteaTransport, configData)
|
||||
if err != nil {
|
||||
common.LogError("Failed to resolve some configuration repositories:", err)
|
||||
if *exitOnConfigError {
|
||||
return
|
||||
}
|
||||
common.LogError("Cannot parse workflow configs:", err)
|
||||
return
|
||||
}
|
||||
|
||||
reviewer, err := giteaTransport.GetCurrentUser()
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
@@ -733,8 +734,16 @@ func ProcessQaProjects(obs common.ObsClientInterface, stagingConfig *common.Stag
|
||||
var qa_projects []string
|
||||
for _, setup := range stagingConfig.QA {
|
||||
QAproject := stagingProject + ":" + setup.Name
|
||||
if len(setup.Label) > 0 {
|
||||
if _, ok := prLabelNames[setup.Label]; !ok {
|
||||
if len(setup.Labels) > 0 {
|
||||
matchedLabel := false
|
||||
for labelName := range prLabelNames {
|
||||
if slices.Contains(setup.Labels, labelName) {
|
||||
matchedLabel = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if matchedLabel == false {
|
||||
if !IsDryRun {
|
||||
// blindly remove, will fail when not existing
|
||||
obs.DeleteProject(QAproject)
|
||||
@@ -910,10 +919,13 @@ func ProcessPullRequest(obs common.ObsClientInterface, gitea common.Gitea, org,
|
||||
if !stagingConfig.RebuildAll {
|
||||
for pkg, headOid := range headSubmodules {
|
||||
if baseOid, exists := baseSubmodules[pkg]; !exists || baseOid != headOid {
|
||||
if pkg != "rpms" && pkg != "dependencies" {
|
||||
_, spkg := filepath.Split(pkg)
|
||||
if exists {
|
||||
modifiedPackages = append(modifiedPackages, pkg)
|
||||
modifiedPackages = append(modifiedPackages, spkg)
|
||||
} else {
|
||||
newPackages = append(newPackages, pkg)
|
||||
newPackages = append(newPackages, spkg)
|
||||
}
|
||||
}
|
||||
common.LogDebug(pkg, ":", baseOid, "->", headOid)
|
||||
}
|
||||
@@ -1023,6 +1035,7 @@ func ProcessPullRequest(obs common.ObsClientInterface, gitea common.Gitea, org,
|
||||
done := false
|
||||
overallBuildStatus := ProcessBuildStatus(stagingResult)
|
||||
commentSuffix := ""
|
||||
common.LogDebug("Number of QA Projects: ", len(qaProjects))
|
||||
if len(qaProjects) > 0 && overallBuildStatus == BuildStatusSummarySuccess {
|
||||
seperator := " in "
|
||||
for _, qaProject := range qaProjects {
|
||||
@@ -1171,6 +1184,7 @@ var IsDryRun bool
|
||||
var ProcessPROnly string
|
||||
var ObsClient common.ObsClientInterface
|
||||
var BotUser string
|
||||
var PollInterval = 5 * time.Minute
|
||||
|
||||
func ObsWebHostFromApiHost(apihost string) string {
|
||||
u, err := url.Parse(apihost)
|
||||
@@ -1193,9 +1207,18 @@ func main() {
|
||||
flag.StringVar(&ObsApiHost, "obs", "", "API for OBS instance")
|
||||
flag.StringVar(&ObsWebHost, "obs-web", "", "Web OBS instance, if not derived from the obs config")
|
||||
flag.BoolVar(&IsDryRun, "dry", false, "Dry-run, don't actually create any build projects or review changes")
|
||||
pollIntervalStr := flag.String("poll-interval", common.GetEnvOverrideString(os.Getenv("AUTOGITS_STAGING_BOT_POLL_INTERVAL"), ""), "Polling interval for notifications (e.g. 5m, 10s)")
|
||||
debug := flag.Bool("debug", false, "Turns on debug logging")
|
||||
flag.Parse()
|
||||
|
||||
if len(*pollIntervalStr) > 0 {
|
||||
if d, err := time.ParseDuration(*pollIntervalStr); err == nil {
|
||||
PollInterval = d
|
||||
} else {
|
||||
common.LogError("Invalid poll interval:", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *debug {
|
||||
common.SetLoggingLevel(common.LogLevelDebug)
|
||||
} else {
|
||||
@@ -1264,6 +1287,6 @@ func main() {
|
||||
for {
|
||||
PollWorkNotifications(ObsClient, gitea)
|
||||
common.LogInfo("Poll cycle finished")
|
||||
time.Sleep(5 * time.Minute)
|
||||
time.Sleep(PollInterval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,13 @@ After=network-online.target
|
||||
Type=exec
|
||||
ExecStart=/usr/bin/obs-staging-bot
|
||||
EnvironmentFile=-/etc/default/obs-staging-bot.env
|
||||
DynamicUser=yes
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
User=autogits_obs_staging_bot
|
||||
Group=users
|
||||
|
||||
# This may work when not using ssh api connections:
|
||||
#DynamicUser=yes
|
||||
#NoNewPrivileges=yes
|
||||
#ProtectSystem=strict
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -123,7 +123,7 @@ func processConfiguredRepositoryAction(action *common.RepositoryWebhookEvent, co
|
||||
common.LogError(" - ", action.Repository.Name, "repo is not sha256. Ignoring.")
|
||||
return
|
||||
}
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name))
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + action.Repository.Name, action.Repository.Name))
|
||||
defer git.GitExecQuietOrPanic(gitPrj, "submodule", "deinit", "--all", "-f")
|
||||
|
||||
branch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, action.Repository.Name), "branch", "--show-current"))
|
||||
@@ -215,7 +215,7 @@ func processConfiguredPushAction(action *common.PushWebhookEvent, config *common
|
||||
}
|
||||
|
||||
if stat, err := os.Stat(filepath.Join(git.GetPath(), gitPrj, action.Repository.Name)); err != nil {
|
||||
git.GitExecOrPanic(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", action.Repository.Clone_Url, action.Repository.Name)
|
||||
git.GitExecOrPanic(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + action.Repository.Name, action.Repository.Name)
|
||||
common.LogDebug("Pushed to package that is not part of the project. Re-adding...", err)
|
||||
} else if !stat.IsDir() {
|
||||
common.LogError("Pushed to a package that is not a submodule but exists in the project. Ignoring.")
|
||||
@@ -420,7 +420,7 @@ next_repo:
|
||||
}
|
||||
|
||||
// add repository to git project
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", r.CloneURL, r.Name))
|
||||
common.PanicOnError(git.GitExec(gitPrj, "submodule", "--quiet", "add", "--force", "--depth", "1", "../" + r.Name, r.Name))
|
||||
|
||||
curBranch := strings.TrimSpace(git.GitExecWithOutputOrPanic(path.Join(gitPrj, r.Name), "branch", "--show-current"))
|
||||
if branch != curBranch {
|
||||
@@ -503,10 +503,7 @@ func updateConfiguration(configFilename string, orgs *[]string) {
|
||||
os.Exit(4)
|
||||
}
|
||||
|
||||
configs, err := common.ResolveWorkflowConfigs(gitea, configFile)
|
||||
if err != nil {
|
||||
common.LogError("Failed to resolve some configuration repositories:", err)
|
||||
}
|
||||
configs, _ := common.ResolveWorkflowConfigs(gitea, configFile)
|
||||
configuredRepos = make(map[string][]*common.AutogitConfig)
|
||||
*orgs = make([]string, 0, 1)
|
||||
for _, c := range configs {
|
||||
|
||||
@@ -58,7 +58,6 @@ func main() {
|
||||
checkOnStart := flag.Bool("check-on-start", common.GetEnvOverrideBool(os.Getenv("AUTOGITS_CHECK_ON_START"), false), "Check all repositories for consistency on start, without delays")
|
||||
checkIntervalHours := flag.Float64("check-interval", 5, "Check interval (+-random delay) for repositories for consitency, in hours")
|
||||
flag.BoolVar(&ListPROnly, "list-prs-only", false, "Only lists PRs without acting on them")
|
||||
exitOnConfigError := flag.Bool("exit-on-config-error", false, "Exit if any repository in configuration cannot be resolved")
|
||||
flag.Int64Var(&PRID, "id", -1, "Process only the specific ID and ignore the rest. Use for debugging")
|
||||
basePath := flag.String("repo-path", common.GetEnvOverrideString(os.Getenv("AUTOGITS_REPO_PATH"), ""), "Repository path. Default is temporary directory")
|
||||
pr := flag.String("only-pr", "", "Only specific PR to process. For debugging")
|
||||
@@ -98,10 +97,8 @@ func main() {
|
||||
|
||||
configs, err := common.ResolveWorkflowConfigs(Gitea, config)
|
||||
if err != nil {
|
||||
common.LogError("Failed to resolve some configuration repositories:", err)
|
||||
if *exitOnConfigError {
|
||||
return
|
||||
}
|
||||
common.LogError("Cannot resolve config files:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range configs {
|
||||
|
||||
@@ -180,6 +180,33 @@ func (pr *PRProcessor) SetSubmodulesToMatchPRSet(prset *common.PRSet) error {
|
||||
}
|
||||
|
||||
updateSubmoduleInPR(submodulePath, prHead, git)
|
||||
err := git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fetch")
|
||||
common.LogError("lfs fetch err: ", err)
|
||||
if err = git.GitExec(path.Join(common.DefaultGitPrj, submodulePath), "lfs", "fsck"); err != nil {
|
||||
|
||||
found_comment := false
|
||||
timeline, terr := common.FetchTimelineSinceLastPush(Gitea, prHead, org, repo, idx)
|
||||
if terr != nil {
|
||||
common.LogError("lfs fsck error, but timeline fetch failed")
|
||||
break
|
||||
}
|
||||
msgPrefix := "The LFS objects are broken!"
|
||||
for _, t := range timeline {
|
||||
if t.Type == common.TimelineCommentType_Comment && strings.HasPrefix(t.Body, msgPrefix) {
|
||||
found_comment = true
|
||||
common.LogError("lfs fsck Comment already found")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found_comment && !common.IsDryRun {
|
||||
Gitea.AddComment(pr.PR, msgPrefix + " Please verify with 'git lfs fsck'")
|
||||
}
|
||||
common.LogError("lfs fsck failed with: ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
status, err := git.GitStatus(common.DefaultGitPrj)
|
||||
common.LogDebug("status:", status)
|
||||
common.LogDebug("submodule", repo, " hash:", id, " -> ", prHead)
|
||||
@@ -314,6 +341,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
||||
// well, wrong place...
|
||||
// common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...")
|
||||
// Gitea.AddComment(PrjGitPR.PR, "This PR does not allow maintainer changes, but referenced package branch has changed!")
|
||||
common.LogError("Warning: source and target branch are in different repositories. We may not have the right permissions...")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -384,7 +412,7 @@ func (pr *PRProcessor) UpdatePrjGitPR(prset *common.PRSet) error {
|
||||
}
|
||||
return CurrentTitle == NewTitle
|
||||
}
|
||||
if PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) {
|
||||
if !pr.config.NoProjectGitPR && PrjGitPR.PR.User.UserName == CurrentUser.UserName && (PrjGitPR.PR.Body != PrjGitBody || !isPrTitleSame(PrjGitPR.PR.Title, PrjGitTitle)) {
|
||||
Gitea.UpdatePullRequest(PrjGit.Owner.UserName, PrjGit.Name, PrjGitPR.PR.Index, &models.EditPullRequestOption{
|
||||
RemoveDeadline: true,
|
||||
Title: PrjGitTitle,
|
||||
|
||||
Reference in New Issue
Block a user