From 743b10c751e146ec7569f39a475c20a0489955f4 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sat, 4 Dec 2021 22:50:00 +0000 Subject: [PATCH] feat(plan): add support for pretest and release candidate builds Add support for naming release and builds accordingly when given a git ref for a pretest (90 or above patch number) or release candidate ("-rcX" at the end of the tag). --- pkg/cask/update.go | 4 +- pkg/plan/create.go | 97 +++++-- pkg/plan/create_test.go | 421 ++++++++++++++++++++++++++++++ pkg/plan/plan.go | 10 +- pkg/release/channel.go | 11 + pkg/release/version.go | 4 +- pkg/repository/repository.go | 12 + pkg/repository/repository_test.go | 54 ++++ pkg/sanitize/string.go | 9 + 9 files changed, 585 insertions(+), 37 deletions(-) create mode 100644 pkg/plan/create_test.go create mode 100644 pkg/release/channel.go create mode 100644 pkg/repository/repository_test.go create mode 100644 pkg/sanitize/string.go diff --git a/pkg/cask/update.go b/pkg/cask/update.go index 4590fa7..cce5bdf 100644 --- a/pkg/cask/update.go +++ b/pkg/cask/update.go @@ -393,9 +393,7 @@ func (s *Updater) renderCask( filename := asset.GetName() s.logger.Debug("processing asset", "filename", filename) - if strings.HasSuffix(filename, ".sha256") { - filename = strings.TrimSuffix(filename, ".sha256") - } + filename = strings.TrimSuffix(filename, ".sha256") if _, ok := info.Assets[filename]; !ok { info.Assets[filename] = &ReleaseAsset{ diff --git a/pkg/plan/create.go b/pkg/plan/create.go index d9abe05..50d59c0 100644 --- a/pkg/plan/create.go +++ b/pkg/plan/create.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "regexp" + "strconv" "strings" "github.com/hashicorp/go-hclog" @@ -13,10 +14,13 @@ import ( "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" + "github.com/jimeh/build-emacs-for-macos/pkg/sanitize" "github.com/jimeh/build-emacs-for-macos/pkg/source" ) -var nonAlphaNum = regexp.MustCompile(`[^\w_-]+`) +var gitTagMatcher = regexp.MustCompile( + `^emacs(-.*)?-((\d+\.\d+)(?:\.(\d+))?(-rc\d+)?(.+)?)$`, +) type TestBuildType string @@ -37,7 +41,7 @@ type Options struct { Output io.Writer } -func Create(ctx context.Context, opts *Options) (*Plan, error) { +func Create(ctx context.Context, opts *Options) (*Plan, error) { //nolint:funlen logger := hclog.FromContext(ctx).Named("plan") repo, err := repository.NewGitHub(opts.EmacsRepo) @@ -66,19 +70,36 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { return nil, err } - version := fmt.Sprintf( + absoluteVersion := fmt.Sprintf( "%s.%s.%s", commitInfo.DateString(), commitInfo.ShortSHA(), - sanitizeString(opts.Ref), + sanitize.String(opts.Ref), ) - releaseName := fmt.Sprintf("Emacs.%s", version) + version, channel, err := parseGitRef(opts.Ref) + if err != nil { + return nil, err + } + + var releaseName string + switch channel { + case release.Stable, release.RC: + releaseName = "Emacs-" + version + case release.Pretest: + version += "-pretest" + absoluteVersion += "-pretest" + releaseName = "Emacs-" + version + default: + version = absoluteVersion + releaseName = "Emacs." + version + } + buildName := fmt.Sprintf( "Emacs.%s.%s.%s", - version, - sanitizeString(osInfo.Name+"-"+osInfo.DistinctVersion()), - sanitizeString(osInfo.Arch), + absoluteVersion, + sanitize.String(osInfo.Name+"-"+osInfo.DistinctVersion()), + sanitize.String(osInfo.Arch), ) diskImage := buildName + ".dmg" @@ -97,7 +118,8 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { OS: osInfo, Release: &Release{ Name: releaseName, - Prerelease: true, + Prerelease: channel != release.Stable, + Channel: channel, }, Output: &Output{ Directory: opts.OutputDir, @@ -105,28 +127,18 @@ 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) + testName := sanitize.String(opts.TestBuild) plan.Build.Name += ".test." + testName - plan.Release.Title = "Test Builds" + plan.Release.Title = "Test Builds (" + testName + ")" plan.Release.Name = "test-builds" - plan.Release.Prerelease = true - plan.Release.Draft = false - if opts.TestBuildType == Draft { - plan.Release.Prerelease = false - plan.Release.Draft = true + plan.Release.Prerelease = false + plan.Release.Draft = true + if opts.TestBuildType == Prerelease { + plan.Release.Prerelease = true + plan.Release.Draft = false } index := strings.LastIndex(diskImage, ".") @@ -137,6 +149,35 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { return plan, nil } -func sanitizeString(s string) string { - return nonAlphaNum.ReplaceAllString(s, "-") +func parseGitRef(ref string) (string, release.Channel, error) { + m := gitTagMatcher.FindStringSubmatch(ref) + + if len(m) == 0 { + return "", release.Nightly, nil + } + + if strings.Contains(m[1], "pretest") { + return m[2], release.Pretest, nil + } + + if m[4] != "" { + n, err := strconv.Atoi(m[4]) + if err != nil { + return "", "", err + } + + if n >= 90 { + return m[2], release.Pretest, nil + } + } + + if strings.HasPrefix(m[5], "-rc") { + return m[2], release.RC, nil + } + + if m[2] == m[3] { + return m[2], release.Stable, nil + } + + return "", "", nil } diff --git a/pkg/plan/create_test.go b/pkg/plan/create_test.go new file mode 100644 index 0000000..ff393ff --- /dev/null +++ b/pkg/plan/create_test.go @@ -0,0 +1,421 @@ +package plan + +import ( + "testing" + + "github.com/jimeh/build-emacs-for-macos/pkg/release" + "github.com/stretchr/testify/assert" +) + +func Test_parseGitRef(t *testing.T) { + t.Parallel() + + type args struct { + ref string + } + type want struct { + version string + channel release.Channel + err string + } + tests := []struct { + name string + args args + want want + }{ + { + name: "master", + args: args{ref: "master"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "emacs-28", + args: args{ref: "emacs-28"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "emacs-27", + args: args{ref: "emacs-27"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "emacs-26", + args: args{ref: "emacs-26"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "emacs-24", + args: args{ref: "emacs-24"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "feature/native-comp", + args: args{ref: "feature/native-comp"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "feature/pgtk", + args: args{ref: "feature/pgtk"}, + want: want{version: "", channel: release.Nightly, err: ""}, + }, + { + name: "emacs-19.34", + args: args{ref: "emacs-19.34"}, + want: want{version: "19.34", channel: release.Stable, err: ""}, + }, + { + name: "emacs-20.4", + args: args{ref: "emacs-20.4"}, + want: want{version: "20.4", channel: release.Stable, err: ""}, + }, + { + name: "emacs-22.3", + args: args{ref: "emacs-22.3"}, + want: want{version: "22.3", channel: release.Stable, err: ""}, + }, + { + name: "emacs-23.4", + args: args{ref: "emacs-23.4"}, + want: want{version: "23.4", channel: release.Stable, err: ""}, + }, + { + name: "emacs-24.0.97", + args: args{ref: "emacs-24.0.97"}, + want: want{version: "24.0.97", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.2", + args: args{ref: "emacs-24.2"}, + want: want{version: "24.2", channel: release.Stable, err: ""}, + }, + { + name: "emacs-24.2.90", + args: args{ref: "emacs-24.2.90"}, + want: want{version: "24.2.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.2.93", + args: args{ref: "emacs-24.2.93"}, + want: want{version: "24.2.93", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.3", + args: args{ref: "emacs-24.3"}, + want: want{version: "24.3", channel: release.Stable, err: ""}, + }, + { + name: "emacs-24.3-rc1", + args: args{ref: "emacs-24.3-rc1"}, + want: want{version: "24.3-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-24.3.90", + args: args{ref: "emacs-24.3.90"}, + want: want{version: "24.3.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.3.94", + args: args{ref: "emacs-24.3.94"}, + want: want{version: "24.3.94", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.4", + args: args{ref: "emacs-24.4"}, + want: want{version: "24.4", channel: release.Stable, err: ""}, + }, + { + name: "emacs-24.4-rc1", + args: args{ref: "emacs-24.4-rc1"}, + want: want{version: "24.4-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-24.4.90", + args: args{ref: "emacs-24.4.90"}, + want: want{version: "24.4.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.4.91", + args: args{ref: "emacs-24.4.91"}, + want: want{version: "24.4.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-24.5", + args: args{ref: "emacs-24.5"}, + want: want{version: "24.5", channel: release.Stable, err: ""}, + }, + { + name: "emacs-24.5-rc1", + args: args{ref: "emacs-24.5-rc1"}, + want: want{version: "24.5-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-24.5-rc3", + args: args{ref: "emacs-24.5-rc3"}, + want: want{version: "24.5-rc3", channel: release.RC, err: ""}, + }, + { + name: "emacs-24.5-rc3-fixed", + args: args{ref: "emacs-24.5-rc3-fixed"}, + want: want{version: "24.5-rc3-fixed", channel: release.RC, err: ""}, + }, + { + name: "emacs-25.0.90", + args: args{ref: "emacs-25.0.90"}, + want: want{version: "25.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-25.0.95", + args: args{ref: "emacs-25.0.95"}, + want: want{version: "25.0.95", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-25.1", + args: args{ref: "emacs-25.1"}, + want: want{version: "25.1", channel: release.Stable, err: ""}, + }, + { + name: "emacs-25.1-rc1", + args: args{ref: "emacs-25.1-rc1"}, + want: want{version: "25.1-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-25.1-rc2", + args: args{ref: "emacs-25.1-rc2"}, + want: want{version: "25.1-rc2", channel: release.RC, err: ""}, + }, + { + name: "emacs-25.1.90", + args: args{ref: "emacs-25.1.90"}, + want: want{version: "25.1.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-25.1.91", + args: args{ref: "emacs-25.1.91"}, + want: want{version: "25.1.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-25.2", + args: args{ref: "emacs-25.2"}, + want: want{version: "25.2", channel: release.Stable, err: ""}, + }, + { + name: "emacs-25.2-rc1", + args: args{ref: "emacs-25.2-rc1"}, + want: want{version: "25.2-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-25.2-rc2", + args: args{ref: "emacs-25.2-rc2"}, + want: want{version: "25.2-rc2", channel: release.RC, err: ""}, + }, + { + name: "emacs-26.0.90", + args: args{ref: "emacs-26.0.90"}, + want: want{version: "26.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-26.0.91", + args: args{ref: "emacs-26.0.91"}, + want: want{version: "26.0.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-26.1", + args: args{ref: "emacs-26.1"}, + want: want{version: "26.1", channel: release.Stable, err: ""}, + }, + { + name: "emacs-26.1-rc1", + args: args{ref: "emacs-26.1-rc1"}, + want: want{version: "26.1-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-26.1.90", + args: args{ref: "emacs-26.1.90"}, + want: want{version: "26.1.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-26.1.92", + args: args{ref: "emacs-26.1.92"}, + want: want{version: "26.1.92", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-26.2", + args: args{ref: "emacs-26.2"}, + want: want{version: "26.2", channel: release.Stable, err: ""}, + }, + { + name: "emacs-26.2.90", + args: args{ref: "emacs-26.2.90"}, + want: want{version: "26.2.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-26.3", + args: args{ref: "emacs-26.3"}, + want: want{version: "26.3", channel: release.Stable, err: ""}, + }, + { + name: "emacs-26.3-rc1", + args: args{ref: "emacs-26.3-rc1"}, + want: want{version: "26.3-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-27.0.90", + args: args{ref: "emacs-27.0.90"}, + want: want{version: "27.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-27.0.91", + args: args{ref: "emacs-27.0.91"}, + want: want{version: "27.0.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-27.1", + args: args{ref: "emacs-27.1"}, + want: want{version: "27.1", channel: release.Stable, err: ""}, + }, + { + name: "emacs-27.1-rc1", + args: args{ref: "emacs-27.1-rc1"}, + want: want{version: "27.1-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-27.1-rc2", + args: args{ref: "emacs-27.1-rc2"}, + want: want{version: "27.1-rc2", channel: release.RC, err: ""}, + }, + { + name: "emacs-27.1.90", + args: args{ref: "emacs-27.1.90"}, + want: want{version: "27.1.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-27.1.91", + args: args{ref: "emacs-27.1.91"}, + want: want{version: "27.1.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-27.2", + args: args{ref: "emacs-27.2"}, + want: want{version: "27.2", channel: release.Stable, err: ""}, + }, + { + name: "emacs-27.2-rc1", + args: args{ref: "emacs-27.2-rc1"}, + want: want{version: "27.2-rc1", channel: release.RC, err: ""}, + }, + { + name: "emacs-27.2-rc2", + args: args{ref: "emacs-27.2-rc2"}, + want: want{version: "27.2-rc2", channel: release.RC, err: ""}, + }, + { + name: "emacs-28.0.90", + args: args{ref: "emacs-28.0.90"}, + want: want{version: "28.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-21.0.100", + args: args{ref: "emacs-pretest-21.0.100"}, + want: want{version: "21.0.100", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-21.0.106", + args: args{ref: "emacs-pretest-21.0.106"}, + want: want{version: "21.0.106", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-21.0.90", + args: args{ref: "emacs-pretest-21.0.90"}, + want: want{version: "21.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-21.0.99", + args: args{ref: "emacs-pretest-21.0.99"}, + want: want{version: "21.0.99", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-22.0.90", + args: args{ref: "emacs-pretest-22.0.90"}, + want: want{version: "22.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-22.0.99", + args: args{ref: "emacs-pretest-22.0.99"}, + want: want{version: "22.0.99", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-22.0.990", + args: args{ref: "emacs-pretest-22.0.990"}, + want: want{version: "22.0.990", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-22.1.90", + args: args{ref: "emacs-pretest-22.1.90"}, + want: want{version: "22.1.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-22.2.90", + args: args{ref: "emacs-pretest-22.2.90"}, + want: want{version: "22.2.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.0.90", + args: args{ref: "emacs-pretest-23.0.90"}, + want: want{version: "23.0.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.1.90", + args: args{ref: "emacs-pretest-23.1.90"}, + want: want{version: "23.1.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.2.90", + args: args{ref: "emacs-pretest-23.2.90"}, + want: want{version: "23.2.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.2.91", + args: args{ref: "emacs-pretest-23.2.91"}, + want: want{version: "23.2.91", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.2.93", + args: args{ref: "emacs-pretest-23.2.93"}, + want: want{version: "23.2.93", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.2.93.1", + args: args{ref: "emacs-pretest-23.2.93.1"}, + want: want{version: "23.2.93.1", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-23.3.90", + args: args{ref: "emacs-pretest-23.3.90"}, + want: want{version: "23.3.90", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-24.0.05", + args: args{ref: "emacs-pretest-24.0.05"}, + want: want{version: "24.0.05", channel: release.Pretest, err: ""}, + }, + { + name: "emacs-pretest-24.0.90", + args: args{ref: "emacs-pretest-24.0.90"}, + want: want{version: "24.0.90", channel: release.Pretest, err: ""}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, gotChannel, err := parseGitRef(tt.args.ref) + + assert.Equal(t, tt.want.version, got) + assert.Equal(t, tt.want.channel, gotChannel) + + if tt.want.err == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.want.err) + } + }) + } +} diff --git a/pkg/plan/plan.go b/pkg/plan/plan.go index 7d119e2..f269951 100644 --- a/pkg/plan/plan.go +++ b/pkg/plan/plan.go @@ -7,6 +7,7 @@ import ( "os" "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/source" "gopkg.in/yaml.v3" ) @@ -78,10 +79,11 @@ type Build struct { } type Release struct { - Name string `yaml:"name" json:"name"` - Title string `yaml:"title,omitempty" json:"title,omitempty"` - Draft bool `yaml:"draft,omitempty" json:"draft,omitempty"` - Prerelease bool `yaml:"prerelease,omitempty" json:"prerelease,omitempty"` + Name string `yaml:"name" json:"name"` + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Draft bool `yaml:"draft,omitempty" json:"draft,omitempty"` + Prerelease bool `yaml:"prerelease,omitempty" json:"prerelease,omitempty"` + Channel release.Channel `yaml:"channel,omitempty" json:"channel,omitempty"` } type Output struct { diff --git a/pkg/release/channel.go b/pkg/release/channel.go new file mode 100644 index 0000000..8718a21 --- /dev/null +++ b/pkg/release/channel.go @@ -0,0 +1,11 @@ +package release + +type Channel string + +// Release channels +const ( + Stable Channel = "stable" + RC Channel = "release-candidate" + Pretest Channel = "pretest" + Nightly Channel = "nightly" +) diff --git a/pkg/release/version.go b/pkg/release/version.go index 2e38b21..9f257bd 100644 --- a/pkg/release/version.go +++ b/pkg/release/version.go @@ -18,7 +18,7 @@ var ( var ( stableVersion = regexp.MustCompile(`^\d+\.\d+(?:[a-z]+)?$`) - stableGetRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`) + stableGitRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`) ) func VersionToName(version string) (string, error) { @@ -34,7 +34,7 @@ func VersionToName(version string) (string, error) { } func GitRefToStableVersion(ref string) (string, error) { - if m := stableGetRef.FindStringSubmatch(ref); len(m) > 1 { + if m := stableGitRef.FindStringSubmatch(ref); len(m) > 1 { return m[1], nil } diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go index 501fa28..b5f1ff5 100644 --- a/pkg/repository/repository.go +++ b/pkg/repository/repository.go @@ -128,3 +128,15 @@ func (s *Repository) ActionRunURL(runID string) string { return "" } } + +func (s *Repository) ReleaseURL(releaseName string) string { + if releaseName == "" { + return "" + } + switch s.Type { + case GitHub: + return GitHubBaseURL + s.Source + "/releases/tag/" + releaseName + default: + return "" + } +} diff --git a/pkg/repository/repository_test.go b/pkg/repository/repository_test.go new file mode 100644 index 0000000..9d5a080 --- /dev/null +++ b/pkg/repository/repository_test.go @@ -0,0 +1,54 @@ +package repository + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRepository_ReleaseURL(t *testing.T) { + type fields struct { + Type Type + Source string + } + type args struct { + releaseName string + } + tests := []struct { + name string + fields fields + args args + want string + }{ + { + name: "empty name", + fields: fields{Type: GitHub, Source: "foo/bar"}, + args: args{releaseName: ""}, + want: "", + }, + { + name: "GitHub, foo/bar, v1.0.0", + fields: fields{Type: GitHub, Source: "foo/bar"}, + args: args{releaseName: "v1.0.0"}, + want: "https://github.com/foo/bar/releases/tag/v1.0.0", + }, + { + name: "Not GitHub, foo/bar, v1.0.0", + fields: fields{Type: Type("oops"), Source: "foo/bar"}, + args: args{releaseName: "v1.0.0"}, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + repo := &Repository{ + Type: tt.fields.Type, + Source: tt.fields.Source, + } + + got := repo.ReleaseURL(tt.args.releaseName) + + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/sanitize/string.go b/pkg/sanitize/string.go new file mode 100644 index 0000000..0b20454 --- /dev/null +++ b/pkg/sanitize/string.go @@ -0,0 +1,9 @@ +package sanitize + +import "regexp" + +var nonAlphaNum = regexp.MustCompile(`[^\w_-]+`) + +func String(s string) string { + return nonAlphaNum.ReplaceAllString(s, "-") +}