Skip to content

Instantly share code, notes, and snippets.

@mlagerberg
Last active July 4, 2018 14:39
Show Gist options
  • Save mlagerberg/c5eeded7c57568f511d82b785dca40ac to your computer and use it in GitHub Desktop.
Save mlagerberg/c5eeded7c57568f511d82b785dca40ac to your computer and use it in GitHub Desktop.
[Common build.gradle stuff.] Adds Lint, Findbugs, Checkstyle and PMD, uses release config keys from global properties, adds debug icon overlay, etc. #android #gradle

Project (Code)name

Android/iOS/Tensorflow/Whatever project for the Codename app for Client. Based on the work from project Research project.

This app does lorem ipsum dolor sit amet.

Quickstart

Examples:

  1. Clone repo
  2. Run from Android Studio

Installation

Requirements

Minimum Android Studio, minimum hardware requirements, Kotlin, related projects, etc.

Running the project

Examples:

  1. Clone repo
  2. Run from Android Studio
  3. Execute run.ssh
  4. cd into a subfolder
  5. php -S localhost:8888 ./something.php.

Branches

The branch feature/yellow contains all the customisations for the yellow version. The develop branch is the original blue.

Product flavors

Flavor Yellow is a special yellow version of the project. The Blue flavor is the same but way more blue.

Building and deploying

Builds are automatically deployed to Beta channel when you push to develop.

Make sure to send clients the yellow version of the main app and the blue version of the other app.

Relevant information

API

There are two important API endpoints used by this project:

  • https://example.com/some/kind/of/important/example - which is the most important example ever.
  • https://example.com/some/kind/of/test/example - test endpoint which is used by the lorem ipsum.

The full documentation is here.

Translations

Managed here by Lorem Ipsum Latin Translations Inc.

Issue tracker

Internal issue tracker Trello board for clients

Misc. links

The underlying algorithm is explained here.

Licenses

This project uses several libraries that are licensed under the Apache 2.0 License.

apply plugin: 'com.akaita.android.easylauncher'
// ...
// Include this in EVERY module!
apply from: '../common.gradle'
android {
compileSdkVersion rootProject.ext.compile_sdk
buildToolsVersion rootProject.ext.build_tools
defaultConfig {
minSdkVersion rootProject.ext.min_sdk
targetSdkVersion rootProject.ext.target_sdk
versionCode rootProject.ext.version_code
versionName rootProject.ext.version_name
// buildConfigField "String", "GIT_SHA", "\"${gitSha()}\""
// buildConfigField "String", "BUILD_TIME", "\"${buildTime()}\""
}
signingConfigs {
release {
storeFile file("${signingPath}release.jks")
storePassword MYPROJECT_STORE_PASSWORD
keyAlias MYPROJECT_KEY_ALIAS
keyPassword MYPROJECT_KEY_PASSWORD
}
}
// ...
buildTypes {
// ...
release {
// ...
///// Fabric Beta
ext.betaDistributionGroupAliasesFilePath = "${signingPath()}fabric_groups.txt"
//ext.betaDistributionGroupAliases = "pixplicity,client,qa"
ext.betaDistributionNotifications = true
ext.crashlyticsApiSecret = "12323abcdef"
ext.crashlyticsApiKey = "12323abcdef"
}
}
// Fixes common duplicate filenames from different libraries
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/rxjava.properties'
exclude 'META-INF/maven/com.squareup.okio/okio/pom.xml'
exclude 'META-INF/maven/com.squareup.okio/okio/pom.properties'
exclude 'META-INF/MANIFEST.MF'
exclude 'META-INF/.readme'
exclude '.readme'
return void
}
lintOptions {
abortOnError false
checkAllWarnings true
checkReleaseBuilds true
quiet false
// lintConfig file("${project.rootDir}/config/lint.xml")
// xmlOutput file("${project.buildDir}/reports/lint/lint-report.xml")
// htmlOutput file("${project.buildDir}/reports/lint/lint-report.html")
}
testOptions {
animationsDisabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$rootProject.ext.kotlin_version"
// UI (example)
implementation "com.android.support:design:$rootProject.ext.supportLib"
implementation "com.android.support:appcompat-v7:$rootProject.ext.supportLib"
implementation "com.android.support:recyclerview-v7:$rootProject.ext.supportLib"
implementation "com.android.support:support-v13:$rootProject.ext.supportLib"
implementation "com.android.support:cardview-v7:$rootProject.ext.supportLib"
// Images (example)
// implementation "com.github.bumptech.glide:glide:$rootProject.ext.glide"
// Reactive (example)
// implementation "io.reactivex.rxjava2:rxjava:$rootProject.ext.rxJava2"
// implementation "io.reactivex.rxjava2:rxkotlin:$rootProject.ext.rxKotlin"
// implementation "io.reactivex.rxjava2:rxandroid:$rootProject.ext.rxAndroid"
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.41'
// ...
dependencies {
// ...
classpath 'com.akaita.android:easylauncher:1.2.0' // Debug ribbon on launcher icon
classpath 'com.noveogroup.android:check:1.2.5' // Code checks
}
}
// ...
// A list of versions shared across modules, collected in
// one place for convenience.
// NOTE: don't add fields that are only relevant for a single
// module in order to keep build times short.
ext {
// Versioning
version_code = 1
version_name = "0.1-beta.1"
// SDK and tools
kotlin_version = "$kotlin_version"
minSdk = 23
targetSdk = 27
compileSdk = 17
supportLib = "27.1.1"
buildTools = "27.0.3"
// === Examples ===
// Images
// glide = "4.6.1"
// Reactive
// rxJava2 = '2.1.9'
// rxKotlin = '2.2.0'
// rxAndroid = '2.0.1'
// ...
}
apply plugin: 'com.noveogroup.android.check'
def homePath = System.properties['user.home'] + File.separator
def buildTime = new Date().format("yyyy-MM-dd'T'HH.mm", TimeZone.getTimeZone("UTC"))
// def buildTime = new Date().format("yyyy-MM-dd-HH:mm", TimeZone.getTimeZone("Europe/Amsterdam"))
// Point to the location of the keystores
def signingPath = file("${project.rootDir}/.signing/")
if (!signingPath.exists()) {
// Jenkins clears the workspace before build
signingPath = file("${homePath}.gradle/keystores/myproject/")
}
/**
* Returns the current Git commit hash
* @return git sha
*/
def getGitSha() {
if (!project.ext.hasProperty('gitSha'))
project.ext.gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim();
return project.ext.gitSha
}
// Returns e.g. "1.3"
def getVersionName(versionMajor, versionMinor) {
// Basic <major>.<minor> version name
return String.format('%d.%d', versionMajor, versionMinor)
}
// Returns e.g. "1030118" for Jenkins build #118
def getVersionCode(versionMajor, versionMinor) {
// Major + minor + Jenkins build number (where available)
return (versionMajor * 1000000)
+ (versionMinor * 10000)
+ Integer.valueOf(System.env.BUILD_NUMBER ?: 0)
}
// --- CODE STYLE CHECKS ---
check {
abortOnError false
checkstyle {
abortOnError false
// directory for report files
report new File(project.buildDir, 'reports/checkstyle')
// XML report file
reportXML new File(project.buildDir, 'reports/checkstyle/checkstyle.xml')
// HTML report file
reportHTML new File(project.buildDir, 'reports/checkstyle/checkstyle.html')
}
pmd {
skip false
abortOnError false
config "${project.rootDir}/config/pmd-easy.xml"
// directory for report files
report new File(project.buildDir, 'reports/pmd')
// XML report file
reportXML new File(project.buildDir, 'reports/pmd/pmd.xml')
// HTML report file
reportHTML new File(project.buildDir, 'reports/pmd/pmd.html')
}
findbugs {
skip false
abortOnError false
config easy()
// directory for report files
report new File(project.buildDir, 'reports/findbugs')
// XML report file
reportXML new File(project.buildDir, 'reports/findbugs/findbugs.xml')
// HTML report file
reportHTML new File(project.buildDir, 'reports/findbugs/findbugs.html')
}
}
// --- EXPORT METHODS ---
ext.homePath = { return homePath }
ext.signingPath = { return signingPath }
ext.gitSha = { return getGitSha() }
// ext.versionName = { return getVersionName }
// ext.versionCode = { return getVersionCode }
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
PMD Configuration
Severity: EASY
-->
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>POM rule set file</description>
<rule ref="rulesets/java/android.xml" />
<rule ref="rulesets/java/basic.xml">
<exclude name="CollapsibleIfStatements" />
</rule>
<rule ref="rulesets/java/braces.xml">
<exclude name="IfStmtsMustUseBraces" />
<exclude name="IfElseStmtsMustUseBraces" />
</rule>
<rule ref="rulesets/java/clone.xml" />
<rule ref="rulesets/java/codesize.xml">
<exclude name="StdCyclomaticComplexity" />
<exclude name="ModifiedCyclomaticComplexity" />
</rule>
<rule ref="rulesets/java/codesize.xml/CyclomaticComplexity">
<properties>
<property name="reportLevel" value="20" />
</properties>
</rule>
<rule ref="rulesets/java/codesize.xml/TooManyMethods">
<properties>
<property name="maxmethods" value="25" />
</properties>
</rule>
<rule ref="rulesets/java/comments.xml">
<exclude name="CommentRequired" />
<exclude name="CommentSize" />
</rule>
<rule ref="rulesets/java/controversial.xml">
<!-- while ((x = update()) != null) { process(x); } -->
<exclude name="AssignmentInOperand" />
<exclude name="AtLeastOneConstructor" />
<exclude name="AvoidLiteralsInIfCondition" />
<exclude name="AvoidPrefixingMethodParameters" />
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="NullAssignment" />
<exclude name="OnlyOneReturn" />
<exclude name="DefaultPackage" />
</rule>
<rule ref="rulesets/java/coupling.xml">
<exclude name="LooseCoupling" />
<exclude name="ExcessiveImports" />
<exclude name="LawOfDemeter" />
</rule>
<rule ref="rulesets/java/design.xml">
<exclude name="AvoidReassigningParameters" />
<!-- if (x != y) { short code block } else { long code block } -->
<exclude name="ConfusingTernary" />
<exclude name="SwitchStmtsShouldHaveDefault" />
<!-- Android listeners contain a lot of such switch statements -->
<exclude name="TooFewBranchesForASwitchStatement" />
<exclude name="ImmutableField" />
<exclude name="GodClass" />
<exclude name="SingularField" />
<exclude name="UncommentedEmptyMethodBody" />
<exclude name="EmptyMethodInAbstractClassShouldBeAbstract" />
</rule>
<rule ref="rulesets/java/empty.xml" />
<rule ref="rulesets/java/finalizers.xml" />
<rule ref="rulesets/java/imports.xml" />
<rule ref="rulesets/java/junit.xml" />
<rule ref="rulesets/java/logging-jakarta-commons.xml">
<exclude name="GuardLogStatement" />
</rule>
<rule ref="rulesets/java/logging-java.xml">
<exclude name="GuardLogStatementJavaUtil" />
</rule>
<rule ref="rulesets/java/migrating.xml" />
<rule ref="rulesets/java/naming.xml">
<exclude name="AbstractNaming" />
<exclude name="AvoidFieldNameMatchingMethodName" />
<exclude name="LongVariable" />
<exclude name="ShortClassName" />
<exclude name="SuspiciousConstantFieldName" />
<exclude name="ShortVariable" />
<exclude name="VariableNamingConventions" />
</rule>
<rule ref="rulesets/java/optimizations.xml">
<exclude name="AvoidInstantiatingObjectsInLoops" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="MethodArgumentCouldBeFinal" />
<exclude name="RedundantFieldInitializer" />
</rule>
<rule ref="rulesets/java/strictexception.xml">
<exclude name="AvoidCatchingGenericException" />
</rule>
<rule ref="rulesets/java/strings.xml" />
<rule ref="rulesets/java/typeresolution.xml">
<exclude name="LooseCoupling" />
</rule>
<rule ref="rulesets/java/unnecessary.xml">
<exclude name="UselessOverridingMethod" />
<exclude name="UselessParentheses" />
</rule>
<rule ref="rulesets/java/unusedcode.xml">
<exclude name="UnusedModifier" />
</rule>
</ruleset>
node('android') {
stage('Checkout') {
checkout scm
}
stage('Build') {
try {
//// 1. Build production builds
// '--refresh-dependencies ensure each build is started completely clean,
// but may cause problems if the CI has connection issues.
// It also adds significantly to the build times.
sh './gradlew --refresh-dependencies clean build lint check assembleRelease --stacktrace'
//// 2. Connected tests
lock('emulator') {
sh './gradlew connectedCheck'
}
//// 3. Notify Slack
currentBuild.result = 'SUCCESS'
slackSend channel: '#myproject', color: 'good', message: "This build is OK: ${env.BUILD_URL}"
} catch(error) {
slackSend channel: '#myproject', color: 'bad', message: "This build is broken: ${env.BUILD_URL}"
currentBuild.result = 'FAILURE'
} finally {
junit '**/test-results/**/*.xml'
}
}
stage('Archive') {
archiveArtifacts 'app/build/outputs/apk/*/release/*.apk'
currentBuild.result = 'SUCCESS'
}
stage('Publish') {
sh "./gradlew crashlyticsUploadDistributionRelease"
}
}
internal,client,pixplicity,qa
# ...
#
# Client name Project name
#
# Certificate fingerprints: (keytool -list -v -keystore <filename>)
# MD5: 7A:CA:28:E1:A2:41:55:...
# SHA1: 2F:35:3A:07:AC:A0:46:...
# SHA256: 3F:63:13:11:92:C3:3E:...
MYPROJECT_STORE_PASSWORD=73g2387d6t28
MYPROJECT_RELEASE_KEY_ALIAS=release
MYPROJECT_RELEASE_KEY_PASSWORD=847g3r634
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment