package main /* * 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 . */ import ( "errors" "flag" "fmt" "os" "os/exec" "path" "slices" "strings" "time" transport "github.com/go-openapi/runtime/client" "src.opensuse.org/autogits/common" apiclient "src.opensuse.org/autogits/common/gitea-generated/client" "src.opensuse.org/autogits/common/gitea-generated/client/organization" "src.opensuse.org/autogits/common/gitea-generated/client/repository" "src.opensuse.org/autogits/common/gitea-generated/models" ) const commandLineHelp = ` SYNTAX devel-importer ` func printHelp() { fmt.Println(commandLineHelp) } func runObsCommand(args ...string) ([]byte, error) { cmd := exec.Command("osc", args...) return cmd.Output() } func gitExec(pwd string, args ...string) string { cmd := exec.Command("git", args...) cmd.Dir = pwd str, err := cmd.CombinedOutput() if err != nil { fmt.Printf("git command failed: %v\n", args) fmt.Println(" err: %v", err) os.Exit(10) } return string(str) } func main() { if err := common.RequireGiteaSecretToken(); err != nil { fmt.Println("Missing GITEA_TOKEN") os.Exit(100) } if err := common.RequireObsSecretToken(); err != nil { fmt.Printf("Missing OBS_PASSWORD and/or OBS_USER\n") os.Exit(100) } webhookBase := os.Getenv("WEBHOOK_BASE") if webhookBase == "" { fmt.Printf("Missing WEBHOOK_BASE\n") os.Exit(100) } purgeOnly := flag.Bool("purge", false, "Purges package repositories. Use with caution") // revNew := flag.Int("nrevs", 20, "Number of new revisions in factory branch. Indicator of broken history import") flag.Parse() if flag.NArg() != 2 { printHelp() os.Exit(1) } prj := flag.Arg(0) org := flag.Arg(1) packageList, err := runObsCommand("ls", prj) if err != nil { fmt.Printf("Cannot list packages for project '%s'. Err: %v\n", prj, err) os.Exit(2) } packages := strings.Split(strings.TrimSpace(string(packageList)), "\n") fmt.Printf("%d packages: %s\n\n", len(packages), strings.Join(packages, " ")) r := transport.New("src.opensuse.org", apiclient.DefaultBasePath, [](string){"https"}) r.DefaultAuthentication = transport.BearerToken(common.GetGiteaToken()) // r.SetDebug(true) client := apiclient.New(r, nil) if *purgeOnly { fmt.Printf("Purging repositories...\n") for _, pkg := range packages { client.Repository.RepoDelete(repository.NewRepoDeleteParams().WithOwner(org).WithRepo(pkg), r.DefaultAuthentication) } os.Exit(10) } oldPackageRepos := make([]*models.Repository, 0, len(packages)) newPackages := make([]string, 0, len(packages)) for _, pkg := range packages { repo, err := client.Repository.RepoGet( repository.NewRepoGetParams(). WithDefaults().WithOwner("pool").WithRepo(pkg), r.DefaultAuthentication) if err != nil { if !errors.Is(err, &repository.RepoGetNotFound{}) { fmt.Println(err) os.Exit(3) } fmt.Printf("Cannot find package: %s\n", pkg) newPackages = append(newPackages, pkg) } else { oldPackageRepos = append(oldPackageRepos, repo.Payload) } } fmt.Printf("Num repos found: %d\n", len(oldPackageRepos)) // add webhooks to prjgit-updater hooksReponse, err := client.Organization.OrgListHooks( organization.NewOrgListHooksParams().WithOrg(org), r.DefaultAuthentication, ) if err != nil { fmt.Printf("Error fetching hooks for '%s'. Err: %v\n", org, err) os.Exit(4) } hooks := hooksReponse.Payload for _, hook := range hooks { fmt.Printf("hook %d: %#v", hook.ID, hook.Config) } /* hookActive := true hookType := models.CreateHookOptionTypeGitea client.Organization.OrgCreateHook( organization.NewOrgCreateHookParams().WithOrg(org).WithBody(&models.CreateHookOption{ Active: &hookActive, Type: &hookType, Config: models.CreateHookOptionConfig{ "method": "POST", "content_type": "application/json", "url": webhookBase + "/prgit-updater", }, Events: []string{ "push", }, }), r.DefaultAuthentication) */ // fork packags from pool cmd := exec.Command("./git-importer", func(r []*models.Repository) []string { ret := make([]string, len(r)) for i := range r { ret[i] = r[i].Name } return ret }(oldPackageRepos)...) out, err := cmd.CombinedOutput() fmt.Print(string(out)) if err != nil { fmt.Printf("Error returned by importer. Err: %v\n", err) os.Exit(14) } reposOK := true for i := range oldPackageRepos { pkg := oldPackageRepos[i] dir := path.Join("repos", pkg.Name) // add remote repos out := gitExec(dir, "remote", "show", "-n") if !slices.Contains(strings.Split(out, "\n"), "factory") { out := gitExec(dir, "remote", "add", "factory", pkg.CloneURL) if len(strings.TrimSpace(out)) > 1 { fmt.Println(out) } } if !slices.Contains(strings.Split(out, "\n"), "rpm") { out := gitExec(dir, "remote", "add", "rpm", "https://src.opensuse.org/rpm/"+pkg.Name+".git") if len(strings.TrimSpace(out)) > 1 { fmt.Println(out) } } out = gitExec(dir, "fetch", "--multiple", "factory", "rpm") if len(strings.TrimSpace(out)) > 1 { fmt.Println(out) } // check that nothing is broken with the update out = gitExec(dir, "rev-list", "factory") old_revs := strings.Split(out, "\n") out = gitExec(dir, "rev-list", "factory", "^factory/factory") added_revs := strings.Split(out, "\n") out = gitExec(dir, "rev-list", "factory", "^rpm/factory") added_rpm_revs := strings.Split(out, "\n") if len(added_revs) == len(old_revs) && len(added_rpm_revs) == len(old_revs) { fmt.Printf("Something is wrong with rev-ist for (len %d): %s\n", len(added_revs), pkg.Name) reposOK = false } } args := make([]string, 2, len(newPackages)+2) args[0] = "-p" args[1] = prj args = append(args, newPackages...) cmd = exec.Command("./git-importer", args...) out, err = cmd.CombinedOutput() fmt.Print(string(out)) if err != nil { fmt.Printf("Error returned by importer. Err: %v\n", err) os.Exit(15) } if !reposOK { fmt.Printf("aborting import due to broken repos above...\n") os.Exit(100) } for _, pkg := range oldPackageRepos { // update package fork, err := client.Repository.CreateFork(repository.NewCreateForkParams(). WithOwner("pool"). WithRepo(pkg.Name). WithBody(&models.CreateForkOption{ Organization: org, }), r.DefaultAuthentication) if err != nil { fmt.Printf("Error while trying to create fork from pool/%s. Err: %v\n", pkg.Name, err) os.Exit(10) } repo := fork.Payload repoList, err := client.Repository.RepoListBranches( repository.NewRepoListBranchesParams().WithOwner(org).WithRepo(pkg.Name), r.DefaultAuthentication, ) if err != nil { fmt.Printf("Cannot get list of branches for forked repo: %s/%s\n", org, pkg.Name) os.Exit(11) } priorityBranches := []string{ "devel", "factory", } idx := len(priorityBranches) for _, branch := range repoList.Payload { i := slices.Index(priorityBranches, branch.Name) if i > -1 && i < idx { idx = i } } branchName := priorityBranches[idx] dir := path.Join("repos", pkg.Name) remotes := gitExec(dir, "remote", "show") if !slices.Contains(strings.Split(remotes, "\n"), "devel") { gitExec(dir, "remote", "add", "devel", repo.SSHURL) } gitExec(dir, "branch", "main", "-f", branchName) time.Sleep(2 * time.Second) gitExec(dir, "push", "devel", "main") time.Sleep(2 * time.Second) _, err = client.Repository.RepoEdit(repository.NewRepoEditParams().WithOwner(org).WithRepo(repo.Name).WithBody(&models.EditRepoOption{ DefaultBranch: "main", DefaultMergeStyle: "fast-forward-only", }), r.DefaultAuthentication) if err != nil { fmt.Printf("Failed to set default branch for package fork: %s/%s Err: %v", repo.Owner.UserName, repo.Name, err) os.Exit(12) } } for _, pkg := range newPackages { ret, err := client.Organization.CreateOrgRepo(organization.NewCreateOrgRepoParams().WithOrg(org).WithBody( &models.CreateRepoOption{ ObjectFormatName: "sha256", AutoInit: false, Name: &pkg, DefaultBranch: "main", }), r.DefaultAuthentication, ) if err != nil { fmt.Printf("Error creating new package repository: %s Err: %v", pkg, err) os.Exit(13) } repo := ret.Payload dir := path.Join("repos", pkg) remotes := gitExec(dir, "remote", "show") if !slices.Contains(strings.Split(remotes, "\n"), "devel") { gitExec(dir, "remote", "add", "devel", repo.SSHURL) } gitExec(dir, "branch", "main", "-f", "factory") time.Sleep(2 * time.Second) gitExec(dir, "push", "devel", "main") time.Sleep(2 * time.Second) _, err = client.Repository.RepoEdit(repository.NewRepoEditParams().WithOwner(org).WithRepo(pkg).WithBody(&models.EditRepoOption{ DefaultBranch: "main", DefaultMergeStyle: "fast-forward-only", }), r.DefaultAuthentication) if err != nil { fmt.Printf("Failed to set default branch for package fork: %s/%s Err: %v", repo.Owner.UserName, repo.Name, err) os.Exit(14) } } }