From b63f0c471343f2256ccd970168db9724e89b865b Mon Sep 17 00:00:00 2001 From: Adrian Renner Date: Thu, 7 Sep 2023 13:21:59 +0200 Subject: [PATCH] --wip-- [skip ci] --- build.gradle.kts | 3 + buildSrc/build.gradle.kts | 24 ++ buildSrc/settings.gradle.kts | 14 + buildSrc/src/main/kotlin/my-detekt.gradle.kts | 26 ++ buildSrc/src/main/kotlin/my-ktlint.gradle.kts | 23 ++ buildSrc/src/main/resources/detekt.yml | 320 ++++++++++++++++++ gradle/libs.versions.toml | 6 + shared/src/commonMain/kotlin/App.kt | 2 +- 8 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/my-detekt.gradle.kts create mode 100644 buildSrc/src/main/kotlin/my-ktlint.gradle.kts create mode 100644 buildSrc/src/main/resources/detekt.yml diff --git a/build.gradle.kts b/build.gradle.kts index ab21504..65d83eb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,9 @@ plugins { alias(libs.plugins.org.jetbrains.compose).apply(false) alias(libs.plugins.org.jetbrains.kotlin.multiplatform).apply(false) alias(libs.plugins.org.jetbrains.kotlin.plugin.serialization).apply(false) + + id("my-ktlint") + id("my-detekt") } tasks.register("clean", Delete::class) { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..ae17fc7 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + `kotlin-dsl` +} + +kotlin { + jvmToolchain { + @Suppress("USELESS_CAST") // BUG: cast is necessary else languageVersion can't be resolved e.g. clean task + (this as JavaToolchainSpec) + .languageVersion + .set(JavaLanguageVersion.of(JavaVersion.VERSION_11.toString())) // TODO: Version number centralisation + } +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() // TODO: Version number centralisation + } +} + +dependencies { + implementation(libs.ktlint.gradle) + implementation(libs.detekt.gradle.plugin) + implementation(libs.gradle.versions.plugin) +} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..c4854de --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,14 @@ +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + gradlePluginPortal() + mavenCentral() + google() + } + + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/my-detekt.gradle.kts b/buildSrc/src/main/kotlin/my-detekt.gradle.kts new file mode 100644 index 0000000..cbbb891 --- /dev/null +++ b/buildSrc/src/main/kotlin/my-detekt.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("io.gitlab.arturbosch.detekt") +} + +dependencies { + detektPlugins("ru.kode:detekt-rules-compose:1.2.2") // TODO: Version number centralisation + detektPlugins("io.nlopez.compose.rules:detekt:0.1.10") // TODO: Version number centralisation +} + +detekt { + // Documentation: https://detekt.dev/gradle.html#options-for-detekt-configuration-closure + + source = files("src") // The directories where detekt looks for source files. + + config = files("${rootProject.rootDir}/buildSrc/src/main/resources/detekt.yml") + + parallel = true // Builds the AST in parallel. + + ignoredBuildTypes = listOf("release") // Don't create tasks for the specified build types (e.g. "release") + ignoredFlavors = listOf("production", "staging") // Don't create tasks for the specified build flavor (e.g. "production") + ignoredVariants = listOf("productionRelease") // Don't create tasks for the specified build variants (e.g. "productionRelease") + + basePath = projectDir.absolutePath + + allRules = true // Turns on all the rules. Default: false +} diff --git a/buildSrc/src/main/kotlin/my-ktlint.gradle.kts b/buildSrc/src/main/kotlin/my-ktlint.gradle.kts new file mode 100644 index 0000000..7b93657 --- /dev/null +++ b/buildSrc/src/main/kotlin/my-ktlint.gradle.kts @@ -0,0 +1,23 @@ +import org.jlleitschuh.gradle.ktlint.reporter.ReporterType + +plugins { + id("org.jlleitschuh.gradle.ktlint") +} + +configure { + version.set("0.49.1") + + android.set(true) + outputToConsole.set(true) + ignoreFailures.set(false) + enableExperimentalRules.set(false) + + reporters { reporter(ReporterType.PLAIN_GROUP_BY_FILE) } + + filter { + exclude("**/generated/**") + } + + verbose.set(false) + debug.set(false) +} diff --git a/buildSrc/src/main/resources/detekt.yml b/buildSrc/src/main/resources/detekt.yml new file mode 100644 index 0000000..5652422 --- /dev/null +++ b/buildSrc/src/main/resources/detekt.yml @@ -0,0 +1,320 @@ +console-reports: + active: true + +output-reports: + active: true + exclude: [ 'TxtOutputReport', 'XmlOutputReport', 'HtmlOutputReport' ] + +processors: + active: true + +compose: + # Rules from https://github.com/appKODE/detekt-rules-compose + ComposableEventParameterNaming: { active: true } + ComposableParametersOrdering: { active: true } + ComposeFunctionName: { active: true } + MissingModifierDefaultValue: { active: true } + ModifierDefaultValue: { active: true } + ModifierHeightWithText: { active: true } + ModifierParameterPosition: { active: true } + PublicComposablePreview: { active: true } + ReusedModifierInstance: { active: true } + TopLevelComposableFunctions: { active: true } + UnnecessaryEventHandlerParameter: { active: true } + +Compose: + # Rules from https://mrmans0n.github.io/compose-rules/ + CompositionLocalAllowlist: { active: false } + # You can optionally define a list of CompositionLocals that are allowed here + # allowedCompositionLocals: LocalSomething,LocalSomethingElse + ContentEmitterReturningValues: { active: true } + ModifierComposable: { active: true } + ModifierMissing: + active: true + checkModifiersForVisibility: only_public # Possible values are: `only_public`, `public_and_internal` and `all` + ModifierReused: { active: true } + ModifierWithoutDefault: { active: true } + MultipleEmitters: { active: true } + MutableParams: { active: true } + ComposableNaming: { active: true } + ComposableParamOrder: { active: true } + PreviewNaming: { active: true } + PreviewPublic: { active: true } + RememberMissing: { active: true } + UnstableCollections: { active: true } + ViewModelForwarding: { active: true } + ViewModelInjection: { active: true } + +comments: + active: true + AbsentOrWrongFileLicense: { active: false } + CommentOverPrivateFunction: { active: false } + CommentOverPrivateProperty: { active: false } + DeprecatedBlockTag: { active: true } + EndOfSentenceFormat: { active: false } + KDocReferencesNonPublicProperty: { active: false } + OutdatedDocumentation: { active: true } + UndocumentedPublicClass: { active: false } + UndocumentedPublicFunction: { active: false } + UndocumentedPublicProperty: { active: false } + +complexity: + active: false + +coroutines: + active: false + +empty-blocks: + active: true + EmptyCatchBlock: { active: true } + EmptyClassBlock: { active: true } + EmptyDefaultConstructor: { active: true } + EmptyDoWhileBlock: { active: true } + EmptyElseBlock: { active: true } + EmptyFinallyBlock: { active: true } + EmptyForBlock: { active: true } + EmptyFunctionBlock: + active: true + ignoreOverridden: true + EmptyIfBlock: { active: true } + EmptyInitBlock: { active: true } + EmptyKtFile: { active: true } + EmptySecondaryConstructor: { active: true } + EmptyTryBlock: { active: true } + EmptyWhenBlock: { active: true } + EmptyWhileBlock: { active: true } + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: [ 'equals', 'finalize', 'hashCode', 'toString' ] + InstanceOfCheckForException: { active: true } + NotImplementedDeclaration: + active: true + excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + ObjectExtendsThrowable: { active: false } # <- Postponed: Requires Type Resolution + PrintStackTrace: { active: true } + RethrowCaughtException: { active: true } + ReturnFromFinally: + active: false # <- Postponed: Requires Type Resolution + ignoreLabeled: false + SwallowedException: { active: true } + ThrowingExceptionFromFinally: { active: true } + ThrowingExceptionInMain: { active: true } + ThrowingExceptionsWithoutMessageOrCause: { active: true } + ThrowingNewInstanceOfSameException: { active: true } + TooGenericExceptionCaught: { active: false } + TooGenericExceptionThrown: { active: true } + +# formatting: { active: false } # ktLint rule set + +naming: + active: true + BooleanPropertyNaming: # <- Postponed: Requires Type Resolution + active: false + allowedPattern: '^(is|has|are)' + ignoreOverridden: true + ClassNaming: { active: true } + ConstructorParameterNaming: { active: true } + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_A-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [ ] + FunctionMaxLength: { active: false } + FunctionMinLength: { active: false } + FunctionNaming: + active: true + ignoreAnnotated: [ 'Composable' ] + FunctionParameterNaming: { active: true } + InvalidPackageDeclaration: { active: true } + LambdaParameterNaming: { active: true } + MatchingDeclarationName: { active: true } + MemberNameEqualsClassName: { active: true } + NoNameShadowing: # <- Postponed: Requires Type Resolution + active: false + NonBooleanPropertyPrefixedWithIs: # <- Postponed: Requires Type Resolution + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][_a-z0-9]*[a-z0-9])*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[a-z][A-Za-z0-9]*' + privatePropertyPattern: '[a-z][A-Za-z0-9]*' + VariableMaxLength: { active: false } + VariableMinLength: { active: false } + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + +performance: + active: true + ArrayPrimitive: { active: true } + CouldBeSequence: { active: false } # <- Postponed: Requires Type Resolution + ForEachOnRange: { active: true } + SpreadOperator: { active: false } + UnnecessaryTemporaryInstantiation: { active: true } + +potential-bugs: + active: true + AvoidReferentialEquality: { active: false } # <- Postponed: Requires Type Resolution + CastToNullableType: { active: false } + Deprecation: { active: false } + DontDowncastCollectionTypes: { active: false } # <- Postponed: Requires Type Resolution + DoubleMutabilityForCollection: { active: false } # <- Postponed: Requires Type Resolution + ElseCaseInsteadOfExhaustiveWhen: { active: false } # <- Postponed: Requires Type Resolution + EqualsAlwaysReturnsTrueOrFalse: { active: true } + EqualsWithHashCodeExist: { active: true } + ExitOutsideMain: { active: false } # <- Postponed: Requires Type Resolution + ExplicitGarbageCollectionCall: { active: true } + HasPlatformType: { active: false } # <- Postponed: Requires Type Resolution + IgnoredReturnValue: { active: false } # <- Postponed: Requires Type Resolution + ImplicitDefaultLocale: { active: false } # <- Postponed: Discuss + ImplicitUnitReturnType: { active: false } # <- Postponed: Requires Type Resolution + InvalidRange: { active: true } + IteratorHasNextCallsNextMethod: { active: true } + IteratorNotThrowingNoSuchElementException: { active: true } + LateinitUsage: { active: false } + MapGetWithNotNullAssertionOperator: { active: true } + MissingPackageDeclaration: { active: true } + NullCheckOnMutableProperty: { active: false } # <- Postponed: Requires Type Resolution + NullableToStringCall: { active: false } # <- Postponed: Requires Type Resolution + UnconditionalJumpStatementInLoop: { active: true } + UnnecessaryNotNullOperator: { active: false } # <- Postponed: Requires Type Resolution + UnnecessarySafeCall: { active: false } # <- Postponed: Requires Type Resolution + UnreachableCatchBlock: { active: false } # <- Postponed: Requires Type Resolution + UnreachableCode: { active: false } # <- Postponed: Requires Type Resolution + UnsafeCallOnNullableType: { active: false } # <- Postponed: Requires Type Resolution + UnsafeCast: { active: false } # <- Postponed: Requires Type Resolution + UnusedUnaryOperator: { active: false } # <- Postponed: Requires Type Resolution + UselessPostfixExpression: { active: true } + WrongEqualsTypeParameter: { active: true } + +style: + active: true + CanBeNonNullable: { active: false } # <- Postponed: Requires Type Resolution + CascadingCallWrapping: { active: true } + ClassOrdering: { active: true } + CollapsibleIfStatements: { active: true } + DataClassContainsFunctions: { active: false } # <- Postponed: Discussion worthy + DataClassShouldBeImmutable: { active: true } + DestructuringDeclarationWithTooManyEntries: { active: true } + EqualsNullCall: { active: true } + EqualsOnSignatureLine: { active: true } + ExplicitCollectionElementAccessMethod: { active: false } # <- Postponed: Requires Type Resolution + ExplicitItLambdaParameter: { active: true } + ExpressionBodySyntax: # <- TODO: Discuss in separate PR + active: false + includeLineWrapping: false + ForbiddenComment: + active: true + values: + - 'FIXME' + - 'STOPSHIP' + # - 'TODO' # <- TODO Enable in follow up PR + allowedPatterns: '' + customMessage: '' + excludes: [ '**/de/stocard/pay/**' ] # TODO remove after pay is decommissioned + ForbiddenImport: + active: true + imports: [ + 'java.text.SimpleDateFormat' # Old Java date APIs used: Use Java 8 dates instead. See DateTimeExt class for more info. + ] + ForbiddenMethodCall: # <- Postponed: Requires Type Resolution + active: false + methods: + - 'kotlin.io.print' + - 'kotlin.io.println' + ForbiddenSuppress: + active: true + rules: [ ] + ForbiddenVoid: # <- Postponed: Requires Type Resolution + active: false + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: { active: true } + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 0 + MagicNumber: { active: false } + MandatoryBracesIfStatements: { active: true } + MandatoryBracesLoops: { active: true } + MaxChainedCallsOnSameLine: { active: false } # <- Postponed: Requires Type Resolution + MaxLineLength: + active: true + maxLineLength: 140 + MayBeConst: { active: true } + ModifierOrder: { active: true } + MultilineLambdaItParameter: { active: false } # <- Postponed: Requires Type Resolution + NestedClassesVisibility: { active: true } + NewLineAtEndOfFile: { active: true } + NoTabs: { active: true } + NullableBooleanCheck: { active: false } # <- Postponed: Requires Type Resolution + ObjectLiteralToLambda: { active: false } # <- Postponed: Requires Type Resolution + OptionalAbstractKeyword: { active: true } + OptionalUnit: { active: true } + OptionalWhenBraces: { active: false } + PreferToOverPairSyntax: { active: false } # <- Postponed: Requires Type Resolution + ProtectedMemberInFinalClass: { active: true } + RedundantExplicitType: { active: false } + RedundantHigherOrderMapUsage: { active: false } # <- Postponed: Requires Type Resolution + RedundantVisibilityModifierRule: { active: true } + ReturnCount: { active: false } + SafeCast: { active: true } + SerialVersionUIDInSerializableClass: { active: false } + SpacingBetweenPackageAndImports: { active: true } + ThrowsCount: { active: false } + TrailingWhitespace: { active: true } + UnderscoresInNumericLiterals: { active: true } + UnnecessaryAbstractClass: { active: true } + UnnecessaryAnnotationUseSiteTarget: { active: true } + UnnecessaryApply: { active: false } # <- Postponed: Requires Type Resolution + UnnecessaryBackticks: { active: true } + UnnecessaryFilter: { active: false } # <- Postponed: Requires Type Resolution + UnnecessaryInheritance: { active: true } + UnnecessaryInnerClass: { active: false } # <- Postponed: Requires Type Resolution + UnnecessaryLet: { active: false } # <- Postponed: Requires Type Resolution + UnnecessaryParentheses: { active: false } # <- Postponed: Maybe enable later + UntilInsteadOfRangeTo: { active: true } + UnusedImports: { active: true } + UnusedPrivateClass: { active: true } + UnusedPrivateMember: + active: true + allowedNames: '(_|ignored|expected|serialVersionUID|LOG_TAG)' + ignoreAnnotated: [ + 'Preview', + 'StocardSmallPreview', + 'StocardSmallPreviews', + 'StocardMediumPreview', + 'StocardMediumPreviews', + 'StocardSpecialPreview', + 'StocardSpecialPreviews', + 'StocardSizesPreview', + 'StocardAllPreviews' + ] + UseAnyOrNoneInsteadOfFind: { active: false } # <- Postponed: Requires Type Resolution + UseArrayLiteralsInAnnotations: { active: true } + UseCheckNotNull: { active: false } + UseCheckOrError: { active: false } + UseDataClass: { active: false } + UseEmptyCounterpart: { active: false } # <- Postponed: Requires Type Resolution + UseIfEmptyOrIfBlank: { active: false } # <- Postponed: Requires Type Resolution + UseIfInsteadOfWhen: { active: false } + UseIsNullOrEmpty: { active: false } # <- Postponed: Requires Type Resolution + UseOrEmpty: { active: false } # <- Postponed: Requires Type Resolution + UseRequire: { active: false } + UseRequireNotNull: { active: false } + UselessCallOnNotNull: { active: false } # <- Postponed: Requires Type Resolution + UtilityClassWithPublicConstructor: { active: true } + VarCouldBeVal: { active: false } # <- Postponed: Requires Type Resolution + WildcardImport: { active: true } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7c027da..c1dfcb4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,6 +42,12 @@ arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-core-serialization = { module = "io.arrow-kt:arrow-core-serialization", version.ref = "arrow" } arrow-fx-coroutines = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } +# Linting, Formatting & dependancy version management +detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version = "1.22.0" } +gradle-versions-plugin = { module = "com.github.ben-manes:gradle-versions-plugin", version = "0.47.0" } +ktlint-gradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", version = "11.5.1" } + + [plugins] com-android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/shared/src/commonMain/kotlin/App.kt b/shared/src/commonMain/kotlin/App.kt index 0889bdd..ac83685 100644 --- a/shared/src/commonMain/kotlin/App.kt +++ b/shared/src/commonMain/kotlin/App.kt @@ -38,4 +38,4 @@ fun App() { } } -expect fun getPlatformName(): String \ No newline at end of file +expect fun getPlatformName(): String