Skip to content

Commit

Permalink
yank command
Browse files Browse the repository at this point in the history
  • Loading branch information
NiclasvanEyk committed Jun 16, 2023
1 parent 048a638 commit b42c9ad
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,15 @@ Turns the `[Unreleased]` section into a proper versioned release.
You may pass a version adhering to SemVer or use the `--major`, `--minor`, `--patch` flags.
Note that there must be prior releases in order to use these!
By default the current date is used as the realease date, but you may override this using the `--date` option.

### `changelog yank <version>`

Marks the specified released as yanked`. To cite [Keep a Changelog](https://keepachangelog.com/en/1.1.0/#yanked):

> Yanked releases are versions that had to be pulled because of a serious bug or security issue. Often these versions don't even appear in change logs. They should. This is how you should display them:
>
> ```markdown
> ## [0.0.5] - 2014-12-13 [YANKED]
> ```
>
> The [YANKED] tag is loud for a reason. It's important for people to notice it. Since it's surrounded by brackets it's also easier to parse programmatically.
55 changes: 55 additions & 0 deletions cmd/yank.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"

clog "github.com/niclasvaneyk/keepac/internal/changelog"
)

// 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,
RunE: func(cmd *cobra.Command, args []string) error {
changelog, changelogPath, err := clog.ResolveChangelog()
if err != nil {
return err
}

target := args[0]

newSource, err := changelog.Yank(target)
if err != nil {
return err
}

err = os.WriteFile(changelogPath, []byte(newSource), 0774)
if err != nil {
return err
}

fmt.Printf("Marked '%s' as yanked!\n", target)

return nil
},
}

func init() {
rootCmd.AddCommand(yankCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// yankCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// yankCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
29 changes: 29 additions & 0 deletions internal/changelog/completions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package changelog

import (
"strings"

"github.com/spf13/cobra"
)

// 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) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}

changelog, _, err := ResolveChangelog()
if err != nil {
return nil, cobra.ShellCompDirectiveNoFileComp
}

matchingVersions := make([]string, 0)
for _, release := range changelog.Releases.Past {
if strings.HasPrefix(release.Version, toComplete) {
matchingVersions = append(matchingVersions, release.Version)
}
}

return matchingVersions, cobra.ShellCompDirectiveNoFileComp
}
6 changes: 6 additions & 0 deletions internal/changelog/inserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,9 @@ func (changelog *Changelog) ReplacedWithinBounds(bounds Bounds, replacement stri

return source[:bounds.Start] + replacement + source[bounds.Stop:]
}

func (changelog *Changelog) WithAddition(insertionPoint int, addition string) string {
source := changelog.source

return source[:insertionPoint] + addition + source[insertionPoint:]
}
12 changes: 12 additions & 0 deletions internal/changelog/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package changelog
import (
"math"
"regexp"
"strings"

"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
Expand Down Expand Up @@ -205,6 +206,7 @@ func Parse(source []byte) Changelog {

line := string(heading.Text(source))
r := NewRelease(parseVersion(line), parseDate(line))
r.Yanked = strings.Contains(line, "[YANKED]")
r.HeadlineBounds = headingBounds
r.Bounds = Bounds{
// we subtract the length of "## " to achieve better insertion points
Expand Down Expand Up @@ -309,3 +311,13 @@ func parseDate(line string) string {

return ""
}

func (changelog *Changelog) FindRelease(version string) *Release {
for _, release := range changelog.Releases.Past {
if release.Version == version {
return &release
}
}

return nil
}
16 changes: 16 additions & 0 deletions internal/changelog/yank.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package changelog

import "fmt"

func (changelog *Changelog) Yank(version string) (string, error) {
release := changelog.FindRelease(version)
if release == nil {
return "", fmt.Errorf("Release '%s' not found", version)
}

if release.Yanked {
return "", fmt.Errorf("Release '%s' was already yanked", version)
}

return changelog.WithAddition(release.HeadlineBounds.Stop, " [YANKED]"), nil
}

0 comments on commit b42c9ad

Please sign in to comment.