carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// Command changelog writes docs/site/changelog.json, which the website
2// renders at carlosframework.com/platform/changelog.
3//
4// Run it from a full checkout of carlos/platform with every input
5// fetched, and with gh and amadan signed in:
6//
7// git fetch origin --tags '+refs/amadan/closed/*:refs/amadan/closed/*'
8// go run ./cmd/changelog
9//
10// It reads the published release notes with gh and Amadan's discussions
11// with the amadan CLI, so it is a release-time tool, not part of the
12// build. The file it writes is checked by internal/changelog's tests.
13package main
14
15import (
16 "bytes"
17 "encoding/json"
18 "errors"
19 "flag"
20 "fmt"
21 "os"
22 "os/exec"
23 "path/filepath"
24 "strings"
25
26 "github.com/carlosframework/platform/internal/changelog"
27 "github.com/carlosframework/platform/internal/docsite"
28)
29
30func main() {
31 out := flag.String("out", "docs/site/changelog.json", "file to write")
32 repo := flag.String("repo", "carlos/platform", "the Amadan repo discussions are read from")
33 releases := flag.String("releases", "carlosframework/releases", "the GitHub repo release notes are read from")
34 flag.Parse()
35 if err := run(*out, *repo, *releases); err != nil {
36 fmt.Fprintln(os.Stderr, "changelog:", err)
37 os.Exit(1)
38 }
39}
40
41func run(out, repo, releasesRepo string) error {
42 people, err := changelog.LoadPeople("docs/site/people.json")
43 if err != nil {
44 return err
45 }
46 links, err := changelog.LoadLinks("docs/site/changelog-links.json")
47 if err != nil {
48 return err
49 }
50 site, err := docsite.Load("docs/site")
51 if err != nil {
52 return err
53 }
54 g, err := changelog.NewExecGit(".")
55 if err != nil {
56 return err
57 }
58 c, err := changelog.Build(changelog.Sources{
59 Git: g,
60 Releases: ghReleases{repo: releasesRepo},
61 Discussions: amadanDiscussions{repo: repo},
62 }, people, links, site)
63 if err != nil {
64 return err
65 }
66 raw, err := changelog.Marshal(c)
67 if err != nil {
68 return err
69 }
70 if err := os.WriteFile(filepath.Clean(out), raw, 0o644); err != nil {
71 return err
72 }
73 for _, r := range c.Releases {
74 notes := "unpublished"
75 if r.Notes != nil {
76 notes = r.Notes.Form
77 }
78 fmt.Printf("%-8s %s %3d changes notes: %s\n", r.Tag, r.Date, len(r.Changes), notes)
79 }
80 return nil
81}
82
83func command(name string, args ...string) (stdout, stderr string, err error) {
84 cmd := exec.Command(name, args...)
85 var o, e bytes.Buffer
86 cmd.Stdout, cmd.Stderr = &o, &e
87 err = cmd.Run()
88 return o.String(), e.String(), err
89}
90
91type ghReleases struct{ repo string }
92
93func (g ghReleases) Body(tag string) (string, bool, error) {
94 out, stderr, err := command("gh", "release", "view", tag, "-R", g.repo, "--json", "body", "-q", ".body")
95 if err != nil {
96 if strings.Contains(stderr, "release not found") {
97 return "", false, nil
98 }
99 return "", false, fmt.Errorf("gh release view %s: %v: %s", tag, err, strings.TrimSpace(stderr))
100 }
101 return out, true, nil
102}
103
104type amadanDiscussions struct{ repo string }
105
106type amadanDiscussion struct {
107 ID int `json:"id"`
108 Title string `json:"title"`
109 State string `json:"state"`
110 Created int64 `json:"created"`
111}
112
113func (d amadanDiscussion) public() changelog.Discussion {
114 return changelog.Discussion{ID: d.ID, Title: d.Title, State: d.State, Created: d.Created}
115}
116
117func (a amadanDiscussions) Show(id int) (*changelog.Discussion, error) {
118 out, stderr, err := command("amadan", "discuss", "show", a.repo, fmt.Sprint(id), "-json")
119 if strings.Contains(stderr, "no such discussion") {
120 return nil, nil
121 }
122 if err != nil {
123 return nil, fmt.Errorf("amadan discuss show %d: %v: %s", id, err, strings.TrimSpace(stderr))
124 }
125 var resp struct {
126 Discussion *amadanDiscussion `json:"discussion"`
127 }
128 if err := json.Unmarshal([]byte(out), &resp); err != nil || resp.Discussion == nil {
129 return nil, fmt.Errorf("amadan discuss show %d: unreadable answer: %v %s", id, err, strings.TrimSpace(stderr))
130 }
131 d := resp.Discussion.public()
132 return &d, nil
133}
134
135func (a amadanDiscussions) ForBranch(branch string) ([]changelog.Discussion, error) {
136 out, stderr, err := command("amadan", "discuss", "list", a.repo, "-branch", branch, "-state", "all", "-json")
137 if err != nil {
138 return nil, fmt.Errorf("amadan discuss list -branch %s: %v: %s", branch, err, strings.TrimSpace(stderr))
139 }
140 var list []amadanDiscussion
141 if err := json.Unmarshal([]byte(out), &list); err != nil {
142 return nil, errors.New("amadan discuss list -branch " + branch + ": unreadable answer: " + err.Error())
143 }
144 res := make([]changelog.Discussion, 0, len(list))
145 for _, d := range list {
146 res = append(res, d.public())
147 }
148 return res, nil
149}
150