Files
coder/scripts/release-action/main.go
T
Garrett Delfosse b95697a370 ci: rewrite release workflow to be fully GitHub Actions-driven (#25162)
Replace the local interactive release CLI and legacy shell scripts with
a non-interactive Go tool (`scripts/release-action/`) and a rewritten
`release.yaml` workflow. Release managers trigger releases from the
GitHub Actions UI by selecting a branch, picking a release type (`rc`,
`release`, or `create-release-branch`), and optionally providing a
commit SHA.

The Go tool has four subcommands: `calculate-version` (computes next
version from git state), `generate-notes` (release notes from commit log
and PR metadata), `publish` (creates GitHub release with checksums), and
the workflow handles tag creation, branch creation, building, and
downstream publishing.

`scripts/version.sh` fallback now uses `git describe` (nearest ancestor
tag) instead of global latest so dev builds on release branches show the
correct version series.
2026-06-04 14:38:48 -04:00

150 lines
3.8 KiB
Go

package main
import (
"errors"
"fmt"
"os"
"golang.org/x/xerrors"
"github.com/coder/serpent"
)
const (
owner = "coder"
repo = "coder"
)
func main() {
var (
releaseType string
ref string
commitSHA string
versionStr string
prevVersionStr string
notesFile string
stable bool
)
cmd := &serpent.Command{
Use: "release-action <subcommand>",
Short: "Non-interactive, CI-oriented release tool for coder/coder.",
Children: []*serpent.Command{
{
Use: "calculate-version",
Short: "Calculate the next release version from git state.",
Options: serpent.OptionSet{
{
Name: "type",
Flag: "type",
Description: "Release type: rc, release, or create-release-branch.",
Value: serpent.StringOf(&releaseType),
Required: true,
},
{
Name: "ref",
Flag: "ref",
Description: "Git ref (branch name) the workflow is running on.",
Value: serpent.StringOf(&ref),
Required: true,
},
{
Name: "commit",
Flag: "commit",
Description: "Commit SHA to tag (defaults to HEAD of --ref if empty).",
Value: serpent.StringOf(&commitSHA),
},
},
Handler: func(inv *serpent.Invocation) error {
result, err := calculateNextVersion(releaseType, ref, commitSHA)
if err != nil {
return err
}
_, _ = fmt.Fprintln(inv.Stdout, result.String())
return nil
},
},
{
Use: "generate-notes",
Short: "Generate release notes from commit log and PR metadata.",
Options: serpent.OptionSet{
{
Name: "version",
Flag: "version",
Description: "New release version (e.g. v2.21.0).",
Value: serpent.StringOf(&versionStr),
Required: true,
},
{
Name: "previous-version",
Flag: "previous-version",
Description: "Previous release version (e.g. v2.20.0).",
Value: serpent.StringOf(&prevVersionStr),
Required: true,
},
},
Handler: func(inv *serpent.Invocation) error {
newVer, err := parseVersion(versionStr)
if err != nil {
return xerrors.Errorf("parse --version: %w", err)
}
prevVer, err := parseVersion(prevVersionStr)
if err != nil {
return xerrors.Errorf("parse --previous-version: %w", err)
}
notes, err := generateReleaseNotes(newVer, prevVer)
if err != nil {
return err
}
_, _ = fmt.Fprint(inv.Stdout, notes)
return nil
},
},
{
Use: "publish",
Short: "Publish a GitHub release with assets and checksums.",
Options: serpent.OptionSet{
{
Name: "version",
Flag: "version",
Description: "Release version tag (e.g. v2.21.0).",
Value: serpent.StringOf(&versionStr),
Required: true,
},
{
Name: "stable",
Flag: "stable",
Description: "Mark this release as the latest stable release.",
Value: serpent.BoolOf(&stable),
},
{
Name: "release-notes-file",
Flag: "release-notes-file",
Description: "Path to release notes markdown file.",
Value: serpent.StringOf(&notesFile),
Required: true,
},
},
Handler: func(inv *serpent.Invocation) error {
assets := inv.Args
if len(assets) == 0 {
return xerrors.New("no asset files provided as arguments")
}
return publishRelease(versionStr, stable, notesFile, assets)
},
},
},
}
err := cmd.Invoke().WithOS().Run()
if err != nil {
// Unwrap serpent's "running command ..." wrapper to keep output clean.
var runErr *serpent.RunCommandError
if errors.As(err, &runErr) {
err = runErr.Err
}
_, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(1)
}
}