2017-04-13 10:59:05 -04:00
|
|
|
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2017-08-02 08:25:05 -04:00
|
|
|
// Package releaser implements a set of utilities and a wrapper around Goreleaser
|
2017-04-13 10:59:05 -04:00
|
|
|
// to help automate the Hugo release process.
|
|
|
|
package releaser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-07-28 08:33:56 -04:00
|
|
|
issueLinkTemplate = "[#%d](https://github.com/gohugoio/hugo/issues/%d)"
|
|
|
|
linkTemplate = "[%s](%s)"
|
|
|
|
releaseNotesMarkdownTemplatePatchRelease = `
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ if eq (len .All) 1 }}
|
|
|
|
This is a bug-fix release with one important fix.
|
|
|
|
{{ else }}
|
2017-06-21 15:17:50 -04:00
|
|
|
This is a bug-fix release with a couple of important fixes.
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ end }}
|
2019-07-28 08:33:56 -04:00
|
|
|
{{ range .All }}
|
|
|
|
{{- if .GitHubCommit -}}
|
|
|
|
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
|
|
|
|
{{ else -}}
|
|
|
|
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ end -}}
|
2019-07-28 08:33:56 -04:00
|
|
|
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
`
|
|
|
|
releaseNotesMarkdownTemplate = `
|
|
|
|
{{- $contribsPerAuthor := .All.ContribCountPerAuthor -}}
|
|
|
|
{{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}}
|
|
|
|
|
|
|
|
This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base.
|
2017-04-13 10:59:05 -04:00
|
|
|
|
|
|
|
{{- if gt (len $contribsPerAuthor) 3 -}}
|
|
|
|
{{- $u1 := index $contribsPerAuthor 0 -}}
|
|
|
|
{{- $u2 := index $contribsPerAuthor 1 -}}
|
|
|
|
{{- $u3 := index $contribsPerAuthor 2 -}}
|
|
|
|
{{- $u4 := index $contribsPerAuthor 3 -}}
|
|
|
|
{{- $u1.AuthorLink }} leads the Hugo development with a significant amount of contributions, but also a big shoutout to {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their ongoing contributions.
|
2021-09-23 08:02:00 -04:00
|
|
|
And thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his ongoing work on keeping the themes site in pristine condition.
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ end }}
|
2021-03-24 23:54:58 -04:00
|
|
|
Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs),
|
2017-08-06 04:42:07 -04:00
|
|
|
which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**.
|
|
|
|
{{- if gt (len $docsContribsPerAuthor) 3 -}}
|
|
|
|
{{- $u1 := index $docsContribsPerAuthor 0 -}}
|
|
|
|
{{- $u2 := index $docsContribsPerAuthor 1 -}}
|
|
|
|
{{- $u3 := index $docsContribsPerAuthor 2 -}}
|
|
|
|
{{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site.
|
|
|
|
{{ end }}
|
2019-07-28 08:33:56 -04:00
|
|
|
|
2017-04-13 10:59:05 -04:00
|
|
|
Hugo now has:
|
|
|
|
|
|
|
|
{{ with .Repo -}}
|
2017-06-13 12:47:17 -04:00
|
|
|
* {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers)
|
|
|
|
* {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors)
|
2017-04-13 10:59:05 -04:00
|
|
|
{{- end -}}
|
|
|
|
{{ with .ThemeCount }}
|
2017-07-06 11:31:04 -04:00
|
|
|
* {{ . }}+ [themes](http://themes.gohugo.io/)
|
2017-08-06 04:42:07 -04:00
|
|
|
{{ end }}
|
2017-05-20 03:58:08 -04:00
|
|
|
{{ with .Notes }}
|
|
|
|
## Notes
|
|
|
|
{{ template "change-section" . }}
|
|
|
|
{{- end -}}
|
2017-04-13 10:59:05 -04:00
|
|
|
## Enhancements
|
|
|
|
{{ template "change-headers" .Enhancements -}}
|
|
|
|
## Fixes
|
|
|
|
{{ template "change-headers" .Fixes -}}
|
|
|
|
|
|
|
|
{{ define "change-headers" }}
|
|
|
|
{{ $tmplChanges := index . "templateChanges" -}}
|
|
|
|
{{- $outChanges := index . "outChanges" -}}
|
|
|
|
{{- $coreChanges := index . "coreChanges" -}}
|
|
|
|
{{- $otherChanges := index . "otherChanges" -}}
|
|
|
|
{{- with $tmplChanges -}}
|
|
|
|
### Templates
|
|
|
|
{{ template "change-section" . }}
|
|
|
|
{{- end -}}
|
|
|
|
{{- with $outChanges -}}
|
|
|
|
### Output
|
2017-05-20 03:58:08 -04:00
|
|
|
{{ template "change-section" . }}
|
2017-04-13 10:59:05 -04:00
|
|
|
{{- end -}}
|
|
|
|
{{- with $coreChanges -}}
|
|
|
|
### Core
|
|
|
|
{{ template "change-section" . }}
|
|
|
|
{{- end -}}
|
|
|
|
{{- with $otherChanges -}}
|
|
|
|
### Other
|
|
|
|
{{ template "change-section" . }}
|
|
|
|
{{- end -}}
|
|
|
|
{{ end }}
|
|
|
|
|
|
|
|
|
|
|
|
{{ define "change-section" }}
|
|
|
|
{{ range . }}
|
|
|
|
{{- if .GitHubCommit -}}
|
2017-06-25 10:29:58 -04:00
|
|
|
* {{ .Subject }} {{ . | commitURL }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }}{{ end }}
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ else -}}
|
2017-06-25 10:29:58 -04:00
|
|
|
* {{ .Subject }} {{ range .Issues }}{{ . | issue }}{{ end }}
|
2017-04-13 10:59:05 -04:00
|
|
|
{{ end -}}
|
|
|
|
{{- end }}
|
|
|
|
{{ end }}
|
|
|
|
`
|
|
|
|
)
|
|
|
|
|
|
|
|
var templateFuncs = template.FuncMap{
|
|
|
|
"isPatch": func(c changeLog) bool {
|
2019-02-01 04:09:14 -05:00
|
|
|
return !strings.HasSuffix(c.Version, "0")
|
2017-04-13 10:59:05 -04:00
|
|
|
},
|
|
|
|
"issue": func(id int) string {
|
|
|
|
return fmt.Sprintf(issueLinkTemplate, id, id)
|
|
|
|
},
|
|
|
|
"commitURL": func(info gitInfo) string {
|
2018-09-06 15:50:54 -04:00
|
|
|
if info.GitHubCommit.HTMLURL == "" {
|
2017-04-13 10:59:05 -04:00
|
|
|
return ""
|
|
|
|
}
|
2018-09-06 15:50:54 -04:00
|
|
|
return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL)
|
2017-04-13 10:59:05 -04:00
|
|
|
},
|
|
|
|
"authorURL": func(info gitInfo) string {
|
|
|
|
if info.GitHubCommit.Author.Login == "" {
|
|
|
|
return ""
|
|
|
|
}
|
2018-09-06 15:50:54 -04:00
|
|
|
return fmt.Sprintf(linkTemplate, "@"+info.GitHubCommit.Author.Login, info.GitHubCommit.Author.HTMLURL)
|
2017-04-13 10:59:05 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-08-06 04:42:07 -04:00
|
|
|
func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
|
2017-09-10 11:14:02 -04:00
|
|
|
client := newGitHubAPI("hugo")
|
2017-08-06 04:42:07 -04:00
|
|
|
changes := gitInfosToChangeLog(infosMain, infosDocs)
|
2017-04-13 10:59:05 -04:00
|
|
|
changes.Version = version
|
2017-09-10 11:14:02 -04:00
|
|
|
repo, err := client.fetchRepo()
|
2017-04-13 10:59:05 -04:00
|
|
|
if err == nil {
|
|
|
|
changes.Repo = &repo
|
|
|
|
}
|
|
|
|
themeCount, err := fetchThemeCount()
|
|
|
|
if err == nil {
|
|
|
|
changes.ThemeCount = themeCount
|
|
|
|
}
|
|
|
|
|
2019-07-28 08:33:56 -04:00
|
|
|
mtempl := releaseNotesMarkdownTemplate
|
|
|
|
|
|
|
|
if !strings.HasSuffix(version, "0") {
|
|
|
|
mtempl = releaseNotesMarkdownTemplatePatchRelease
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl)
|
2017-04-13 10:59:05 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = tmpl.Execute(to, changes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchThemeCount() (int, error) {
|
2021-07-21 09:07:31 -04:00
|
|
|
resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemesSiteBuilder/main/themes.txt")
|
2017-04-13 10:59:05 -04:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
b, _ := ioutil.ReadAll(resp.Body)
|
2021-07-21 09:07:31 -04:00
|
|
|
return bytes.Count(b, []byte("\n")) - bytes.Count(b, []byte("#")), nil
|
2017-04-13 10:59:05 -04:00
|
|
|
}
|
|
|
|
|
2017-08-06 04:42:07 -04:00
|
|
|
func writeReleaseNotesToTmpFile(version string, infosMain, infosDocs gitInfos) (string, error) {
|
2017-04-13 10:59:05 -04:00
|
|
|
f, err := ioutil.TempFile("", "hugorelease")
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
2017-08-06 04:42:07 -04:00
|
|
|
if err := writeReleaseNotes(version, infosMain, infosDocs, f); err != nil {
|
2017-04-13 10:59:05 -04:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return f.Name(), nil
|
|
|
|
}
|
|
|
|
|
2017-09-10 11:14:02 -04:00
|
|
|
func getReleaseNotesDocsTempDirAndName(version string, final bool) (string, string) {
|
|
|
|
if final {
|
|
|
|
return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes-ready.md", version)
|
|
|
|
}
|
2017-06-15 14:36:40 -04:00
|
|
|
return hugoFilepath("temp"), fmt.Sprintf("%s-relnotes.md", version)
|
2017-04-13 10:59:05 -04:00
|
|
|
}
|
|
|
|
|
2017-09-10 11:14:02 -04:00
|
|
|
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
|
2017-04-13 10:59:05 -04:00
|
|
|
}
|
|
|
|
|
2019-07-28 08:33:56 -04:00
|
|
|
func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) {
|
|
|
|
docsTempPath, name := getReleaseNotesDocsTempDirAndName(version, isPatch)
|
2017-04-13 10:59:05 -04:00
|
|
|
|
2020-12-02 07:23:25 -05:00
|
|
|
var w io.WriteCloser
|
2017-04-13 10:59:05 -04:00
|
|
|
|
2017-07-05 03:43:47 -04:00
|
|
|
if !r.try {
|
|
|
|
os.Mkdir(docsTempPath, os.ModePerm)
|
2017-04-13 10:59:05 -04:00
|
|
|
|
2017-07-05 03:43:47 -04:00
|
|
|
f, err := os.Create(filepath.Join(docsTempPath, name))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
name = f.Name()
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
w = f
|
|
|
|
|
|
|
|
} else {
|
|
|
|
w = os.Stdout
|
|
|
|
}
|
|
|
|
|
2017-08-06 04:42:07 -04:00
|
|
|
if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil {
|
2017-04-13 10:59:05 -04:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2017-07-05 03:43:47 -04:00
|
|
|
return name, nil
|
2017-04-13 10:59:05 -04:00
|
|
|
}
|
|
|
|
|
2019-07-30 03:02:03 -04:00
|
|
|
func (r *ReleaseHandler) writeReleaseNotesToDocs(title, description, sourceFilename string) (string, error) {
|
2018-02-21 04:22:08 -05:00
|
|
|
targetFilename := "index.md"
|
|
|
|
bundleDir := strings.TrimSuffix(filepath.Base(sourceFilename), "-ready.md")
|
2018-05-25 11:53:03 -04:00
|
|
|
contentDir := hugoFilepath("docs/content/en/news/" + bundleDir)
|
2017-04-13 10:59:05 -04:00
|
|
|
targetFullFilename := filepath.Join(contentDir, targetFilename)
|
2017-07-05 03:43:47 -04:00
|
|
|
|
|
|
|
if r.try {
|
2018-02-21 04:22:08 -05:00
|
|
|
fmt.Printf("Write release notes to /docs: Bundle %q Dir: %q\n", bundleDir, contentDir)
|
2017-07-05 03:43:47 -04:00
|
|
|
return targetFullFilename, nil
|
|
|
|
}
|
|
|
|
|
2018-02-21 04:22:08 -05:00
|
|
|
if err := os.MkdirAll(contentDir, os.ModePerm); err != nil {
|
|
|
|
return "", nil
|
|
|
|
}
|
2017-04-13 10:59:05 -04:00
|
|
|
|
|
|
|
b, err := ioutil.ReadFile(sourceFilename)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(targetFullFilename)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
2017-10-19 06:34:03 -04:00
|
|
|
fmTail := ""
|
2019-04-01 03:07:19 -04:00
|
|
|
if !strings.HasSuffix(title, ".0") {
|
2017-10-19 06:34:03 -04:00
|
|
|
// Bug fix release
|
|
|
|
fmTail = `
|
|
|
|
images:
|
|
|
|
- images/blog/hugo-bug-poster.png
|
|
|
|
`
|
|
|
|
}
|
|
|
|
|
2017-04-13 10:59:05 -04:00
|
|
|
if _, err := f.WriteString(fmt.Sprintf(`
|
|
|
|
---
|
|
|
|
date: %s
|
2017-08-07 02:54:40 -04:00
|
|
|
title: %q
|
|
|
|
description: %q
|
2017-10-19 06:34:03 -04:00
|
|
|
categories: ["Releases"]%s
|
2017-04-13 10:59:05 -04:00
|
|
|
---
|
|
|
|
|
2019-07-30 03:02:03 -04:00
|
|
|
`, time.Now().Format("2006-01-02"), title, description, fmTail)); err != nil {
|
2017-04-13 10:59:05 -04:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := f.Write(b); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetFullFilename, nil
|
|
|
|
}
|