mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
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.
150 lines
3.8 KiB
Go
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(¬esFile),
|
|
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)
|
|
}
|
|
}
|