blob: 93288668595e865a3e2c2529553319d7bf2bbfec [file] [log] [blame]
// 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 {
AnnounceMailTasks
TweetTasks
}
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.
}