diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55ebdce..a11093d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: run: sbt ++${{ matrix.scala }} scripted - name: Compress target directories - run: tar cf targets.tar sbt/target target meta/docs/target cli/target core/target example/target project/target + run: tar cf targets.tar sbt/target meta/integration-tests/target target meta/docs/target cli/target core/target example/target project/target - name: Upload target directories uses: actions/upload-artifact@v2 diff --git a/README.md b/README.md index b5ef96f..c95e383 100644 --- a/README.md +++ b/README.md @@ -98,123 +98,6 @@ The pattern supports two wildcards: The --dir, --jar, and --url options specify the directories and JAR files that are used when locating SemanticDB files. Each of these can be provided multiple times. -By default, the classpath that was used when executing Scala2PlantUML is also used. - - -d, --dir Directories of the SemanticDB target roots containing META-INF/semanticdb/**/*.semanticdb files. - - Example: - --dir 'my-project/target/scala-2.13/meta' - - -j, --jar JAR containing META-INF/semanticdb/**/*.semanticdb files. - - Example: - --jar 'foo.jar' - - -u, --url A URL to a JAR containing META-INF/semanticdb/**/*.semanticdb files. - - Example: - --url 'https://repo1.maven.org/maven2/com/example/foo/foo_2.13/1.0.0/foo_2.13-1.0.0-semanticdb.jar' - -The --project and --source options specify where within the search locations the SemanticDB files can be found. -Each of these can be provided multiple times. The result will be all combinations of projects and source roots. - - -p, --project The name of the projects that have SemanticDB files. - - The project name will be used when looking for SemanticDB files such as: - META-INF/semanticdb///*.semanticdb - - An empty project name will search in: - META-INF/semanticdb//*.semanticdb - - Default: '' - - Example: - --project my-project - - -s, --source The directory relative to the project where the source files were located. - - The source will be used when looking for SemanticDB files such as - META-INF/semanticdb///*.semanticdb. - - Default: src/main/scala - - Example: - --source 'source/scala' - - -l, --max-level The maximum number of levels that will be traversed when following symbol references. - - This means that parent symbols that would be beyond the max level will not be shown. - - A diagram with a max-level of 1 will only contain the initial symbol. - - Default: Unlimited - - Example: - --max-level 3 - - -o, --output Write the output to the given file. - - Example: - --output docs/diagrams/my-project.puml - - -c, --colour Enables coloured output. - - Default: true - - Example: - --colour false - - -v, --verbose Increases the log level. - - This can be provided twice for the most verbose logging. - - Example: - -vv - - -h, --help - --version -Error: Missing argument symbol -Scala2PlantUML version 0.2.0 -Usage: scala2plantuml [options] symbol - -Scala2PlantUML generates PlantUML Class Diagrams from Scala SemanticDB files. - - symbol The symbol to use as the starting point for generating the diagram. - - To get a symbol from a class name, convert the package name separate '.' to '/' and add a '#' - suffix. For an object use a suffix of '.'. - - See https://scalameta.org/docs/semanticdb/specification.html#symbol-1 for the full syntax. - - Examples: - 'com/example/Foo#' (class com.example.Foo) - 'com/example/Foo.' (object com.example.Foo) - 'com/example/Foo.bar.' (value/variable bar on object com.example.Foo) - 'com/example/Foo#baz().' (function baz on class com.example.Foo) - -The --include and --exclude options control which symbols will be processed. Each of these can be provided multiple times. - -The pattern supports two wildcards: -1) ** (matches any character) -2) * (matches all characters except for '/') - - -i, --include Only include symbols that match the pattern. - - Default: '**' - - Example: - --include 'com/example/**/model/*' - - -e, --exclude Excludes all symbols that match the pattern. - - Default: 'scala/**', 'java/**' - - Example: - --exclude 'com/example/**/data/*' - -The --dir, --jar, and --url options specify the directories and JAR files that are used when locating SemanticDB files. -Each of these can be provided multiple times. - By default, the classpath that was used when executing Scala2PlantUML is also used. -d, --dir Directories of the SemanticDB target roots containing META-INF/semanticdb/**/*.semanticdb files. diff --git a/build.sbt b/build.sbt index 6908ee2..ef9f674 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import com.typesafe.tools.mima.core._ + val scala212 = "2.12.13" val scala213 = "2.13.4" val supportedScalaVersions = List(scala212, scala213) @@ -75,7 +77,7 @@ inThisBuild( ) ), githubWorkflowPublishTargetBranches := List(RefPredicate.StartsWith(Ref.Tag("v"))), - githubWorkflowTargetTags ++= Seq("v*"), + githubWorkflowTargetTags ++= List("v*"), pgpSigningKey := Some("0x8DB7DFA142551359!"), // This needs to be set otherwise the GitHub workflow plugin gets confused about which // version to use for the publish job. @@ -87,6 +89,9 @@ inThisBuild( val commonProjectSettings = List( isScala213 := isScala213Setting.value, + // Who cares about these. Forwards binary compatibility is used as an approximation for source + // backwards compatibility and missing classes isn't a problem. + mimaForwardIssueFilters += "0.2.0" -> List(ProblemFilters.exclude[MissingClassProblem]("nz.co.bottech.scala2plantuml.*")), name := s"${(LocalRootProject / name).value}-${name.value}", scalastyleFailOnError := true, scalastyleFailOnWarning := true, @@ -109,7 +114,7 @@ val metaProjectSettings = List( ) lazy val root = (project in file(".")) - .aggregate(cli, core, docs, example, sbtProject) + .aggregate(cli, core, docs, example, integrationTests, sbtProject) .settings(metaProjectSettings) .settings( crossScalaVersions := supportedScalaVersions, @@ -156,6 +161,18 @@ lazy val cli = project ) ) +// Use a separate project rather than a configuration to get IntelliJ support. +lazy val integrationTests = (project in (file("meta") / "integration-tests")) + .settings(metaProjectSettings) + .settings( + libraryDependencies ++= List( + "com.lihaoyi" %% "utest" % utestVersion % Test + ), + testFrameworks += new TestFramework("utest.runner.Framework"), + Test / fork := true, + Test / javaOptions += s"-Dit.classpath=${(cli / Compile / fullClasspathAsJars).value.map(_.data).mkString(":")}" + ) + lazy val sbtProject = (project in file("sbt")) .dependsOn(core) .enablePlugins(SbtPlugin) @@ -191,7 +208,7 @@ lazy val sbtProject = (project in file("sbt")) scriptedLaunchOpts += s"-Dplugin.version=${version.value}" ) -lazy val docs = (project in file("meta/docs")) +lazy val docs = (project in (file("meta") / "docs")) // Include build info here so that we can override the version. .enablePlugins(BuildInfoPlugin, MdocPlugin) .dependsOn(cli) diff --git a/cli/src/main/scala/nz/co/bottech/scala2plantuml/ConfigParser.scala b/cli/src/main/scala/nz/co/bottech/scala2plantuml/ConfigParser.scala index 8903ccb..8d9f9bb 100644 --- a/cli/src/main/scala/nz/co/bottech/scala2plantuml/ConfigParser.scala +++ b/cli/src/main/scala/nz/co/bottech/scala2plantuml/ConfigParser.scala @@ -1,19 +1,24 @@ package nz.co.bottech.scala2plantuml -import scopt.{DefaultOEffectSetup, DefaultOParserSetup, OEffectSetup, OParser, OParserSetup} +import scopt._ import java.io.File object ConfigParser { + final private object Terminated extends RuntimeException + private val parserSetup: OParserSetup = new DefaultOParserSetup { override def showUsageOnError: Option[Boolean] = Some(true) } private val effectSetup: OEffectSetup = new DefaultOEffectSetup { + // Don't exit otherwise sbt traps this and there are confusing errors. // This is especially troublesome when running this from mdoc. - override def terminate(exitState: Either[String, Unit]): Unit = () + @SuppressWarnings(Array("org.wartremover.warts.Throw")) + override def terminate(exitState: Either[String, Unit]): Unit = + throw Terminated } // scalastyle:off method.length @@ -218,7 +223,10 @@ object ConfigParser { // scalastyle:on method.length def parse(args: Array[String]): Option[Config] = - OParser.parse(parser, args, Config(), parserSetup, effectSetup) + try OParser.parse(parser, args, Config(), parserSetup, effectSetup) + catch { + case Terminated => None + } private def validateConfig(config: Config): Either[String, Unit] = if (config.ignore(config.symbol)) Left("Symbol must match include patterns and not match exclude patterns.") diff --git a/meta/integration-tests/src/test/scala/nz/co/bottech/scala2plantuml/cli/CLIItTests.scala b/meta/integration-tests/src/test/scala/nz/co/bottech/scala2plantuml/cli/CLIItTests.scala new file mode 100644 index 0000000..f00051e --- /dev/null +++ b/meta/integration-tests/src/test/scala/nz/co/bottech/scala2plantuml/cli/CLIItTests.scala @@ -0,0 +1,17 @@ +package nz.co.bottech.scala2plantuml.cli + +import utest._ + +import scala.sys.process._ + +object CLIItTests extends TestSuite { + + private val classpath = Option(System.getProperty("it.classpath")).get + + val tests: Tests = Tests { + test("show help") { + val exitCode = List("java", "-classpath", classpath, "nz.co.bottech.scala2plantuml.Scala2PlantUML").! + assert(exitCode == 0) + } + } +}