diff options
| author | Russ Cox <rsc@golang.org> | 2023-07-28 13:53:30 -0400 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2023-08-03 02:08:39 +0000 |
| commit | 1e75b26cba0e55a9f61c79dbb46459a4e6ddbd65 (patch) | |
| tree | 926db32bbabc6420067cf58e7bb9e254b2586bfd | |
| parent | c999d513375b4ada88a6da915515cd15868bb20d (diff) | |
| download | go-x-website-1e75b26cba0e55a9f61c79dbb46459a4e6ddbd65.tar.xz | |
_content: add rebuild page with reproducible build information
We now have a command to reproduce Go builds posted on go.dev/dl.
Add a dashboard that people can check to see its results.
We should be able to link to this page from https://reproducible-builds.org/citests/.
For golang/go#57120.
For golang/go#58884.
Change-Id: I0bd1f9c26a9a003aa1f301125083195fdeb048b4
Reviewed-on: https://go-review.googlesource.com/c/website/+/513700
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
| -rw-r--r-- | _content/rebuild.html | 94 | ||||
| -rw-r--r-- | cmd/golangorg/server.go | 76 |
2 files changed, 169 insertions, 1 deletions
diff --git a/_content/rebuild.html b/_content/rebuild.html new file mode 100644 index 00000000..8931be0a --- /dev/null +++ b/_content/rebuild.html @@ -0,0 +1,94 @@ +<!--{ + "Title": "Go Reproducible Build Report", + "layout": "article", + "template": true +}--> + +<style> +details { margin-left: 2em; } +pre { margin-left: 2em; } +.time { color: #777; } +</style> + +<p> +As of Go 1.21, Go's binary toolchain downloads served by <a href="/dl/">go.dev/dl/</a> can be +reproduced and verified by anyone, on any platform, +using <a href="https://pkg.go.dev/golang.org/x/tools/cmd/gorebuild">golang.org/x/tools/cmd/gorebuild</a>. +</p> + +<p> +This page is updated daily with the results of running <code>gorebuild</code> in an Ubuntu VM, +using this script: +</p> + +<pre> +apt-get update && +apt-get -y install software-properties-common && +add-apt-repository universe && +apt-get -y install golang-go msitools && +go run golang.org/x/build/cmd/gorebuild@latest -p=4 +</pre> + +<p> +The installation of <code>msitools</code> lets <code>gorebuild</code> +check the contents of the Windows MSI installation file. +The <code>-p=4</code> means to run up to four builds in parallel. +</p> + +{{define "marker"}}<span style="marker">{{template "markersymbol" .}}</span>{{end}} +{{define "markersymbol"}} +{{- if eq . "PASS" -}} ✅ +{{- else if eq . "SKIP" -}} — +{{- else -}} ❌ +{{- end -}} +{{end}} + +{{define "log"}} +<pre> +{{range .Messages}}<span class="time">{{(rfc3339 .Time).UTC.Format "15:04:05"}}</span> {{.Text}} +{{end}} +</pre> +{{end}} + +{{define "autoopen"}} {{if not (eq . "PASS")}} open {{end}} {{end}} + +{{$Report := json gorebuild}} +{{with $Report}} + +Using gorebuild from {{.Version}}<br><br> + +Rebuild started at {{(rfc3339 .Start).UTC.Format "2006-01-02 15:04:05"}} UTC.<br> +Rebuild finished at {{(rfc3339 .End).UTC.Format "2006-01-02 15:04:05"}} UTC.<br> +Elapsed time: {{((rfc3339 .End).Sub (rfc3339 .Start)).Round 1e9}}. + +<h2 id="releases">Releases</h2> + +{{range .Releases}} +<details {{template "autoopen" .Log.Status}} > +<summary><b id="{{.Version}}">{{template "marker" .Log.Status}} {{.Version}}</b></summary> + +<details> +<summary>Log</summary> +{{template "log" .Log}} +</details> + +{{range .Files}} +<details {{template "autoopen" .Log.Status}}> +<summary><b id="{{.Name}}">{{template "marker" .Log.Status}} {{.Name}}</b></summary> +{{template "log" .Log}} +</details> +{{end}} + +</details> +{{end}} + +<h2 id="bootstraps">Bootstraps</h2> + +{{range .Bootstraps}} +<details> +<summary><b>Bootstrap {{.Version}}</b></summary> +{{template "log" .Log}} +</details> +{{end}} + +{{end}} diff --git a/cmd/golangorg/server.go b/cmd/golangorg/server.go index 1fb1a7c8..2a7f66b0 100644 --- a/cmd/golangorg/server.go +++ b/cmd/golangorg/server.go @@ -16,6 +16,7 @@ import ( "fmt" "go/format" "html/template" + "io" "io/fs" "io/ioutil" "log" @@ -27,6 +28,7 @@ import ( "runtime" "runtime/debug" "strings" + "sync" "sync/atomic" "time" @@ -242,6 +244,8 @@ func NewHandler(contentDir, goroot string) http.Handler { return h } +var gorebuild = NewCachedURL("https://gorebuild.storage.googleapis.com/gorebuild.json", 5*time.Minute) + // newSite creates a new site for a given content and goroot file system pair // and registers it in mux to handle requests for host. // If host is the empty string, the registrations are for the wildcard host. @@ -251,11 +255,14 @@ func newSite(mux *http.ServeMux, host string, content, goroot fs.FS) (*web.Site, site.Funcs(template.FuncMap{ "googleAnalytics": func() string { return googleAnalytics }, "googleCN": func() bool { return host == "golang.google.cn" }, + "gorebuild": gorebuild.Get, + "json": jsonUnmarshal, "newest": newest, + "now": func() time.Time { return time.Now() }, "releases": func() []*history.Major { return history.Majors }, + "rfc3339": parseRFC3339, "section": section, "version": func() string { return runtime.Version() }, - "now": func() time.Time { return time.Now() }, }) docs, err := pkgdoc.NewServer(fsys, site, googleCN) if err != nil { @@ -269,6 +276,10 @@ func newSite(mux *http.ServeMux, host string, content, goroot fs.FS) (*web.Site, return site, nil } +func parseRFC3339(s string) (time.Time, error) { + return time.Parse(time.RFC3339, s) +} + // watchTip is a background goroutine that watches the main Go repo for updates. // When a new commit is available, watchTip downloads the new tree and calls // tipGoroot.Set to install the new file system. @@ -803,3 +814,66 @@ func redirectPrefix(prefix string) http.Handler { http.Redirect(w, r, url, http.StatusMovedPermanently) }) } + +type CachedURL struct { + url string + timeout time.Duration + + mu sync.Mutex + data []byte + err error + etag string + updated time.Time +} + +func NewCachedURL(url string, timeout time.Duration) *CachedURL { + return &CachedURL{url: url, timeout: timeout} +} + +func (c *CachedURL) Get() (data []byte, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + if time.Since(c.updated) < c.timeout { + return c.data, c.err + } + defer func() { + c.updated = time.Now() + c.data, c.err = data, err + }() + + cli := &http.Client{Timeout: 60 * time.Second} + req, err := http.NewRequest("GET", c.url, nil) + if err != nil { + return nil, err + } + if c.etag != "" { + req.Header.Set("If-None-Match", c.etag) + } + resp, err := cli.Do(req) + if err != nil { + return nil, fmt.Errorf("loading rebuild report JSON: %v", err) + } + defer resp.Body.Close() + if resp.StatusCode == 206 { + // Unmodified. + log.Printf("checked %s - unmodified", c.url) + return c.data, c.err + } + log.Printf("reloading %s", c.url) + if resp.StatusCode != 200 { + return nil, fmt.Errorf("loading rebuild report JSON: %v", resp.Status) + } + c.etag = resp.Header.Get("Etag") + data, err = io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("loading rebuild report JSON: %v", err) + } + return data, nil +} + +func jsonUnmarshal(data []byte) (any, error) { + var x any + err := json.Unmarshal(data, &x) + return x, err +} |
