+ {{else if or (eq $p.Type.String "string") (eq $p.Type.String "task.Date")}}
+
{{$p.Name}}
0 && name != "announce-minor.md" {
- // The Security field isn't supported in templates other than minor,
- // so report an error instead of silently dropping it.
- //
- // Note: Maybe in the future we'd want to consider support for including sentences like
- // "This beta release includes the same security fixes as in Go X.Y.Z and Go A.B.C.",
- // but we'll have a better idea after these initial templates get more practical use.
- return mailContent{}, fmt.Errorf("email template %q doesn't support the Security field; this field can only be used in minor releases", name)
- } else if r.SecondaryVersion != "" && name != "announce-minor.md" {
- return mailContent{}, fmt.Errorf("email template %q doesn't support more than one release; the SecondaryVersion field can only be used in minor releases", name)
+ switch r := data.(type) {
+ case releaseAnnouncement:
+ if i := strings.Index(r.Version, "beta"); i != -1 { // A beta release.
+ name = "announce-beta.md"
+ } else if i := strings.Index(r.Version, "rc"); i != -1 { // Release Candidate.
+ name = "announce-rc.md"
+ } else if strings.Count(r.Version, ".") == 1 { // Major release like "go1.X".
+ name = "announce-major.md"
+ } else if strings.Count(r.Version, ".") == 2 { // Minor release like "go1.X.Y".
+ name = "announce-minor.md"
+ } else {
+ return mailContent{}, fmt.Errorf("unknown version format: %q", r.Version)
+ }
+
+ if len(r.Security) > 0 && name != "announce-minor.md" {
+ // The Security field isn't supported in templates other than minor,
+ // so report an error instead of silently dropping it.
+ //
+ // Note: Maybe in the future we'd want to consider support for including sentences like
+ // "This beta release includes the same security fixes as in Go X.Y.Z and Go A.B.C.",
+ // but we'll have a better idea after these initial templates get more practical use.
+ return mailContent{}, fmt.Errorf("email template %q doesn't support the Security field; this field can only be used in minor releases", name)
+ } else if r.SecondaryVersion != "" && name != "announce-minor.md" {
+ return mailContent{}, fmt.Errorf("email template %q doesn't support more than one release; the SecondaryVersion field can only be used in minor releases", name)
+ }
+ case releasePreAnnouncement:
+ name = "pre-announce-minor.md"
+ default:
+ return mailContent{}, fmt.Errorf("unknown template data type %T", data)
}
- // Render the announcement email template.
+ // Render the (pre-)announcement email template.
//
// It'll produce a valid message with a MIME header and a body, so parse it as such.
var buf bytes.Buffer
- if err := announceTmpl.ExecuteTemplate(&buf, name, r); err != nil {
+ if err := announceTmpl.ExecuteTemplate(&buf, name, data); err != nil {
return mailContent{}, err
}
m, err := mail.ReadMessage(&buf)
@@ -292,7 +412,7 @@ var announceTmpl = template.Must(template.New("").Funcs(template.FuncMap{
}
return "", fmt.Errorf("internal error: unhandled pre-release Go version %q", v)
},
-}).ParseFS(tmplDir, "template/announce-*.md"))
+}).ParseFS(tmplDir, "template/announce-*.md", "template/pre-announce-minor.md"))
//go:embed template
var tmplDir embed.FS
diff --git a/internal/task/announce_test.go b/internal/task/announce_test.go
index 1954bdb943..1f5cdb363c 100644
--- a/internal/task/announce_test.go
+++ b/internal/task/announce_test.go
@@ -34,11 +34,11 @@ func TestAnnounceReleaseShortContext(t *testing.T) {
func TestAnnouncementMail(t *testing.T) {
tests := [...]struct {
name string
- in releaseAnnouncement
+ in any
wantSubject string
}{
{
- name: "minor",
+ name: "announce-minor",
in: releaseAnnouncement{
Version: "go1.18.1",
SecondaryVersion: "go1.17.9",
@@ -47,7 +47,7 @@ func TestAnnouncementMail(t *testing.T) {
wantSubject: "Go 1.18.1 and Go 1.17.9 are released",
},
{
- name: "minor-with-security",
+ name: "announce-minor-with-security",
in: releaseAnnouncement{
Version: "go1.18.1",
SecondaryVersion: "go1.17.9",
@@ -80,7 +80,7 @@ This is CVE-2022-27536 and https://go.dev/issue/51759.`,
wantSubject: "[security] Go 1.18.1 and Go 1.17.9 are released",
},
{
- name: "minor-solo",
+ name: "announce-minor-solo",
in: releaseAnnouncement{
Version: "go1.11.1",
Security: []string{"abc: security fix 1", "xyz: security fix 2"},
@@ -89,26 +89,48 @@ This is CVE-2022-27536 and https://go.dev/issue/51759.`,
wantSubject: "[security] Go 1.11.1 is released",
},
{
- name: "beta",
+ name: "announce-beta",
in: releaseAnnouncement{
Version: "go1.19beta5",
},
wantSubject: "Go 1.19 Beta 5 is released",
},
{
- name: "rc",
+ name: "announce-rc",
in: releaseAnnouncement{
Version: "go1.19rc6",
},
wantSubject: "Go 1.19 Release Candidate 6 is released",
},
{
- name: "major",
+ name: "announce-major",
in: releaseAnnouncement{
Version: "go1.19",
},
wantSubject: "Go 1.19 is released",
},
+
+ {
+ name: "pre-announce-minor",
+ in: releasePreAnnouncement{
+ Target: Date{2022, time.July, 12},
+ Version: "go1.18.4",
+ SecondaryVersion: "go1.17.12",
+ Security: "the standard library",
+ Names: []string{"Alice"},
+ },
+ wantSubject: "[security] Go 1.18.4 and Go 1.17.12 pre-announcement",
+ },
+ {
+ name: "pre-announce-minor-solo",
+ in: releasePreAnnouncement{
+ Target: Date{2022, time.July, 12},
+ Version: "go1.18.4",
+ Security: "the toolchain",
+ Names: []string{"Alice", "Bob"},
+ },
+ wantSubject: "[security] Go 1.18.4 pre-announcement",
+ },
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
@@ -117,17 +139,17 @@ This is CVE-2022-27536 and https://go.dev/issue/51759.`,
t.Fatal("announcementMail returned non-nil error:", err)
}
if *updateFlag {
- writeTestdataFile(t, "announce-"+tc.name+".html", []byte(m.BodyHTML))
- writeTestdataFile(t, "announce-"+tc.name+".txt", []byte(m.BodyText))
+ writeTestdataFile(t, tc.name+".html", []byte(m.BodyHTML))
+ writeTestdataFile(t, tc.name+".txt", []byte(m.BodyText))
return
}
if diff := cmp.Diff(tc.wantSubject, m.Subject); diff != "" {
t.Errorf("subject mismatch (-want +got):\n%s", diff)
}
- if diff := cmp.Diff(testdataFile(t, "announce-"+tc.name+".html"), m.BodyHTML); diff != "" {
+ if diff := cmp.Diff(testdataFile(t, tc.name+".html"), m.BodyHTML); diff != "" {
t.Errorf("body HTML mismatch (-want +got):\n%s", diff)
}
- if diff := cmp.Diff(testdataFile(t, "announce-"+tc.name+".txt"), m.BodyText); diff != "" {
+ if diff := cmp.Diff(testdataFile(t, tc.name+".txt"), m.BodyText); diff != "" {
t.Errorf("body text mismatch (-want +got):\n%s", diff)
}
if t.Failed() {
@@ -245,6 +267,73 @@ Heschi and Dmitri for the Go team` + "\n",
}
}
+func TestPreAnnounceRelease(t *testing.T) {
+ if testing.Short() {
+ t.Skip("not running test that uses internet in short mode")
+ }
+
+ tests := [...]struct {
+ name string
+ versions []string
+ target Date
+ security string
+ coordinators []string
+ want SentMail
+ wantLog string
+ }{
+ {
+ name: "minor",
+ versions: []string{"go1.18.4", "go1.17.11"}, // Intentionally not 1.17.12 so the real email doesn't get in the way.
+ target: Date{2022, time.July, 12},
+ security: "the standard library",
+ coordinators: []string{"tatiana"},
+ want: SentMail{Subject: "[security] Go 1.18.4 and Go 1.17.11 pre-announcement"},
+ wantLog: `pre-announcement subject: [security] Go 1.18.4 and Go 1.17.11 pre-announcement
+
+pre-announcement body HTML:
+
Hello gophers,
+
We plan to issue Go 1.18.4 and Go 1.17.11 on Tuesday, July 12.
+
These minor releases include PRIVATE security fixes to the standard library.
+
Following our security policy, this is the pre-announcement of those releases.
+
Thanks,
+Tatiana for the Go team
+
+pre-announcement body text:
+Hello gophers,
+
+We plan to issue Go 1.18.4 and Go 1.17.11 on Tuesday, July 12.
+
+These minor releases include PRIVATE security fixes to the standard library.
+
+Following our security policy, this is the pre-announcement of those releases.
+
+Thanks,
+Tatiana for the Go team` + "\n",
+ },
+ // TestAnnouncementMail has additional coverage.
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ tasks := AnnounceMailTasks{
+ SendMail: func(h MailHeader, c mailContent) error { return nil },
+ testHookNow: func() time.Time { return time.Date(2022, time.July, 7, 0, 0, 0, 0, time.UTC) },
+ }
+ var buf bytes.Buffer
+ ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}}
+ sentMail, err := tasks.PreAnnounceRelease(ctx, tc.versions, tc.target, tc.security, tc.coordinators)
+ if err != nil {
+ t.Fatal("task function returned non-nil error:", err)
+ }
+ if diff := cmp.Diff(tc.want, sentMail); diff != "" {
+ t.Errorf("sent mail mismatch (-want +got):\n%s", diff)
+ }
+ if diff := cmp.Diff(tc.wantLog, buf.String()); diff != "" {
+ t.Errorf("log mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
func TestFindGoogleGroupsThread(t *testing.T) {
if testing.Short() {
t.Skip("not running test that uses internet in short mode")
diff --git a/internal/task/template/pre-announce-minor.md b/internal/task/template/pre-announce-minor.md
new file mode 100644
index 0000000000..79bd071608
--- /dev/null
+++ b/internal/task/template/pre-announce-minor.md
@@ -0,0 +1,15 @@
+Subject: [security] Go {{short .Version}}{{with .SecondaryVersion}} and Go {{. | short}}{{end}} pre-announcement
+
+Hello gophers,
+
+We plan to issue Go {{short .Version}}{{with .SecondaryVersion}} and Go {{. | short}}{{end}} on {{.Target.Format "Monday, January 2"}}.
+
+{{if .SecondaryVersion -}}
+These minor releases include
+{{- else -}}
+This minor release includes{{end}} PRIVATE security fixes to {{.Security}}.
+
+Following our security policy, this is the pre-announcement of {{if .SecondaryVersion}}those releases{{else}}the release{{end}}.
+
+Thanks,
+{{with .Names}}{{join .}} for the{{else}}The{{end}} Go team
diff --git a/internal/task/testdata/pre-announce-minor-solo.html b/internal/task/testdata/pre-announce-minor-solo.html
new file mode 100644
index 0000000000..209757f812
--- /dev/null
+++ b/internal/task/testdata/pre-announce-minor-solo.html
@@ -0,0 +1,6 @@
+
Hello gophers,
+
We plan to issue Go 1.18.4 on Tuesday, July 12.
+
This minor release includes PRIVATE security fixes to the toolchain.
+
Following our security policy, this is the pre-announcement of the release.
+
Thanks,
+Alice and Bob for the Go team
diff --git a/internal/task/testdata/pre-announce-minor-solo.txt b/internal/task/testdata/pre-announce-minor-solo.txt
new file mode 100644
index 0000000000..2a06bb4988
--- /dev/null
+++ b/internal/task/testdata/pre-announce-minor-solo.txt
@@ -0,0 +1,10 @@
+Hello gophers,
+
+We plan to issue Go 1.18.4 on Tuesday, July 12.
+
+This minor release includes PRIVATE security fixes to the toolchain.
+
+Following our security policy, this is the pre-announcement of the release.
+
+Thanks,
+Alice and Bob for the Go team
diff --git a/internal/task/testdata/pre-announce-minor.html b/internal/task/testdata/pre-announce-minor.html
new file mode 100644
index 0000000000..2a6a949916
--- /dev/null
+++ b/internal/task/testdata/pre-announce-minor.html
@@ -0,0 +1,6 @@
+
Hello gophers,
+
We plan to issue Go 1.18.4 and Go 1.17.12 on Tuesday, July 12.
+
These minor releases include PRIVATE security fixes to the standard library.
+
Following our security policy, this is the pre-announcement of those releases.
+
Thanks,
+Alice for the Go team
diff --git a/internal/task/testdata/pre-announce-minor.txt b/internal/task/testdata/pre-announce-minor.txt
new file mode 100644
index 0000000000..ef760f49d6
--- /dev/null
+++ b/internal/task/testdata/pre-announce-minor.txt
@@ -0,0 +1,10 @@
+Hello gophers,
+
+We plan to issue Go 1.18.4 and Go 1.17.12 on Tuesday, July 12.
+
+These minor releases include PRIVATE security fixes to the standard library.
+
+Following our security policy, this is the pre-announcement of those releases.
+
+Thanks,
+Alice for the Go team
diff --git a/internal/task/version.go b/internal/task/version.go
index e1a17343ec..a6101a54ce 100644
--- a/internal/task/version.go
+++ b/internal/task/version.go
@@ -42,6 +42,19 @@ func (t *VersionTasks) tagInfo(ctx context.Context) (tags map[string]bool, curre
return tags, currentMajor, nil
}
+// GetNextVersions returns the next for each of the given types of release.
+func (t *VersionTasks) GetNextVersions(ctx context.Context, kinds []ReleaseKind) ([]string, error) {
+ var next []string
+ for _, k := range kinds {
+ n, err := t.GetNextVersion(ctx, k)
+ if err != nil {
+ return nil, err
+ }
+ next = append(next, n)
+ }
+ return next, nil
+}
+
// GetNextVersion returns the next for the given type of release.
func (t *VersionTasks) GetNextVersion(ctx context.Context, kind ReleaseKind) (string, error) {
tags, currentMajor, err := t.tagInfo(ctx)
diff --git a/internal/workflow/workflow.go b/internal/workflow/workflow.go
index fa914ce67b..c68e147a24 100644
--- a/internal/workflow/workflow.go
+++ b/internal/workflow/workflow.go
@@ -125,6 +125,7 @@ type MetaParameter interface {
Type() reflect.Type
HTMLElement() string
HTMLInputType() string
+ HTMLSelectOptions() []string
Doc() string
Example() string
}
@@ -145,12 +146,13 @@ type parameter[T any] struct {
d ParamDef[T]
}
-func (p parameter[T]) Name() string { return p.d.Name }
-func (p parameter[T]) Type() reflect.Type { return p.typ() }
-func (p parameter[T]) HTMLElement() string { return p.d.HTMLElement }
-func (p parameter[T]) HTMLInputType() string { return p.d.HTMLInputType }
-func (p parameter[T]) Doc() string { return p.d.Doc }
-func (p parameter[T]) Example() string { return p.d.Example }
+func (p parameter[T]) Name() string { return p.d.Name }
+func (p parameter[T]) Type() reflect.Type { return p.typ() }
+func (p parameter[T]) HTMLElement() string { return p.d.HTMLElement }
+func (p parameter[T]) HTMLInputType() string { return p.d.HTMLInputType }
+func (p parameter[T]) HTMLSelectOptions() []string { return p.d.HTMLSelectOptions }
+func (p parameter[T]) Doc() string { return p.d.Doc }
+func (p parameter[T]) Example() string { return p.d.Example }
func (p parameter[T]) RequireNonZero() bool {
return !strings.HasSuffix(p.d.Name, " (optional)")
}
@@ -169,12 +171,15 @@ func (p parameter[T]) dependencies() []*taskDefinition { return nil }
// there are some HTML-related knobs available.
type ParamType[T any] struct {
// HTMLElement configures the HTML element for entering the parameter value.
- // Supported values are "input" and "textarea".
+ // Supported values are "input", "textarea" and "select".
HTMLElement string
// HTMLInputType optionally configures the
type attribute when HTMLElement is "input".
// If this attribute is not specified,
elements default to type="text".
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types.
HTMLInputType string
+ // HTMLSelectOptions configures the available options when HTMLElement is "select".
+ // See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option.
+ HTMLSelectOptions []string
}
var (