Updated on 2026-08-14

This commit is contained in:
Tangem 2023-03-07 17:09:56 +03:00
parent a5a280e9b3
commit 6b377e0e08
24 changed files with 681 additions and 48 deletions

1
plugins/configuration/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,27 @@
plugins {
`kotlin-dsl`
`java-gradle-plugin`
}
repositories {
google()
mavenCentral()
}
configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
dependencies {
implementation(deps.gradle.kotlin)
implementation(deps.gradle.android)
implementation(deps.gradle.detekt)
}
gradlePlugin {
plugins.register("configuration") {
id = "configuration"
implementationClass = "com.tangem.plugin.configuration.ConfigurationPlugin"
}
}

View file

@ -0,0 +1,836 @@
build:
maxIssues: 0
config:
validation: true
warningsAsErrors: false
processors:
active: true
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
- 'FindingsReport'
- 'FileBasedFindingsReport'
# - 'LiteFindingsReport'
output-reports:
active: true
exclude:
- 'HtmlOutputReport'
# - 'TxtOutputReport'
- 'XmlOutputReport'
- 'SarifOutputReport'
- 'MdOutputReport'
comments:
active: false
AbsentOrWrongFileLicense:
active: false
licenseTemplateFile: 'license.template'
licenseTemplateIsRegex: false
CommentOverPrivateFunction:
active: false
CommentOverPrivateProperty:
active: false
DeprecatedBlockTag:
active: false
EndOfSentenceFormat:
active: false
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
KDocReferencesNonPublicProperty:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
OutdatedDocumentation:
active: false
matchTypeParameters: true
matchDeclarationsOrder: true
allowParamOnConstructorProperties: false
UndocumentedPublicClass:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
searchInNestedClass: true
searchInInnerClass: true
searchInInnerObject: true
searchInInnerInterface: true
UndocumentedPublicFunction:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
UndocumentedPublicProperty:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
complexity:
active: true
ComplexMethod:
active: true
ComplexCondition:
active: true
threshold: 4
ComplexInterface:
active: false
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
LabeledExpression:
active: false
ignoredLabels: [ ]
LargeClass:
active: true
threshold: 300
LongMethod:
active: true
threshold: 60
LongParameterList:
active: true
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: true
ignoreDataClasses: true
ignoreAnnotated: [ 'Provides' ]
MethodOverloading:
active: true
threshold: 6
NamedArguments:
active: true
threshold: 3
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
threshold: 5
NestedScopeFunctions:
active: false
threshold: 1
functions:
- 'kotlin.apply'
- 'kotlin.run'
- 'kotlin.with'
- 'kotlin.let'
- 'kotlin.also'
ReplaceSafeCallChainWithRun:
active: true
StringLiteralDuplication:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
threshold: 3
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
thresholdInFiles: 20
thresholdInClasses: 20
thresholdInInterfaces: 20
thresholdInObjects: 20
thresholdInEnums: 20
ignoreDeprecated: false
ignorePrivate: true
ignoreOverridden: false
coroutines:
active: true
GlobalCoroutineUsage:
active: true
InjectDispatcher:
active: false #TODO
dispatcherNames:
- 'IO'
- 'Default'
- 'Unconfined'
RedundantSuspendModifier:
active: true
SleepInsteadOfDelay:
active: true
SuspendFunWithCoroutineScopeReceiver:
active: false
SuspendFunWithFlowReturnType:
active: true
empty-blocks:
active: true
EmptyCatchBlock:
active: true
allowedExceptionNameRegex: '_|(ignore|expected).*'
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: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
NotImplementedDeclaration:
active: false
ObjectExtendsThrowable:
active: true
PrintStackTrace:
active: true
RethrowCaughtException:
active: true
ReturnFromFinally:
active: true
ignoreLabeled: false
SwallowedException:
active: false
ignoredExceptionTypes:
- 'InterruptedException'
- 'MalformedURLException'
- 'NumberFormatException'
- 'ParseException'
allowedExceptionNameRegex: '_|(ignore|expected).*'
ThrowingExceptionFromFinally:
active: true
ThrowingExceptionInMain:
active: false
ThrowingExceptionsWithoutMessageOrCause:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
exceptions:
- 'ArrayIndexOutOfBoundsException'
- 'Exception'
- 'IllegalArgumentException'
- 'IllegalMonitorStateException'
- 'IllegalStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
ThrowingNewInstanceOfSameException:
active: true
TooGenericExceptionCaught:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
exceptionNames:
- 'ArrayIndexOutOfBoundsException'
- 'Error'
- 'Exception'
- 'IllegalMonitorStateException'
- 'IndexOutOfBoundsException'
- 'NullPointerException'
- 'RuntimeException'
- 'Throwable'
allowedExceptionNameRegex: '_|(ignore|expected).*'
TooGenericExceptionThrown:
active: true
exceptionNames:
- 'Error'
- 'Exception'
- 'RuntimeException'
- 'Throwable'
naming:
active: true
BooleanPropertyNaming:
active: true
allowedPattern: '^(is|has|are)'
ignoreOverridden: true
ClassNaming:
active: true
classPattern: '[A-Z][a-zA-Z0-9]*'
ConstructorParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
EnumNaming:
active: true
enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName:
active: false
forbiddenName: [ ]
FunctionMaxLength:
active: false
maximumFunctionNameLength: 30
FunctionMinLength:
active: false
minimumFunctionNameLength: 3
FunctionNaming:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
functionPattern: '[a-z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
ignoreAnnotated: [ 'Composable' ]
FunctionParameterNaming:
active: true
parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
InvalidPackageDeclaration:
active: true
rootPackage: ''
requireRootInDeclaration: false
LambdaParameterNaming:
active: false
parameterPattern: '[a-z][A-Za-z0-9]*|_'
MatchingDeclarationName: # Same as FileName
active: false
mustBeFirst: true
MemberNameEqualsClassName:
active: false
ignoreOverridden: true
NoNameShadowing:
active: true
NonBooleanPropertyPrefixedWithIs:
active: true
ObjectPropertyNaming:
active: true
constantPattern: '[A-Za-z][_A-Za-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-Za-z0-9]*)*'
TopLevelPropertyNaming:
active: true
constantPattern: '[A-Z][_A-Z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength:
active: false
maximumVariableNameLength: 50
VariableMinLength:
active: false
minimumVariableNameLength: 3
VariableNaming:
active: true
variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
performance:
active: true
ArrayPrimitive:
active: true
CouldBeSequence:
active: false
threshold: 3
ForEachOnRange:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
SpreadOperator:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
UnnecessaryTemporaryInstantiation:
active: true
potential-bugs:
active: true
AvoidReferentialEquality:
active: true
forbiddenTypePatterns:
- 'kotlin.String'
CastToNullableType:
active: true
Deprecation:
active: false
DontDowncastCollectionTypes:
active: true
DoubleMutabilityForCollection:
active: true
mutableTypes:
- 'kotlin.collections.MutableList'
- 'kotlin.collections.MutableMap'
- 'kotlin.collections.MutableSet'
- 'java.util.ArrayList'
- 'java.util.LinkedHashSet'
- 'java.util.HashSet'
- 'java.util.LinkedHashMap'
- 'java.util.HashMap'
ElseCaseInsteadOfExhaustiveWhen:
active: false
EqualsAlwaysReturnsTrueOrFalse:
active: true
EqualsWithHashCodeExist:
active: true
ExitOutsideMain:
active: false
ExplicitGarbageCollectionCall:
active: true
HasPlatformType:
active: true
IgnoredReturnValue:
active: true
returnValueAnnotations:
- '*.CheckResult'
- '*.CheckReturnValue'
ignoreReturnValueAnnotations:
- '*.CanIgnoreReturnValue'
ignoreFunctionCall: [ ]
ImplicitDefaultLocale:
active: true
ImplicitUnitReturnType:
active: false
allowExplicitReturnType: true
InvalidRange:
active: true
IteratorHasNextCallsNextMethod:
active: true
IteratorNotThrowingNoSuchElementException:
active: true
LateinitUsage:
active: false
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
ignoreAnnotated: [ 'Inject' ]
ignoreOnClassesPattern: ''
MapGetWithNotNullAssertionOperator:
active: true
MissingPackageDeclaration:
active: true
excludes: [ '**/*.kts' ]
NullCheckOnMutableProperty:
active: true
NullableToStringCall:
active: true
UnconditionalJumpStatementInLoop:
active: true
UnnecessaryNotNullOperator:
active: true
UnnecessarySafeCall:
active: true
UnreachableCatchBlock:
active: true
UnreachableCode:
active: true
UnsafeCallOnNullableType:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
UnsafeCast:
active: true
UnusedUnaryOperator:
active: true
UselessPostfixExpression:
active: true
WrongEqualsTypeParameter:
active: true
style:
active: true
CanBeNonNullable:
active: false
CascadingCallWrapping:
active: false
includeElvis: true
ClassOrdering:
active: true
CollapsibleIfStatements:
active: true
DataClassContainsFunctions:
active: false
conversionFunctionPrefix:
- 'to'
DataClassShouldBeImmutable:
active: false
DestructuringDeclarationWithTooManyEntries:
active: true
maxDestructuringEntries: 3
EqualsNullCall:
active: false
EqualsOnSignatureLine:
active: true
ExplicitCollectionElementAccessMethod:
active: true
ExplicitItLambdaParameter:
active: true
ExpressionBodySyntax:
active: false
includeLineWrapping: true
ForbiddenComment:
active: false
values:
- 'FIXME:'
- 'STOPSHIP:'
- 'TODO:'
allowedPatterns: ''
customMessage: ''
ForbiddenImport:
active: false
imports: [ ]
forbiddenPatterns: ''
ForbiddenMethodCall:
active: false
methods:
- reason: 'print does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.print'
- reason: 'println does not allow you to configure the output stream. Use a logger instead.'
value: 'kotlin.io.println'
ForbiddenSuppress:
active: false
rules: [ ]
ForbiddenVoid:
active: false
ignoreOverridden: false
ignoreUsageInGenerics: false
FunctionOnlyReturningConstant:
active: true
ignoreOverridableFunction: true
ignoreActualFunction: true
LoopWithTooManyJumpStatements:
active: true
maxJumpCount: 2
MagicNumber:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/*.kts' ]
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
ignoreHashCodeFunction: true
ignorePropertyDeclaration: true
ignoreLocalVariableDeclaration: false
ignoreConstantDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotation: false
ignoreNamedArgument: true
ignoreEnums: false
ignoreRanges: false
ignoreExtensionFunctions: true
ignoreAnnotated: [ 'Preview' ]
MandatoryBracesIfStatements:
active: true
MandatoryBracesLoops:
active: true
MaxChainedCallsOnSameLine:
active: true
maxChainedCalls: 5
MaxLineLength:
active: false # Same as MaximumLineLength
maxLineLength: 120
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
MayBeConst:
active: true
ModifierOrder:
active: false # Same as ModifierOrdering
MultilineLambdaItParameter:
active: true
NestedClassesVisibility:
active: true
NewLineAtEndOfFile:
active: false # This rules overlaps with style>NewLineAtEndOfFile from the standard rules
NoTabs:
active: true
NullableBooleanCheck:
active: true
ObjectLiteralToLambda:
active: true
OptionalAbstractKeyword:
active: true
OptionalUnit:
active: true
OptionalWhenBraces:
active: false
PreferToOverPairSyntax:
active: false
ProtectedMemberInFinalClass:
active: true
RedundantExplicitType:
active: false
RedundantHigherOrderMapUsage:
active: true
RedundantVisibilityModifierRule:
active: true
ReturnCount:
active: false
max: 2
excludedFunctions:
- 'equals'
excludeLabeled: false
excludeReturnFromLambda: true
excludeGuardClauses: false
SafeCast:
active: true
SerialVersionUIDInSerializableClass:
active: true
SpacingBetweenPackageAndImports:
active: true
ThrowsCount:
active: false
max: 2
excludeGuardClauses: false
TrailingWhitespace:
active: false
UnderscoresInNumericLiterals:
active: false
acceptableLength: 4
allowNonStandardGrouping: false
UnnecessaryAbstractClass:
active: true
UnnecessaryAnnotationUseSiteTarget:
active: false
UnnecessaryApply:
active: true
UnnecessaryBackticks:
active: true
UnnecessaryFilter:
active: true
UnnecessaryInheritance:
active: true
UnnecessaryInnerClass:
active: false
UnnecessaryLet:
active: true
UnnecessaryParentheses:
active: true
UntilInsteadOfRangeTo:
active: true
UnusedImports:
active: true
UnusedPrivateClass:
active: true
ignoreAnnotated: [ 'UnusedRequiredComponent' ]
UnusedPrivateMember:
active: true
allowedNames: '(_|ignored|expected|serialVersionUID)'
ignoreAnnotated: [ 'Preview', 'UnusedRequiredComponent' ]
UseAnyOrNoneInsteadOfFind:
active: true
UseArrayLiteralsInAnnotations:
active: true
UseCheckNotNull:
active: true
UseCheckOrError:
active: true
UseDataClass:
active: false
allowVars: false
UseEmptyCounterpart:
active: true
UseIfEmptyOrIfBlank:
active: true
UseIfInsteadOfWhen:
active: false
UseIsNullOrEmpty:
active: true
UseOrEmpty:
active: true
UseRequire:
active: true
UseRequireNotNull:
active: true
UselessCallOnNotNull:
active: true
UtilityClassWithPublicConstructor:
active: true
VarCouldBeVal:
active: true
ignoreLateinitVar: false
WildcardImport:
active: false
excludeImports:
- 'java.util.*'
formatting:
active: true
android: true
AnnotationOnSeparateLine:
active: false
AnnotationSpacing:
active: true
ArgumentListWrapping:
active: true
indentSize: 4
maxLineLength: 120
BlockCommentInitialStarAlignment:
active: true
ChainWrapping:
active: true
CommentSpacing:
active: true
CommentWrapping:
active: false
indentSize: 4
DiscouragedCommentLocation:
active: true
EnumEntryNameCase:
active: true
Filename:
active: true # This rules overlaps with naming>MatchingDeclarationName from the standard rules
mustBeFirst: true
FinalNewline:
active: false
insertFinalNewLine: true
FunKeywordSpacing:
active: true
FunctionReturnTypeSpacing:
active: true
FunctionSignature:
active: true
forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647
functionBodyExpressionWrapping: 'default'
maxLineLength: 120
indentSize: 4
FunctionStartOfBodySpacing:
active: true
FunctionTypeReferenceSpacing:
active: true
ImportOrdering:
active: false
layout: 'java.**,javax.**,kotlin.**,kotlinx.**,android.**,androidx.**,*,^'
Indentation:
active: true
indentSize: 4
KdocWrapping:
active: true
indentSize: 4
MaximumLineLength:
excludes: [ "**/assets/**" ]
active: true # This rules overlaps with style>MaxLineLength from the standard rules
maxLineLength: 120
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
ModifierListSpacing:
active: true
ModifierOrdering:
active: true # This rules overlaps with style>ModifierOrder from the standard rules
MultiLineIfElse:
active: true
NoBlankLineBeforeRbrace:
active: true
NoBlankLinesInChainedMethodCalls:
active: true
NoConsecutiveBlankLines:
active: true
NoEmptyClassBody:
active: true
NoEmptyFirstLineInMethodBlock:
active: true
NoLineBreakAfterElse:
active: true
NoLineBreakBeforeAssignment:
active: true
NoMultipleSpaces:
active: true
NoSemicolons:
active: true
NoTrailingSpaces:
active: true
NoUnitReturn:
active: true
NoUnusedImports:
active: true
NoWildcardImports:
active: false
packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**'
NullableTypeSpacing:
active: true
PackageName:
active: true
ParameterListSpacing:
active: true
ParameterListWrapping:
active: true
maxLineLength: 120
SpacingAroundAngleBrackets:
active: true
SpacingAroundColon:
active: true
SpacingAroundComma:
active: true
SpacingAroundCurly:
active: true
SpacingAroundDot:
active: true
SpacingAroundDoubleColon:
active: true
SpacingAroundKeyword:
active: true
SpacingAroundOperators:
active: true
SpacingAroundParens:
active: true
SpacingAroundRangeOperator:
active: true
SpacingAroundUnaryOperator:
active: true
SpacingBetweenDeclarationsWithAnnotations:
active: true
SpacingBetweenDeclarationsWithComments:
active: false
SpacingBetweenFunctionNameAndOpeningParenthesis:
active: true
StringTemplate:
active: true
TrailingCommaOnCallSite:
active: true
useTrailingCommaOnCallSite: false
TrailingCommaOnDeclarationSite:
active: true
useTrailingCommaOnCallSite: false
TypeArgumentListSpacing:
active: true
TypeParameterListSpacing:
active: true
UnnecessaryParenthesesBeforeTrailingLambda:
active: true
Wrapping:
active: false
indentSize: 4
compose:
ReusedModifierInstance:
active: true
UnnecessaryEventHandlerParameter:
active: true
ComposableEventParameterNaming:
active: true
ComposableParametersOrdering:
active: true
ModifierDefaultValue:
active: true
MissingModifierDefaultValue:
active: true
ModifierHeightWithText:
active: true
ModifierParameterPosition:
active: true
PublicComposablePreview:
active: true
TopLevelComposableFunctions:
active: true
ComposeFunctionName:
active: true

View file

@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("deps") {
from(files("../../gradle/dependencies.toml"))
}
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.plugin.configuration
import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.plugins.AppPlugin
import com.android.build.gradle.internal.plugins.LibraryPlugin
import com.tangem.plugin.configuration.configurations.configure
import com.tangem.plugin.configuration.configurations.extension.configure
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class ConfigurationPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.plugins.all(startConfigurationAction(project = target))
}
private fun startConfigurationAction(project: Project) = Action<Plugin<*>> {
project.configure()
when (this) {
is AppPlugin -> {
project.configure<AppExtension> { configure(project) }
}
is LibraryPlugin -> {
project.configure<LibraryExtension> { configure(project) }
}
}
}
}

View file

@ -0,0 +1,52 @@
package com.tangem.plugin.configuration.configurations
import com.tangem.plugin.configuration.utils.findLibrary
import com.tangem.plugin.configuration.utils.findPlugin
import io.gitlab.arturbosch.detekt.CONFIGURATION_DETEKT
import io.gitlab.arturbosch.detekt.CONFIGURATION_DETEKT_PLUGINS
import io.gitlab.arturbosch.detekt.Detekt
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
internal fun Project.configureDetektRules() {
plugins.apply(findPlugin(alias = CONFIGURATION_DETEKT).pluginId)
extensions.configure<DetektExtension> { configure(project) }
configureDetektPlugins()
configureDetektTask()
}
private fun DetektExtension.configure(project: Project) {
parallel = true
ignoreFailures = false
autoCorrect = true
buildUponDefaultConfig = true
config.setFrom(project.rootProject.files("plugins/configuration/detekt.yml"))
}
private fun Project.configureDetektPlugins() {
listOf(
findLibrary(alias = "detekt-formatting"),
findLibrary(alias = "detekt-compose"),
).forEach {
dependencies.add(CONFIGURATION_DETEKT_PLUGINS, it)
}
}
private fun Project.configureDetektTask() {
tasks.withType<Detekt> {
include("**/*.kt")
exclude("**/resources/**", "**/build/**")
reports {
sarif {
required.set(false)
}
txt {
required.set(true)
}
}
jvmTarget = "11"
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.plugin.configuration.configurations
import org.gradle.api.Project
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
internal fun Project.configureKotlinCompilerOptions() {
project.tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
allWarningsAsErrors = false
// this is required to produce a unique META-INF/*.kotlin_module files
moduleName = project.path.removePrefix(":").replace(':', '-')
}
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.plugin.configuration.configurations
import org.gradle.api.Project
internal fun Project.configure() {
configureKotlinCompilerOptions()
configureDetektRules()
}

View file

@ -0,0 +1,94 @@
package com.tangem.plugin.configuration.configurations.extension
import com.android.build.gradle.AppExtension
import com.tangem.plugin.configuration.model.AppConfig
import com.tangem.plugin.configuration.model.BuildType
import com.tangem.plugin.configuration.utils.BuildConfigFieldFactory
import org.gradle.api.Project
import com.android.build.gradle.internal.dsl.BuildType as AndroidBuildType
internal fun AppExtension.configure(project: Project) {
configureCompileSdk()
configureDefaultConfig(project)
configureBuildFeatures()
configureBuildTypes()
configurePackagingOptions()
configureCompose(project)
configureCompilerOptions()
}
private fun AppExtension.configureDefaultConfig(project: Project) {
defaultConfig {
applicationId = AppConfig.packageName
minSdk = AppConfig.minSdkVersion
targetSdk = AppConfig.targetSdkVersion
versionCode = if (project.hasProperty("versionCode")) {
(project.property("versionCode") as String).toInt()
} else {
AppConfig.versionCode
}
versionName = if (project.hasProperty("versionName")) {
project.property("versionName") as String
} else {
AppConfig.versionName
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}
// TODO: [REDACTED_JIRA]
private fun AppExtension.configureBuildFeatures() {
buildFeatures.apply {
viewBinding = true
}
}
private fun AppExtension.configureBuildTypes() {
buildTypes {
BuildType.values().forEach { buildVariant ->
maybeCreate(buildVariant.id).apply {
configureBuildVariant(extension = this@configureBuildTypes, buildVariant)
BuildConfigFieldFactory(
fields = buildVariant.configFields,
builder = ::buildConfigField,
).create()
}
}
}
}
private fun AndroidBuildType.configureBuildVariant(extension: AppExtension, buildType: BuildType) {
when (buildType) {
BuildType.Release -> {
isDebuggable = false
isMinifyEnabled = false
proguardFiles(extension.getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
BuildType.Debug -> {
isDebuggable = true
isMinifyEnabled = false
}
BuildType.External -> {
initWith(extension.buildTypes.getByName(BuildType.Release.id))
signingConfig = extension.signingConfigs.getByName(BuildType.Debug.id)
}
}
versionNameSuffix = buildType.versionSuffix?.let { "-$it" }
applicationIdSuffix = buildType.appIdSuffix?.let { ".$it" }
}
private fun AppExtension.configurePackagingOptions() {
packagingOptions {
resources {
excludes += "lib/x86_64/darwin/libscrypt.dylib"
excludes += "lib/x86_64/freebsd/libscrypt.so"
excludes += "lib/x86_64/linux/libscrypt.so"
excludes += "META-INF/gradle/incremental.annotation.processors"
}
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.plugin.configuration.configurations.extension
import com.android.build.gradle.BaseExtension
import com.tangem.plugin.configuration.model.AppConfig
import com.tangem.plugin.configuration.utils.findVersion
import org.gradle.api.JavaVersion
import org.gradle.api.Project
internal fun BaseExtension.configureCompileSdk() {
compileSdkVersion(AppConfig.compileSdkVersion)
}
internal fun BaseExtension.configureCompilerOptions() {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
internal fun BaseExtension.configureCompose(project: Project) {
val useCompose = with(project.path) {
contains(":ui") ||
contains(":presentation") ||
contains(":app") || // TODO: [REDACTED_JIRA]
contains(":tester:impl") // TODO: Rename module
}
buildFeatures.compose = useCompose
if (useCompose) {
composeOptions {
kotlinCompilerExtensionVersion = project.findVersion(alias = "compose-compiler").requiredVersion
}
}
}

View file

@ -0,0 +1,49 @@
package com.tangem.plugin.configuration.configurations.extension
import com.android.build.gradle.LibraryExtension
import com.tangem.plugin.configuration.model.AppConfig
import com.tangem.plugin.configuration.model.BuildType
import com.tangem.plugin.configuration.utils.BuildConfigFieldFactory
import org.gradle.api.Project
internal fun LibraryExtension.configure(project: Project) {
configureCompileSdk()
configureDefaultConfig()
configureBuildTypes()
configurePackagingOptions()
configureCompose(project)
configureCompilerOptions()
}
private fun LibraryExtension.configureDefaultConfig() {
defaultConfig {
minSdk = AppConfig.minSdkVersion
vectorDrawables {
useSupportLibrary = true
}
}
}
private fun LibraryExtension.configureBuildTypes() {
buildTypes {
BuildType.values().forEach { buildVariant ->
maybeCreate(buildVariant.id).apply {
BuildConfigFieldFactory(
fields = buildVariant.configFields,
builder = ::buildConfigField,
).create()
}
}
}
}
private fun LibraryExtension.configurePackagingOptions() {
packagingOptions {
resources {
excludes += "lib/x86_64/darwin/libscrypt.dylib"
excludes += "lib/x86_64/freebsd/libscrypt.so"
excludes += "lib/x86_64/linux/libscrypt.so"
excludes += "META-INF/gradle/incremental.annotation.processors"
}
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.plugin.configuration.model
internal object AppConfig {
const val packageName = "com.tangem.wallet"
const val versionCode = 1
const val versionName = "1.0.0-SNAPSHOT"
const val minSdkVersion = 21
const val targetSdkVersion = 33
const val compileSdkVersion = 33
}

View file

@ -0,0 +1,35 @@
package com.tangem.plugin.configuration.model
/**
* Build config field
*
* @property type field type
* @property name field name
* @property value field value
*/
internal sealed class BuildConfigField(val type: String, val name: String, val value: String) {
class Environment(value: String) : BuildConfigField(
type = "String",
name = "ENVIRONMENT",
value = "\"$value\"",
)
class TestActionEnabled(isEnabled: Boolean) : BuildConfigField(
type = "Boolean",
name = "TEST_ACTION_ENABLED",
value = isEnabled.toString(),
)
class LogEnabled(isEnabled: Boolean) : BuildConfigField(
type = "Boolean",
name = "LOG_ENABLED",
value = isEnabled.toString(),
)
class TesterMenuAvailability(isEnabled: Boolean) : BuildConfigField(
type = "Boolean",
name = "TESTER_MENU_ENABLED",
value = isEnabled.toString(),
)
}

View file

@ -0,0 +1,72 @@
package com.tangem.plugin.configuration.model
internal enum class BuildType(
val id: String,
val appIdSuffix: String? = null,
val versionSuffix: String? = null,
val configFields: List<BuildConfigField>,
) {
/**
* Production ready build type for clients
*
* Features:
* - Env: prod
* - Signing config: release
* - Proguard
* */
Release(
id = "release",
configFields = listOf(
BuildConfigField.Environment(value = "prod"),
BuildConfigField.TestActionEnabled(isEnabled = false),
BuildConfigField.LogEnabled(isEnabled = false),
BuildConfigField.TesterMenuAvailability(isEnabled = false),
),
),
/**
* Build type for developers
*
* Features:
* - Env: dev
* - Signing config: debug
* - Debuggable
* - Logs
* - Tester menu
* - Test action
* - Traffic sniffing
* */
Debug(
id = "debug",
appIdSuffix = "dev",
configFields = listOf(
BuildConfigField.Environment(value = "dev"),
BuildConfigField.TestActionEnabled(isEnabled = true),
BuildConfigField.LogEnabled(isEnabled = true),
BuildConfigField.TesterMenuAvailability(isEnabled = true),
),
),
/**
* Build type for QA and business
*
* Features:
* - Env: prod
* - Signing config: debug
* - Tester menu
* - Test action
* - Proguard
* */
External(
id = "debug_beta",
appIdSuffix = "debug",
versionSuffix = "debug",
configFields = listOf(
BuildConfigField.Environment(value = "prod"),
BuildConfigField.TestActionEnabled(isEnabled = true),
BuildConfigField.LogEnabled(isEnabled = false),
BuildConfigField.TesterMenuAvailability(isEnabled = true),
),
),
}

View file

@ -0,0 +1,14 @@
package com.tangem.plugin.configuration.utils
import com.tangem.plugin.configuration.model.BuildConfigField
private typealias ConfigFieldBuilder = (String, String, String) -> Unit
internal class BuildConfigFieldFactory(
private val fields: List<BuildConfigField>,
private val builder: ConfigFieldBuilder,
) {
fun create() {
fields.forEach { field -> builder(field.type, field.name, field.value) }
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.plugin.configuration.utils
import org.gradle.api.Project
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.artifacts.VersionConstraint
import org.gradle.kotlin.dsl.getByType
import org.gradle.plugin.use.PluginDependency
import kotlin.jvm.optionals.getOrNull
private val Project.depsVersionCatalog: VersionCatalog
get() = extensions.getByType<VersionCatalogsExtension>().named("deps")
internal fun Project.findVersion(alias: String): VersionConstraint {
return depsVersionCatalog.findVersion(alias).getOrNull()
?: error("Unable to find version $alias")
}
internal fun Project.findPlugin(alias: String): PluginDependency {
return depsVersionCatalog.findPlugin(alias).getOrNull()?.orNull
?: error("Unable to find plugin $alias")
}
internal fun Project.findLibrary(alias: String): MinimalExternalModuleDependency {
return depsVersionCatalog.findLibrary(alias).getOrNull()?.orNull
?: error("Unable to find library $alias")
}