github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/releaser/releasenotes_writer.go (about) 1 // Copyright 2017-present The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Package releaser implements a set of utilities and a wrapper around Goreleaser 15 // to help automate the Hugo release process. 16 package releaser 17 18 import ( 19 "bytes" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "os" 25 "path/filepath" 26 "strings" 27 "text/template" 28 ) 29 30 const ( 31 issueLinkTemplate = "#%d" 32 linkTemplate = "[%s](%s)" 33 releaseNotesMarkdownTemplatePatchRelease = ` 34 {{ if eq (len .All) 1 }} 35 This is a bug-fix release with one important fix. 36 {{ else }} 37 This is a bug-fix release with a couple of important fixes. 38 {{ end }} 39 {{ range .All }} 40 {{- if .GitHubCommit -}} 41 * {{ .Subject }} {{ .Hash }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }} {{ end }} 42 {{ else -}} 43 * {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }} 44 {{ end -}} 45 {{- end }} 46 47 48 ` 49 releaseNotesMarkdownTemplate = ` 50 {{- $contribsPerAuthor := .All.ContribCountPerAuthor -}} 51 {{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}} 52 53 This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base. 54 55 {{- if gt (len $contribsPerAuthor) 3 -}} 56 {{- $u1 := index $contribsPerAuthor 0 -}} 57 {{- $u2 := index $contribsPerAuthor 1 -}} 58 {{- $u3 := index $contribsPerAuthor 2 -}} 59 {{- $u4 := index $contribsPerAuthor 3 -}} 60 {{- $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. 61 And thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his ongoing work on keeping the themes site in pristine condition. 62 {{ end }} 63 Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs), 64 which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**. 65 {{- if gt (len $docsContribsPerAuthor) 3 -}} 66 {{- $u1 := index $docsContribsPerAuthor 0 -}} 67 {{- $u2 := index $docsContribsPerAuthor 1 -}} 68 {{- $u3 := index $docsContribsPerAuthor 2 -}} 69 {{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site. 70 {{ end }} 71 72 Hugo now has: 73 74 {{ with .Repo -}} 75 * {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers) 76 * {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors) 77 {{- end -}} 78 {{ with .ThemeCount }} 79 * {{ . }}+ [themes](http://themes.gohugo.io/) 80 {{ end }} 81 {{ with .Notes }} 82 ## Notes 83 {{ template "change-section" . }} 84 {{- end -}} 85 {{ with .All }} 86 ## Changes 87 {{ template "change-section" . }} 88 {{ end }} 89 90 {{ define "change-section" }} 91 {{ range . }} 92 {{- if .GitHubCommit -}} 93 * {{ .Subject }} {{ .Hash }} {{ . | authorURL }} {{ range .Issues }}{{ . | issue }} {{ end }} 94 {{ else -}} 95 * {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }} 96 {{ end -}} 97 {{- end }} 98 {{ end }} 99 ` 100 ) 101 102 var templateFuncs = template.FuncMap{ 103 "isPatch": func(c changeLog) bool { 104 return !strings.HasSuffix(c.Version, "0") 105 }, 106 "issue": func(id int) string { 107 return fmt.Sprintf(issueLinkTemplate, id) 108 }, 109 "commitURL": func(info gitInfo) string { 110 if info.GitHubCommit.HTMLURL == "" { 111 return "" 112 } 113 return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL) 114 }, 115 "authorURL": func(info gitInfo) string { 116 if info.GitHubCommit.Author.Login == "" { 117 return "" 118 } 119 return fmt.Sprintf(linkTemplate, "@"+info.GitHubCommit.Author.Login, info.GitHubCommit.Author.HTMLURL) 120 }, 121 } 122 123 func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error { 124 client := newGitHubAPI("hugo") 125 changes := newChangeLog(infosMain, infosDocs) 126 changes.Version = version 127 repo, err := client.fetchRepo() 128 if err == nil { 129 changes.Repo = &repo 130 } 131 themeCount, err := fetchThemeCount() 132 if err == nil { 133 changes.ThemeCount = themeCount 134 } 135 136 mtempl := releaseNotesMarkdownTemplate 137 138 if !strings.HasSuffix(version, "0") { 139 mtempl = releaseNotesMarkdownTemplatePatchRelease 140 } 141 142 tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl) 143 if err != nil { 144 return err 145 } 146 147 err = tmpl.Execute(to, changes) 148 if err != nil { 149 return err 150 } 151 152 return nil 153 } 154 155 func fetchThemeCount() (int, error) { 156 resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemesSiteBuilder/main/themes.txt") 157 if err != nil { 158 return 0, err 159 } 160 defer resp.Body.Close() 161 162 b, _ := ioutil.ReadAll(resp.Body) 163 return bytes.Count(b, []byte("\n")) - bytes.Count(b, []byte("#")), nil 164 } 165 166 func getReleaseNotesFilename(version string) string { 167 return filepath.FromSlash(fmt.Sprintf("temp/%s-relnotes-ready.md", version)) 168 169 } 170 171 func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) { 172 filename := getReleaseNotesFilename(version) 173 174 var w io.WriteCloser 175 176 if !r.try { 177 f, err := os.Create(filename) 178 if err != nil { 179 return "", err 180 } 181 182 defer f.Close() 183 184 w = f 185 186 } else { 187 w = os.Stdout 188 } 189 190 if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil { 191 return "", err 192 } 193 194 return filename, nil 195 }