diff options
Diffstat (limited to 'releaser/releaser.go')
-rw-r--r-- | releaser/releaser.go | 254 |
1 files changed, 94 insertions, 160 deletions
diff --git a/releaser/releaser.go b/releaser/releaser.go index ebc344e98..fc16a2572 100644 --- a/releaser/releaser.go +++ b/releaser/releaser.go @@ -17,7 +17,6 @@ package releaser import ( "fmt" - "io/ioutil" "log" "os" "path/filepath" @@ -25,167 +24,105 @@ import ( "strings" "github.com/gohugoio/hugo/common/hexec" - - "errors" - "github.com/gohugoio/hugo/common/hugo" ) const commitPrefix = "releaser:" -// ReleaseHandler provides functionality to release a new version of Hugo. -// Test this locally without doing an actual release: -// go run -tags release main.go release --skip-publish --try -r 0.90.0 -// Or a variation of the above -- the skip-publish flag makes sure that any changes are performed to the local Git only. -type ReleaseHandler struct { - cliVersion string - - skipPublish bool - - // Just simulate, no actual changes. - try bool - - git func(args ...string) (string, error) -} - -func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) { - newVersion := hugo.MustParseVersion(r.cliVersion) - finalVersion := newVersion.Next() - finalVersion.PatchLevel = 0 +// New initialises a ReleaseHandler. +func New(skipPush, try bool, step int) (*ReleaseHandler, error) { + if step < 1 || step > 2 { + return nil, fmt.Errorf("step must be 1 or 2") + } - if newVersion.Suffix != "-test" { - newVersion.Suffix = "" + prefix := "release-" + branch, err := git("rev-parse", "--abbrev-ref", "HEAD") + if err != nil { + return nil, err + } + if !strings.HasPrefix(branch, prefix) { + return nil, fmt.Errorf("branch %q is not a release branch", branch) } - finalVersion.Suffix = "-DEV" + logf("Branch: %s\n", branch) - return newVersion, finalVersion -} - -// New initialises a ReleaseHandler. -func New(version string, skipPublish, try bool) *ReleaseHandler { - // When triggered from CI release branch - version = strings.TrimPrefix(version, "release-") + version := strings.TrimPrefix(branch, prefix) version = strings.TrimPrefix(version, "v") - rh := &ReleaseHandler{cliVersion: version, skipPublish: skipPublish, try: try} + rh := &ReleaseHandler{branchVersion: version, skipPush: skipPush, try: try, step: step} if try { rh.git = func(args ...string) (string, error) { - fmt.Println("git", strings.Join(args, " ")) + logln("git", strings.Join(args, " ")) return "", nil } } else { rh.git = git } - return rh + return rh, nil } -// Run creates a new release. -func (r *ReleaseHandler) Run() error { - if os.Getenv("GITHUB_TOKEN") == "" { - return errors.New("GITHUB_TOKEN not set, create one here with the repo scope selected: https://github.com/settings/tokens/new") - } +// ReleaseHandler provides functionality to release a new version of Hugo. +// Test this locally without doing an actual release: +// go run -tags release main.go release --skip-publish --try -r 0.90.0 +// Or a variation of the above -- the skip-publish flag makes sure that any changes are performed to the local Git only. +type ReleaseHandler struct { + branchVersion string - fmt.Printf("Start release from %q\n", wd()) + // 1 or 2. + step int - newVersion, finalVersion := r.calculateVersions() + // No remote pushes. + skipPush bool + + // Just simulate, no actual changes. + try bool + + git func(args ...string) (string, error) +} +// Run creates a new release. +func (r *ReleaseHandler) Run() error { + newVersion, finalVersion := r.calculateVersions() version := newVersion.String() tag := "v" + version - isPatch := newVersion.PatchLevel > 0 mainVersion := newVersion mainVersion.PatchLevel = 0 - // Exit early if tag already exists - exists, err := tagExists(tag) - if err != nil { - return err - } - - if exists { - return fmt.Errorf("tag %q already exists", tag) - } - - var changeLogFromTag string + defer r.gitPush() - if newVersion.PatchLevel == 0 { - // There may have been patch releases between, so set the tag explicitly. - changeLogFromTag = "v" + newVersion.Prev().String() - exists, _ := tagExists(changeLogFromTag) - if !exists { - // fall back to one that exists. - changeLogFromTag = "" + if r.step == 1 { + if err := r.bumpVersions(newVersion); err != nil { + return err } - } - - var ( - gitCommits gitInfos - gitCommitsDocs gitInfos - ) - defer r.gitPush() // TODO(bep) - - gitCommits, err = getGitInfos(changeLogFromTag, "hugo", "", !r.try) - if err != nil { - return err - } - - // TODO(bep) explicit tag? - gitCommitsDocs, err = getGitInfos("", "hugoDocs", "../hugoDocs", !r.try) - if err != nil { - return err - } - - releaseNotesFile, err := r.writeReleaseNotesToTemp(version, isPatch, gitCommits, gitCommitsDocs) - if err != nil { - return err - } - - if _, err := r.git("add", releaseNotesFile); err != nil { - return err - } - - commitMsg := fmt.Sprintf("%s Add release notes for %s", commitPrefix, newVersion) - commitMsg += "\n[ci skip]" - - if _, err := r.git("commit", "-m", commitMsg); err != nil { - return err - } - - if err := r.bumpVersions(newVersion); err != nil { - return err - } - - if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Bump versions for release of %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil { - return err - } + if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Bump versions for release of %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil { + return err + } - if _, err := r.git("tag", "-a", tag, "-m", fmt.Sprintf("%s %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil { - return err - } + // The above commit will be the target for this release, so print it to the console in a env friendly way. + sha, err := git("rev-parse", "HEAD") + if err != nil { + return err + } - if !r.skipPublish { - if _, err := r.git("push", "origin", tag); err != nil { + // Hugoreleaser will do the actual release using these values. + if err := r.replaceInFile("hugoreleaser.env", + `HUGORELEASER_TAG=(\S*)`, "HUGORELEASER_TAG="+tag, + `HUGORELEASER_COMMITISH=(\S*)`, "HUGORELEASER_COMMITISH="+sha, + ); err != nil { return err } - } + logf("HUGORELEASER_TAG=%s\n", tag) + logf("HUGORELEASER_COMMITISH=%s\n", sha) - if err := r.release(releaseNotesFile); err != nil { - return err + return nil } if err := r.bumpVersions(finalVersion); err != nil { return err } - if !r.try { - // No longer needed. - if err := os.Remove(releaseNotesFile); err != nil { - return err - } - } - if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Prepare repository for %s\n\n[ci skip]", commitPrefix, finalVersion)); err != nil { return err } @@ -193,36 +130,6 @@ func (r *ReleaseHandler) Run() error { return nil } -func (r *ReleaseHandler) gitPush() { - if r.skipPublish { - return - } - if _, err := r.git("push", "origin", "HEAD"); err != nil { - log.Fatal("push failed:", err) - } -} - -func (r *ReleaseHandler) release(releaseNotesFile string) error { - if r.try { - fmt.Println("Skip goreleaser...") - return nil - } - - args := []string{"--parallelism", "2", "--timeout", "120m", "--rm-dist", "--release-notes", releaseNotesFile} - if r.skipPublish { - args = append(args, "--skip-publish") - } - - cmd, _ := hexec.SafeCommand("goreleaser", args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - return fmt.Errorf("goreleaser failed: %w", err) - } - return nil -} - func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error { toDev := "" @@ -264,6 +171,29 @@ func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error { return nil } +func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) { + newVersion := hugo.MustParseVersion(r.branchVersion) + finalVersion := newVersion.Next() + finalVersion.PatchLevel = 0 + + if newVersion.Suffix != "-test" { + newVersion.Suffix = "" + } + + finalVersion.Suffix = "-DEV" + + return newVersion, finalVersion +} + +func (r *ReleaseHandler) gitPush() { + if r.skipPush { + return + } + if _, err := r.git("push", "origin", "HEAD"); err != nil { + log.Fatal("push failed:", err) + } +} + func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error { filename = filepath.FromSlash(filename) fi, err := os.Stat(filename) @@ -272,11 +202,11 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error } if r.try { - fmt.Printf("Replace in %q: %q\n", filename, oldNew) + logf("Replace in %q: %q\n", filename, oldNew) return nil } - b, err := ioutil.ReadFile(filename) + b, err := os.ReadFile(filename) if err != nil { return err } @@ -287,18 +217,22 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error newContent = re.ReplaceAllString(newContent, oldNew[i+1]) } - return ioutil.WriteFile(filename, []byte(newContent), fi.Mode()) -} - -func isCI() bool { - return os.Getenv("CI") != "" + return os.WriteFile(filename, []byte(newContent), fi.Mode()) } -func wd() string { - p, err := os.Getwd() +func git(args ...string) (string, error) { + cmd, _ := hexec.SafeCommand("git", args...) + out, err := cmd.CombinedOutput() if err != nil { - log.Fatal(err) + return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args) } - return p + return string(out), nil +} + +func logf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format, args...) +} +func logln(args ...interface{}) { + fmt.Fprintln(os.Stderr, args...) } |