Jenkins Pipeline Androidプロジェクトの自動配信化をする

## はじめに
iOSを書いたらAndroidもだろうってことでAndroidの記事です
iOSの記事は[こちら](https://qiita.com/Monden_M/items/2c42137497faaeed4ed7)

## この記事の内容
jenkinsのpipelineで
- 開始と終了のSlackの通知
- Gitでのソースの取得
- apkの作成
- apkの成果物の保存
- FireBaseDistributionでの配信

を自動化してやってしまおうという内容です。

## 前提条件
- WORKSPACE直下にAndroidProjectがある状態
- AABバージョンで送信する場合はGooglePlayに公開済みのアプリ + FireBaseとGooglePlayを連携させる必要があります。
    - 上記の問題があるためFirebaseDistributionで配信をする場合は基本apkで配信の設定までする方が現状は無難かと思います。
- FireBaseCLIを導入し、配信を行うのID,PWにてログイン済み※「Firebase Distribution」をする場合のみ
    - gradleにFireBaseDistributionのプロパティを追加することも可ですが、今回はコマンドで送信する方法で実装しています。
    - 上記のものを参考にする場合は[Codelab](https://firebase.google.com/codelabs/appdistribution-app-bundles)を参考にしてください。

## PipeLine
各変数を修正すれば環境を整えれるようにしております。
まずはPipeLineの文をすべて載せ、下記にて一つづつ解説を載せていきます。

```
// ノードの指定
node("NodeName") {
    // Slack Settings
    def strSlackChannel = "#sample_channel"
    def strSlackMessageJob = "Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
    def strSlackCredentialId = "" 
    
    // Git Settings
    def strGitRepo = "https://hoge.com/git/fugo.git"
    def strBranch = "*/branch"
    def strCredentialsId = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

    // Android Settings
    def strBuildType = "debug" 
    def strBuildProductFlavor = "productflavor"
    def strApkPath = "app/build/outputs/apk/${strBuildFlavor}/${strBuildType}/app-${strBuildFlavor}-${strBuildType}.apk"
    
    // Firebase Settings
    def strFirebaseAppID = "1:1234567890:android:1234567890"
    def strFirebaseDistributionGroup = "GroupName"
    
    
    try {
        slackSend channel: "${strSlackChannel}", color: 'FFFF00', message: "START JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"

        stage ("Checkout") {
            def scm = checkout(
                [$class: 'GitSCM', branches: [[name: strBranch]],
                doGenerateSubmoduleConfigurations: false, 
                extensions: [[$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: false, timeout: 60]],
                submoduleCfg: [],
                userRemoteConfigs: [[credentialsId: strCredentialsId, url: strGitRepo]]])
        }

        stage ("Android Build(APK)") {
            sh "chmod +x ./gradlew &&" +
            "./gradlew clean &&" +
            "./gradlew assemble${strBuildFlavor}${strBuildType} -PversionCode=${BUILD_NUMBER}"
        }
    
        stage ("Archive Artifacts(APK)") {
            archiveArtifacts "*/**/*.apk"
        }

        stage ("Firebase Distribution") {
            sh "firebase appdistribution:distribute ${strApkPath} --app ${strFirebaseAppID} --groups ${strFirebaseDistributionGroup} --debug"
        }
        
        // Slackへの成功通知など
        slackSend channel: "${strSlackChannel}", color: '0000FF', message: "SUCCESS JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"
    }catch (e) {
        // Slackへの失敗通知など
        slackSend channel: "${strSlackChannel}", color: 'FF0000', message: "FAILED JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"
throw e
    }
}
```

## 各種解説
### Node
コマンドを実行するNode名を設定する
```
node("NodeName")
```

### 定数の解説
#### Slack Settings
Slack通知を行う場合のみ、不要なら消してください。
またjenkinsにSlack通知用のプラグイン(Slack Notification)を追加する必要があります。
```
    // Slack Settings
    def strSlackChannel = "#sample_channel"
    def strSlackMessageJob = "Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
    def strSlackCredentialId = "" 
```
- strSlackChannel : 通知を行うチャンネル
- strSlackMessageJob : Slackに送るJobについての情報メッセージ
- strSlackCredentialId : Jenkinsの設定画面より登録をしたJenkinsのSlackの送信用ID

#### Git Settings
Gitの設定
strCredentialsIdのみJenkinsにて事前に登録をしておく必要あり
```
   // Git Settings
    def strGitRepo = "https://hoge.com/git/fugo.git"
    def strBranch = "*/branch"
    def strCredentialsId = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
```
- strGitRepo : GitのURL
- strBranch : ビルドを行うGitのBranch名
- strCredentialsId : Jenkinsの設定画面より登録をしたJenkinsのGitの接続用ID

#### Android Settings
Androidの設定
```
    // Android Settings
    def strBuildType = "debug" 
    def strBuildProductFlavor = "productflavor"
    def strApkPath = "app/build/outputs/apk/${strBuildProductFlavor}/${strBuildType}/app-${strBuildProductFlavor}-${strBuildType}.apk"
```
- strBuildType : debugかreleaseのどちらか。
- strBuildProductFlavor : gradleで記載するProductFlavor、ProductFlavorsを記載をしない場合はフォルダ階層やapkの名前から`strBuildProductFlavor`が要らなくなります
- strApkPath : apkファイルが作成されるファイルパス

#### Firebase Settings
FirebaseDistributionの設定
必要があればお使いください。
```
    // Firebase Settings
    def strFirebaseAppID = "1:1234567890:android:1234567890"
    def strFirebaseDistributionGroup = "GroupName"
```
- strFirebaseAppID : FirebaseプロジェクトのAppIDを設定する。
- strFirebaseDistributionGroup : Firebaseプロジェクト内に作成をした配信グループIDを設定する。

### 各ステージの解説
#### Slack関連
SCMをポーリングなど、ビルドトリガを設定している場合に
jenkinsが走っているのかの確認が取りづらいためSlackで通知をするようにしている。
開始時は黄色、成功時は緑、失敗時は赤と見た目でもわかりやすいようにしている。
slackSendの設定で指定のメッセージにスタンプを付けるような動作もできるため、開始のメッセージに成功か失敗かでスタンプを付けるような仕様にするのも良いとは思います。
```
slackSend channel: "${strSlackChannel}", color: 'FFFF00', message: "START JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"

slackSend channel: "${strSlackChannel}", color: '0000FF', message: "SUCCESS JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"

slackSend channel: "${strSlackChannel}", color: 'FF0000', message: "FAILED JOB : ${strSlackMessageJob}", tokenCredentialId: "${strSlackCredentialId}"
```

#### Checkout
GitのCheckoutを行う
「SCMをポーリング」などを使うことが出来るのでこちらの方法でGitのCheckOutをすると、Gitのコミットをキャッチしてビルドをするなどが楽に実行できます。
```
    stage ("Checkout") {
        def scm = checkout(
            [$class: 'GitSCM', branches: [[name: strBranch]],
              doGenerateSubmoduleConfigurations: false, 
              extensions: [[$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: false, timeout: 60]],
              submoduleCfg: [],
              userRemoteConfigs: [[credentialsId: strCredentialsId, url: strGitRepo]]])
    }
```
Pipeline Syntaxのcheckoutで設定できる項目を一部変数化してカスタムしたもの

#### Android Build(APK)
```
    stage ("Android Build(APK)") {
        sh "chmod +x ./gradlew &&" +
        "./gradlew clean &&" +
        "./gradlew assemble${strBuildFlavor}${strBuildType} -PversionCode=${BUILD_NUMBER}"
    }
```
- `chmod +x ./gradlew` で権限追加を行う。
- apkの作成のため`assemble${strBuildFlavor}${strBuildType}`にしているが、aabの場合は`bundle${strBuildFlavor}${strBuildType}`に置き換える
- `-PversionCode=${BUILD_NUMBER}`でbuildNumberを更新するように設定

#### Archive Artifacts(APK)
成果物の保存用
主にapkファイルを保存しておく用として使用している。
```
    stage ("Archive Artifacts(APK)") {
        archiveArtifacts  allowEmptyArchive: true, artifacts: "*/**/*.apk"
    }
```

#### FireBase
FireBaseのdistributionにuploadをする。
```
    stage ("Firebase Distribution") {
        sh "firebase appdistribution:distribute ${strApkPath} --app ${strFirebaseAppID} --groups ${strFirebaseDistributionGroup} --debug"
    }
```
FirebaseCLIの導入については[こちら](https://firebase.google.com/docs/cli)を参考に導入をしてください。

### 参考URL
- [Codelab](https://firebase.google.com/codelabs/appdistribution-app-bundles)
- [FirebaseCLIリファレンス](https://firebase.google.com/docs/cli)