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

refactor: #195 verbose and porcelain are a cli feature and not in core #210

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
114 changes: 113 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.internal.logging.text.StyledTextOutput
import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.gradle.kotlin.dsl.support.serviceOf
import java.util.*

plugins {
id("org.jetbrains.kotlin.jvm") version Versions.kotlin apply false
id("com.palantir.graal") version "0.10.0" apply false
id("com.palantir.git-version") version "0.12.3"
id("com.adarshr.test-logger") version "3.1.0" apply false
id("maven-publish")
signing
}
Expand Down Expand Up @@ -48,4 +53,111 @@ configure(subprojects) {
tasks.withType<KotlinJvmCompile>().all {
kotlinOptions.jvmTarget = "1.8"
}

tasks.withType<Test>().all {
testLogging {
// set options for log level LIFECYCLE
events = setOf(
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT
)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true

// set options for log level DEBUG and INFO
debug {
events = setOf(
TestLogEvent.STARTED,
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_ERROR,
TestLogEvent.STANDARD_OUT
)
exceptionFormat = TestExceptionFormat.FULL
}
info.events = debug.events
info.exceptionFormat = debug.exceptionFormat

addTestListener(object : TestListener {
val failedTests = mutableListOf<TestDescriptor>()
val skippedTests = mutableListOf<TestDescriptor>()

override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}

/**
* Add test to list if failed or skipped
*/
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
when (result.resultType) {
TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor)
TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor)
else -> Unit
}
}

/**
* Print test suite summary
*/
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent != null) {
return
}

val summaryStyledTextOutput = serviceOf<StyledTextOutputFactory>().create("test-summary")

val output = "Test results: ${result.resultType} (${result.testCount} tests, " +
"${result.successfulTestCount} passed, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped)"

summaryStyledTextOutput.println()

val style = when (result.resultType) {
TestResult.ResultType.SUCCESS -> StyledTextOutput.Style.SuccessHeader
TestResult.ResultType.SKIPPED -> StyledTextOutput.Style.SuccessHeader
TestResult.ResultType.FAILURE -> StyledTextOutput.Style.FailureHeader
else -> StyledTextOutput.Style.Error
}
summaryStyledTextOutput.style(style).println(output)

failedTests.takeIf { it.isNotEmpty() }?.summary("FAILED", summaryStyledTextOutput.style(StyledTextOutput.Style.Failure))
summaryStyledTextOutput.println()
skippedTests.takeIf { it.isNotEmpty() }?.summary("SKIPPED", summaryStyledTextOutput.style(StyledTextOutput.Style.Info))
}

fun List<TestDescriptor>.summary(
subject: String,
subjectStyledTextOutput: StyledTextOutput,
prefix: String = " "
) {
subjectStyledTextOutput.println(subject)
forEach { test -> subjectStyledTextOutput.println(prefix + test.computeFullName()) }
}

fun TestDescriptor.computeFullName(separator: String = " > "): String = this.computePath()
.dropWhile { it.className == null }
.map { it.displayName }
.joinToString(separator)

fun TestDescriptor.computePath(): Sequence<TestDescriptor> {
val path = LinkedList<TestDescriptor>()
path.addFirst(this)

var testDescriptorParent = parent
while (testDescriptorParent != null) {
path.addFirst(testDescriptorParent)
testDescriptorParent = testDescriptorParent.parent
}

return path.asSequence()
}
})
}
}
}
6 changes: 3 additions & 3 deletions docs/cli-configuration.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Options
|Names|Default value|Needs argument|Possible arguments|Description|Examples|
|---|---|---|---|---|---|
|--version, --v, -v, -version|No default value|✘|N/A|Show the version and exit||
|--version, --V, -V, -version|No default value|✘|N/A|Show the version and exit||
|--working-directory-path, --wd|No default value|✔| |path to the working directory. Can be relative but prefer absolute path. All relative paths configured will be relative to this path if set.||
|--config-file-path|No default value|✔| |Path to config file. File must exist.|```myProject/src/main/resources/config/datamaintain.properties```|
|--db-type|mongo|✔|```mongo``` or ```jdbc```|db type||
|--db-uri|No default value|✔|TEXT|mongo uri with at least database name. Ex: mongodb://localhost:27017/newName|```mongodb://localhost:27017/newName```|
|--trust-uri|false|✘|N/A|Deactivate all controls on the URI you provide Datamaintain||
|--mongo-tmp-path|/tmp/datamaintain.tmp|✔|TEXT|mongo tmp file path||
|--verbose, -v|false|✘|N/A|verbose||
|-vv|false|✘|N/A|verbose with more details||
|--config|No default value|✘|N/A|Print the configuration without executing the subcommand||
|-h, --help|No default value|✘|N/A|Display command help and exit||
# Subcommands
Expand All @@ -25,7 +27,6 @@
|--execution-mode|NORMAL|✔|```NORMAL``` or ```DRY```|execution mode||
|--action|RUN|✔|```RUN``` or ```MARK_AS_EXECUTED``` or ```OVERRIDE_EXECUTED```|script action||
|--allow-auto-override|false|✘|N/A|Allow datamaintain to automaticaly override scripts||
|--verbose|false|✘|N/A|verbose||
|--save-db-output|false|✘|N/A|save your script and db output||
|--print-db-output|false|✘|N/A|print your script and db output||
|--tag|No default value|✔| |Tag defined using glob path matchers. To define multiple tags, use option multiple times. Syntax example: MYTAG1=[pathMatcher1, pathMatcher2]|```MYTAG1=[pathMatcher1, pathMatcher2]```|
Expand All @@ -48,5 +49,4 @@
|Names|Default value|Needs argument|Possible arguments|Description|Examples|
|---|---|---|---|---|---|
|--path|./scripts/|✔|TEXT|path to the script you want to mark as executed|```scripts/myScript1.js```|
|--verbose|false|✘|N/A|verbose||
|-h, --help|No default value|✘|N/A|Display command help and exit||
37 changes: 19 additions & 18 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@

## Core configuration

| Key | Description | Default value | Mandatory? | Values examples |
|---|---|---|---|---|
| name | Allow to name your config. For now will only be used to logging purpose | None | no | |
| working.directory.path | Indicates the directory to use to find relative paths | Java default working directory path | no | |
| parent.config.path | Path to an other configuration file to inherit properties. Can be absolute or relative to `working.directory.path` | None | no | |
| default.script.action | The default script action | ```RUN``` | no | ```RUN``` or ```MARK_AS_EXECUTED``` |
| scan.path | Path to the folder containing all your scripts. Can be absolute or relative to `working.directory.path` | ```./scripts/``` | yes | |
| scan.identifier.regex | Regex that will be used to determine an identifier for each file. It has to contain a capturing group. Identifiers are then used to sort the scripts before running them. | ```(.*)``` (with this regex, the script's whole name will be its identifier) | no | With the regex ```(.*?)_.*```, a script named ```1.23_my-script.js``` will have ```1.23``` as its identifier |
| scan.tags.createFromFolder | If true, scripts will have their parent folders names as tags. Relative path to ```scan.path``` is used. | ```false``` | no | ```false``` or ```true``` |
| tag.*your_tag* | Glob paths to your scripts that you want to apply the tag "your_tag" on. To declare multiple tags, you will have to add multiple properties in your settings. A tag ```my_tag``` will have as as property name ```tag.my_tag``` **WARNING:** ALWAYS declare your tags using absolute paths. Relative paths and even using a tilde (~) won't do the trick. | | no | ```[data/*, script1.js, old/old_script1.js]``` |
| filter.tags.whitelisted | Scripts that have these tags will be considered | None | no | ```DATA,tag``` |
| filter.tags.blacklisted | Scripts that have these tags will be ignored. A script having a whitelisted tag and a blacklisted tag will be ignored | None | no | ```DATA,tag``` |
| prune.tags.to.run.again | Scripts that have these tags will be run, even they were already executed | None | no | ```tag,again``` |
| execution.mode | Execution mode. Possible values:<br />- ```NORMAL```: Regular execution: your scripts will be run on your database.<br />- ```DRY```: Scripts will not be executed. A full report of what would happen is you ran Datamaintain normally will be logged.<br /> | ```NORMAL``` | no | ```NORMAL```, ```DRY``` |
| verbose | If true, more logs will be printed. **WARNING:** Can't be used alongside with porcelain. If both are set, porcelain will prevail | ```false``` | no | ```true``` or ```false``` |
| prune.scripts.override.executed | Allow datamaintain to override a script if it detect a checksum change on a script already runned (assuming its filename) | ```false``` | no | ```true``` or ```false``` |
| db.trust.uri | Bypass all checks that could be done on your URI because you are very sure of it and think our checks are just liars | ```false``` | no | ```true``` or ```false``` |
| porcelain | For each executed script, print path relative to scan path **WARNING:** Can't be used alongside with verbose. If both are set, porcelain will prevail | ```false``` | no | ```true``` or ```false``` |
| Key | Description | Default value | Mandatory? | Values examples |
|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|---|
| name | Allow to name your config. For now will only be used to logging purpose | None | no | |
| working.directory.path | Indicates the directory to use to find relative paths | Java default working directory path | no | |
| parent.config.path | Path to an other configuration file to inherit properties. Can be absolute or relative to `working.directory.path` | None | no | |
| default.script.action | The default script action | ```RUN``` | no | ```RUN``` or ```MARK_AS_EXECUTED``` |
| scan.path | Path to the folder containing all your scripts. Can be absolute or relative to `working.directory.path` | ```./scripts/``` | yes | |
| scan.identifier.regex | Regex that will be used to determine an identifier for each file. It has to contain a capturing group. Identifiers are then used to sort the scripts before running them. | ```(.*)``` (with this regex, the script's whole name will be its identifier) | no | With the regex ```(.*?)_.*```, a script named ```1.23_my-script.js``` will have ```1.23``` as its identifier |
| scan.tags.createFromFolder | If true, scripts will have their parent folders names as tags. Relative path to ```scan.path``` is used. | ```false``` | no | ```false``` or ```true``` |
| tag.*your_tag* | Glob paths to your scripts that you want to apply the tag "your_tag" on. To declare multiple tags, you will have to add multiple properties in your settings. A tag ```my_tag``` will have as as property name ```tag.my_tag``` **WARNING:** ALWAYS declare your tags using absolute paths. Relative paths and even using a tilde (~) won't do the trick. | | no | ```[data/*, script1.js, old/old_script1.js]``` |
| filter.tags.whitelisted | Scripts that have these tags will be considered | None | no | ```DATA,tag``` |
| filter.tags.blacklisted | Scripts that have these tags will be ignored. A script having a whitelisted tag and a blacklisted tag will be ignored | None | no | ```DATA,tag``` |
| prune.tags.to.run.again | Scripts that have these tags will be run, even they were already executed | None | no | ```tag,again``` |
| execution.mode | Execution mode. Possible values:<br />- ```NORMAL```: Regular execution: your scripts will be run on your database.<br />- ```DRY```: Scripts will not be executed. A full report of what would happen is you ran Datamaintain normally will be logged.<br /> | ```NORMAL``` | no | ```NORMAL```, ```DRY``` |
| verbose | If true, more logs will be printed. **WARNING:** Can't be used alongside with porcelain. If both are set, porcelain will prevail | ```false``` | no | ```true``` or ```false``` |
| trace | If true, more logs will be printed than verbose. **WARNING:** Can't be used alongside with porcelain. If both are set, porcelain will prevail | ```false``` | no | ```true``` or ```false``` |
| prune.scripts.override.executed | Allow datamaintain to override a script if it detect a checksum change on a script already runned (assuming its filename) | ```false``` | no | ```true``` or ```false``` |
| db.trust.uri | Bypass all checks that could be done on your URI because you are very sure of it and think our checks are just liars | ```false``` | no | ```true``` or ```false``` |
| porcelain | For each executed script, print path relative to scan path **WARNING:** Can't be used alongside with verbose. If both are set, porcelain will prevail | ```false``` | no | ```true``` or ```false``` |

### Common driver configuration

Expand Down
Loading