diff --git a/.github/workflows/diktat.yml b/.github/workflows/diktat.yml index 97b331ff2e..0d04f84ae8 100644 --- a/.github/workflows/diktat.yml +++ b/.github/workflows/diktat.yml @@ -1,4 +1,4 @@ -name: Run diKTat from release version +name: Run diKTat (release) on: push: diff --git a/.github/workflows/diktat_snapshot.yml b/.github/workflows/diktat_snapshot.yml index c194575cad..742ab6255b 100644 --- a/.github/workflows/diktat_snapshot.yml +++ b/.github/workflows/diktat_snapshot.yml @@ -1,4 +1,4 @@ -name: Run diKTat from snapshot +name: Run diKTat (snapshot) on: push: @@ -12,7 +12,6 @@ env: jobs: diktat_snapshot_check: runs-on: ubuntu-20.04 - steps: - uses: actions/checkout@v2.4.0 - name: Set up JDK 11 @@ -47,4 +46,9 @@ jobs: mvn -B versions:set-property -Dproperty=diktat-check.version -DnewVersion=${{ env.BASE_VERSION }}-pre - name: Run diktat snapshot via maven plugin run: | - mvn -B diktat:check@diktat -Ddiktat.debug=true + mvn -B diktat:check@diktat -Ddiktat.debug=true -Ddiktat.githubActions=true + - name: Upload SARIF to Github using the upload-sarif action + uses: github/codeql-action/upload-sarif@v1 + if: ${{ always() }} + with: + sarif_file: ${{ github.workspace }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7029e242d..1e7f8f390f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -If you reading this - then you have decided to contribute to our project. Oh, poor you... +If you are reading this - then you have decided to contribute to our project. Oh, poor you... Rules are very simple: 1. Fork this repository to your own account 2. Make your changes and verify that tests pass (or wait that our CI/CD will do everything for you) @@ -8,6 +8,29 @@ Rules are very simple: 4. Submit a pull request 5. Participate in the code review process by responding to feedback +# Technical part + +Main components are: +1) diktat-rules — number of rules that are supported by diKTat; +2) diktat-test-framework — functional/unit test framework that can be used for running your code fixer on the initial code and compare it with the expected result; +3) also see our demo: diktat-demo in a separate repository. + +Mainly we wanted to create a common configurable mechanism that +will give us a chance to enable/disable and customize all rules. +That's why we added logic for: +1) Parsing `.yml` file with configurations of rules and passing it to visitors; +2) Passing information about properties to visitors. + This information is very useful, when you are trying to get, + for example, a filename of file where the code is stored; +3) We added a bunch of visitors, checkers and fixers that will extended KTlint functionaliity with code style rules; +4) We have proposed a code style for Kotlin language. + +Before you make a pull request, make sure the build is clean as we have lot of tests and other prechecks: + +```bash +$ mvn clean install +``` + # Hooks We have some hooks to a commit messages: @@ -20,4 +43,4 @@ Brief Description 2) Long description ``` -2) Please also do not forget to update documentation on Wiki after the merge approval and before merge. \ No newline at end of file +2) Please also do not forget to update documentation on Wiki after the merge approval and before merge. diff --git a/README.md b/README.md index b8d7429acd..42f8afa871 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,11 @@ ![Build and test](https://github.com/cqfn/diKTat/workflows/Build%20and%20test/badge.svg) ![deteKT static analysis](https://github.com/cqfn/diKTat/workflows/Run%20deteKT/badge.svg) ![diKTat code style](https://github.com/cqfn/diKTat/workflows/Run%20diKTat%20from%20release%20version/badge.svg?branch=master) -[![License](https://img.shields.io/github/license/cqfn/diKtat)](https://github.com/cqfn/diKTat/blob/master/LICENSE) [![codecov](https://codecov.io/gh/analysis-dev/diKTat/branch/master/graph/badge.svg)](https://codecov.io/gh/analysis-dev/diKTat) [![Releases](https://img.shields.io/github/v/release/cqfn/diKTat)](https://github.com/cqfn/diKTat/releases) [![Maven Central](https://img.shields.io/maven-central/v/org.cqfn.diktat/diktat-rules)](https://mvnrepository.com/artifact/org.cqfn.diktat) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fcqfn%2FdiKTat.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fcqfn%2FdiKTat?ref=badge_shield) -[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/) [![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/diktat_help) [![Hits-of-Code](https://hitsofcode.com/github/cqfn/diktat)](https://hitsofcode.com/view/github/cqfn/diktat) @@ -35,16 +33,15 @@ Now diKTat was already added to the lists of [static analysis tools](https://git There are several tools like `detekt` and `ktlint` that are doing static analysis. Why do I need diktat? First of all - actually you can combine diktat with any other static analyzers. And diKTat is even using ktlint framework for parsing the code into the AST. -And we are trying to contribute to those projects. Main features of diktat are the following: -1) **More inspections.** It has 100+ inspections that are tightly coupled with it's codestyle. - -2) **Unique inspections** that are missing in other linters. +1) **More inspections.** It has 100+ inspections that are tightly coupled with it's [Codestyle](info/guide/diktat-coding-convention.md). + +2) **Unique [Inspections](info/available-rules.md)** that are missing in other linters. -3) **Highly configurable**. Each and every inspection can be configured and suppressed both from the code or from the configuration file. +3) **Highly configurable**. Each and every inspection can be [configured](#config) or [suppressed](#suppress). -4) **Strict detailed coding convention** that you can use in your project. +4) **Strict detailed [Codestyle](info/guide/diktat-coding-convention.md)** that you can adopt and use in your project. ## Run as CLI-application
@@ -63,7 +60,10 @@ Main features of diktat are the following: ```bash $ curl -sSLO https://github.com/cqfn/diKTat/releases/download/v1.0.2/diktat-1.0.2.jar ``` - +
+ +
+Run diktat: 3. Finally, run KTlint (with diKTat injected) to check your `*.kt` files in `dir/your/dir`: ```bash $ ./ktlint -R diktat.jar --disabled_rules=standard "dir/your/dir/**/*.kt" @@ -72,25 +72,6 @@ Main features of diktat are the following: To **autofix** all code style violations use `-F` option.
-## GitHub Native Integration -We suggest everyone to use common ["sarif"](https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html) format as a `reporterType` in CI/CD. -GitHub has an [integration](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) -with SARIF format and provides you a native reporting of diktat issues in Pull Requests. - -```text - reporterType = "sarif" - output = "diktat-report.sarif" -``` - -Add the following code to your GitHub Action to upload diktat sarif report (after it was generated). - -```yml - - name: Upload SARIF to Github using the upload-sarif action - uses: github/codeql-action/upload-sarif@v1 - if: ${{ always() }} - with: - sarif_file: build/diktat-report.sarif -``` ## Run with Maven using diktat-maven-plugin This plugin is available since version 0.1.3. You can see how it is configured in our project for self-checks: [pom.xml](pom.xml). @@ -256,7 +237,44 @@ Diktat can be run via spotless-maven-plugin since version 2.8.0 ``` -## Customizations via `diktat-analysis.yml` +## GitHub Native Integration +We suggest everyone to use common ["sarif"](https://docs.oasis-open.org/sarif/sarif/v2.0/sarif-v2.0.html) format as a `reporterType` in CI/CD. +GitHub has an [integration](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) +with SARIF format and provides you a native reporting of diktat issues in Pull Requests. + +![img.png](example.png) + +
+ Github Integration +1) Add the following configuration to your project's setup for GitHub Actions: + +Gradle Plugin: +```text + githubActions = true +``` + +Maven Plugin (pom.xml): +```xml + true +``` + +Maven Plugin (cli options): +```text +mvn -B diktat:check@diktat -Ddiktat.githubActions=true +``` + +2) Add the following code to your GitHub Action to upload diktat SARIF report (after it was generated): + +```yml + - name: Upload SARIF to Github using the upload-sarif action + uses: github/codeql-action/upload-sarif@v1 + if: ${{ always() }} + with: + sarif_file: ${{ github.workspace }} +``` +
+ +## Customizations via `diktat-analysis.yml` In KTlint, rules can be configured via `.editorconfig`, but this does not give a chance to customize or enable/disable @@ -281,7 +299,11 @@ Note, that you can specify and put `diktat-analysis.yml` that contains configura See default configuration in [diktat-analysis.yml](diktat-rules/src/main/resources/diktat-analysis.yml) \ Also see [the list of all rules supported by diKTat](info/available-rules.md). -## Suppress warnings on individual code blocks + +## Suppress warnings/inspections + +
+Suppress warnings on individual code blocks In addition to enabling/disabling warning globally via config file (`enable = false`), you can suppress warnings by adding `@Suppress` annotation on individual code blocks For example: @@ -293,9 +315,11 @@ class SomeClass { } } -``` +``` +
-## Suppress groups of inspections +
+Suppress groups of inspections It is easy to suppress even groups of inspections in diKTat. These groups are linked to chapters of [Codestyle](info/guide/diktat-coding-convention.md). @@ -305,7 +329,8 @@ To disable chapters, you will need to add the following configuration to common disabledChapters: "1, 2, 3" ``` -Mapping of inspections to chapters can be found in [Groups of Inspections](info/rules-mapping.md) . +Mapping of inspections to chapters can be found in [Groups of Inspections](info/rules-mapping.md). +
## Running against the baseline When setting up code style analysis on a large existing project, one often doesn't have an ability to fix all findings at once. @@ -318,27 +343,5 @@ java -jar ktlint -R dikat.jar --baseline=diktat-baseline.xml **/*.kt or with corresponding configuration options in maven or gradle plugins. Baseline report is intended to be added into the VCS, but it can be removed and re-generated later, if needed. -## How to contribute? - -Main components are: -1) diktat-rules — number of rules that are supported by diKTat; -2) diktat-test-framework — functional/unit test framework that can be used for running your code fixer on the initial code and compare it with the expected result; -3) also see our demo: diktat-demo in a separate repository. - -Mainly we wanted to create a common configurable mechanism that -will give us a chance to enable/disable and customize all rules. -That's why we added logic for: -1) Parsing `.yml` file with configurations of rules and passing it to visitors; -2) Passing information about properties to visitors. -This information is very useful, when you are trying to get, -for example, a filename of file where the code is stored; -3) We added a bunch of visitors, checkers and fixers that will extended KTlint functionaliity with code style rules; -4) We have proposed a code style for Kotlin language. - -Before you make a pull request, make sure the build is clean as we have lot of tests and other prechecks: - -```bash -$ mvn clean install -``` - -Also see our [Contributing Policy](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) +## Contribution +See our [Contributing Policy](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt index 0adb9e78ec..b2fcd797fa 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt @@ -24,6 +24,11 @@ open class DiktatExtension( */ var debug = false + /** + * Property that will be used if you need to publish the report to GitHub + */ + var githubActions = false + /** * Type of the reporter to use */ diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt index 6f983e5861..91ea183895 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt @@ -157,22 +157,37 @@ open class DiktatJavaExecTaskBase @Inject constructor( // appending the flag with the reporter setReporter(diktatExtension, flag) - if (diktatExtension.output.isNotEmpty()) { - flag.append(",output=${diktatExtension.output}") + val outFlag = when { + // githubActions should have higher priority than a custom input + diktatExtension.githubActions -> ",output=${project.projectDir}/${project.name}" + diktatExtension.output.isNotEmpty() -> ",output=${diktatExtension.output}" + else -> "" } + flag.append(outFlag) + return flag.toString() } + @Suppress("SAY_NO_TO_VAR") private fun setReporter(diktatExtension: DiktatExtension, flag: java.lang.StringBuilder) { val name = diktatExtension.reporter.trim() val validReporters = listOf("sarif", "plain", "json", "html") - if (name.isEmpty() || !validReporters.contains(name)) { + var reporterFlag = if (name.isEmpty() || !validReporters.contains(name)) { project.logger.warn("Reporter name $name was not specified or is invalid. Falling to 'plain' reporter") - flag.append("--reporter=plain") + "--reporter=plain" } else { - flag.append("--reporter=$name") + "--reporter=$name" + } + + // githubActions should have higher priority than a custom input + if (diktatExtension.githubActions) { + // need to set user.home specially for ktlint, so it will be able to put a relative path URI in SARIF + System.setProperty("user.home", project.projectDir.toString()) + reporterFlag = "--reporter=sarif" } + + flag.append(reporterFlag) } @Suppress("MagicNumber") diff --git a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt index 617e8a4dad..df2802b59e 100644 --- a/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt +++ b/diktat-maven-plugin/src/main/kotlin/org/cqfn/diktat/plugin/maven/DiktatBaseMojo.kt @@ -14,6 +14,7 @@ import com.pinterest.ktlint.reporter.html.HtmlReporter import com.pinterest.ktlint.reporter.json.JsonReporter import com.pinterest.ktlint.reporter.plain.PlainReporter import com.pinterest.ktlint.reporter.sarif.SarifReporter +import org.apache.maven.execution.MavenSession import org.apache.maven.plugin.AbstractMojo import org.apache.maven.plugin.MojoExecutionException import org.apache.maven.plugin.MojoFailureException @@ -34,6 +35,12 @@ abstract class DiktatBaseMojo : AbstractMojo() { @Parameter(property = "diktat.debug") var debug = false + /** + * Property that will be used if you need to publish the report to GitHub + */ + @Parameter(property = "diktat.githubActions") + var githubActions = false + /** * Type of the reporter to use */ @@ -80,6 +87,9 @@ abstract class DiktatBaseMojo : AbstractMojo() { @Parameter(property = "diktat.excludes", defaultValue = "") lateinit var excludes: List + @Parameter(defaultValue = "\${session}", readonly = true) + private lateinit var mavenSession: MavenSession + /** * @param params instance of [KtLint.Params] used in analysis */ @@ -106,6 +116,7 @@ abstract class DiktatBaseMojo : AbstractMojo() { val baselineResults = baseline?.let { loadBaseline(it.absolutePath) } ?: CurrentBaseline(emptyMap(), false) reporterImpl = resolveReporter(baselineResults) + reporterImpl.beforeAll() val lintErrors: MutableList = mutableListOf() inputs @@ -121,16 +132,30 @@ abstract class DiktatBaseMojo : AbstractMojo() { } private fun resolveReporter(baselineResults: CurrentBaseline): Reporter { - val output = if (this.output.isBlank()) System.`out` else PrintStream(FileOutputStream(this.output, true)) - - val actualReporter = when (this.reporter) { - "sarif" -> SarifReporter(output) - "plain" -> PlainReporter(output) - "json" -> JsonReporter(output) - "html" -> HtmlReporter(output) - else -> { - log.warn("Reporter name ${this.reporter} was not specified or is invalid. Falling to 'plain' reporter") - PlainReporter(output) + val output = if (this.output.isBlank()) { + if (this.githubActions) { + // need to set user.home specially for ktlint, so it will be able to put a relative path URI in SARIF + System.setProperty("user.home", mavenSession.executionRootDirectory) + PrintStream(FileOutputStream("${mavenProject.basedir}/${mavenProject.name}.sarif", false)) + } else { + System.`out` + } + } else { + PrintStream(FileOutputStream(this.output, false)) + } + + val actualReporter = if (this.githubActions) { + SarifReporter(output) + } else { + when (this.reporter) { + "sarif" -> SarifReporter(output) + "plain" -> PlainReporter(output) + "json" -> JsonReporter(output) + "html" -> HtmlReporter(output) + else -> { + log.warn("Reporter name ${this.reporter} was not specified or is invalid. Falling to 'plain' reporter") + PlainReporter(output) + } } } diff --git a/example.png b/example.png new file mode 100644 index 0000000000..272f8d44dc Binary files /dev/null and b/example.png differ