| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package task |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/format" |
| "path" |
| "regexp" |
| "strings" |
| "text/template" |
| "time" |
| |
| "golang.org/x/build/gerrit" |
| "golang.org/x/build/internal/workflow" |
| ) |
| |
| // MailDLCL mails a golang.org/dl CL that adds commands for the |
| // specified Go versions. It accepts one or two versions only. |
| // |
| // The versions must use the same format as Go tags. For example: |
| // - "go1.17.2" and "go1.16.9" for a minor Go release |
| // - "go1.18" for a major Go release |
| // - "go1.18beta1" or "go1.18rc1" for a pre-release |
| // |
| // On success, the ID of the change is returned, like "dl~1234". |
| func (t *VersionTasks) MailDLCL(ctx *workflow.TaskContext, versions []string, reviewers []string, dryRun bool) (changeID string, _ error) { |
| if err := oneOrTwoGoVersions(versions); err != nil { |
| return "", err |
| } |
| |
| var files = make(map[string]string) // Map key is relative path, and map value is file content. |
| |
| // Generate main.go files for versions from the template. |
| for _, ver := range versions { |
| var buf bytes.Buffer |
| versionNoPatch, err := versionNoPatch(ver) |
| if err != nil { |
| return "", err |
| } |
| if err := dlTmpl.Execute(&buf, struct { |
| Year int |
| Version string // "go1.5.3rc2" |
| VersionNoPatch string // "go1.5" |
| CapitalSpaceVersion string // "Go 1.5" |
| DocHost string // "go.dev" or "tip.golang.org" for rc/beta |
| }{ |
| Year: time.Now().UTC().Year(), |
| Version: ver, |
| VersionNoPatch: versionNoPatch, |
| DocHost: docHost(ver), |
| CapitalSpaceVersion: strings.Replace(ver, "go", "Go ", 1), |
| }); err != nil { |
| return "", fmt.Errorf("dlTmpl.Execute: %v", err) |
| } |
| gofmted, err := format.Source(buf.Bytes()) |
| if err != nil { |
| return "", fmt.Errorf("could not gofmt: %v", err) |
| } |
| files[path.Join(ver, "main.go")] = string(gofmted) |
| ctx.Printf("file %q (command %q):\n%s", path.Join(ver, "main.go"), "golang.org/dl/"+ver, gofmted) |
| } |
| |
| // Create a Gerrit CL using the Gerrit API. |
| if dryRun { |
| return "(dry-run)", nil |
| } |
| changeInput := gerrit.ChangeInput{ |
| Project: "dl", |
| Subject: "dl: add " + strings.Join(versions, " and "), |
| Branch: "master", |
| } |
| return t.Gerrit.CreateAutoSubmitChange(ctx, changeInput, reviewers, files) |
| } |
| |
| // oneOrTwoGoVersions returns true iff len(versions) is exactly 1 or 2 |
| // and each version passes some lightweight checks that catch problems. |
| func oneOrTwoGoVersions(versions []string) error { |
| if len(versions) < 1 || len(versions) > 2 { |
| return fmt.Errorf("got %d Go versions, want 1 or 2", len(versions)) |
| } |
| for _, ver := range versions { |
| if ver != strings.ToLower(ver) { |
| return fmt.Errorf("version %q is not lowercase", ver) |
| } else if strings.Contains(ver, " ") { |
| return fmt.Errorf("version %q contains a space", ver) |
| } else if !strings.HasPrefix(ver, "go") { |
| return fmt.Errorf("version %q doesn't have the 'go' prefix", ver) |
| } |
| } |
| return nil |
| } |
| |
| func docHost(ver string) string { |
| if strings.Contains(ver, "rc") || strings.Contains(ver, "beta") { |
| return "tip.golang.org" |
| } |
| return "go.dev" |
| } |
| |
| func versionNoPatch(ver string) (string, error) { |
| rx := regexp.MustCompile(`^(go\d+\.\d+)($|[\.]?\d*)($|rc|beta|\.)`) |
| m := rx.FindStringSubmatch(ver) |
| if m == nil { |
| return "", fmt.Errorf("unrecognized version %q", ver) |
| } |
| if m[2] != "" { |
| return "devel/release#" + m[1] + ".minor", nil |
| } |
| return m[1], nil |
| } |
| |
| var dlTmpl = template.Must(template.New("").Parse(`// Copyright {{.Year}} The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // The {{.Version}} command runs the go command from {{.CapitalSpaceVersion}}. |
| // |
| // To install, run: |
| // |
| // $ go install golang.org/dl/{{.Version}}@latest |
| // $ {{.Version}} download |
| // |
| // And then use the {{.Version}} command as if it were your normal go |
| // command. |
| // |
| // See the release notes at https://{{.DocHost}}/doc/{{.VersionNoPatch}}. |
| // |
| // File bugs at https://go.dev/issue/new. |
| package main |
| |
| import "golang.org/dl/internal/version" |
| |
| func main() { |
| version.Run("{{.Version}}") |
| } |
| `)) |