summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml49
-rw-r--r--commands/release.go5
-rw-r--r--helpers/hugo.go4
-rw-r--r--helpers/hugo_test.go2
-rw-r--r--releaser/git.go10
-rw-r--r--releaser/git_test.go5
-rw-r--r--releaser/github.go31
-rw-r--r--releaser/github_test.go6
-rw-r--r--releaser/releasenotes_writer.go37
-rw-r--r--releaser/releasenotes_writer_test.go2
-rw-r--r--releaser/releaser.go126
11 files changed, 194 insertions, 83 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..b1897072c
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,49 @@
+defaults: &defaults
+ working_directory: /go/src/github.com/gohugoio
+ docker:
+ - image: bepsays/ci-goreleaser:0.30.5-2
+
+version: 2
+jobs:
+ build:
+ <<: *defaults
+ steps:
+ - checkout:
+ path: hugo
+ - run:
+ command: |
+ git clone git@github.com:gohugoio/hugoDocs.git
+ cd hugo
+ make vendor
+ make check
+ - persist_to_workspace:
+ root: .
+ paths: .
+ release:
+ <<: *defaults
+ steps:
+ - attach_workspace:
+ at: /go/src/github.com/gohugoio
+ - run:
+ command: |
+ cd hugo
+ git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
+ git config --global user.name "hugoreleaser"
+ go run -tags release main.go release -r ${CIRCLE_BRANCH}
+
+workflows:
+ version: 2
+ release:
+ jobs:
+ - build:
+ filters:
+ branches:
+ only: /release-.*/
+ - hold:
+ type: approval
+ requires:
+ - build
+ - release:
+ context: org-global
+ requires:
+ - hold
diff --git a/commands/release.go b/commands/release.go
index 0764685f0..8ccf8bcc2 100644
--- a/commands/release.go
+++ b/commands/release.go
@@ -33,8 +33,6 @@ type releaseCommandeer struct {
skipPublish bool
try bool
-
- step int
}
func createReleaser() *releaseCommandeer {
@@ -53,7 +51,6 @@ func createReleaser() *releaseCommandeer {
}
r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
- r.cmd.PersistentFlags().IntVarP(&r.step, "step", "s", -1, "release step, defaults to -1 for all steps.")
r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
@@ -64,5 +61,5 @@ func (r *releaseCommandeer) release() error {
if r.version == "" {
return errors.New("must set the --rel flag to the relevant version number")
}
- return releaser.New(r.version, r.step, r.skipPublish, r.try).Run()
+ return releaser.New(r.version, r.skipPublish, r.try).Run()
}
diff --git a/helpers/hugo.go b/helpers/hugo.go
index da0000937..f5b7f6431 100644
--- a/helpers/hugo.go
+++ b/helpers/hugo.go
@@ -41,6 +41,10 @@ func (v HugoVersion) String() string {
// ParseHugoVersion parses a version string.
func ParseHugoVersion(s string) (HugoVersion, error) {
var vv HugoVersion
+ if strings.HasSuffix(s, "-test") {
+ vv.Suffix = "-test"
+ s = strings.TrimSuffix(s, "-test")
+ }
if strings.Contains(s, "DEV") {
return vv, errors.New("DEV versions not supported by parse")
diff --git a/helpers/hugo_test.go b/helpers/hugo_test.go
index a59d8ee16..1f5e5193f 100644
--- a/helpers/hugo_test.go
+++ b/helpers/hugo_test.go
@@ -53,7 +53,7 @@ func TestCompareVersions(t *testing.T) {
func TestParseHugoVersion(t *testing.T) {
require.Equal(t, "0.25", MustParseHugoVersion("0.25").String())
require.Equal(t, "0.25.2", MustParseHugoVersion("0.25.2").String())
-
+ require.Equal(t, "0.25-test", MustParseHugoVersion("0.25-test").String())
_, err := ParseHugoVersion("0.25-DEV")
require.Error(t, err)
}
diff --git a/releaser/git.go b/releaser/git.go
index cfef434dd..8d8bbd68d 100644
--- a/releaser/git.go
+++ b/releaser/git.go
@@ -156,8 +156,8 @@ func git(args ...string) (string, error) {
return string(out), nil
}
-func getGitInfos(tag, repoPath string, remote bool) (gitInfos, error) {
- return getGitInfosBefore("HEAD", tag, repoPath, remote)
+func getGitInfos(tag, repo, repoPath string, remote bool) (gitInfos, error) {
+ return getGitInfosBefore("HEAD", tag, repo, repoPath, remote)
}
type countribCount struct {
@@ -213,8 +213,8 @@ func (g gitInfos) ContribCountPerAuthor() contribCounts {
return c
}
-func getGitInfosBefore(ref, tag, repoPath string, remote bool) (gitInfos, error) {
-
+func getGitInfosBefore(ref, tag, repo, repoPath string, remote bool) (gitInfos, error) {
+ client := newGitHubAPI(repo)
var g gitInfos
log, err := gitLogBefore(ref, tag, repoPath)
@@ -234,7 +234,7 @@ func getGitInfosBefore(ref, tag, repoPath string, remote bool) (gitInfos, error)
Body: items[3],
}
if remote {
- gc, err := fetchCommit(gi.Hash)
+ gc, err := client.fetchCommit(gi.Hash)
if err == nil {
gi.GitHubCommit = &gc
}
diff --git a/releaser/git_test.go b/releaser/git_test.go
index 8053f7702..f0d6fd24b 100644
--- a/releaser/git_test.go
+++ b/releaser/git_test.go
@@ -14,7 +14,6 @@
package releaser
import (
- "os"
"testing"
"github.com/stretchr/testify/require"
@@ -22,7 +21,7 @@ import (
func TestGitInfos(t *testing.T) {
skipIfCI(t)
- infos, err := getGitInfos("v0.20", "", false)
+ infos, err := getGitInfos("v0.20", "hugo", "", false)
require.NoError(t, err)
require.True(t, len(infos) > 0)
@@ -68,7 +67,7 @@ func TestTagExists(t *testing.T) {
}
func skipIfCI(t *testing.T) {
- if os.Getenv("CI") != "" {
+ if isCI() {
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
// Also Travis clones very shallowly, making some of the tests above shaky.
t.Skip("Skip git test on Linux to make Travis happy.")
diff --git a/releaser/github.go b/releaser/github.go
index c1e7691b8..11f617007 100644
--- a/releaser/github.go
+++ b/releaser/github.go
@@ -6,14 +6,29 @@ import (
"io/ioutil"
"net/http"
"os"
+ "strings"
)
var (
- gitHubCommitsApi = "https://api.github.com/repos/gohugoio/hugo/commits/%s"
- gitHubRepoApi = "https://api.github.com/repos/gohugoio/hugo"
- gitHubContributorsApi = "https://api.github.com/repos/gohugoio/hugo/contributors"
+ gitHubCommitsAPI = "https://api.github.com/repos/gohugoio/REPO/commits/%s"
+ gitHubRepoAPI = "https://api.github.com/repos/gohugoio/REPO"
+ gitHubContributorsAPI = "https://api.github.com/repos/gohugoio/REPO/contributors"
)
+type gitHubAPI struct {
+ commitsAPITemplate string
+ repoAPI string
+ contributorsAPITemplate string
+}
+
+func newGitHubAPI(repo string) *gitHubAPI {
+ return &gitHubAPI{
+ commitsAPITemplate: strings.Replace(gitHubCommitsAPI, "REPO", repo, -1),
+ repoAPI: strings.Replace(gitHubRepoAPI, "REPO", repo, -1),
+ contributorsAPITemplate: strings.Replace(gitHubContributorsAPI, "REPO", repo, -1),
+ }
+}
+
type gitHubCommit struct {
Author gitHubAuthor `json:"author"`
HtmlURL string `json:"html_url"`
@@ -42,10 +57,10 @@ type gitHubContributor struct {
Contributions int `json:"contributions"`
}
-func fetchCommit(ref string) (gitHubCommit, error) {
+func (g *gitHubAPI) fetchCommit(ref string) (gitHubCommit, error) {
var commit gitHubCommit
- u := fmt.Sprintf(gitHubCommitsApi, ref)
+ u := fmt.Sprintf(g.commitsAPITemplate, ref)
req, err := http.NewRequest("GET", u, nil)
if err != nil {
@@ -57,10 +72,10 @@ func fetchCommit(ref string) (gitHubCommit, error) {
return commit, err
}
-func fetchRepo() (gitHubRepo, error) {
+func (g *gitHubAPI) fetchRepo() (gitHubRepo, error) {
var repo gitHubRepo
- req, err := http.NewRequest("GET", gitHubRepoApi, nil)
+ req, err := http.NewRequest("GET", g.repoAPI, nil)
if err != nil {
return repo, err
}
@@ -75,7 +90,7 @@ func fetchRepo() (gitHubRepo, error) {
for {
page++
var currPage []gitHubContributor
- url := fmt.Sprintf(gitHubContributorsApi+"?page=%d", page)
+ url := fmt.Sprintf(g.contributorsAPITemplate+"?page=%d", page)
req, err = http.NewRequest("GET", url, nil)
if err != nil {
diff --git a/releaser/github_test.go b/releaser/github_test.go
index 7feae75f5..1187cbb2c 100644
--- a/releaser/github_test.go
+++ b/releaser/github_test.go
@@ -23,14 +23,16 @@ import (
func TestGitHubLookupCommit(t *testing.T) {
skipIfNoToken(t)
- commit, err := fetchCommit("793554108763c0984f1a1b1a6ee5744b560d78d0")
+ client := newGitHubAPI("hugo")
+ commit, err := client.fetchCommit("793554108763c0984f1a1b1a6ee5744b560d78d0")
require.NoError(t, err)
fmt.Println(commit)
}
func TestFetchRepo(t *testing.T) {
skipIfNoToken(t)
- repo, err := fetchRepo()
+ client := newGitHubAPI("hugo")
+ repo, err := client.fetchRepo()
require.NoError(t, err)
fmt.Println(">>", len(repo.Contributors))
}
diff --git a/releaser/releasenotes_writer.go b/releaser/releasenotes_writer.go
index 0c6f297a4..e94ed25e1 100644
--- a/releaser/releasenotes_writer.go
+++ b/releaser/releasenotes_writer.go
@@ -139,9 +139,10 @@ var templateFuncs = template.FuncMap{
}
func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
+ client := newGitHubAPI("hugo")
changes := gitInfosToChangeLog(infosMain, infosDocs)
changes.Version = version
- repo, err := fetchRepo()
+ repo, err := client.fetchRepo()
if err == nil {
changes.Repo = &repo
}
@@ -190,17 +191,43 @@ func writeReleaseNotesToTmpFile(version string, infosMain, infosDocs gitInfos) (
return f.Name(), nil
}
-func getReleaseNotesDocsTempDirAndName(version string) (string, string) {
+func getReleaseNotesDocsTempDirAndName(version string, final bool) (string, string) {
+ if final {
+ return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes-ready.md", version)
+ }
return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes.md", version)
}
-func getReleaseNotesDocsTempFilename(version string) string {
- return filepath.Join(getReleaseNotesDocsTempDirAndName(version))
+func getReleaseNotesDocsTempFilename(version string, final bool) string {
+ return filepath.Join(getReleaseNotesDocsTempDirAndName(version, final))
+}
+
+func (r *ReleaseHandler) releaseNotesState(version string) (releaseNotesState, error) {
+ docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, false)
+ _, err := os.Stat(filepath.Join(docsTempPath, name))
+
+ if err == nil {
+ return releaseNotesCreated, nil
+ }
+
+ docsTempPath, name = getReleaseNotesDocsTempDirAndName(version, true)
+ _, err = os.Stat(filepath.Join(docsTempPath, name))
+
+ if err == nil {
+ return releaseNotesReady, nil
+ }
+
+ if !os.IsNotExist(err) {
+ return releaseNotesNone, err
+ }
+
+ return releaseNotesNone, nil
+
}
func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, infosMain, infosDocs gitInfos) (string, error) {
- docsTempPath, name := getReleaseNotesDocsTempDirAndName(version)
+ docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, false)
var (
w io.WriteCloser
diff --git a/releaser/releasenotes_writer_test.go b/releaser/releasenotes_writer_test.go
index f3e984d55..f5b7a87d3 100644
--- a/releaser/releasenotes_writer_test.go
+++ b/releaser/releasenotes_writer_test.go
@@ -34,7 +34,7 @@ func _TestReleaseNotesWriter(t *testing.T) {
var b bytes.Buffer
// TODO(bep) consider to query GitHub directly for the gitlog with author info, probably faster.
- infos, err := getGitInfosBefore("HEAD", "v0.20", "", false)
+ infos, err := getGitInfosBefore("HEAD", "v0.20", "hugo", "", false)
require.NoError(t, err)
require.NoError(t, writeReleaseNotes("0.21", infos, infos, &b))
diff --git a/releaser/releaser.go b/releaser/releaser.go
index d05683033..1271cf179 100644
--- a/releaser/releaser.go
+++ b/releaser/releaser.go
@@ -31,15 +31,18 @@ import (
const commitPrefix = "releaser:"
+type releaseNotesState int
+
+const (
+ releaseNotesNone = iota
+ releaseNotesCreated
+ releaseNotesReady
+)
+
// ReleaseHandler provides functionality to release a new version of Hugo.
type ReleaseHandler struct {
cliVersion string
- // If set, we do the releases in 3 steps:
- // 1: Create and write a draft release note
- // 2: Prepare files for new version
- // 3: Release
- step int
skipPublish bool
// Just simulate, no actual changes.
@@ -48,29 +51,14 @@ type ReleaseHandler struct {
git func(args ...string) (string, error)
}
-func (r ReleaseHandler) shouldRelease() bool {
- return r.step < 1 || r.shouldContinue()
-}
-
-func (r ReleaseHandler) shouldContinue() bool {
- return r.step >= 3
-}
-
-func (r ReleaseHandler) shouldPrepareReleasenotes() bool {
- return r.step < 1 || r.step == 1
-}
-
-func (r ReleaseHandler) shouldPrepareVersions() bool {
- return r.step < 1 || r.step == 2 || r.step > 3
-}
-
func (r ReleaseHandler) calculateVersions() (helpers.HugoVersion, helpers.HugoVersion) {
-
newVersion := helpers.MustParseHugoVersion(r.cliVersion)
finalVersion := newVersion
finalVersion.PatchLevel = 0
- newVersion.Suffix = ""
+ if newVersion.Suffix != "-test" {
+ newVersion.Suffix = ""
+ }
if newVersion.PatchLevel == 0 {
finalVersion = finalVersion.Next()
@@ -82,8 +70,11 @@ func (r ReleaseHandler) calculateVersions() (helpers.HugoVersion, helpers.HugoVe
}
// New initialises a ReleaseHandler.
-func New(version string, step int, skipPublish, try bool) *ReleaseHandler {
- rh := &ReleaseHandler{cliVersion: version, step: step, skipPublish: skipPublish, try: try}
+func New(version string, skipPublish, try bool) *ReleaseHandler {
+ // When triggered from CI release branch
+ version = strings.TrimPrefix(version, "release-")
+ version = strings.TrimPrefix(version, "v")
+ rh := &ReleaseHandler{cliVersion: version, skipPublish: skipPublish, try: try}
if try {
rh.git = func(args ...string) (string, error) {
@@ -133,20 +124,38 @@ func (r *ReleaseHandler) Run() error {
var (
gitCommits gitInfos
gitCommitsDocs gitInfos
+ relNotesState releaseNotesState
)
- if r.shouldPrepareReleasenotes() || r.shouldRelease() {
- gitCommits, err = getGitInfos(changeLogFromTag, "", !r.try)
+ relNotesState, err = r.releaseNotesState(version)
+ if err != nil {
+ return err
+ }
+
+ prepareRelaseNotes := relNotesState == releaseNotesNone
+ shouldRelease := relNotesState == releaseNotesReady
+
+ defer r.gitPush() // TODO(bep)
+
+ if prepareRelaseNotes || shouldRelease {
+ gitCommits, err = getGitInfos(changeLogFromTag, "hugo", "", !r.try)
if err != nil {
return err
}
- gitCommitsDocs, err = getGitInfos(changeLogFromTag, "../hugoDocs", !r.try)
+
+ // TODO(bep) explicit tag?
+ gitCommitsDocs, err = getGitInfos("", "hugoDocs", "../hugoDocs", !r.try)
if err != nil {
return err
}
}
- if r.shouldPrepareReleasenotes() {
+ if relNotesState == releaseNotesCreated {
+ fmt.Println("Release notes created, but not ready. Reneame to *-ready.md to continue ...")
+ return nil
+ }
+
+ if prepareRelaseNotes {
releaseNotesFile, err := r.writeReleaseNotesToTemp(version, gitCommits, gitCommitsDocs)
if err != nil {
return err
@@ -155,33 +164,30 @@ func (r *ReleaseHandler) Run() error {
if _, err := r.git("add", releaseNotesFile); err != nil {
return err
}
- if _, err := r.git("commit", "-m", fmt.Sprintf("%s Add release notes draft for %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil {
+ if _, err := r.git("commit", "-m", fmt.Sprintf("%s Add release notes draft for %s\n\nRename to *-ready.md to continue. [ci skip]", commitPrefix, newVersion)); err != nil {
return err
}
}
- if r.shouldPrepareVersions() {
-
- // For docs, for now we assume that:
- // The /docs subtree is up to date and ready to go.
- // The hugoDocs/dev and hugoDocs/master must be merged manually after release.
- // TODO(bep) improve this when we see how it works.
+ if !shouldRelease {
+ fmt.Printf("Skip release ... ")
+ return nil
+ }
- if err := r.bumpVersions(newVersion); err != nil {
- return err
- }
+ // For docs, for now we assume that:
+ // The /docs subtree is up to date and ready to go.
+ // The hugoDocs/dev and hugoDocs/master must be merged manually after release.
+ // TODO(bep) improve this when we see how it works.
- 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.bumpVersions(newVersion); err != nil {
+ return err
}
- if !r.shouldRelease() {
- fmt.Printf("Skip release ... Use --state=%d for next or --state=4 to finish\n", r.step+1)
- return nil
+ 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
}
- releaseNotesFile := getReleaseNotesDocsTempFilename(version)
+ releaseNotesFile := getReleaseNotesDocsTempFilename(version, true)
// Write the release notes to the docs site as well.
docFile, err := r.writeReleaseNotesToDocs(version, releaseNotesFile)
@@ -196,12 +202,14 @@ func (r *ReleaseHandler) Run() error {
return err
}
- if _, err := r.git("tag", "-a", tag, "-m", fmt.Sprintf("%s %s [ci deploy]", commitPrefix, newVersion)); err != nil {
+ if _, err := r.git("tag", "-a", tag, "-m", fmt.Sprintf("%s %s [ci skip]", commitPrefix, newVersion)); err != nil {
return err
}
- if _, err := r.git("push", "origin", tag); err != nil {
- return err
+ if !r.skipPublish {
+ if _, err := r.git("push", "origin", tag); err != nil {
+ return err
+ }
}
if err := r.release(releaseNotesFile); err != nil {
@@ -226,6 +234,15 @@ 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...")
@@ -243,19 +260,16 @@ func (r *ReleaseHandler) release(releaseNotesFile string) error {
}
func (r *ReleaseHandler) bumpVersions(ver helpers.HugoVersion) error {
- fromDev := ""
toDev := ""
if ver.Suffix != "" {
- toDev = "-DEV"
- } else {
- fromDev = "-DEV"
+ toDev = ver.Suffix
}
if err := r.replaceInFile("helpers/hugo.go",
`Number:(\s{4,})(.*),`, fmt.Sprintf(`Number:${1}%.2f,`, ver.Number),
`PatchLevel:(\s*)(.*),`, fmt.Sprintf(`PatchLevel:${1}%d,`, ver.PatchLevel),
- fmt.Sprintf(`Suffix:(\s{4,})"%s",`, fromDev), fmt.Sprintf(`Suffix:${1}"%s",`, toDev)); err != nil {
+ `Suffix:(\s{4,})".*",`, fmt.Sprintf(`Suffix:${1}"%s",`, toDev)); err != nil {
return err
}
@@ -325,3 +339,7 @@ func hugoFilepath(filename string) string {
}
return filepath.Join(pwd, filename)
}
+
+func isCI() bool {
+ return os.Getenv("CI") != ""
+}