From 62c5a0f8bcbc30d20afdcfbdfd108b65ed52977d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 17:56:24 +0100 Subject: [PATCH 1/7] Ignore .idea folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 50e1def..701fb69 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ release-notes.txt # End of https://www.gitignore.io/api/go +.idea/ From bca39a49350ef80830a5871a91c8bcb72ee4d4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 18:48:17 +0100 Subject: [PATCH 2/7] Make Changelog JSONable --- chg/change.go | 4 +-- chg/changelog.go | 4 +-- chg/changelog_test.go | 78 +++++++++++++++++++++++++++++++++++++++++++ chg/item.go | 4 +-- chg/version.go | 10 +++--- 5 files changed, 89 insertions(+), 11 deletions(-) diff --git a/chg/change.go b/chg/change.go index e9107c0..38ccf6e 100644 --- a/chg/change.go +++ b/chg/change.go @@ -12,8 +12,8 @@ import ( // Valid change types are "Added", "Changed", "Deprecated", "Fixed", // "Removed" and "Security" type ChangeList struct { - Type ChangeType - Items []*Item + Type ChangeType `json:"type"` + Items []*Item `json:"items"` } // ChangeType is the type of the changes diff --git a/chg/changelog.go b/chg/changelog.go index 8f0952f..48aa035 100644 --- a/chg/changelog.go +++ b/chg/changelog.go @@ -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 diff --git a/chg/changelog_test.go b/chg/changelog_test.go index aaca33f..c54f525 100644 --- a/chg/changelog_test.go +++ b/chg/changelog_test.go @@ -2,6 +2,7 @@ package chg import ( "bytes" + "encoding/json" "testing" "github.com/stretchr/testify/assert" @@ -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": 1, + "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{ diff --git a/chg/item.go b/chg/item.go index a6a9726..b7402e5 100644 --- a/chg/item.go +++ b/chg/item.go @@ -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)) } diff --git a/chg/version.go b/chg/version.go index 28aa2a2..35f6de2 100644 --- a/chg/version.go +++ b/chg/version.go @@ -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 From 70aaee766eda80dd2e3e96bfa82240933238c072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 19:00:00 +0100 Subject: [PATCH 3/7] Add --json flag to show command --- cmd/show.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cmd/show.go b/cmd/show.go index 79565bc..926b4d3 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "fmt" "github.com/rcmachado/changelog/parser" @@ -8,7 +9,9 @@ import ( ) func newShowCmd(iostreams *IOStreams) *cobra.Command { - return &cobra.Command{ + var jsonFlag bool + + show := &cobra.Command{ Use: "show [version]", Short: "Show changelog for [version]", Long: `Show changelog section and entries for version [version]`, @@ -22,9 +25,17 @@ func newShowCmd(iostreams *IOStreams) *cobra.Command { cmd.SilenceUsage = true return fmt.Errorf("Unknown version: '%s'\n", version) } + if jsonFlag { + enc := json.NewEncoder(iostreams.Out) + enc.SetIndent("", " ") + return enc.Encode(v) - v.RenderChanges(iostreams.Out) - return nil + } else { + v.RenderChanges(iostreams.Out) + return nil + } }, } + show.Flags().BoolVar(&jsonFlag, "json", false, "output JSON") + return show } From 9f2d2401420d3facddf8972b8288d6ac46f67002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 19:36:23 +0100 Subject: [PATCH 4/7] Add --json to fmt command --- cmd/fmt.go | 19 ++++++++++-- cmd/fmt_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ cmd/show.go | 9 +++--- 3 files changed, 100 insertions(+), 8 deletions(-) diff --git a/cmd/fmt.go b/cmd/fmt.go index 41d6b82..760e580 100644 --- a/cmd/fmt.go +++ b/cmd/fmt.go @@ -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 } diff --git a/cmd/fmt_test.go b/cmd/fmt_test.go index 525a399..3fce7de 100644 --- a/cmd/fmt_test.go +++ b/cmd/fmt_test.go @@ -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": 2, + "items": [ + { + "description": "Out of order entries" + } + ] + }, + { + "type": 1, + "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": 1, + "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())) +} diff --git a/cmd/show.go b/cmd/show.go index 926b4d3..5d16ddb 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -11,8 +11,8 @@ import ( func newShowCmd(iostreams *IOStreams) *cobra.Command { var jsonFlag bool - show := &cobra.Command{ - Use: "show [version]", + command := &cobra.Command{ + Use: "command [version]", Short: "Show changelog for [version]", Long: `Show changelog section and entries for version [version]`, Args: cobra.ExactArgs(1), @@ -29,13 +29,12 @@ func newShowCmd(iostreams *IOStreams) *cobra.Command { enc := json.NewEncoder(iostreams.Out) enc.SetIndent("", " ") return enc.Encode(v) - } else { v.RenderChanges(iostreams.Out) return nil } }, } - show.Flags().BoolVar(&jsonFlag, "json", false, "output JSON") - return show + command.Flags().BoolVar(&jsonFlag, "json", false, "output JSON") + return command } From 965e2e51cd690bfb4574c11534778fc042d7f207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 20:39:49 +0100 Subject: [PATCH 5/7] Output change type as string instead of int --- chg/change.go | 35 +++++++++++++++++++++++++++++++++-- chg/changelog_test.go | 2 +- cmd/fmt_test.go | 6 +++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/chg/change.go b/chg/change.go index 38ccf6e..1459b2c 100644 --- a/chg/change.go +++ b/chg/change.go @@ -3,6 +3,7 @@ package chg //go:generate stringer -type=ChangeType import ( + "encoding/json" "fmt" "io" "strings" @@ -12,8 +13,18 @@ import ( // Valid change types are "Added", "Changed", "Deprecated", "Fixed", // "Removed" and "Security" type ChangeList struct { - Type ChangeType `json:"type"` - Items []*Item `json:"items"` + Type ChangeType + 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 @@ -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) { diff --git a/chg/changelog_test.go b/chg/changelog_test.go index c54f525..946872c 100644 --- a/chg/changelog_test.go +++ b/chg/changelog_test.go @@ -163,7 +163,7 @@ func TestChangelogEncodeJson(t *testing.T) { "yanked": false, "changes": [ { - "type": 1, + "type": "added", "items": [ { "description": "New feature" diff --git a/cmd/fmt_test.go b/cmd/fmt_test.go index 3fce7de..66a0bc5 100644 --- a/cmd/fmt_test.go +++ b/cmd/fmt_test.go @@ -102,7 +102,7 @@ Hello "yanked": false, "changes": [ { - "type": 2, + "type": "changed", "items": [ { "description": "Out of order entries" @@ -110,7 +110,7 @@ Hello ] }, { - "type": 1, + "type": "added", "items": [ { "description": "Something else" @@ -126,7 +126,7 @@ Hello "yanked": false, "changes": [ { - "type": 1, + "type": "added", "items": [ { "description": "Command A" From 295914022bba775289e3c504dd78d1c6a37616e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Wed, 22 Sep 2021 20:54:00 +0100 Subject: [PATCH 6/7] Update docs --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c50eae6..df51aa8 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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. From e859a4e237be83d2df4d69ad949dd5c8c3f8884b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Thu, 23 Sep 2021 22:25:23 +0100 Subject: [PATCH 7/7] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29f2348..9a6fa5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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