Skip to content

Json output #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ release-notes.txt


# End of https://www.gitignore.io/api/go
.idea/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added
- Add `--json` flag to `fmt` and `show` commands.

## [0.7.0] - 2020-07-03
### Changed
- Install git and openssh on docker image
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ Show the change log for a specific version:
changelog show 1.2.3
```

The `show` command accepts a `--json` option which formats the version as JSON.

### release

Create a new release:
Expand All @@ -143,6 +145,8 @@ Currently, the following transformations are applied:
- Version links are put at the bottom of the file
- List bullet is always `-`

The `fmt` command accepts a `--json` option which formats the changelog as JSON.

## Contributing

Feel free to fork and submit a PR. You can also take a look, at the [Issues][] tab to see some ideas.
Expand Down
31 changes: 31 additions & 0 deletions chg/change.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package chg
//go:generate stringer -type=ChangeType

import (
"encoding/json"
"fmt"
"io"
"strings"
Expand All @@ -16,6 +17,16 @@ type ChangeList struct {
Items []*Item
}

func (cl *ChangeList) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Type string `json:"type"`
Items []*Item `json:"items"`
}{
Type: ChangeStringFromType(cl.Type),
Items: cl.Items,
})
}

// ChangeType is the type of the changes
type ChangeType int

Expand All @@ -30,6 +41,26 @@ const (
Security
)

// ChangeTypeFromString creates a type based on its string name
func ChangeStringFromType(ct ChangeType) string {
switch ct {
case Added:
return "added"
case Changed:
return "changed"
case Deprecated:
return "deprecated"
case Fixed:
return "fixed"
case Removed:
return "removed"
case Security:
return "security"
default:
return "unknown"
}
}

// ChangeTypeFromString creates a type based on its string name
func ChangeTypeFromString(ct string) ChangeType {
switch strings.ToLower(ct) {
Expand Down
4 changes: 2 additions & 2 deletions chg/changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
// Changelog is the main struct that holds all the data
// in a format specific to the spec
type Changelog struct {
Preamble string
Versions []*Version
Preamble string `json:"preamble"`
Versions []*Version `json:"versions"`
}

// NewChangelog creates the Changelog struct
Expand Down
78 changes: 78 additions & 0 deletions chg/changelog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package chg

import (
"bytes"
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -115,6 +116,83 @@ func TestChangelogRelease(t *testing.T) {
})
}

func TestChangelogEncodeJson(t *testing.T) {
c := Changelog{
Preamble: "This is the preamble",
Versions: []*Version{
{
Name: "Unreleased",
Link: "http://example.com/1.0.0..HEAD",
Changes: []*ChangeList{
{
Type: Added,
Items: []*Item{
{Description: "New feature"},
},
},
},
},
{
Name: "1.0.0",
Link: "http://example.com/abcdef..1.0.0",
},
{
Name: "0.2.0",
Link: "http://example.com/abcdef..0.2.0",
},
},
}

t.Run("RenderJson", func(t *testing.T) {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetIndent("", " ")
err := enc.Encode(c)

assert.Nil(t, err)

result := string(buf.Bytes())

expectedJson := `{
"preamble": "This is the preamble",
"versions": [
{
"name": "Unreleased",
"date": "",
"link": "http://example.com/1.0.0..HEAD",
"yanked": false,
"changes": [
{
"type": "added",
"items": [
{
"description": "New feature"
}
]
}
]
},
{
"name": "1.0.0",
"date": "",
"link": "http://example.com/abcdef..1.0.0",
"yanked": false,
"changes": null
},
{
"name": "0.2.0",
"date": "",
"link": "http://example.com/abcdef..0.2.0",
"yanked": false,
"changes": null
}
]
}
`
assert.Equal(t, expectedJson, result)
})
}

func TestChangelogReleaseMinimal(t *testing.T) {
c := Changelog{
Versions: []*Version{
Expand Down
4 changes: 2 additions & 2 deletions chg/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (

// Item holds the change itself
type Item struct {
Description string
Description string `json:"description"`
}

// Render rendes the change as a list item
// Render renders the change as a list item
func (i *Item) Render(w io.Writer) {
io.WriteString(w, fmt.Sprintf("- %s\n", i.Description))
}
10 changes: 5 additions & 5 deletions chg/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
// Version stores information about the version being defined and
// its sections
type Version struct {
Name string
Date string // Date in the format YYYY-MM-DD
Link string
Yanked bool // True if the release was yanked/removed
Changes []*ChangeList
Name string `json:"name"`
Date string `json:"date"` // Date in the format YYYY-MM-DD
Link string `json:"link"`
Yanked bool `json:"yanked"` // True if the release was yanked/removed
Changes []*ChangeList `json:"changes"`
}

// Change returns the Change with name
Expand Down
19 changes: 16 additions & 3 deletions cmd/fmt.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
package cmd

import (
"encoding/json"
"github.com/rcmachado/changelog/parser"
"github.com/spf13/cobra"
)

func newFmtCmd(iostreams *IOStreams) *cobra.Command {
return &cobra.Command{
var jsonFlag bool

command := &cobra.Command{
Use: "fmt",
Short: "Reformat the change log file",
Long: "Reformats changelog input following keepachangelog.com spec",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
changelog := parser.Parse(iostreams.In)
changelog.Render(iostreams.Out)
if jsonFlag {
enc := json.NewEncoder(iostreams.Out)
enc.SetIndent("", " ")
return enc.Encode(changelog)
} else {
changelog.Render(iostreams.Out)
return nil
}
},
}
command.Flags().BoolVar(&jsonFlag, "json", false, "output JSON")

return command
}
80 changes: 80 additions & 0 deletions cmd/fmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,83 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
assert.Nil(t, err)
assert.Equal(t, expected, string(out.Bytes()))
}

func TestFmtCmdJson(t *testing.T) {
changelog := `# Changelog
Hello
## [Unreleased]
### Changed
- Out of order entries

### Added
- Something else

## [0.1.0] - 2018-06-17

### Added

- Command A

[Unreleased]: https://github.com/rcmachado/changelog/compare/0.2.0...HEAD
[0.1.0]: https://github.com/rcmachado/changelog/compare/ae761ff...0.1.0`

expected := `{
"preamble": "Hello",
"versions": [
{
"name": "Unreleased",
"date": "",
"link": "https://github.com/rcmachado/changelog/compare/0.2.0...HEAD",
"yanked": false,
"changes": [
{
"type": "changed",
"items": [
{
"description": "Out of order entries"
}
]
},
{
"type": "added",
"items": [
{
"description": "Something else"
}
]
}
]
},
{
"name": "0.1.0",
"date": "2018-06-17",
"link": "https://github.com/rcmachado/changelog/compare/ae761ff...0.1.0",
"yanked": false,
"changes": [
{
"type": "added",
"items": [
{
"description": "Command A"
}
]
}
]
}
]
}
`

out := new(bytes.Buffer)
iostreams := &IOStreams{
In: strings.NewReader(changelog),
Out: out,
}

fmt := newFmtCmd(iostreams)
fmt.SetArgs([]string{"--json"})
_, err := fmt.ExecuteC()

assert.Nil(t, err)
assert.Equal(t, expected, string(out.Bytes()))
}
20 changes: 15 additions & 5 deletions cmd/show.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/rcmachado/changelog/parser"
"github.com/spf13/cobra"
)

func newShowCmd(iostreams *IOStreams) *cobra.Command {
return &cobra.Command{
Use: "show [version]",
var jsonFlag bool

command := &cobra.Command{
Use: "command [version]",
Short: "Show changelog for [version]",
Long: `Show changelog section and entries for version [version]`,
Args: cobra.ExactArgs(1),
Expand All @@ -22,9 +25,16 @@ func newShowCmd(iostreams *IOStreams) *cobra.Command {
cmd.SilenceUsage = true
return fmt.Errorf("Unknown version: '%s'\n", version)
}

v.RenderChanges(iostreams.Out)
return nil
if jsonFlag {
enc := json.NewEncoder(iostreams.Out)
enc.SetIndent("", " ")
return enc.Encode(v)
} else {
v.RenderChanges(iostreams.Out)
return nil
}
},
}
command.Flags().BoolVar(&jsonFlag, "json", false, "output JSON")
return command
}