Permalink
Browse files

Set up Hugo release flow on CircleCI

This rewrites the release logic to use CircleCI 2.0 and its approve workflow in combination with the state of the release notes to determine what to do next.

Fixes #3779
  • Loading branch information...
bep committed Sep 10, 2017
1 parent f4bf214 commit d2249c50991ba7b00b092aca6e315ca1a4de75a1
View
@@ -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
View
@@ -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()
}
View
@@ -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")
View
@@ -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)
}
View
@@ -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
}
View
@@ -14,15 +14,14 @@
package releaser
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
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.")
View
@@ -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 {
View
@@ -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))
}
@@ -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
@@ -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))
Oops, something went wrong.

0 comments on commit d2249c5

Please sign in to comment.