aboutsummaryrefslogtreecommitdiffhomepage
path: root/releaser/releaser.go
diff options
context:
space:
mode:
Diffstat (limited to 'releaser/releaser.go')
-rw-r--r--releaser/releaser.go254
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...)
}