From 3772987688097582fed054d23fc19fa0d0ddb909 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Mar 2022 18:07:24 +0200 Subject: [PATCH 1/7] Add new changelog command --- cmd/changelog.go | 211 +++++++++++++++++++++++++++++++++++++ cmd/root.go | 1 + internal/cobraext/const.go | 15 +++ 3 files changed, 227 insertions(+) create mode 100644 cmd/changelog.go diff --git a/cmd/changelog.go b/cmd/changelog.go new file mode 100644 index 0000000000..2f40106782 --- /dev/null +++ b/cmd/changelog.go @@ -0,0 +1,211 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package cmd + +import ( + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + + "github.com/elastic/elastic-package/internal/cobraext" + "github.com/elastic/elastic-package/internal/packages" + "github.com/elastic/elastic-package/internal/packages/changelog" +) + +const changelogLongDescription = `Use this command to work with the changelog of the package.` + +const changelogAddLongDescription = `Use this command to add an entry to the changelog file. + +The entry is added on top of the last entry in the current version. Or optionally in the next +major, minor or patch version, or as a new given version.` + +func setupChangelogCommand() *cobraext.Command { + addChangelogCmd := &cobra.Command{ + Use: "add", + Short: "Add an entry to the changelog file", + Long: changelogAddLongDescription, + RunE: changelogAddCmd, + } + addChangelogCmd.Flags().String(cobraext.ChangelogAddNextFlagName, "", cobraext.ChangelogAddNextFlagDescription) + addChangelogCmd.Flags().String(cobraext.ChangelogAddVersionFlagName, "", cobraext.ChangelogAddVersionFlagDescription) + + addChangelogCmd.Flags().String(cobraext.ChangelogAddDescriptionFlagName, "", cobraext.ChangelogAddDescriptionFlagDescription) + addChangelogCmd.MarkFlagRequired(cobraext.ChangelogAddDescriptionFlagName) + addChangelogCmd.Flags().String(cobraext.ChangelogAddTypeFlagName, "", cobraext.ChangelogAddTypeFlagDescription) + addChangelogCmd.MarkFlagRequired(cobraext.ChangelogAddTypeFlagName) + addChangelogCmd.Flags().String(cobraext.ChangelogAddLinkFlagName, "", cobraext.ChangelogAddLinkFlagDescription) + addChangelogCmd.MarkFlagRequired(cobraext.ChangelogAddLinkFlagName) + + cmd := &cobra.Command{ + Use: "changelog", + Short: "Utilities to work with the changelog of the package", + Long: changelogLongDescription, + } + + cmd.AddCommand(addChangelogCmd) + + return cobraext.NewCommand(cmd, cobraext.ContextPackage) +} + +func changelogAddCmd(cmd *cobra.Command, args []string) error { + packageRoot, err := packages.MustFindPackageRoot() + if err != nil { + return errors.Wrap(err, "locating package root failed") + } + + version, _ := cmd.Flags().GetString(cobraext.ChangelogAddVersionFlagName) + nextMode, _ := cmd.Flags().GetString(cobraext.ChangelogAddNextFlagName) + if version != "" && nextMode != "" { + return errors.Errorf("flags %q and %q cannot be used at the same time", + cobraext.ChangelogAddVersionFlagName, + cobraext.ChangelogAddNextFlagName) + } + if version == "" { + v, err := changelogCmdVersion(nextMode, packageRoot) + if err != nil { + return err + } + version = v.String() + } + + description, _ := cmd.Flags().GetString(cobraext.ChangelogAddDescriptionFlagName) + changeType, _ := cmd.Flags().GetString(cobraext.ChangelogAddTypeFlagName) + link, _ := cmd.Flags().GetString(cobraext.ChangelogAddLinkFlagName) + + entry := changelog.Revision{ + Version: version, + Changes: []changelog.Entry{ + { + Description: description, + Type: changeType, + Link: link, + }, + }, + } + + return patchChangelog(packageRoot, entry) +} + +func changelogCmdVersion(nextMode, packageRoot string) (*semver.Version, error) { + revisions, err := changelog.ReadChangelogFromPackageRoot(packageRoot) + if err != nil { + return nil, errors.Wrap(err, "failed to read current changelog") + } + if len(revisions) == 0 { + return semver.MustParse("0.0.0"), nil + } + + version, err := semver.NewVersion(revisions[0].Version) + if err != nil { + return nil, errors.Wrapf(err, "invalid version in changelog %q", revisions[0].Version) + } + + switch nextMode { + case "": + break + case "major": + v := version.IncMajor() + version = &v + case "minor": + v := version.IncMinor() + version = &v + case "patch": + v := version.IncPatch() + version = &v + default: + return nil, errors.Errorf("invalid value for %q: %s", + cobraext.ChangelogAddNextFlagName, nextMode) + } + + return version, nil +} + +// patchChangelog looks for the proper place to add the new revision in the changelog, +// trying to conserve original format and comments. +func patchChangelog(packageRoot string, patch changelog.Revision) error { + changelogPath := filepath.Join(packageRoot, changelog.PackageChangelogFile) + d, err := ioutil.ReadFile(changelogPath) + if err != nil { + return err + } + + var nodes []yaml.Node + err = yaml.Unmarshal(d, &nodes) + if err != nil { + return err + } + + patchVersion, err := semver.NewVersion(patch.Version) + if err != nil { + return err + } + + patched := false + var result []yaml.Node + for _, node := range nodes { + if patched { + result = append(result, node) + continue + } + + var entry changelog.Revision + err := node.Decode(&entry) + if err != nil { + result = append(result, node) + continue + } + + foundVersion, err := semver.NewVersion(entry.Version) + if err != nil { + return err + } + + var newNode yaml.Node + if patchVersion.Equal(foundVersion) { + // Add the change to current entry. + fmt.Println("Adding changelog entry in version", foundVersion) + entry.Changes = append(patch.Changes, entry.Changes...) + err := newNode.Encode(entry) + if err != nil { + return err + } + fmt.Printf("%+v\n", newNode) + result = append(result, newNode) + patched = true + continue + } + + // Add the change before first entry + fmt.Println("Adding changelog entry before version", foundVersion) + err = newNode.Encode(patch) + if err != nil { + return err + } + fmt.Printf("%+v\n", newNode) + // If there is a comment on top, leave it there. + if node.HeadComment != "" { + newNode.HeadComment = node.HeadComment + node.HeadComment = "" + } + result = append(result, newNode, node) + patched = true + } + + if !patched { + return errors.New("patch was not applied, this is probably a bug") + } + + d, err = yaml.Marshal(result) + if err != nil { + return errors.Wrap(err, "failed to encode resulting changelog") + } + + return ioutil.WriteFile(changelogPath, d, 0644) +} diff --git a/cmd/root.go b/cmd/root.go index 6d6f5e3a7c..0997b70eec 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,6 +16,7 @@ import ( var commands = []*cobraext.Command{ setupBuildCommand(), + setupChangelogCommand(), setupCheckCommand(), setupCleanCommand(), setupCreateCommand(), diff --git a/internal/cobraext/const.go b/internal/cobraext/const.go index ede12e9b65..c0e5b799f3 100644 --- a/internal/cobraext/const.go +++ b/internal/cobraext/const.go @@ -9,6 +9,21 @@ const ( BuildZipFlagName = "zip" BuildZipFlagDescription = "archive the built package" + ChangelogAddNextFlagName = "next" + ChangelogAddNextFlagDescription = "changelog entry is added in the next `major`, `minor` or `patch` version" + + ChangelogAddVersionFlagName = "version" + ChangelogAddVersionFlagDescription = "changelog entry is added in the given version" + + ChangelogAddDescriptionFlagName = "description" + ChangelogAddDescriptionFlagDescription = "description for the changelog entry" + + ChangelogAddTypeFlagName = "type" + ChangelogAddTypeFlagDescription = "type of change (bugfix, enhancement or breaking-change) for the changelog entry" + + ChangelogAddLinkFlagName = "link" + ChangelogAddLinkFlagDescription = "link to the pull request or issue with more information about the changelog entry" + CheckConditionFlagName = "check-condition" CheckConditionFlagDescription = "check if the condition is met for the package, but don't install the package (e.g. kibana.version=7.10.0)" From c54368e0dc6c572853a0a490731f74966ae04bc7 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Mar 2022 19:08:41 +0200 Subject: [PATCH 2/7] Move to internal package, add tests --- cmd/changelog.go | 76 +----------- .../testdata/changelog-one-patch-multiple.yml | 12 ++ .../changelog-one-patch-next-major.yml | 11 ++ .../changelog-one-patch-same-version.yml | 9 ++ .../changelog/testdata/changelog-one.yml | 6 + internal/packages/changelog/yaml.go | 117 ++++++++++++++++++ internal/packages/changelog/yaml_test.go | 93 ++++++++++++++ 7 files changed, 252 insertions(+), 72 deletions(-) create mode 100644 internal/packages/changelog/testdata/changelog-one-patch-multiple.yml create mode 100644 internal/packages/changelog/testdata/changelog-one-patch-next-major.yml create mode 100644 internal/packages/changelog/testdata/changelog-one-patch-same-version.yml create mode 100644 internal/packages/changelog/testdata/changelog-one.yml create mode 100644 internal/packages/changelog/yaml.go create mode 100644 internal/packages/changelog/yaml_test.go diff --git a/cmd/changelog.go b/cmd/changelog.go index 2f40106782..558dfe41c3 100644 --- a/cmd/changelog.go +++ b/cmd/changelog.go @@ -5,14 +5,12 @@ package cmd import ( - "fmt" "io/ioutil" "path/filepath" "github.com/Masterminds/semver" "github.com/pkg/errors" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" "github.com/elastic/elastic-package/internal/cobraext" "github.com/elastic/elastic-package/internal/packages" @@ -90,7 +88,7 @@ func changelogAddCmd(cmd *cobra.Command, args []string) error { }, } - return patchChangelog(packageRoot, entry) + return patchChangelogFile(packageRoot, entry) } func changelogCmdVersion(nextMode, packageRoot string) (*semver.Version, error) { @@ -127,85 +125,19 @@ func changelogCmdVersion(nextMode, packageRoot string) (*semver.Version, error) return version, nil } -// patchChangelog looks for the proper place to add the new revision in the changelog, +// patchChangelogFile looks for the proper place to add the new revision in the changelog, // trying to conserve original format and comments. -func patchChangelog(packageRoot string, patch changelog.Revision) error { +func patchChangelogFile(packageRoot string, patch changelog.Revision) error { changelogPath := filepath.Join(packageRoot, changelog.PackageChangelogFile) d, err := ioutil.ReadFile(changelogPath) if err != nil { return err } - var nodes []yaml.Node - err = yaml.Unmarshal(d, &nodes) + d, err = changelog.PatchYAML(d, patch) if err != nil { return err } - patchVersion, err := semver.NewVersion(patch.Version) - if err != nil { - return err - } - - patched := false - var result []yaml.Node - for _, node := range nodes { - if patched { - result = append(result, node) - continue - } - - var entry changelog.Revision - err := node.Decode(&entry) - if err != nil { - result = append(result, node) - continue - } - - foundVersion, err := semver.NewVersion(entry.Version) - if err != nil { - return err - } - - var newNode yaml.Node - if patchVersion.Equal(foundVersion) { - // Add the change to current entry. - fmt.Println("Adding changelog entry in version", foundVersion) - entry.Changes = append(patch.Changes, entry.Changes...) - err := newNode.Encode(entry) - if err != nil { - return err - } - fmt.Printf("%+v\n", newNode) - result = append(result, newNode) - patched = true - continue - } - - // Add the change before first entry - fmt.Println("Adding changelog entry before version", foundVersion) - err = newNode.Encode(patch) - if err != nil { - return err - } - fmt.Printf("%+v\n", newNode) - // If there is a comment on top, leave it there. - if node.HeadComment != "" { - newNode.HeadComment = node.HeadComment - node.HeadComment = "" - } - result = append(result, newNode, node) - patched = true - } - - if !patched { - return errors.New("patch was not applied, this is probably a bug") - } - - d, err = yaml.Marshal(result) - if err != nil { - return errors.Wrap(err, "failed to encode resulting changelog") - } - return ioutil.WriteFile(changelogPath, d, 0644) } diff --git a/internal/packages/changelog/testdata/changelog-one-patch-multiple.yml b/internal/packages/changelog/testdata/changelog-one-patch-multiple.yml new file mode 100644 index 0000000000..ae5cea826d --- /dev/null +++ b/internal/packages/changelog/testdata/changelog-one-patch-multiple.yml @@ -0,0 +1,12 @@ +# newer versions go on top +- version: "1.0.0" + changes: + - description: One change + type: enhancement + link: http://github.com/elastic/elastic-package + - description: Other change + type: enhancement + link: http://github.com/elastic/elastic-package + - description: Initial version + type: enhancement + link: http://github.com/elastic/elastic-package diff --git a/internal/packages/changelog/testdata/changelog-one-patch-next-major.yml b/internal/packages/changelog/testdata/changelog-one-patch-next-major.yml new file mode 100644 index 0000000000..dc3c77e2ba --- /dev/null +++ b/internal/packages/changelog/testdata/changelog-one-patch-next-major.yml @@ -0,0 +1,11 @@ +# newer versions go on top +- version: "2.0.0" + changes: + - description: One change + type: enhancement + link: http://github.com/elastic/elastic-package +- version: "1.0.0" + changes: + - description: Initial version + type: enhancement + link: http://github.com/elastic/elastic-package diff --git a/internal/packages/changelog/testdata/changelog-one-patch-same-version.yml b/internal/packages/changelog/testdata/changelog-one-patch-same-version.yml new file mode 100644 index 0000000000..3103f072cd --- /dev/null +++ b/internal/packages/changelog/testdata/changelog-one-patch-same-version.yml @@ -0,0 +1,9 @@ +# newer versions go on top +- version: "1.0.0" + changes: + - description: One change + type: enhancement + link: http://github.com/elastic/elastic-package + - description: Initial version + type: enhancement + link: http://github.com/elastic/elastic-package diff --git a/internal/packages/changelog/testdata/changelog-one.yml b/internal/packages/changelog/testdata/changelog-one.yml new file mode 100644 index 0000000000..308f139728 --- /dev/null +++ b/internal/packages/changelog/testdata/changelog-one.yml @@ -0,0 +1,6 @@ +# newer versions go on top +- version: "1.0.0" + changes: + - description: Initial version + type: enhancement + link: http://github.com/elastic/elastic-package diff --git a/internal/packages/changelog/yaml.go b/internal/packages/changelog/yaml.go new file mode 100644 index 0000000000..1b3527f701 --- /dev/null +++ b/internal/packages/changelog/yaml.go @@ -0,0 +1,117 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package changelog + +import ( + "fmt" + + "github.com/Masterminds/semver" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +// PatchYAML looks for the proper place to add the new revision in the changelog, +// trying to conserve original format and comments. +func PatchYAML(d []byte, patch Revision) ([]byte, error) { + var nodes []yaml.Node + err := yaml.Unmarshal(d, &nodes) + if err != nil { + return nil, err + } + + patchVersion, err := semver.NewVersion(patch.Version) + if err != nil { + return nil, err + } + + patched := false + var result []yaml.Node + for _, node := range nodes { + if patched { + result = append(result, node) + continue + } + + var entry Revision + err := node.Decode(&entry) + if err != nil { + result = append(result, node) + continue + } + + foundVersion, err := semver.NewVersion(entry.Version) + if err != nil { + return nil, err + } + + var newNode yaml.Node + if patchVersion.Equal(foundVersion) { + // Add the change to current entry. + fmt.Println("Adding changelog entry in version", foundVersion) + entry.Changes = append(patch.Changes, entry.Changes...) + err := newNode.Encode(entry) + if err != nil { + return nil, err + } + // Keep comments of the original node. + newNode.HeadComment = node.HeadComment + newNode.LineComment = node.LineComment + newNode.FootComment = node.FootComment + // Quote version to keep common style. + setYamlMapValueVersionStyle(&newNode, "version", yaml.DoubleQuotedStyle) + result = append(result, newNode) + patched = true + continue + } + + // Add the change before first entry + fmt.Println("Adding changelog entry before version", foundVersion) + err = newNode.Encode(patch) + if err != nil { + return nil, err + } + // If there is a comment on top, leave it there. + if node.HeadComment != "" { + newNode.HeadComment = node.HeadComment + node.HeadComment = "" + } + // Quote version to keep common style. + setYamlMapValueVersionStyle(&newNode, "version", yaml.DoubleQuotedStyle) + result = append(result, newNode, node) + patched = true + } + + if !patched { + return nil, errors.New("changelog entry was not added, this is probably a bug") + } + + d, err = yaml.Marshal(result) + if err != nil { + return nil, errors.Wrap(err, "failed to encode resulting changelog") + } + + return d, nil +} + +// setYamlMapValueVersionStyle changes the style of one value in a YAML map. If the key +// is not found, it does nothing. +func setYamlMapValueVersionStyle(node *yaml.Node, key string, style yaml.Style) { + // Check first if this is a map. + if node == nil || node.Kind != yaml.MappingNode { + return + } + // Look for the key, the value will be the next one. + var keyIdx int + for keyIdx = range node.Content { + child := node.Content[keyIdx] + if child.Kind == yaml.ScalarNode && child.Value == key { + break + } + } + valueIdx := keyIdx + 1 + if valueIdx < len(node.Content) { + node.Content[valueIdx].Style = style + } +} diff --git a/internal/packages/changelog/yaml_test.go b/internal/packages/changelog/yaml_test.go new file mode 100644 index 0000000000..cba43c8192 --- /dev/null +++ b/internal/packages/changelog/yaml_test.go @@ -0,0 +1,93 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package changelog + +import ( + "errors" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPatchYAML(t *testing.T) { + cases := []struct { + title string + original string + expected string + patch Revision + }{ + { + title: "Change in current version", + original: "testdata/changelog-one.yml", + expected: "testdata/changelog-one-patch-same-version.yml", + patch: Revision{ + Version: "1.0.0", + Changes: []Entry{ + { + Description: "One change", + Type: "enhancement", + Link: "http://github.com/elastic/elastic-package", + }, + }, + }, + }, + { + title: "Change in next major", + original: "testdata/changelog-one.yml", + expected: "testdata/changelog-one-patch-next-major.yml", + patch: Revision{ + Version: "2.0.0", + Changes: []Entry{ + { + Description: "One change", + Type: "enhancement", + Link: "http://github.com/elastic/elastic-package", + }, + }, + }, + }, + { + title: "Multiple changes", + original: "testdata/changelog-one.yml", + expected: "testdata/changelog-one-patch-multiple.yml", + patch: Revision{ + Version: "1.0.0", + Changes: []Entry{ + { + Description: "One change", + Type: "enhancement", + Link: "http://github.com/elastic/elastic-package", + }, + { + Description: "Other change", + Type: "enhancement", + Link: "http://github.com/elastic/elastic-package", + }, + }, + }, + }, + } + + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + d, err := ioutil.ReadFile(c.original) + require.NoError(t, err) + + result, err := PatchYAML(d, c.patch) + require.NoError(t, err) + + expected, err := ioutil.ReadFile(c.expected) + if errors.Is(err, os.ErrNotExist) { + err := ioutil.WriteFile(c.expected, result, 0644) + require.NoError(t, err) + t.Skip("file generated, run again") + } + require.NoError(t, err) + require.Equal(t, string(expected), string(result)) + }) + } +} From 5ed075310eb179adb04fc4fec57924c1b0fd7b9f Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Mar 2022 19:36:02 +0200 Subject: [PATCH 3/7] Update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4a6c77da9b..38b5d0bf4f 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,12 @@ Built packages can also be published to the global package registry service. For details on how to enable dependency management, see the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/dependency_management.md). +### `elastic-package changelog` + +_Context: package_ + +Use this command to work with the changelog of the package. + ### `elastic-package check` _Context: package_ From 74a8ed7ad6449957a37984dcf43a0a4850727aaf Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Mar 2022 19:43:37 +0200 Subject: [PATCH 4/7] Remove prints --- internal/packages/changelog/yaml.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/packages/changelog/yaml.go b/internal/packages/changelog/yaml.go index 1b3527f701..bcea573c97 100644 --- a/internal/packages/changelog/yaml.go +++ b/internal/packages/changelog/yaml.go @@ -5,8 +5,6 @@ package changelog import ( - "fmt" - "github.com/Masterminds/semver" "github.com/pkg/errors" "gopkg.in/yaml.v3" @@ -49,7 +47,6 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { var newNode yaml.Node if patchVersion.Equal(foundVersion) { // Add the change to current entry. - fmt.Println("Adding changelog entry in version", foundVersion) entry.Changes = append(patch.Changes, entry.Changes...) err := newNode.Encode(entry) if err != nil { @@ -67,7 +64,6 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { } // Add the change before first entry - fmt.Println("Adding changelog entry before version", foundVersion) err = newNode.Encode(patch) if err != nil { return nil, err From 793c89932f3ef2145a53573fb01858635cf4866a Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Mar 2022 20:41:16 +0200 Subject: [PATCH 5/7] Patch the manifest too --- cmd/changelog.go | 27 +++++++++++- internal/packages/changelog/yaml.go | 55 ++++++++++++++++++++++-- internal/packages/changelog/yaml_test.go | 14 +++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/cmd/changelog.go b/cmd/changelog.go index 558dfe41c3..d390f5ec8a 100644 --- a/cmd/changelog.go +++ b/cmd/changelog.go @@ -88,7 +88,17 @@ func changelogAddCmd(cmd *cobra.Command, args []string) error { }, } - return patchChangelogFile(packageRoot, entry) + err = patchChangelogFile(packageRoot, entry) + if err != nil { + return err + } + + err = setManifestVersion(packageRoot, version) + if err != nil { + return err + } + + return nil } func changelogCmdVersion(nextMode, packageRoot string) (*semver.Version, error) { @@ -141,3 +151,18 @@ func patchChangelogFile(packageRoot string, patch changelog.Revision) error { return ioutil.WriteFile(changelogPath, d, 0644) } + +func setManifestVersion(packageRoot string, version string) error { + manifestPath := filepath.Join(packageRoot, packages.PackageManifestFile) + d, err := ioutil.ReadFile(manifestPath) + if err != nil { + return err + } + + d, err = changelog.SetManifestVersion(d, version) + if err != nil { + return err + } + + return ioutil.WriteFile(manifestPath, d, 0644) +} diff --git a/internal/packages/changelog/yaml.go b/internal/packages/changelog/yaml.go index bcea573c97..a576b1a59b 100644 --- a/internal/packages/changelog/yaml.go +++ b/internal/packages/changelog/yaml.go @@ -5,6 +5,8 @@ package changelog import ( + "bytes" + "github.com/Masterminds/semver" "github.com/pkg/errors" "gopkg.in/yaml.v3" @@ -44,6 +46,10 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { return nil, err } + if foundVersion.GreaterThan(patchVersion) { + return nil, errors.New("cannot add change to old version") + } + var newNode yaml.Node if patchVersion.Equal(foundVersion) { // Add the change to current entry. @@ -57,7 +63,7 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { newNode.LineComment = node.LineComment newNode.FootComment = node.FootComment // Quote version to keep common style. - setYamlMapValueVersionStyle(&newNode, "version", yaml.DoubleQuotedStyle) + setYamlMapValueStyle(&newNode, "version", yaml.DoubleQuotedStyle) result = append(result, newNode) patched = true continue @@ -74,7 +80,7 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { node.HeadComment = "" } // Quote version to keep common style. - setYamlMapValueVersionStyle(&newNode, "version", yaml.DoubleQuotedStyle) + setYamlMapValueStyle(&newNode, "version", yaml.DoubleQuotedStyle) result = append(result, newNode, node) patched = true } @@ -91,9 +97,30 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { return d, nil } -// setYamlMapValueVersionStyle changes the style of one value in a YAML map. If the key +func SetManifestVersion(d []byte, version string) ([]byte, error) { + var node yaml.Node + err := yaml.Unmarshal(d, &node) + if err != nil { + return nil, errors.Wrap(err, "failed to decode manifest") + } + + // Manifest is a document, with a single element, that should be a map. + if len(node.Content) == 0 || node.Content[0].Kind != yaml.MappingNode { + return nil, errors.Wrap(err, "unexpected manifest content") + } + + setYamlMapValue(node.Content[0], "version", version) + + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + err = encoder.Encode(&node) + return buf.Bytes(), err +} + +// setYamlMapValueStyle changes the style of one value in a YAML map. If the key // is not found, it does nothing. -func setYamlMapValueVersionStyle(node *yaml.Node, key string, style yaml.Style) { +func setYamlMapValueStyle(node *yaml.Node, key string, style yaml.Style) { // Check first if this is a map. if node == nil || node.Kind != yaml.MappingNode { return @@ -111,3 +138,23 @@ func setYamlMapValueVersionStyle(node *yaml.Node, key string, style yaml.Style) node.Content[valueIdx].Style = style } } + +// setYamlMapValue sets a value in a map. +func setYamlMapValue(node *yaml.Node, key string, value string) { + // Check first if this is a map. + if node == nil || node.Kind != yaml.MappingNode { + return + } + // Look for the key, the value will be the next one. + var keyIdx int + for keyIdx = range node.Content { + child := node.Content[keyIdx] + if child.Kind == yaml.ScalarNode && child.Value == key { + break + } + } + valueIdx := keyIdx + 1 + if valueIdx < len(node.Content) { + node.Content[valueIdx].Value = value + } +} diff --git a/internal/packages/changelog/yaml_test.go b/internal/packages/changelog/yaml_test.go index cba43c8192..40ab400310 100644 --- a/internal/packages/changelog/yaml_test.go +++ b/internal/packages/changelog/yaml_test.go @@ -10,6 +10,7 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -87,7 +88,18 @@ func TestPatchYAML(t *testing.T) { t.Skip("file generated, run again") } require.NoError(t, err) - require.Equal(t, string(expected), string(result)) + + assert.Equal(t, string(expected), string(result)) }) } } + +func TestManifestVersion(t *testing.T) { + manifest := "name: test\nversion: 1.0.0\n" + expected := "name: test\nversion: 1.1.0\n" + + result, err := SetManifestVersion([]byte(manifest), "1.1.0") + require.NoError(t, err) + + assert.Equal(t, string(expected), string(result)) +} From 4473f7e2edff8d8fafddaa456b08f7b33bb3aabc Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Mar 2022 14:45:01 +0200 Subject: [PATCH 6/7] Update documentation --- README.md | 4 ++++ cmd/changelog.go | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 38b5d0bf4f..077e767357 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,10 @@ _Context: package_ Use this command to work with the changelog of the package. +You can use this command to modify the changelog following the expected format and good practices. +This can be useful when introducing changelog entries for changes done by automated processes. + + ### `elastic-package check` _Context: package_ diff --git a/cmd/changelog.go b/cmd/changelog.go index d390f5ec8a..3008d23ec1 100644 --- a/cmd/changelog.go +++ b/cmd/changelog.go @@ -17,12 +17,20 @@ import ( "github.com/elastic/elastic-package/internal/packages/changelog" ) -const changelogLongDescription = `Use this command to work with the changelog of the package.` +const changelogLongDescription = `Use this command to work with the changelog of the package. + +You can use this command to modify the changelog following the expected format and good practices. +This can be useful when introducing changelog entries for changes done by automated processes. +` const changelogAddLongDescription = `Use this command to add an entry to the changelog file. -The entry is added on top of the last entry in the current version. Or optionally in the next -major, minor or patch version, or as a new given version.` +The entry added will include the given description, type and link. It is added on top of the +last entry in the current version + +Alternatively, you can start a new version indicating the specific version, or if it should +be the next major, minor or patch version. +` func setupChangelogCommand() *cobraext.Command { addChangelogCmd := &cobra.Command{ From 8d7a46ba9d13137b365b3f6b2544ca5a7f14224f Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Mar 2022 14:58:12 +0200 Subject: [PATCH 7/7] Use common formatter --- cmd/changelog.go | 2 -- internal/packages/changelog/yaml.go | 31 ++++++++++++++++-------- internal/packages/changelog/yaml_test.go | 4 +-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/cmd/changelog.go b/cmd/changelog.go index 3008d23ec1..4ac9d77ef6 100644 --- a/cmd/changelog.go +++ b/cmd/changelog.go @@ -41,7 +41,6 @@ func setupChangelogCommand() *cobraext.Command { } addChangelogCmd.Flags().String(cobraext.ChangelogAddNextFlagName, "", cobraext.ChangelogAddNextFlagDescription) addChangelogCmd.Flags().String(cobraext.ChangelogAddVersionFlagName, "", cobraext.ChangelogAddVersionFlagDescription) - addChangelogCmd.Flags().String(cobraext.ChangelogAddDescriptionFlagName, "", cobraext.ChangelogAddDescriptionFlagDescription) addChangelogCmd.MarkFlagRequired(cobraext.ChangelogAddDescriptionFlagName) addChangelogCmd.Flags().String(cobraext.ChangelogAddTypeFlagName, "", cobraext.ChangelogAddTypeFlagDescription) @@ -54,7 +53,6 @@ func setupChangelogCommand() *cobraext.Command { Short: "Utilities to work with the changelog of the package", Long: changelogLongDescription, } - cmd.AddCommand(addChangelogCmd) return cobraext.NewCommand(cmd, cobraext.ContextPackage) diff --git a/internal/packages/changelog/yaml.go b/internal/packages/changelog/yaml.go index a576b1a59b..6f0a8d6072 100644 --- a/internal/packages/changelog/yaml.go +++ b/internal/packages/changelog/yaml.go @@ -5,11 +5,11 @@ package changelog import ( - "bytes" - "github.com/Masterminds/semver" "github.com/pkg/errors" "gopkg.in/yaml.v3" + + "github.com/elastic/elastic-package/internal/formatter" ) // PatchYAML looks for the proper place to add the new revision in the changelog, @@ -89,11 +89,10 @@ func PatchYAML(d []byte, patch Revision) ([]byte, error) { return nil, errors.New("changelog entry was not added, this is probably a bug") } - d, err = yaml.Marshal(result) + d, err = formatResult(result) if err != nil { - return nil, errors.Wrap(err, "failed to encode resulting changelog") + return nil, errors.Wrap(err, "failed to format manifest") } - return d, nil } @@ -111,11 +110,23 @@ func SetManifestVersion(d []byte, version string) ([]byte, error) { setYamlMapValue(node.Content[0], "version", version) - var buf bytes.Buffer - encoder := yaml.NewEncoder(&buf) - encoder.SetIndent(2) - err = encoder.Encode(&node) - return buf.Bytes(), err + d, err = formatResult(&node) + if err != nil { + return nil, errors.Wrap(err, "failed to format manifest") + } + return d, nil +} + +func formatResult(result interface{}) ([]byte, error) { + d, err := yaml.Marshal(result) + if err != nil { + return nil, errors.New("failed to encode") + } + d, _, err = formatter.YAMLFormatter(d) + if err != nil { + return nil, errors.New("failed to format") + } + return d, nil } // setYamlMapValueStyle changes the style of one value in a YAML map. If the key diff --git a/internal/packages/changelog/yaml_test.go b/internal/packages/changelog/yaml_test.go index 40ab400310..c1ccb923ac 100644 --- a/internal/packages/changelog/yaml_test.go +++ b/internal/packages/changelog/yaml_test.go @@ -95,8 +95,8 @@ func TestPatchYAML(t *testing.T) { } func TestManifestVersion(t *testing.T) { - manifest := "name: test\nversion: 1.0.0\n" - expected := "name: test\nversion: 1.1.0\n" + manifest := "name: test\nversion: 1.0.0\ncategories:\n - custom\n" + expected := "name: test\nversion: 1.1.0\ncategories:\n - custom\n" result, err := SetManifestVersion([]byte(manifest), "1.1.0") require.NoError(t, err)