1
0

relnotes.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (C) 2025 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. //go:build ignore
  7. // +build ignore
  8. package main
  9. import (
  10. "bytes"
  11. "cmp"
  12. "encoding/json"
  13. "errors"
  14. "flag"
  15. "fmt"
  16. "io"
  17. "log"
  18. "net/http"
  19. "os"
  20. "regexp"
  21. "strings"
  22. "text/template"
  23. )
  24. var (
  25. githubToken = os.Getenv("GITHUB_TOKEN")
  26. githubRepo = cmp.Or(os.Getenv("GITHUB_REPOSITORY"), "syncthing/syncthing")
  27. )
  28. func main() {
  29. ver := flag.String("new-ver", "", "New version tag")
  30. prevVer := flag.String("prev-ver", "", "Previous version tag")
  31. branch := flag.String("branch", "HEAD", "Branch to release from")
  32. flag.Parse()
  33. log.SetOutput(os.Stderr)
  34. if *ver == "" {
  35. log.Fatalln("Must set --new-ver")
  36. }
  37. if githubToken == "" {
  38. log.Fatalln("Must set $GITHUB_TOKEN")
  39. }
  40. notes, err := additionalNotes(*ver)
  41. if err != nil {
  42. log.Fatalln("Gathering additional notes:", err)
  43. }
  44. gh, err := generatedNotes(*ver, *branch, *prevVer)
  45. if err != nil {
  46. log.Fatalln("Gathering github notes:", err)
  47. }
  48. notes = append(notes, gh)
  49. fmt.Println(strings.Join(notes, "\n\n"))
  50. }
  51. // Load potential additional release notes from within the repo
  52. func additionalNotes(newVer string) ([]string, error) {
  53. data := map[string]string{
  54. "version": strings.TrimLeft(newVer, "v"),
  55. }
  56. var notes []string
  57. ver, _, _ := strings.Cut(newVer, "-")
  58. for {
  59. file := fmt.Sprintf("relnotes/%s.md", ver)
  60. if bs, err := os.ReadFile(file); err == nil {
  61. tpl, err := template.New("notes").Parse(string(bs))
  62. if err != nil {
  63. return nil, err
  64. }
  65. buf := new(bytes.Buffer)
  66. if err := tpl.Execute(buf, data); err != nil {
  67. return nil, err
  68. }
  69. notes = append(notes, strings.TrimSpace(buf.String()))
  70. } else if !os.IsNotExist(err) {
  71. return nil, err
  72. }
  73. if idx := strings.LastIndex(ver, "."); idx > 0 {
  74. ver = ver[:idx]
  75. } else {
  76. break
  77. }
  78. }
  79. return notes, nil
  80. }
  81. // Load generated release notes (list of pull requests and contributors)
  82. // from GitHub.
  83. func generatedNotes(newVer, targetCommit, prevVer string) (string, error) {
  84. fields := map[string]string{
  85. "tag_name": newVer,
  86. "target_commitish": targetCommit,
  87. "previous_tag_name": prevVer,
  88. }
  89. bs, err := json.Marshal(fields)
  90. if err != nil {
  91. return "", err
  92. }
  93. req, err := http.NewRequest(http.MethodPost, "https://api.github.com/repos/"+githubRepo+"/releases/generate-notes", bytes.NewReader(bs)) //nolint:noctx
  94. if err != nil {
  95. return "", err
  96. }
  97. req.Header.Set("Accept", "application/vnd.github+json")
  98. req.Header.Set("Authorization", "Bearer "+githubToken)
  99. req.Header.Set("X-Github-Api-Version", "2022-11-28")
  100. res, err := http.DefaultClient.Do(req)
  101. if err != nil {
  102. return "", err
  103. }
  104. if res.StatusCode != http.StatusOK {
  105. bs, _ := io.ReadAll(res.Body)
  106. log.Print(string(bs))
  107. return "", errors.New(res.Status) //nolint:err113
  108. }
  109. defer res.Body.Close()
  110. var resJSON struct {
  111. Body string
  112. }
  113. if err := json.NewDecoder(res.Body).Decode(&resJSON); err != nil {
  114. return "", err
  115. }
  116. return strings.TrimSpace(removeHTMLComments(resJSON.Body)), nil
  117. }
  118. func removeHTMLComments(s string) string {
  119. return regexp.MustCompile(`<!--.*?-->`).ReplaceAllString(s, "")
  120. }