From 07ad321221590e61cd34898a7d92bccda03c5dd8 Mon Sep 17 00:00:00 2001 From: Niclas van Eyk Date: Tue, 20 Jun 2023 21:21:17 +0200 Subject: [PATCH] almost working show VERSION command shows slightly to much (2 characters at the end) --- cmd/show.go | 98 +++++++++++++++++++++++++++++-- cmd/yank.go | 33 +++++++++-- internal/changelog/completions.go | 6 +- internal/changelog/show.go | 4 ++ 4 files changed, 131 insertions(+), 10 deletions(-) diff --git a/cmd/show.go b/cmd/show.go index 4228592..4fe32e0 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -13,9 +13,33 @@ var shouldShowPlain bool // showCmd represents the show command var showCmd = &cobra.Command{ - Use: "show", + Use: "show [VERSION|latest|next|unreleased]", Short: "Displays the contents of the nearest changelog.", - Long: `Displays the contents of the nearest changelog.`, + Long: `Displays the contents of the nearest changelog. + + If a VERSION (e.g. "1.2.3") is specified, only the release notes for that given version will be shown. + Instead of a specific version you can also use one of the following aliases: + - "latest" will show the latest release + - "next" or "unreleased" will show the contents of the [Unreleased] + `, + Args: cobra.MaximumNArgs(1), + ValidArgsFunction: func( + cmd *cobra.Command, + args []string, + toComplete string, + ) ([]string, cobra.ShellCompDirective) { + matchingVersions, directive := clog.CompleteReleasesAsFirstArgument(cmd, args, toComplete) + if matchingVersions == nil { + return matchingVersions, directive + } + + // We also support 'latest' as an alias + matchingVersions = append(matchingVersions, "latest") + matchingVersions = append(matchingVersions, "next") + matchingVersions = append(matchingVersions, "unreleased") + + return matchingVersions, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { path, err := clog.ResolvePathToChangelog() if err != nil { @@ -27,12 +51,30 @@ var showCmd = &cobra.Command{ return err } + if len(args) == 0 { + if shouldShowPlain { + fmt.Print(string(source)) + return nil + } + + return clog.Show(string(source)) + } + + changelog := clog.Parse(source) + // TODO: These bounds are always off by two (too much) at the Stop + bounds, err := findReleaseBounds(args[0], &changelog) + if err != nil { + return err + } + + contents := changelog.ContentWithin(bounds) + if shouldShowPlain { - fmt.Print(string(source)) + fmt.Print(contents) return nil } - return clog.Show(string(source)) + return clog.Show(contents) }, } @@ -51,3 +93,51 @@ func init() { // is called directly, e.g.: // showCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } + +func isAlias(alias string) bool { + versionAliases := []string{ + "latest", + "next", + "unreleased", + } + for _, knownAlias := range versionAliases { + if alias == knownAlias { + return true + } + } + + return false +} + +func findReleaseBounds(versionOrAlias string, changelog *clog.Changelog) (*clog.Bounds, error) { + if isAlias(versionOrAlias) { + alias := versionOrAlias + + if alias == "latest" { + if len(changelog.Releases.Past) < 1 { + return nil, fmt.Errorf("Cannot show latest release, since there are none") + } + + return &changelog.Releases.Past[0].Bounds, nil + } + + if alias == "next" || alias == "unreleased" { + nextRelease := changelog.Releases.Next + if nextRelease == nil { + return nil, fmt.Errorf("Cannot show next release, since there is none") + } + + return &nextRelease.Bounds, nil + } + + return nil, fmt.Errorf("Unknown version or alias '%s'") + } + + version := versionOrAlias + release := changelog.FindRelease(version) + if release == nil { + return nil, fmt.Errorf("Release '%s' not found", version) + } + + return &release.Bounds, nil +} diff --git a/cmd/yank.go b/cmd/yank.go index 6312fd7..fec5f58 100644 --- a/cmd/yank.go +++ b/cmd/yank.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "strings" "github.com/spf13/cobra" @@ -11,11 +12,33 @@ import ( // yankCmd represents the yank command var yankCmd = &cobra.Command{ - Use: "yank", - Short: "Marks the specified release as yanked", - Long: `As described by https://keepachangelog.com, yanked releases are versions that had to be pulled because of a serious bug or security issue.`, - Args: cobra.ExactArgs(1), - ValidArgsFunction: clog.CompleteReleasesAsFirstArgument, + Use: "yank", + Short: "Marks the specified release as yanked", + Long: `As described by https://keepachangelog.com, yanked releases are versions that had to be pulled because of a serious bug or security issue.`, + Args: cobra.ExactArgs(1), + ValidArgsFunction: func( + cmd *cobra.Command, + args []string, + toComplete string, + ) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + changelog, _, err := clog.ResolveChangelog() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + matchingVersions := make([]string, 0) + for _, release := range changelog.Releases.Past { + if !release.Yanked && strings.HasPrefix(release.Version, toComplete) { + matchingVersions = append(matchingVersions, release.Version) + } + } + + return matchingVersions, cobra.ShellCompDirectiveNoFileComp + }, RunE: func(cmd *cobra.Command, args []string) error { changelog, changelogPath, err := clog.ResolveChangelog() if err != nil { diff --git a/internal/changelog/completions.go b/internal/changelog/completions.go index b2fbe70..5013902 100644 --- a/internal/changelog/completions.go +++ b/internal/changelog/completions.go @@ -8,7 +8,11 @@ import ( // Can be passed as ValidArgsFunction to support custom completions if the // first argument is a released version. -func CompleteReleasesAsFirstArgument(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func CompleteReleasesAsFirstArgument( + cmd *cobra.Command, + args []string, + toComplete string, +) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp } diff --git a/internal/changelog/show.go b/internal/changelog/show.go index 366d540..b64d0c1 100644 --- a/internal/changelog/show.go +++ b/internal/changelog/show.go @@ -6,6 +6,10 @@ import ( "github.com/charmbracelet/glamour" ) +func (changelog *Changelog) ContentWithin(bounds *Bounds) string { + return changelog.source[bounds.Start:bounds.Stop] +} + func Show(contents string) error { renderer, _ := glamour.NewTermRenderer( // detect background color and pick either the default dark or light theme