Skip to content
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

Add "add" command #47

Merged
merged 4 commits into from
Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ changelog-tool get 0.2.2
changelog-tool unreleased -e
```

## Add an entry to an unreleased section
```bash
changelog-tool add fixed "We fixed some bad issues" -e
changelog-tool add added "We just added some new cool stuff" -e
changelog-tool add changed "And changed things a bit" -e
```

## Prepare a Changelog for a Release

```bash
Expand Down
62 changes: 46 additions & 16 deletions changelog-tool/changelog.pony
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use "peg"
use "debug"
Theodus marked this conversation as resolved.
Show resolved Hide resolved

class Changelog
let heading: String
Expand Down Expand Up @@ -45,6 +46,11 @@ class Changelog
unreleased = Release._unreleased()
end

fun ref add_entry(section_name: String, entry: String) ? =>
match unreleased
| let r: Release => r.add_entry(section_name, entry)?
end

fun string(): String iso^ =>
let str = (recover String end)
.> append("# Change Log\n\n")
Expand Down Expand Up @@ -79,25 +85,49 @@ class Release
added = Section._empty(Added)
changed = Section._empty(Changed)

fun ref add_entry(section_name: String, entry: String) ? =>
let section =
match section_name
| "fixed" =>
if fixed is None then fixed = Section._empty(Fixed) end
fixed as Section
| "added" =>
if added is None then added = Section._empty(Added) end
added as Section
| "changed" =>
if changed is None then changed = Section._empty(Changed) end
changed as Section
else error
end
section.entries.push("- " + entry)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A newline should be added to the end of the entry if it doesn't already have one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be done by using "String.contains" and appending "\n" or is there other idiomatic way of doing it?


fun string(): String iso^ =>
if heading == _unreleased_heading then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think you meant to remove all of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if we don't remove this, it will print the unrelease thing with an empty body, without the entries that we're adding. That's why I removed it 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this break the "unreleased" command?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "unreleased" release should always show the fixed, added, and changed sections, even if they are empty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that this is the cause of the CI failure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah!

## [unreleased] - unreleased

### Fixed


### Added


### Changed

It's yielding ## [unreleased] - unreleased and makes the test fail.

I'm struggling to reach a point where I can print the empty sections only from unlreleased versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out that unreleased sections were being parsed as None, so I added an extra check for each section saying that if it's unreleased and the section is None, it will use the Section._empty(....) for printing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, for the parse test that's failing, a changelog must now contain all sections in "unreleased" because it can no longer assume that the "unreleased" release is either empty or nonexistent. It may be best to create a new class, Unreleased that uses Section instead of (Section | None) as field types. This would be simpler than treating it as any other release at this point.

Copy link
Contributor

@Theodus Theodus Nov 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ordepdev would you want to include the changes above in this PR? If not, I can do it after this PR is merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to but I think it's better to let you do it and take a look at your changes after!

"\n\n".join(
[ heading
"### Fixed\n"
"### Added\n"
"### Changed\n"
""
].values())
else
let str = recover String .> append(heading) .> append("\n\n") end
for section in [fixed; added; changed].values() do
match section
| let s: Section box =>
str .> append(s.string()) .> append("\n")
end
// In order to represent the empty sections of unreleased releases,
// we must use the empty correspoding section when printing instead
// of None, otherwise it will be ignored.
let fixed' = match fixed
Theodus marked this conversation as resolved.
Show resolved Hide resolved
| None if heading == _unreleased_heading => Section._empty(Fixed)
else fixed
end

let added' = match added
| None if heading == _unreleased_heading => Section._empty(Added)
else added
end

let changed' = match changed
| None if heading == _unreleased_heading => Section._empty(Changed)
else changed
end

let str = recover String .> append(heading) .> append("\n\n") end
for section in [fixed'; added'; changed'].values() do
match section
| let s: Section box =>
str .> append(s.string()) .> append("\n")
end
str
end
str

class Section
let label: TSection
Expand Down
29 changes: 29 additions & 0 deletions changelog-tool/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ actor Main
].values()),
[edit],
[ArgSpec.string("version")])?
CommandSpec.leaf(
"add",
"Add a new entry at the end of the section",
[edit],
[ ArgSpec.string("section")
ArgSpec.string("entry")
])?
])?
.> add_help("help", "Print this message and exit")?

Expand All @@ -95,6 +102,10 @@ actor Main
| "changelog-tool/release" =>
cmd_release(
path, filename, cmd.arg("version").string(), cmd.option("edit").bool())
| "changelog-tool/add" =>
cmd_add(
path, filename, cmd.arg("section").string(),
cmd.arg("entry").string(),cmd.option("edit").bool())
else
err("unknown command: " + cmd.fullname())
please_report()
Expand Down Expand Up @@ -171,6 +182,24 @@ actor Main
err("unable to perform release preparation")
end

fun cmd_add(
filepath: FilePath,
filename: String,
section: String,
entry: String,
edit: Bool)
=>
try
edit_or_print(
filepath,
edit,
Changelog(parse(filepath, filename)?)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding .> create_unreleased() here before add_entry will likely solve the problem. An unreleased section should be created anyway if someone is using this command.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, creating a new "unreleased" release creates new empty sections.

changelog-tool new
changelog-tool unreleased -e
changelog-tool add fixed "We fixed some nasty bug." -e
changelog-tool add added "And we added some cool feature." -e
changelog-tool add changed "And changed some little things as well." -e
changelog-tool release 1.0.0

Currently, this works as expected. If I add that command only the last "changed" will be reflected in the file, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the problem is that the "unreleased" release must contain all sections if it exists. Previously the string function would assume that it is empty if it exists. But this is no longer a reasonable assumption, so the sections must be created if they don't exist. That's why it may be useful to create a new class for unreleased that reflects the requirement that the sections cannot have type None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, make the types do the work instead.

.> add_entry(section, entry)?
.string())
else
err("unable add a new changelog entry")
end

fun parse(filepath: FilePath, filename: String): peg.AST ? =>
let source = peg.Source(filepath)?
match recover val ChangelogParser().parse(source) end
Expand Down
66 changes: 66 additions & 0 deletions tests/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,39 @@ class iso _TestRelease is UnitTest

""")?

_ReleaseTestAfterAddingSomeEntries(h, ChangelogParser()).run(
"""
# Change Log

## [unreleased] - unreleased

### Fixed

### Added

### Changed

""",
"""
# Change Log

## [0.0.0] - 0000-00-00

### Fixed

- We made some fixes...
- Oh, and we made a final one.

### Added

- We added some stuff as well.

### Changed

- And we changed a few things also.

""")?

class ParseTest
let _h: TestHelper
let _parser: Parser
Expand Down Expand Up @@ -286,6 +319,39 @@ class _ReleaseTest
_h.fail()
end

class _ReleaseTestAfterAddingSomeEntries
let _h: TestHelper
let _parser: Parser

new create(h: TestHelper, parser: Parser) =>
(_h, _parser) = (h, parser)

fun run(input: String, expected: String) ? =>
let source = Source.from_string(input)
match recover val _parser.parse(source) end
| (let n: USize, let r: (AST | Token | NotPresent)) =>
match r
| let ast: AST =>
_h.log(recover val _Printer(ast) end)
let changelog = Changelog(ast)?
.> add_entry("fixed", "We made some fixes...\n")?
.> add_entry("fixed", "Oh, and we made a final one.\n")?
.> add_entry("added", "We added some stuff as well.\n")?
.> add_entry("changed", "And we changed a few things also.\n")?
.> create_release("0.0.0", "0000-00-00")
let output: String = changelog.string()
_h.log(output)
_h.assert_eq[String](expected, output)
else
_h.log(recover val _Printer(r) end)
_h.fail()
end
| (let offset: USize, let r: Parser val) =>
let e = recover val SyntaxError(source, offset, r) end
_Logv(_h, PegFormatError.console(e))
_h.fail()
end

primitive _Logv
fun apply(h: TestHelper, bsi: ByteSeqIter) =>
let str = recover String end
Expand Down