diff --git a/Makefile b/Makefile index c88b045..c38a9d0 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ deps: bin/changelog-tool: deps mkdir -p bin - stable env ponyc -o bin + stable env ponyc -V1 -o bin install: mkdir -p $(prefix)/bin @@ -15,8 +15,10 @@ install: test: deps cd tests && \ - stable env ponyc -d && ./tests && \ - rm tests && cd .. + stable env ponyc -d -V1 && ./tests && \ + rm tests && \ + sh verification.sh && \ + cd .. clean: rm -rf bin diff --git a/changelog.pony b/changelog.pony index 2f3afbe..cfc7c67 100644 --- a/changelog.pony +++ b/changelog.pony @@ -9,7 +9,17 @@ class Changelog let children = ast.children.values() released = Array[Release] - heading = (children.next()? as Token).string() + heading = + if children.has_next() then + match children.next()? + | let t: Token => t.string() + | NotPresent => "" + else error + end + else + "" + end + if ast.size() > 1 then unreleased = try Release(children.next()? as AST)? end for child in children do @@ -32,8 +42,12 @@ class Changelog fun string(): String iso^ => let str = (recover String end) - .> append(heading) - .> append("\n") + .> append("# Change Log\n\n") + if heading != "" then + str + .> append(heading) + .> append("\n\n") + end if unreleased isnt None then str.append(unreleased.string()) end for release in released.values() do str.append(release.string()) diff --git a/changelog_parser.pony b/changelog_parser.pony index 19a8f13..aa9035b 100644 --- a/changelog_parser.pony +++ b/changelog_parser.pony @@ -3,13 +3,13 @@ use "peg" primitive ChangelogParser fun apply(): Parser val => recover - head() * release(false).opt() * release().many() + (head() * release(false).opt() * release().many()).eof() end fun head(): Parser val => recover - (not L("\n## [") * Unicode).many().term() - * -L("\n").opt() + let para = not L("#") * (not L("\n") * Unicode).many1().term() + -L("# Change Log\n\n") * para.opt() * -L("\n").many() end fun release(released: Bool = true): Parser val => diff --git a/changelog_tool.pony b/changelog_tool.pony index 61643ad..cdebe47 100644 --- a/changelog_tool.pony +++ b/changelog_tool.pony @@ -1,3 +1,4 @@ +use "debug" use "files" use "peg" use "time" @@ -10,39 +11,28 @@ class ChangelogTool new create(env: Env, filename: String, filepath: FilePath) => (_env, _filename, _filepath) = (env, filename, filepath) - fun verify() => - _env.out.print("verifying " + _filename + "...") - try - let ast = _parse()? - - // TODO: - // let changelog = Changelog(ast)? - // _env.out.print(changelog.string()) - // try changelog .> create_release("0.0.0", "0000-00-00") - // else _env.out.print("fail.") - // end - _env.out.print(_filename + " is a valid changelog") - end + fun verify() ? => + let ast = _parse()? + let changelog = Changelog(ast)? + Debug(recover Printer(ast) end) + Debug(changelog.string()) - fun release(version: String, edit: Bool) => - try - _check_version(version)? - let date = Date(Time.seconds()).format("%Y-%m-%d") - let changelog: String = - Changelog(_parse()?)? - .> create_release(version, date) - .string() - _edit_or_print(edit, changelog) - else - _env.err.print("unable to perform release prep") - end + fun release(version: String, edit: Bool) ? => + _check_version(version)? + let date = Date(Time.seconds()).format("%Y-%m-%d") + let changelog: String = + Changelog(_parse()?)? + .> create_release(version, date) + .string() + _edit_or_print(edit, changelog) fun _check_version(version: String) ? => let source = Source.from_string(version) match recover val ChangelogParser.version().parse(source) end | (_, let t: Token) => None else - _env.err.print("invalid version number: '" + version + "'") + _env.out.print("invalid version number: '" + version + "'") + _env.exitcode(1) error end @@ -77,9 +67,11 @@ class ChangelogTool ast | (let offset: USize, let r: Parser val) => let e = recover val SyntaxError(source, offset, r) end - _env.err.writev(PegFormatError.console(e)) + _env.out.writev(PegFormatError.console(e)) + _env.exitcode(1) error else - _env.err.print("unable to parse file: " + _filename) + _env.out.print("unable to parse file: " + _filename) + _env.exitcode(1) error end diff --git a/main.pony b/main.pony index e922646..b817d82 100644 --- a/main.pony +++ b/main.pony @@ -1,57 +1,83 @@ use "files" -use "options" +use "itertools" actor Main + let help_text: String = + """ + changelog-tool COMMAND [...] + + Commands: + verify Verify that the given changelog is valid. + release Print a changelog that is prepared for release. + Example: `changelog-tool release CHANGELOG.md 0.13.1` + unreleased Add unreleased section to changelog if none exists. + + Options: + --edit, -e Edit the changelog file (release and unreleased only). + """ + new create(env: Env) => - // TODO use the cli package instead of options - let options = - Options(env.args) - .> add("edit", "e", None) - - try - var edit = false - for option in options do - match option - | ("edit", None) => edit = true - | let err: ParseError => - err.report(env.err) - error - end + // TODO use the cli package + + if env.args.size() == 1 then + env.out.print(help_text) + return + end + + (let cmd, let filename) = + try (env.args(1)?, env.args(2)?) + else + env.out.print(help_text) + env.exitcode(1) + return + end + + let filepath = + try + FilePath(env.root as AmbientAuth, filename)? + else + env.out.print("unable to open: " + filename) + env.exitcode(1) + return end + let tool = ChangelogTool(env, filename, filepath) - let args = Array[String] - for arg in options.remaining().values() do - args.push(arg.clone()) + var edit = false + for arg in Iter[String](env.args.values()).skip(3) do + if (arg == "-e") or (arg == "--edit") then + edit = true + break end + end - let filename = args(2)? - let filepath = - try - FilePath(env.root as AmbientAuth, filename)? + match cmd + | "verify" => + env.out.print("Verifying " + filename + "...") + try + tool.verify()? + env.out.print(filename + " is a valid changelog.") + else + env.out.print(filename + " is not a valid changelog.") + env.exitcode(1) + return + end + | "release" => + let version = + try env.args(3)? else - env.err.print("unable to open: " + filename) + env.out.print("A release version must be provided.") + env.exitcode(1) return end - let tool = ChangelogTool(env, filename, filepath) - - match args(1)? - | "verify" => tool.verify() - | "release" => tool.release(args(3)?, edit) - | "unreleased" => tool.unreleased(edit) - else error + try tool.release(version, edit)? + else + env.out.print("Unable to perform release prep.") + env.exitcode(1) + return end + | "unreleased" => tool.unreleased(edit) else - env.out.print( - """ - changelog-tool COMMAND [...] - - Commands: - verify Verify that the given changelog is valid. - release Print a changelog that is prepared for release. - Example: `changelog-tool release CHANGELOG.md 0.13.1` - unreleased Add unreleased section to changelog if none exists. - - Options: - --edit, -e Edit the changelog file (release and unreleased only). - """) + env.out.print(help_text) + env.exitcode(1) + return end diff --git a/tests/bad-changelogs/empty.md b/tests/bad-changelogs/empty.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/bad-changelogs/malformed-version.md b/tests/bad-changelogs/malformed-version.md new file mode 100644 index 0000000..c1538f0 --- /dev/null +++ b/tests/bad-changelogs/malformed-version.md @@ -0,0 +1,8 @@ +# Change Log + +## [unreleased] - unreleased + +### Fixed +- things + +## [0.1.] - 2017-04-07 diff --git a/tests/bad-changelogs/no-changelog.md b/tests/bad-changelogs/no-changelog.md new file mode 100644 index 0000000..f42bc07 --- /dev/null +++ b/tests/bad-changelogs/no-changelog.md @@ -0,0 +1 @@ +## [unreleased] - unreleased diff --git a/tests/bad-changelogs/unreleased-sub-heading.md b/tests/bad-changelogs/unreleased-sub-heading.md new file mode 100644 index 0000000..314c309 --- /dev/null +++ b/tests/bad-changelogs/unreleased-sub-heading.md @@ -0,0 +1,17 @@ +# Change Log + +Stuff + +## [unreleased] - unreleased + +### Fixed +- things + +### Adde +- stuff + +## [0.13.0] - 2017-04-07 + +### Fixed + +- Do not allow capability subtyping when checking constraint subtyping. ([PR #1816](https://github.com/ponylang/ponyc/pull/1816)) diff --git a/tests/main.pony b/tests/main.pony index 6e4ab2b..9f2479d 100644 --- a/tests/main.pony +++ b/tests/main.pony @@ -1,3 +1,4 @@ +use "collections" use "files" use "peg" use "ponytest" @@ -14,14 +15,13 @@ actor Main is TestList test(_TestParseChangelog) test(_TestRelease) - class iso _TestParseVersion is UnitTest fun name(): String => "parse version" fun apply(h: TestHelper) => ParseTest(h, ChangelogParser.version()).run( - [ ("0.0.0", "(Version 0.0.0)\n") - ("1.23.9", "(Version 1.23.9)\n") + [ ("0.0.0", "$(Version$0.0.0)") + ("1.23.9", "$(Version$1.23.9)") ("0..0", "") (".0.0", "") ("0..", "") @@ -33,8 +33,8 @@ class iso _TestParseDate is UnitTest fun apply(h: TestHelper) => ParseTest(h, ChangelogParser.date()).run( - [ ("2017-04-07", "(Date 2017-04-07)\n") - ("0000-00-00", "(Date 0000-00-00)\n") + [ ("2017-04-07", "$(Date$2017-04-07)") + ("0000-00-00", "$(Date$0000-00-00)") ("0000-00-0", "") ("0000-0-00", "") ("000-00-00", "") @@ -48,8 +48,8 @@ class iso _TestParseEntries is UnitTest ParseTest(h, ChangelogParser.entries()).run( [ ("32-bit ARM port.", "") ( "- 32-bit ARM port.\n", - "(Entries\n (Entry - 32-bit ARM port.\n)\n)\n" ) - ("- abc\n - def\n\n", "(Entries\n (Entry - abc\n - def\n)\n)\n") + "$(Entries$(Entry$- 32-bit ARM port.\n))" ) + ("- abc\n - def\n\n", "$(Entries$(Entry$- abc\n - def\n))") ( """ - abc - def @@ -57,11 +57,11 @@ class iso _TestParseEntries is UnitTest - jkl """, - "(Entries\n (Entry - abc\n - def\n - ghi\n)\n)\n" ) + "$(Entries$(Entry$- abc\n - def\n - ghi\n))" ) ( "- @fowles: handle regex empty match.\n", - "(Entries\n (Entry - @fowles: handle regex empty match.\n)\n)\n" ) + "$(Entries$(Entry$- @fowles: handle regex empty match.\n))" ) ( "- Upgrade to LLVM 3.9.1 ([PR #1498](https://github.com/ponylang/ponyc/pull/1498))\n", - "(Entries\n (Entry - Upgrade to LLVM 3.9.1 ([PR #1498](https://github.com/ponylang/ponyc/pull/1498))\n)\n)\n" ) + "$(Entries$(Entry$- Upgrade to LLVM 3.9.1 ([PR #1498](https://github.com/ponylang/ponyc/pull/1498))\n))" ) ( """ - stuff @@ -73,16 +73,7 @@ class iso _TestParseEntries is UnitTest # """, - """ - (Entries - (Entry - stuff - ) - (Entry - things - ) - (Entry - more things - ) - ) - """ + "$(Entries$(Entry$- stuff\n)$(Entry$- things\n)$(Entry$- more things\n))" ) ]) @@ -96,12 +87,8 @@ class iso _TestParseHead is UnitTest All notable changes to the Pony compiler and standard library will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com/). """, - """ - ( # Change Log - - All notable changes to the Pony compiler and standard library will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com/). - ) - """) + "$($All notable changes to the Pony compiler and standard library will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com/).)" + ) ( """ # Change Log @@ -109,27 +96,32 @@ class iso _TestParseHead is UnitTest ## [unreleased] - unreleased """, - """ - ( # Change Log + "$($Some other text)" + ) + ( """ + # Change Log - Some other text - ) - """ ) + Some other text that contains: `## [unreleased] - unreleased` + + ## [unreleased] - unreleased + """, + "$($Some other text that contains: `## [unreleased] - unreleased`)" + ) ( """ # Change Log - Some other text that contains: - `## [unreleased] - unreleased` ## [unreleased] - unreleased """, - """ - ( # Change Log + "$()" + ) + ( """ + # Change Log - Some other text that contains: - `## [unreleased] - unreleased` - ) - """ ) + ## [unreleased] - unreleased + """, + "$()" + ) ]) class iso _TestParseChangelog is UnitTest @@ -151,7 +143,7 @@ class iso _TestParseChangelog is UnitTest let changelog = Changelog(ast)? h.assert_eq[String](source, changelog.string()) else - h.log(recover val Printer(r) end) + h.log(recover val _Printer(r) end) h.fail() end | (let offset: USize, let r: Parser val) => @@ -251,7 +243,8 @@ class ParseTest let source' = Source.from_string(source) match recover val _parser.parse(source') end | (_, let r: (AST | Token | NotPresent)) => - let result = recover val Printer(r) end + let result = recover val _Printer(r) end + _h.log(recover Printer(r) end) _h.assert_eq[String](expected, result) | (let offset: USize, let r: Parser val) => let e = recover val SyntaxError(source', offset, r) end @@ -275,14 +268,14 @@ class _ReleaseTest | (let n: USize, let r: (AST | Token | NotPresent)) => match r | let ast: AST => - _h.log(recover val Printer(ast) end) + _h.log(recover val _Printer(ast) end) // _h.log(Changelog(ast)?.string()) let changelog = Changelog(ast)? .> 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.log(recover val _Printer(r) end) _h.fail() end | (let offset: USize, let r: Parser val) => @@ -302,3 +295,22 @@ primitive _Logv end) end h.log(consume str) + +primitive _Printer + fun apply(p: ASTChild, depth: USize = 0, indent: String = " ", + s: String ref = String): String ref + => + s.append("$(") + s.append(p.label().text()) + + match p + | let ast: AST => + for child in ast.children.values() do + _Printer(child, depth + 1, indent, s) + end + | let token: Token => + s.append("$") + s.append(token.source.content, token.offset, token.length) + end + s.append(")") + s diff --git a/tests/verification.sh b/tests/verification.sh new file mode 100755 index 0000000..1239025 --- /dev/null +++ b/tests/verification.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +echo_red() { + echo -e "\x1b[31;1m$1\x1b[0m" +} + +stable env ponyc -d -V1 .. -b changelog-tool + +for f in bad-changelogs/*.md; do + if ./changelog-tool verify "$f"; then + echo_red "changelog-tool failed to catch bad changelog: $f" + exit 1 + fi +done + +rm changelog-tool