golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/releaseschedule/main.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Releaseschedule generates the release schedule diagram used
     6  // on the release schedule wiki.
     7  //
     8  // When this program is updated, regenerate the SVG and replace the old version
     9  // on the Go Release Cycle wiki page
    10  //
    11  // https://go.dev/s/release
    12  // https://golang.org/wiki/Go-Release-Cycle
    13  package main
    14  
    15  import (
    16  	"fmt"
    17  	"math"
    18  	"os"
    19  	"strings"
    20  
    21  	svg "github.com/ajstarks/svgo"
    22  )
    23  
    24  func main() {
    25  	if err := doMain(); err != nil {
    26  		panic(err)
    27  	}
    28  }
    29  
    30  func doMain() error {
    31  	f, err := os.OpenFile("release.svg", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    32  	if err != nil {
    33  		return err
    34  	}
    35  	defer f.Close()
    36  	canvas := svg.New(f)
    37  	canvas.Start(600, 400)
    38  	canvas.Style("text/css",
    39  		`text {
    40  	fill: black;
    41  }
    42  line, path {
    43  	stroke: black;
    44  	fill: none;
    45  }
    46  @media (prefers-color-scheme: dark) {
    47  	text {
    48  		fill: white;
    49  	}
    50  	line, path {
    51  		stroke: white;
    52  	}
    53  }`)
    54  	canvas.Translate(300, 200)
    55  	for i, month := range strings.Split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", " ") {
    56  		angle := func(midx int) float64 {
    57  			return (float64(midx) - 3) * 2 * math.Pi / 12
    58  		}
    59  		begin, end := angle(i), angle(i+1)
    60  
    61  		// Draw a single black wedge of the calendar.
    62  		path := fmt.Sprintf("M 0,0 L %v,%v A 100,100 0 0 0 %v,%v L 0 0",
    63  			100*math.Sin(begin), 100*math.Cos(begin), 100*math.Sin(end), 100*math.Cos(end))
    64  		canvas.Path(path)
    65  
    66  		// Draw the text. Spin it around for readability in the second half.
    67  		canvas.RotateTranslate(50, 0, angle(i)*360/(2*math.Pi)+20)
    68  		if i < 6 {
    69  			canvas.Text(0, 0, month)
    70  		} else {
    71  			canvas.Group(`transform="rotate(180)"`, `transform-origin="13 -3"`)
    72  			canvas.Text(0, 0, month)
    73  			canvas.Gend()
    74  		}
    75  		canvas.Gend()
    76  	}
    77  
    78  	type milestone struct {
    79  		month, week int
    80  		name        string
    81  	}
    82  	milestones := []milestone{
    83  		{1, 1, "Planning"},
    84  		{1, 3, "General development"},
    85  		{5, 4, "Freeze"},
    86  		{6, 2, "First RC"},
    87  		{8, 2, "Release"},
    88  	}
    89  	for relIdx, relName := range []string{"Summer", "Winter"} {
    90  		angle := func(m milestone) float64 {
    91  			return (float64(m.month-1) - 3 + (float64(m.week)-0.5)/4) * 2 * math.Pi / 12
    92  		}
    93  
    94  		// Shift the milestones 6 months for the winter release.
    95  		milestones := milestones
    96  		for i := range milestones {
    97  			milestones[i].month = (milestones[i].month + 6*relIdx) % 12
    98  		}
    99  
   100  		frozen := false
   101  		for i, m := range milestones {
   102  			x, y := math.Cos(angle(m)), math.Sin(angle(m))
   103  			// Align the text away from the center of the circle.
   104  			textAnchor := "start"
   105  			if x < 0 {
   106  				textAnchor = "end"
   107  			}
   108  			// Color the arc depending on the freeze state.
   109  			if m.name == "Freeze" {
   110  				frozen = true
   111  			}
   112  			color := "green"
   113  			if frozen {
   114  				color = "blue"
   115  			}
   116  
   117  			// Center radius of the release arc.
   118  			arcRadius := float64(120 + 20*relIdx)
   119  			// Length of the line to the label. Vary a bit to avoid text overlap.
   120  			lineLength := float64(30 + 5*((i+1)%2))
   121  			// Distance from the end of the line to the text.
   122  			textoff := float64(10)
   123  
   124  			// Draw the arc to the next milestone.
   125  			if i+1 < len(milestones) {
   126  				nx, ny := math.Cos(angle(milestones[i+1])), math.Sin(angle(milestones[i+1]))
   127  				canvas.Arc(int(x*arcRadius), int(y*arcRadius), int(arcRadius), int(arcRadius), 0, false, true, int(nx*arcRadius), int(ny*arcRadius), "stroke-width:10; fill:none; stroke: "+color)
   128  			}
   129  			// Draw the line from the inner edge of the arc.
   130  			canvas.Line(int(x*(arcRadius-5)), int(y*(arcRadius-5)), int(x*(arcRadius+lineLength)), int(y*(arcRadius+lineLength)))
   131  			canvas.Text(int(x*(arcRadius+lineLength+textoff)), int(y*(arcRadius+lineLength+textoff)), relName+": "+m.name, "text-anchor: "+textAnchor)
   132  		}
   133  	}
   134  	canvas.Gend()
   135  	canvas.End()
   136  	return f.Close()
   137  }