From f4d6e3a56d2c15b0c86af18e8d16bebbeb92a8ab Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 3 Jul 2021 00:11:59 +0100 Subject: [PATCH] feat(builds): add support for stable builds Stable builds are based off of release git tags in Emacs' git repo. Examples of what release tags look like: - emacs-26.1 - emacs-26.2 - emacs-26.3 - emacs-27.1 - emacs-27.2 When the specified git ref looks like a stable release, the plan command will generate a release a different and simpler release name that does not include the date, git sha or ref. Instead, for "emacs-27.2" for example, the emacs-builds release name will be "Emacs-27.2". The "build name", used for naming the disk image, still retains the same format as the nightly builds. Also, non-stable releases are now marked as pre-release on GitHub by default. The reason for the different release name format for stable builds is both to separate them, but also to make it easier to keep the version of the homebrew cask as simply "27.2". --- pkg/cask/update.go | 10 ++- pkg/plan/create.go | 24 +++++-- pkg/release/version.go | 42 +++++++++++ pkg/release/version_test.go | 138 ++++++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 pkg/release/version.go create mode 100644 pkg/release/version_test.go diff --git a/pkg/cask/update.go b/pkg/cask/update.go index 1263ef1..4590fa7 100644 --- a/pkg/cask/update.go +++ b/pkg/cask/update.go @@ -19,6 +19,7 @@ import ( "github.com/hexops/gotextdiff/myers" "github.com/hexops/gotextdiff/span" "github.com/jimeh/build-emacs-for-macos/pkg/gh" + "github.com/jimeh/build-emacs-for-macos/pkg/release" "github.com/jimeh/build-emacs-for-macos/pkg/repository" ) @@ -363,9 +364,14 @@ func (s *Updater) renderCask( ctx context.Context, chk *LiveCheck, ) ([]byte, error) { - releaseName := "Emacs." + chk.Version.Latest + releaseName, err := release.VersionToName(chk.Version.Latest) + if err != nil { + return nil, err + } - s.logger.Info("fetching release details", "release", releaseName) + s.logger.Info("fetching release details", + "release", releaseName, "repo", s.BuildsRepo.URL(), + ) release, resp, err := s.gh.Repositories.GetReleaseByTag( ctx, s.BuildsRepo.Owner(), s.BuildsRepo.Name(), releaseName, ) diff --git a/pkg/plan/create.go b/pkg/plan/create.go index fc0eecc..47f0d43 100644 --- a/pkg/plan/create.go +++ b/pkg/plan/create.go @@ -11,6 +11,7 @@ import ( "github.com/jimeh/build-emacs-for-macos/pkg/commit" "github.com/jimeh/build-emacs-for-macos/pkg/gh" "github.com/jimeh/build-emacs-for-macos/pkg/osinfo" + "github.com/jimeh/build-emacs-for-macos/pkg/release" "github.com/jimeh/build-emacs-for-macos/pkg/repository" ) @@ -64,15 +65,17 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { return nil, err } - releaseName := fmt.Sprintf( - "Emacs.%s.%s.%s", + version := fmt.Sprintf( + "%s.%s.%s", commitInfo.DateString(), commitInfo.ShortSHA(), sanitizeString(opts.Ref), ) + + releaseName := fmt.Sprintf("Emacs.%s", version) buildName := fmt.Sprintf( - "%s.%s.%s", - releaseName, + "Emacs.%s.%s.%s", + version, sanitizeString(osInfo.Name+"-"+osInfo.MajorMinor()), sanitizeString(osInfo.Arch), ) @@ -92,7 +95,8 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { }, OS: osInfo, Release: &Release{ - Name: releaseName, + Name: releaseName, + Prerelease: true, }, Output: &Output{ Directory: opts.OutputDir, @@ -100,6 +104,16 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { }, } + // If given git ref is a stable release tag (emacs-23.2b, emacs-27.2, etc.) + // we modify release properties accordingly. + if v, err := release.GitRefToStableVersion(opts.Ref); err == nil { + plan.Release.Prerelease = false + plan.Release.Name, err = release.VersionToName(v) + if err != nil { + return nil, err + } + } + if opts.TestBuild != "" { testName := sanitizeString(opts.TestBuild) diff --git a/pkg/release/version.go b/pkg/release/version.go new file mode 100644 index 0000000..2e38b21 --- /dev/null +++ b/pkg/release/version.go @@ -0,0 +1,42 @@ +package release + +import ( + "errors" + "fmt" + "regexp" +) + +// Errors +var ( + Err = errors.New("release") + ErrInvalidName = fmt.Errorf("%w: invalid name", Err) + ErrEmptyVersion = fmt.Errorf("%w: empty version", Err) + ErrNotStableRef = fmt.Errorf( + "%w: git ref is not stable tagged release", Err, + ) +) + +var ( + stableVersion = regexp.MustCompile(`^\d+\.\d+(?:[a-z]+)?$`) + stableGetRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`) +) + +func VersionToName(version string) (string, error) { + if version == "" { + return "", ErrEmptyVersion + } + + if stableVersion.MatchString(version) { + return "Emacs-" + version, nil + } + + return "Emacs." + version, nil +} + +func GitRefToStableVersion(ref string) (string, error) { + if m := stableGetRef.FindStringSubmatch(ref); len(m) > 1 { + return m[1], nil + } + + return "", fmt.Errorf("%w: \"%s\"", ErrNotStableRef, ref) +} diff --git a/pkg/release/version_test.go b/pkg/release/version_test.go new file mode 100644 index 0000000..fdcfb18 --- /dev/null +++ b/pkg/release/version_test.go @@ -0,0 +1,138 @@ +package release + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestVersionToName(t *testing.T) { + type args struct { + version string + } + tests := []struct { + name string + args args + want string + wantErr string + }{ + { + name: "empty", + args: args{ + version: "", + }, + wantErr: "release: empty version", + }, + { + name: "nightly", + args: args{ + version: "2021-07-01.1b88404.master", + }, + want: "Emacs.2021-07-01.1b88404.master", + }, + { + name: "stable", + args: args{ + version: "27.2", + }, + want: "Emacs-27.2", + }, + { + name: "stable with letter", + args: args{ + version: "23.3b", + }, + want: "Emacs-23.3b", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := VersionToName(tt.args.version) + + assert.Equal(t, tt.want, got) + + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestGitRefToStableVersion(t *testing.T) { + type args struct { + version string + } + tests := []struct { + name string + args args + want string + wantErr string + }{ + { + name: "empty", + args: args{ + version: "", + }, + wantErr: "release: git ref is not stable tagged release: \"\"", + }, + { + name: "master", + args: args{ + version: "master", + }, + wantErr: "release: git ref is not stable tagged release: " + + "\"master\"", + }, + { + name: "feature", + args: args{ + version: "feature/native-comp", + }, + wantErr: "release: git ref is not stable tagged release: " + + "\"feature/native-comp\"", + }, + { + name: "stable", + args: args{ + version: "emacs-27.2", + }, + want: "27.2", + }, + { + name: "stable with letter", + args: args{ + version: "emacs-23.3b", + }, + want: "23.3b", + }, + { + name: "future stable", + args: args{ + version: "emacs-239.33", + }, + want: "239.33", + }, + { + name: "future stable with letter", + args: args{ + version: "emacs-239.33c", + }, + want: "239.33c", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GitRefToStableVersion(tt.args.version) + + assert.Equal(t, tt.want, got) + + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +}