| // Copyright 2021 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package task implements tasks involved in making a Go release. | |
| package task | |
| import ( | |
| "bytes" | |
| "context" | |
| "sync" | |
| "time" | |
| wf "golang.org/x/build/internal/workflow" | |
| ) | |
| // Published holds information for a Go release | |
| // that by this time has already been published. | |
| // | |
| // Published in this context refers to it being | |
| // available for download at https://go.dev/dl/. | |
| // It doesn't mean it has been announced by now; | |
| // that step happens sometime after publication. | |
| type Published struct { | |
| Version string // Version that's published, in the same format as Go tags. For example, "go1.21rc1". | |
| Files []WebsiteFile // Files that are published. | |
| } | |
| // CommunicationTasks combines communication tasks together. | |
| type CommunicationTasks struct { | |
| SecurityCommunicationTasks | |
| AnnounceMailTasks | |
| SocialMediaTasks | |
| } | |
| var AwaitDivisor int = 1 | |
| // AwaitCondition calls the condition function every period until it returns | |
| // true to indicate success, or an error. If the condition succeeds, | |
| // AwaitCondition returns its result. | |
| func AwaitCondition[T any](ctx *wf.TaskContext, period time.Duration, condition func() (T, bool, error)) (T, error) { | |
| pollTimer := time.NewTicker(period / time.Duration(AwaitDivisor)) | |
| defer pollTimer.Stop() | |
| for { | |
| res, done, err := condition() | |
| if done || err != nil { | |
| return res, err | |
| } | |
| select { | |
| case <-ctx.Done(): | |
| var zero T | |
| return zero, ctx.Err() | |
| case <-pollTimer.C: | |
| ctx.ResetWatchdog() | |
| } | |
| } | |
| } | |
| // LogWriter is an io.Writer that writes to a workflow task's log, flushing | |
| // its buffer periodically to avoid too many writes. | |
| type LogWriter struct { | |
| Logger wf.Logger | |
| flushTicker *time.Ticker | |
| mu sync.Mutex | |
| buf []byte | |
| } | |
| func (w *LogWriter) Write(b []byte) (int, error) { | |
| w.mu.Lock() | |
| defer w.mu.Unlock() | |
| w.buf = append(w.buf, b...) | |
| if len(w.buf) > 1<<20 { | |
| w.flushLocked(false) | |
| w.flushTicker.Reset(10 * time.Second) | |
| } | |
| return len(b), nil | |
| } | |
| func (w *LogWriter) flush(force bool) { | |
| w.mu.Lock() | |
| defer w.mu.Unlock() | |
| w.flushLocked(force) | |
| } | |
| func (w *LogWriter) flushLocked(force bool) { | |
| if len(w.buf) == 0 { | |
| return | |
| } | |
| log, rest := w.buf, []byte(nil) | |
| if !force { | |
| nl := bytes.LastIndexByte(w.buf, '\n') | |
| if nl == -1 { | |
| return | |
| } | |
| log, rest = w.buf[:nl], w.buf[nl+1:] | |
| } | |
| w.Logger.Printf("\n%s", string(log)) | |
| w.buf = append([]byte(nil), rest...) // don't leak | |
| } | |
| func (w *LogWriter) Run(ctx context.Context) { | |
| w.flushTicker = time.NewTicker(10 * time.Second) | |
| defer w.flushTicker.Stop() | |
| for { | |
| select { | |
| case <-w.flushTicker.C: | |
| w.flush(false) | |
| case <-ctx.Done(): | |
| w.flush(true) | |
| return | |
| } | |
| } | |
| } | |
| // WebsiteFile represents a file on the go.dev downloads page. | |
| // It should be kept in sync with the download code in x/website/internal/dl. | |
| type WebsiteFile struct { | |
| Filename string `json:"filename"` | |
| OS string `json:"os"` | |
| Arch string `json:"arch"` | |
| Version string `json:"version"` | |
| ChecksumSHA256 string `json:"sha256"` | |
| Size int64 `json:"size"` | |
| Kind string `json:"kind"` // "archive", "installer", "source" | |
| } | |
| func (f WebsiteFile) GOARCH() string { | |
| if f.OS == "linux" && f.Arch == "armv6l" { | |
| return "arm" | |
| } | |
| return f.Arch | |
| } | |
| type WebsiteRelease struct { | |
| Version string `json:"version"` | |
| Stable bool `json:"stable"` | |
| Files []WebsiteFile `json:"files"` | |
| Visible bool `json:"-"` // show files on page load | |
| SplitPortTable bool `json:"-"` // whether files should be split by primary/other ports. | |
| } |