summaryrefslogtreecommitdiff
path: root/cmd/generate
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/generate')
-rw-r--r--cmd/generate/calendar.go122
-rw-r--r--cmd/generate/gitcache.go66
-rw-r--r--cmd/generate/httpcache.go95
-rw-r--r--cmd/generate/imworkingon.html.tmpl160
-rw-r--r--cmd/generate/main.go154
-rw-r--r--cmd/generate/src_contribs.go400
-rw-r--r--cmd/generate/src_contribs_test.go39
-rw-r--r--cmd/generate/src_mastodon.go48
-rw-r--r--cmd/generate/src_tags.go25
-rw-r--r--cmd/generate/src_upstreams.go48
10 files changed, 0 insertions, 1157 deletions
diff --git a/cmd/generate/calendar.go b/cmd/generate/calendar.go
deleted file mode 100644
index 29c3318..0000000
--- a/cmd/generate/calendar.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package main
-
-import (
- "time"
-)
-
-//////////////////////////////////////////////////////////////////////
-
-type Date struct {
- Year int
- Month time.Month
- Day int
-}
-
-func DateOf(t time.Time) Date {
- y, m, d := t.Date()
- return Date{Year: y, Month: m, Day: d}
-}
-
-func (d Date) Time() time.Time {
- return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, time.Local)
-}
-
-func (d Date) AddDays(delta int) Date {
- return DateOf(d.Time().AddDate(0, 0, delta))
-}
-
-func (d Date) Weekday() time.Weekday {
- return d.Time().Weekday()
-}
-
-func (a Date) Cmp(b Date) int {
- switch {
- case a.Year < b.Year:
- return -1
- case a.Year > b.Year:
- return 1
- }
- switch {
- case a.Month < b.Month:
- return -1
- case a.Month > b.Month:
- return 1
- }
- switch {
- case a.Day < b.Day:
- return -1
- case a.Day > b.Day:
- return 1
- }
- return 0
-}
-
-//////////////////////////////////////////////////////////////////////
-
-type CalendarDay[T any] struct {
- Date
- Data T
-}
-
-//////////////////////////////////////////////////////////////////////
-
-// keyed by time.Weekday
-type CalendarWeek[T any] [7]CalendarDay[T]
-
-//////////////////////////////////////////////////////////////////////
-
-// must be sorted, must be non-sparse
-type Calendar[T any] []CalendarWeek[T]
-
-func (c Calendar[T]) NumWeekdaysInMonth(weekday time.Weekday, target Date) int {
- num := 0
- for _, w := range c {
- if w[weekday].Date == (Date{}) {
- continue
- }
- switch {
- case w[weekday].Year == target.Year:
- switch {
- case w[weekday].Month == target.Month:
- num++
- case w[weekday].Month > target.Month:
- return num
- }
- case w[weekday].Year > target.Year:
- return num
- }
- }
- return num
-}
-
-//////////////////////////////////////////////////////////////////////
-
-func BuildCalendar[T any](things []T, dateOfThing func(T) Date) Calendar[T] {
- if len(things) == 0 {
- return nil
- }
-
- newestDate := DateOf(time.Now().Local())
-
- oldestDate := dateOfThing(things[0])
- byDate := make(map[Date]T, len(things))
- for _, thing := range things {
- date := dateOfThing(thing)
- if oldestDate.Cmp(date) > 0 {
- oldestDate = date
- }
- byDate[date] = thing
- }
-
- var ret Calendar[T]
- for date := oldestDate; date.Cmp(newestDate) <= 0; date = date.AddDays(1) {
- if len(ret) == 0 || date.Weekday() == 0 {
- ret = append(ret, CalendarWeek[T]{})
- }
- ret[len(ret)-1][date.Weekday()] = CalendarDay[T]{
- Date: date,
- Data: byDate[date],
- }
- }
- return ret
-}
diff --git a/cmd/generate/gitcache.go b/cmd/generate/gitcache.go
deleted file mode 100644
index 7caf024..0000000
--- a/cmd/generate/gitcache.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
- "os/exec"
- "strings"
-
- "git.mothstuff.lol/lukeshu/eclipse/lib/gitcache"
-)
-
-var gitFetched = map[string]struct{}{}
-
-var gitCache = &gitcache.Cache{
- Dir: ".git-cache",
-}
-
-func withGit(u string, fn func(dir string) error) error {
- if _, ok := gitFetched[u]; !ok {
- if err := gitCache.Fetch(os.Stderr, u); err != nil {
- return err
- }
- }
- return gitCache.WithFastClone(os.Stderr, u, fn)
-}
-
-func getGitTagThatContainsAll(gitURL string, gitHashes ...string) (string, error) {
- if len(gitHashes) == 0 {
- return "", nil
- }
- var tag string
- err := withGit(gitURL, func(dir string) error {
- gitHash := gitHashes[0]
- if len(gitHashes) > 1 {
- cmdline := append([]string{"git", "merge-base", "--independent", "--"}, gitHashes...)
- cmd := exec.Command(cmdline[0], cmdline[1:]...)
- cmd.Dir = dir
- var stdout strings.Builder
- cmd.Stdout = &stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- return err
- }
- gitHash = strings.TrimSpace(stdout.String())
- }
- cmd := exec.Command("git", "for-each-ref",
- "--count=1",
- "--format=%(refname:lstrip=2)",
- "--contains="+gitHash,
- "refs/tags/",
- )
- cmd.Dir = dir
- var stdout strings.Builder
- cmd.Stdout = &stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- return err
- }
- tag = strings.TrimSpace(stdout.String())
- return nil
- })
- if err != nil {
- return "", fmt.Errorf("%q: %w", gitURL, err)
- }
- return tag, nil
-}
diff --git a/cmd/generate/httpcache.go b/cmd/generate/httpcache.go
deleted file mode 100644
index 04762e3..0000000
--- a/cmd/generate/httpcache.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
-)
-
-var httpCache = map[string]string{}
-
-func httpGet(u string) (string, error) {
- if cache, ok := httpCache[u]; ok {
- fmt.Printf("CACHE-GET %q\n", u)
- return cache, nil
- }
- if err := os.Mkdir(".http-cache", 0777); err != nil && !os.IsExist(err) {
- return "", err
- }
- cacheFile := filepath.Join(".http-cache", url.QueryEscape(u))
- if bs, err := os.ReadFile(cacheFile); err == nil {
- httpCache[u] = string(bs)
- fmt.Printf("CACHE-GET %q\n", u)
- return httpCache[u], nil
- } else if !os.IsNotExist(err) {
- return "", err
- }
-
- fmt.Printf("GET %q...", u)
- resp, err := http.Get(u)
- if err != nil {
- fmt.Printf(" err\n")
- return "", err
- }
- if resp.StatusCode != http.StatusOK {
- fmt.Printf(" err\n")
- return "", fmt.Errorf("unexpected HTTP status: %v", resp.Status)
- }
- bs, err := io.ReadAll(resp.Body)
- if err != nil {
- fmt.Printf(" err\n")
- return "", err
- }
- fmt.Printf(" ok\n")
- if err := os.WriteFile(cacheFile, bs, 0666); err != nil {
- return "", err
- }
- httpCache[u] = string(bs)
- return httpCache[u], nil
-}
-
-func httpGetJSON(u string, out any) error {
- str, err := httpGet(u)
- if err != nil {
- return err
- }
- return json.Unmarshal([]byte(str), out)
-}
-
-func httpGetPaginatedJSON[T any](uStr string, out *[]T, pageFn func(i int) url.Values) error {
- u, err := url.Parse(uStr)
- if err != nil {
- return err
- }
- query := u.Query()
-
- for i := 0; true; i++ {
- pageParams := pageFn(i)
- for k, v := range pageParams {
- query[k] = v
- }
-
- u.RawQuery = query.Encode()
- var resp []T
- if err := httpGetJSON(u.String(), &resp); err != nil {
- return err
- }
- fmt.Printf(" -> %d records\n", len(resp))
- if len(resp) == 0 {
- break
- }
- *out = append(*out, resp...)
- }
-
- return nil
-}
-
-func githubPagination(i int) url.Values {
- params := make(url.Values)
- params.Set("page", fmt.Sprintf("%v", i+1))
- return params
-}
diff --git a/cmd/generate/imworkingon.html.tmpl b/cmd/generate/imworkingon.html.tmpl
deleted file mode 100644
index 85a56e1..0000000
--- a/cmd/generate/imworkingon.html.tmpl
+++ /dev/null
@@ -1,160 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>Luke is working on…</title>
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" type="text/css" href="../blog/assets/style.css">
- <link rel="stylesheet" type="text/css" href="imworkingon.css">
-</head>
-<body>
- <header><a href="/">Luke T. Shumaker</a> Β» imworkingon</header>
-
- <section id="intro">
- <h1>Luke is working on<br/><small>improving the GNU/Linux ecosystem</small></h1>
- <nav>
- <p>This page provides several views into what I'm doing to improve the ecosystem:</p>
- <ol>
- <li><a href="#tags">Top themes</a></li>
- <li><a href="#contribs-pending">In-progress work</a></li>
- <li><a href="#contribs-completed">Completed work</a></li>
- <li><a href="#standups">Daily statuses</a></li>
- </ol>
- </nav>
- <p>If you find this work valuable, please consider <a class="em" href="../sponsor/">sponsoring me</a>.</p>
- </section>
-
- <section id="tags">
- <h2>Top themes <a href="#tags">πŸ”—</a></h2>
- {{- range $tagName, $tagInfo := .Tags }}
- <article class="tag" id="tag-{{ $tagName }}">
- <h2><a href="#tag-{{ $tagName }}">#{{ $tagName }}</a> : {{ $tagInfo.PrettyName }}</h2>
- <div clasg="tag-desc">{{ $tagInfo.Desc | md2html }}</div>
- </article>
- {{- end }}
- </section>
-
-{{- define "contrib" }}
- {{ $contrib := . }}
- {{ $upstream := $contrib | getUpstream }}
- <article class="contrib {{ $contrib.StatusClass }}-contrib">
- <div class="contrib-upstream-name"><a class="em" href="{{ index $upstream.URLs 0 }}">{{ $upstream.Name }}</a></div>
- <div class="contrib-upstream-desc">{{ $upstream.Desc | md2html }}</div>
- <div class="contrib-urls">
- {{- range $url := $contrib.URLs }}
- <a href="{{ $url }}"><code>{{ $url }}</code></a><br />
- {{- end }}
- </div>
- <div class="contrib-tags">
- {{- range $tag := $contrib.Tags }}
- <a href="#tag-{{ $tag }}">#{{ $tag }}</a> {{/* */}}
- {{- end }}
- </div>
- <div class="contrib-submitted">Submitted: {{ timeTag $contrib.SubmittedAt "2006-01-02" }}</div>
- <div class="contrib-updated">
- {{- if not $contrib.LastUpdatedAt.IsZero -}}
- Last updated: {{ timeTag $contrib.LastUpdatedAt "2006-01-02" }}
- {{- if $contrib.LastUpdatedBy.Name }} by <a href="{{ $contrib.LastUpdatedBy.URL }}">{{ $contrib.LastUpdatedBy.Name }}</a>{{ end }}
- {{- end -}}
- </div>
- <div class="contrib-status">Status: {{ $contrib.Status }}</div>
- <div class="contrib-desc">
- {{- $contrib.Desc | md2html }}
- {{- if $contrib.SponsoredBy }}<p>Sponsored-by: {{ $contrib.SponsoredBy }}</p>{{ end -}}
- </div>
- </article>
-{{- end }}
-
- <section id="contribs-pending">
- <h2>In-progress work <a href="#contribs-pending">πŸ”—</a></h2>
- {{- range $contrib := .Contribs }}
- {{- if or (eq $contrib.StatusClass "merged") (eq $contrib.StatusClass "released") }}{{ continue }}{{ end }}
- {{ template "contrib" $contrib }}
- {{- end }}
- </section>
- <section id="contribs-completed">
- <h2>Completed work <a href="#contribs-completed">πŸ”—</a></h2>
- {{- range $contrib := .Contribs }}
- {{- if or (eq $contrib.StatusClass "merged") (eq $contrib.StatusClass "released") | not }}{{ continue }}{{ end }}
- {{ template "contrib" $contrib }}
- {{- end }}
- </section>
- <section id="standups">
- <h2>Daily statuses <a href="#standups">πŸ”—</a></h2>
- <p>Posted daily on <a href="https://fosstodon.org/@lukeshu">Mastodon</a> with the #DailyStandup tag.</p>
-
- <details><summary>Calendar view</summary>
- <table>
- <thead>
- <tr>
- <th></th>
- <th><abbr title="Sunday">Su</abbr></th>
- <th><abbr title="Monday">M</abbr></th>
- <th><abbr title="Tuesday">Tu</abbr></th>
- <th><abbr title="Wednesday">W</abbr></th>
- <th><abbr title="Thursday">Th</abbr></th>
- <th><abbr title="Friday">F</abbr></th>
- <th><abbr title="Saturday">S</abbr></th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- {{- $cal := .StandupCalendar }}
- {{- $curSunMonth := 0 }}
- {{- $curSatMonth := 0 }}
- {{- range $i, $week := reverse .StandupCalendar }}
- <tr>
- {{- $sun := (index $week time.Sunday) }}
- {{- if not $sun.Day }}
- <th></th>
- {{- else if ne $sun.Month $curSunMonth }}
- <th class="{{ monthClass $sun.Month }}" rowspan="{{ $cal.NumWeekdaysInMonth time.Sunday $sun.Date }}">
- <span>{{ $sun.Month }} {{ $sun.Year }}</span>
- </th>
- {{- $curSunMonth = $sun.Month }}
- {{- end }}
- {{- range $day := $week }}
- {{- if not $day.Day }}
- <td></td>
- {{- else if not $day.Data }}
- <td class="{{ monthClass $day.Month }}">
- {{ $day.Day }}
- </td>
- {{- else }}
- <td class="{{ monthClass $day.Month }}">
- <a href="#standup-id-{{ $day.Data.ID }}">
- {{ $day.Day }}
- </a>
- </td>
- {{- end }}
- </td>
- {{- end }}
- {{- $sat := (index $week time.Saturday) }}
- {{- if not $sat.Day }}
- <th></th>
- {{- else if ne $sat.Month $curSatMonth }}
- <th class="{{ monthClass $sat.Month }}" rowspan="{{ $cal.NumWeekdaysInMonth time.Saturday $sat.Date }}">
- <span>{{ $sat.Month }} {{ $sat.Year }}</span>
- </th>
- {{- $curSatMonth = $sat.Month }}
- {{- end }}
- {{- end }}
- </tr>
- </tbody>
- </table>
- </details>
-
- {{- range $status := .Standups }}
- <article class="standup" id="standup-id-{{ $status.ID }}">
- <div class="standup-title"><a href="{{ $status.URL }}">{{ timeTag $status.CreatedAt "Mon 2006-01-02" }}</a></div>
- <div class="standup-content">{{ $status.Content }}</div>
- </article>
- {{- end }}
- </section>
-
- <footer>
- <p>The content of this page is Copyright Β© Luke T. Shumaker.</p>
- <p>This page is licensed under the <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a> license.</p>
- </footer>
-</body>
-</html>
diff --git a/cmd/generate/main.go b/cmd/generate/main.go
deleted file mode 100644
index e322e5c..0000000
--- a/cmd/generate/main.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package main
-
-import (
- "bytes"
- _ "embed"
- "fmt"
- "os"
- "reflect"
- "sort"
- "strings"
- "time"
-
- "html/template"
-
- "github.com/yuin/goldmark"
-)
-
-func MarkdownToHTML(md string) (template.HTML, error) {
- var html strings.Builder
- if err := goldmark.Convert([]byte(md), &html); err != nil {
- return template.HTML(""), err
- }
- return template.HTML(html.String()), nil
-}
-
-var githubProjects = map[string]string{
- "flori/json": "ruby-json",
-}
-
-func main() {
- if err := mainWithError(); err != nil {
- fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err)
- os.Exit(1)
- }
-}
-
-//go:embed imworkingon.html.tmpl
-var htmlTmplStr string
-
-var timeTagTmpl = template.Must(template.New("time.tag.tmpl").
- Parse(`<time datetime="{{ .Machine }}" title="{{ .HumanVerbose }}">{{ .HumanPretty }}</time>`))
-
-func mainWithError() error {
- standups, err := ReadStandups("https://fosstodon.org", "lukeshu")
- if err != nil {
- return err
- }
- contribs, err := ReadContribs("imworkingon/contribs.yml")
- if err != nil {
- return err
- }
- tags, err := ReadTags("imworkingon/tags.yml")
- if err != nil {
- return err
- }
- upstreams, err := ReadUpstreams("imworkingon/upstreams.yml")
- if err != nil {
- return err
- }
-
- sort.Slice(contribs, func(i, j int) bool {
- iDate := contribs[i].LastUpdatedAt
- if iDate.IsZero() {
- iDate = contribs[i].SubmittedAt
- }
- jDate := contribs[j].LastUpdatedAt
- if jDate.IsZero() {
- jDate = contribs[j].SubmittedAt
- }
- return iDate.After(jDate)
- })
-
- tmpl := template.Must(template.New("imworkingon.html.tmpl").
- Funcs(template.FuncMap{
- "time": func() map[string]time.Weekday {
- return map[string]time.Weekday{
- "Sunday": time.Sunday,
- "Monday": time.Monday,
- "Tuesday": time.Tuesday,
- "Wednesday": time.Wednesday,
- "Thursday": time.Thursday,
- "Friday": time.Friday,
- "Saturday": time.Saturday,
- }
- },
- "reverse": func(x any) any {
- in := reflect.ValueOf(x)
- l := in.Len()
- out := reflect.MakeSlice(in.Type(), l, l)
- for i := 0; i < l; i++ {
- out.Index(l - (i + 1)).Set(in.Index(i))
- }
- return out.Interface()
- },
- "timeTag": func(ts time.Time, prettyFmt string) (template.HTML, error) {
- ts = ts.Local()
- var out strings.Builder
- err := timeTagTmpl.Execute(&out, map[string]string{
- "Machine": ts.Format(time.RFC3339),
- "HumanVerbose": ts.Format("2006-01-02 15:04:05Z07:00"),
- "HumanPretty": ts.Format(prettyFmt),
- })
- return template.HTML(out.String()), err
- },
- "monthClass": func(m time.Month) string {
- if m%2 == 0 {
- return "even-month"
- } else {
- return "odd-month"
- }
- },
- "md2html": MarkdownToHTML,
- "getUpstream": func(c Contribution) Upstream {
- // First try any of the documented upstreams.
- for _, cURL := range c.URLs {
- for _, upstream := range upstreams {
- for _, uURL := range upstream.URLs {
- prefix := uURL
- if !strings.HasSuffix(prefix, "/") {
- prefix += "/"
- }
- if cURL == uURL || strings.HasPrefix(cURL, prefix) {
- return upstream
- }
- }
- }
- }
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- return Upstream{URLs: []string{c.URLs[0]}, Name: user + "/" + repo}
- }
- return Upstream{URLs: []string{c.URLs[0]}, Name: "???"}
- },
- }).
- Parse(htmlTmplStr))
- var out bytes.Buffer
- if err := tmpl.Execute(&out, map[string]any{
- "Contribs": contribs,
- "Tags": tags,
- "Upstreams": upstreams,
- "Standups": standups,
- "StandupCalendar": BuildCalendar(standups, func(status *MastodonStatus) Date { return DateOf(status.CreatedAt.Local()) }),
- }); err != nil {
- return err
- }
- if err := os.WriteFile("public/imworkingon/index.new.html", out.Bytes(), 0666); err != nil {
- return err
- }
- if err := os.Rename("public/imworkingon/index.new.html", "public/imworkingon/index.html"); err != nil {
- return err
- }
- return nil
-}
diff --git a/cmd/generate/src_contribs.go b/cmd/generate/src_contribs.go
deleted file mode 100644
index 6db6764..0000000
--- a/cmd/generate/src_contribs.go
+++ /dev/null
@@ -1,400 +0,0 @@
-package main
-
-import (
- "fmt"
- "net/url"
- "os"
- "regexp"
- "strings"
- "time"
-
- "sigs.k8s.io/yaml"
-)
-
-type User struct {
- Name string `json:"name"`
- URL string `json:"url"`
-}
-
-type Contribution struct {
- URLs []string `json:"urls"`
- Tags []string `json:"tags"`
- SponsoredBy string `json:"sponsored-by"`
- Desc string `json:"desc"`
-
- SubmittedAt time.Time `json:"submitted-at"`
- LastUpdatedAt time.Time `json:"last-updated-at"`
- LastUpdatedBy User `json:"last-updated-by"`
- Status string `json:"status"`
-
- StatusClass string `json:"-"`
-}
-
-func ReadContribs(filename string) ([]Contribution, error) {
- bs, err := os.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("contribs: %q: %w", filename, err)
- }
- var ret []Contribution
- if err := yaml.UnmarshalStrict(bs, &ret); err != nil {
- return nil, fmt.Errorf("contribs: %q: %w", filename, err)
- }
- for i := range ret {
- contrib := ret[i]
- if err := contrib.Fill(); err != nil {
- return nil, fmt.Errorf("contribs: %q: %w", filename, err)
- }
- ret[i] = contrib
- }
- return ret, nil
-}
-
-func (c *Contribution) Fill() error {
- var err error
- if c.SubmittedAt.IsZero() {
- c.SubmittedAt, err = c.fetchSubmittedAt()
- if err != nil {
- return err
- }
- }
- if c.LastUpdatedAt.IsZero() {
- c.LastUpdatedAt, c.LastUpdatedBy, err = c.fetchLastUpdated()
- if err != nil {
- return err
- }
- }
- if c.Status == "" {
- c.Status, err = c.fetchStatus()
- if err != nil {
- return err
- }
- }
- c.StatusClass, err = classifyStatus(c.Status)
- if err != nil {
- return err
- }
- for _, u := range c.URLs {
- if m := reGoLangGerritCL.FindStringSubmatch(u); m != nil {
- c.URLs = append(c.URLs, "https://golang.org/cl/"+m[1])
- }
- }
- return nil
-}
-
-func classifyStatus(status string) (string, error) {
- switch {
- case strings.Contains(status, "released") || strings.Contains(status, "deployed"):
- return "released", nil
- case strings.Contains(status, "merged"):
- return "merged", nil
- case strings.Contains(status, "open"):
- return "open", nil
- case strings.Contains(status, "closed") || strings.Contains(status, "locked"):
- return "closed", nil
- default:
- return "", fmt.Errorf("unrecognized status string: %q", status)
- }
-}
-
-var (
- reGoLangGerritCL = regexp.MustCompile(`https://go-review\.googlesource\.com/c/[^/?#]+/\+/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitHubPR = regexp.MustCompile(`^https://github\.com/([^/?#]+)/([^/?#]+)/pull/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitHubCommit = regexp.MustCompile(`^https://github\.com/([^/?#]+)/([^/?#]+)/commit/([0-9a-f]+)(?:\?[^#]*)?(?:#.*)?$`)
- reGitLabMR = regexp.MustCompile(`^https://([^/]+)/([^?#]+)/-/merge_requests/([0-9]+)(?:\?[^#]*)?(?:#.*)?$`)
- rePiperMailDate = regexp.MustCompile(`^\s*<I>([^<]+)</I>\s*$`)
-)
-
-const (
- statusOpen = "open"
- statusMerged = "merged, not yet in a release"
- statusReleasedFmt = "merged, released in %s"
-)
-
-func (c Contribution) fetchStatus() (string, error) {
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/pulls/" + prnum
-
- var obj struct {
- // State values are "open" and "closed".
- State string `json:"state"`
- Merged bool `json:"merged"`
- MergeCommitSha string `json:"merge_commit_sha"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return "", err
- }
- ret := obj.State
- if obj.Merged {
- ret = statusMerged
- tag, err := getGitTagThatContainsAll("https://github.com/"+user+"/"+repo, obj.MergeCommitSha)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- }
-
- return ret, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
-
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
-
- var obj struct {
- // State values are "opened", "closed", "locked", and "merged".
- State string `json:"state"`
- MergeCommitSha string `json:"merge_commit_sha"`
- SquashCommitSha string `json:"squash_commit_sha"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return "", err
- }
-
- ret := obj.State
- if ret == "opened" {
- ret = statusOpen
- }
-
- if ret == "merged" {
- ret = statusMerged
- var mergeCommit string
- if obj.MergeCommitSha != "" {
- mergeCommit = obj.MergeCommitSha
- }
- if obj.SquashCommitSha != "" {
- mergeCommit = obj.SquashCommitSha
- }
- if mergeCommit != "" {
- tag, err := getGitTagThatContainsAll("https://"+authority+"/"+projectID+".git", mergeCommit)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- }
- }
-
- return ret, nil
- }
- if len(c.URLs) > 1 {
- var gitURL string
- var gitCommits []string
- for _, u := range c.URLs[1:] {
- if m := reGitHubCommit.FindStringSubmatch(u); m != nil {
- user := m[1]
- repo := m[2]
- hash := m[3]
-
- gitURL = "https://github.com/" + user + "/" + repo
- gitCommits = append(gitCommits, hash)
- }
- }
- if len(gitCommits) > 0 {
- ret := statusMerged
- tag, err := getGitTagThatContainsAll(gitURL, gitCommits...)
- if err != nil {
- return "", err
- }
- if tag != "" {
- ret = fmt.Sprintf(statusReleasedFmt, tag)
- }
- return ret, nil
- }
- }
- return "", fmt.Errorf("idk how to get status for %q", c.URLs[0])
-}
-
-func (c Contribution) fetchSubmittedAt() (time.Time, error) {
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/pulls/" + prnum
-
- var obj struct {
- CreatedAt time.Time `json:"created_at"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return time.Time{}, err
- }
- return obj.CreatedAt, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
-
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
-
- var obj struct {
- CreatedAt time.Time `json:"created_at"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return time.Time{}, err
- }
- return obj.CreatedAt, nil
- }
- if strings.Contains(c.URLs[0], "/pipermail/") {
- htmlStr, err := httpGet(c.URLs[0])
- if err != nil {
- return time.Time{}, err
- }
- for _, line := range strings.Split(htmlStr, "\n") {
- if m := rePiperMailDate.FindStringSubmatch(line); m != nil {
- return time.Parse(time.UnixDate, m[1])
- }
- }
- }
- return time.Time{}, fmt.Errorf("idk how to get created timestamp for %q", c.URLs[0])
-}
-
-func (c Contribution) fetchLastUpdated() (time.Time, User, error) {
- if m := reGitHubPR.FindStringSubmatch(c.URLs[0]); m != nil {
- user := m[1]
- repo := m[2]
- prnum := m[3]
-
- var obj struct {
- UpdatedAt time.Time `json:"updated_at"`
- MergedAt time.Time `json:"merged_at"`
- MergedBy struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"merged_by"`
- }
- if err := httpGetJSON("https://api.github.com/repos/"+user+"/"+repo+"/pulls/"+prnum, &obj); err != nil {
- return time.Time{}, User{}, err
- }
-
- retUpdatedAt := obj.UpdatedAt
- var retUser User
-
- if obj.MergedAt == retUpdatedAt {
- retUser.Name = obj.MergedBy.Login
- retUser.URL = obj.MergedBy.HTMLURL
- }
- if retUser == (User{}) {
- // "normal" comments
- var comments []struct {
- UpdatedAt time.Time `json:"updated_at"`
- User struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"user"`
- }
- if err := httpGetPaginatedJSON("https://api.github.com/repos/"+user+"/"+repo+"/issues/"+prnum+"/comments", &comments, githubPagination); err != nil {
- return time.Time{}, User{}, err
- }
- for _, comment := range comments {
- if comment.UpdatedAt == retUpdatedAt || comment.UpdatedAt.Add(1*time.Second) == retUpdatedAt {
- retUser.Name = comment.User.Login
- retUser.URL = comment.User.HTMLURL
- break
- }
- }
- }
- if retUser == (User{}) {
- // comments on a specific part of the diff
- var reviewComments []struct {
- UpdatedAt time.Time `json:"updated_at"`
- User struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"user"`
- }
- if err := httpGetPaginatedJSON("https://api.github.com/repos/"+user+"/"+repo+"/pulls/"+prnum+"/comments", &reviewComments, githubPagination); err != nil {
- return time.Time{}, User{}, err
- }
- for _, comment := range reviewComments {
- if comment.UpdatedAt == retUpdatedAt {
- retUser.Name = comment.User.Login
- retUser.URL = comment.User.HTMLURL
- break
- }
- }
- }
- if retUser == (User{}) {
- var events []struct {
- CreatedAt time.Time `json:"created_at"`
- Actor struct {
- Login string `json:"login"`
- HTMLURL string `json:"html_url"`
- } `json:"actor"`
- }
- if err := httpGetJSON("https://api.github.com/repos/"+user+"/"+repo+"/issues/"+prnum+"/events", &events); err != nil {
- return time.Time{}, User{}, err
- }
- for _, event := range events {
- if event.CreatedAt == retUpdatedAt {
- retUser.Name = event.Actor.Login
- retUser.URL = event.Actor.HTMLURL
- break
- }
- }
- }
-
- return retUpdatedAt, retUser, nil
- }
- if m := reGitLabMR.FindStringSubmatch(c.URLs[0]); m != nil {
- authority := m[1]
- projectID := m[2]
- mrnum := m[3]
-
- urlStr := "https://" + authority + "/api/v4/projects/" + url.QueryEscape(projectID) + "/merge_requests/" + mrnum
-
- var obj struct {
- UpdatedAt time.Time `json:"updated_at"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return time.Time{}, User{}, err
- }
- return obj.UpdatedAt, User{}, nil
- }
-
- var ret time.Time
- if len(c.URLs) > 1 {
- for _, u := range c.URLs[1:] {
- if m := reGitHubCommit.FindStringSubmatch(u); m != nil {
- user := m[1]
- repo := m[2]
- hash := m[3]
-
- urlStr := "https://api.github.com/repos/" + user + "/" + repo + "/commits/" + hash
- var obj struct {
- Commit struct {
- Author struct {
- Date time.Time `json:"date"`
- } `json:"author"`
- Committer struct {
- Date time.Time `json:"date"`
- } `json:"committer"`
- } `json:"commit"`
- }
- if err := httpGetJSON(urlStr, &obj); err != nil {
- return time.Time{}, User{}, err
- }
- if obj.Commit.Author.Date.After(ret) {
- ret = obj.Commit.Author.Date
- }
- if obj.Commit.Committer.Date.After(ret) {
- ret = obj.Commit.Committer.Date
- }
- }
- }
- }
- if !ret.IsZero() {
- return ret, User{}, nil
- }
-
- return time.Time{}, User{}, nil //fmt.Errorf("idk how to get updated timestamp for %q", c.URLs[0])
-}
diff --git a/cmd/generate/src_contribs_test.go b/cmd/generate/src_contribs_test.go
deleted file mode 100644
index 57ffc0f..0000000
--- a/cmd/generate/src_contribs_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package main
-
-import (
- "testing"
-
- "github.com/alecthomas/assert/v2"
-)
-
-func TestClassifyStatus(t *testing.T) {
- testcases := map[string]struct {
- Str string
- Err string
- }{
- "merged+deployed": {"released", ""},
- "merged, deployed": {"released", ""},
- "released in v1.2": {"released", ""},
- "merged, released in v1.2": {"released", ""},
- statusReleasedFmt: {"released", ""},
-
- "merged": {"merged", ""},
- statusMerged: {"merged", ""},
-
- "open": {"open", ""},
-
- "closed": {"closed", ""},
- "locked": {"closed", ""},
- }
- for in, exp := range testcases {
- t.Run(in, func(t *testing.T) {
- actStr, actErr := classifyStatus(in)
- assert.Equal(t, exp.Str, actStr)
- if exp.Err == "" {
- assert.NoError(t, actErr)
- } else {
- assert.EqualError(t, actErr, exp.Err)
- }
- })
- }
-}
diff --git a/cmd/generate/src_mastodon.go b/cmd/generate/src_mastodon.go
deleted file mode 100644
index b4b54a8..0000000
--- a/cmd/generate/src_mastodon.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package main
-
-import (
- "html/template"
- "net/url"
- "slices"
- "time"
-)
-
-type MastodonStatus struct {
- ID string `json:"id"`
- CreatedAt time.Time `json:"created_at"`
- URL string `json:"url"`
- Content template.HTML `json:"content"`
-}
-
-// Returns statuses sorted from newest to oldest.
-func ReadStandups(server, username string) ([]*MastodonStatus, error) {
- var account struct {
- ID string `json:"id"`
- }
- if err := httpGetJSON(server+"/api/v1/accounts/lookup?acct="+username, &account); err != nil {
- return nil, err
- }
-
- var statuses []*MastodonStatus
- if err := httpGetPaginatedJSON(server+"/api/v1/accounts/"+account.ID+"/statuses", &statuses, func(_ int) url.Values {
- params := make(url.Values)
- params.Set("tagged", "DailyStandUp")
- params.Set("exclude_reblogs", "true")
- if len(statuses) > 0 {
- params.Set("max_id", statuses[len(statuses)-1].ID)
- }
- return params
- }); err != nil {
- return nil, err
- }
-
- ignoreList := []string{
- "https://fosstodon.org/@lukeshu/112198267818432116",
- "https://fosstodon.org/@lukeshu/112198241414760456",
- }
- statuses = slices.DeleteFunc(statuses, func(status *MastodonStatus) bool {
- return slices.Contains(ignoreList, status.URL)
- })
-
- return statuses, nil
-}
diff --git a/cmd/generate/src_tags.go b/cmd/generate/src_tags.go
deleted file mode 100644
index 8dcf554..0000000
--- a/cmd/generate/src_tags.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
-
- "sigs.k8s.io/yaml"
-)
-
-type TagInfo struct {
- PrettyName string `json:"prettyName"`
- Desc string `json:"desc"`
-}
-
-func ReadTags(filename string) (map[string]TagInfo, error) {
- bs, err := os.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("tags: %q: %w", filename, err)
- }
- var ret map[string]TagInfo
- if err := yaml.UnmarshalStrict(bs, &ret); err != nil {
- return nil, fmt.Errorf("tags: %q: %w", filename, err)
- }
- return ret, nil
-}
diff --git a/cmd/generate/src_upstreams.go b/cmd/generate/src_upstreams.go
deleted file mode 100644
index 03f72ec..0000000
--- a/cmd/generate/src_upstreams.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package main
-
-import (
- _ "embed"
- "fmt"
- "net/url"
- "os"
- "path"
- "strings"
-
- "sigs.k8s.io/yaml"
-)
-
-type Upstream struct {
- URLs []string `json:"urls"`
- Name string `json:"name"`
- Desc string `json:"desc"`
-}
-
-func ReadUpstreams(filename string) ([]Upstream, error) {
- bs, err := os.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("upstreams: %q: %w", filename, err)
- }
- var ret []Upstream
- if err := yaml.UnmarshalStrict(bs, &ret); err != nil {
- return nil, fmt.Errorf("upstreams: %q: %w", filename, err)
- }
- for i := range ret {
- upstream := ret[i]
- if err := upstream.Fill(); err != nil {
- return nil, fmt.Errorf("upstreams: %q: %w", filename, err)
- }
- ret[i] = upstream
- }
- return ret, nil
-}
-
-func (upstream *Upstream) Fill() error {
- if upstream.Name == "" {
- u, err := url.Parse(upstream.URLs[0])
- if err != nil {
- return err
- }
- _, upstream.Name = path.Split(strings.TrimSuffix(path.Clean(u.Path), ".git"))
- }
- return nil
-}