package main import ( "bytes" "context" "encoding/json" "fmt" "net/http" "os" "path/filepath" "text/template" "git.unistack.org/unistack-org/pkgdash/internal/modules" yamlcodec "go.unistack.org/micro-codec-yaml/v4" envconfig "go.unistack.org/micro-config-env/v4" fileconfig "go.unistack.org/micro-config-file/v4" "go.unistack.org/micro/v4/config" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/options" "golang.org/x/mod/modfile" "golang.org/x/mod/module" ) // https://docs.github.com/ru/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file var ( DefaultPullRequestTitle = `Bump {{.Name}} from {{.VersionOld}} to {{.VersionNew}}` DefaultPullRequestBody = `Bumps {{.Name}} from {{.VersionOld}} to {{.VersionNew}}.` ) type Config struct { PullRequestTitle string `json:"pull_request_title" yaml:"pull_request_title"` PullRequestBody string `json:"pull_request_body" yaml:"pull_request_body"` } var ( configFiles = []string{ "dependabot.yml", "pkgdash.yml", "pkgdash.yaml", } configDirs = []string{ ".gitea", ".github", ".gitlab", } repoMgmt = map[string]string{ ".gitea": "gitea", ".gogs": "gogs", ".github": "github", ".gitlab": "gitlab", } ) type Data struct { Modules []module.Version } func main() { var err error ctx, cancel := context.WithCancel(context.Background()) defer cancel() cfg := &Config{} if err = config.Load(ctx, []config.Config{ config.NewConfig( config.Struct(cfg), ), envconfig.NewConfig( config.Struct(cfg), ), }, config.LoadOverride(true), ); err != nil { logger.Fatalf(ctx, "failed to load config: %v", err) } for _, configDir := range configDirs { for _, configFile := range configFiles { c := fileconfig.NewConfig( config.AllowFail(true), config.Struct(cfg), options.Codec(yamlcodec.NewCodec()), fileconfig.Path(filepath.Join(configDir, configFile)), ) if err = c.Load(ctx, config.LoadOverride(true)); err != nil { logger.Fatalf(ctx, "failed to load config: %v", err) } } } path := "." if len(os.Args) > 1 { path = os.Args[1] } name, err := modules.FindModFile(path) if err != nil { panic(err) } buf, err := os.ReadFile(name) if err != nil { panic(err) } mfile, err := modfile.Parse(name, buf, nil) if err != nil { panic(err) } mvs := make(map[string]modules.Update) updateOptions := modules.UpdateOptions{ Pre: false, Major: false, Cached: true, OnUpdate: func(u modules.Update) { if u.Err != nil { logger.Errorf(ctx, "%s: failed: %v", u.Module.Path, u.Err) return } mvs[u.Module.Path] = u }, } for _, req := range mfile.Require { updateOptions.Modules = append(updateOptions.Modules, req.Mod) } modules.Updates(updateOptions) for path, u := range mvs { fmt.Printf("%s from %s to %s\n", path, u.Module.Version, u.Version) } repoMgmt := getRepoMgmt() if repoMgmt == "unknown" { logger.Fatalf(ctx, "failed to get repo management") } switch repoMgmt { case "gitea": err = giteaPullRequest(ctx, mvs) } if err != nil { logger.Fatalf(ctx, "failed to create pr: %v", err) } } func getRepoMgmt() string { for _, configDir := range configDirs { _, err := os.Stat(configDir) if err != nil { continue } if name, ok := repoMgmt[configDir]; ok { return name } } return "unknown" } func giteaPullRequest(ctx context.Context, cfg *Config, mods []module.Version) error { envAPIURL := os.Getenv("GITHUB_API_URL") envREPOSITORY := os.Getenv("GITHUB_REPOSITORY") envTOKEN := os.Getenv("GITHUB_TOKEN") var buf []byte var err error data := &Data{Modules: mods} tplTitle, err := template.New("pull_request_title").Parse(cfg.PullRequestTitle) if err != nil { logger.Fatalf(ctx, "failed to parse template: %v", err) } wTitle := bytes.NewBuffer(nil) if err = tplTitle.Execute(wTitle, data); err != nil { logger.Fatalf(ctx, "failed to execute template: %v", err) } tplBody, err := template.New("pull_request_title").Parse(cfg.PullRequestBody) if err != nil { logger.Fatalf(ctx, "failed to parse template: %v", err) } wBody := bytes.NewBuffer(nil) if err = tplBody.Execute(wBody, data); err != nil { logger.Fatalf(ctx, "failed to execute template: %v", err) } for _, mod := range mods { body := map[string]string{ "base": "", "body": "", "head": "", "title": "", } buf, err := json.Marshal(body) if err != nil { return err } /* 'https://try.gitea.io/api/v1/repos/org/repo/pulls?token=myaccesstoken' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "base": "main", "body": "This is a PR", "head": "username:feature-branch", "title": "PR title" */ req, err := http.NewRequestWithContext(ctx, http.MethodPost, envAPIURL+"/repos/"+envREPOSITORY+"/pulls?token="+envTOKEN, bytes.NewReader(buf)) if err != nil { return err } req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") rsp, err := http.DefaultClient.Do(req) if err != nil { return err } if rsp.StatusCode != http.StatusOK { return fmt.Errorf("unknown error") } } return nil }