Lento con forza

大学生気分のIT系エンジニアが色々書いてく何か。ブログ名決めました。

Bitriseのカスタムステップを作る

モバイルアプリのCI/CDプロダクトとして有名なBitriseでは、ワークフロー上で行う処理をステップの組み合わせで実現しています。このステップは十分なものが公式やサードパーティが用意しているのですが、自分でカスタムステップを作ることもできます。最近カスタムステップを作ってみたので、その方法と過程について書いておきます。

公式ドキュメントはこちらです devcenter.bitrise.io

BitriseのCLIを使うため、こちらを参考にインストールを済ませておきます。 devcenter.bitrise.io

カスタムステップの作り方

セットアップ

カスタムステップを記述する方法として、GolangかBashを選択することができます。ここではGolangを使った方法について説明します。

まず、ステップ用のディレクトリを作り、Bitrise CLIでカスタムステップのセットアップを行います。このコマンドを入力するとインタラクティブなセットアップフローが始まります。このフローでGolangかBashの選択も行います。

bitrise :step create

インタラクティブセットアップ全体を見る

Who are you / who's the author? [Kouki Saito] :
What's the title / name of the Step? : My Step
Generated Step ID (from provided Title): my-step
Please provide a summary : This is a summary
Please provide a description : This is a description

What's the primary category of this Step?
Please select from the list:
[1] : access-control
[2] : artifact-info
[3] : installer
[4] : deploy
[5] : utility
[6] : dependency
[7] : code-sign
[8] : build
[9] : test
[10] : notification
(type in the option's number, then hit Enter) : 5

Toolkit: the entry/base language of the Step.
Our recommendation is to use Bash for very simple Steps
 and for more complex ones use another language, one which we have toolkit support for.
If you're just getting started with Step development our suggestion is to select Bash,
 as that's the easiest option. It's possible to convert the step later, if needed.
Note: Of course even if you select e.g. Bash as the entry language, you can run other scripts from there,
 so it's possible to write the majority of the step's code in e.g. Ruby,
 and have an entry Bash script which does nothing else except running the Ruby script.
Which toolkit (language) would you like to use?
Please select from the list:
[1] : bash
[2] : go
(type in the option's number, then hit Enter) : 2

Website & source code URL:
Will you host the source code on GitHub? [YES/no]: yes
What's your GitHub username (user/org where you'll register the step's repository)? : kouki-dan
We'll use https://github.com/kouki-dan/bitrise-step-my-step as the website/repo URL for this step.
Please when you create the repository on GitHub for the step
 create it under the user/org: kouki-dan
 and the name of the repository should be: bitrise-step-my-step

Creating Step directory at: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/README.md
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/LICENSE
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/.gitignore
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/step.yml
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/bitrise.yml
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/.bitrise.secrets.yml
 * [OK] created: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step/main.go

Initializing git repository in step directory ...
 $ git "init"
 $ git "remote" "add" "origin" "https://github.com/kouki-dan/bitrise-step-my-step"

Step is ready!

You can find it at: /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step

TIP: cd into /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step and run bitrise run test for a quick test drive!

途中でToolkitをGoにするかBashにするかを聞かれるので間違えないようにしましょう。また、確認に便利なのでリポジトリも指定しておきましょう。

最後に表示される通り、作成されたディレクトリに移動しましょう。

TIP: cd into /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step and run bitrise run test for a quick test drive!
cd /Users/kouki_dan/go/src/github.com/kouki-dan/bitrise-step-my-step

コミットしてプッシュしておきましょう。GitHubにリポジトリを作ってない場合は先に作成しておきましょう。

git add .
git commit -m "Initial commit"
git push -u origin master

デフォルトで生成されるGitリポジトリのoriginはhttpsのURLになっているため、必要に応じてSSHのURLに置き換えてください

git remote set-url origin git@github.com:kouki-dan/bitrise-step-my-step.git

カスタムステップの開発

カスタムステップの開発は以下のようなフローで行います。

  1. ローカルマシンでのテスト
  2. Bitrise上での単体で行うテスト
  3. 実際のワークフローに組み込むテスト

1. ローカルマシンでのテスト

ローカルマシンでのテストは、Bitrise CLIで行えます。 bitrise run test とコマンドを入力することで実行できます。

bitrise run test

と入力することで、main.goに書かれたコードが実行されます。

2. Bitrise上での単体で行うテスト

ローカルでのテストが終わったらBitrise上でのテストも行いたいと思います。作ったリポジトリテスト用のAppをBitriseで作成し、ローカルのステップを実行することができます。 こちらで説明されています。 devcenter.bitrise.io

path で指定する方法だとプライベートリポジトリでもBitriseで動かすことができます。カスタムステップを作った時はテスト用のAppをBitriseに作成しておくと良いでしょう。

Appを作成するときはバリデーションが通らないので、バリデーションせずに作る方法で作成すると良いです。

Workflow Editorからbitrise.ymlを直接編集します。

---
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: other
trigger_map:
- push_branch: "*"
  workflow: primary
- pull_request_source_branch: "*"
  workflow: primary
workflows:
  primary:
    steps:
    - activate-ssh-key@4:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@4: {}
    - path::$BITRISE_SOURCE_DIR: {}
    - script@1:
        inputs:
        - content: |-
            # カスタムステップの出力を確かめる
            echo "$EXAMPLE_STEP_OUTPUT"

このように、- git-clone: でリポジトリをクローンした後、 - path::$BITRISE_SOURCE_DIR: で ローカルのステップを指定します。 その後、- script: でローカルのステップの検証を行えます。カスタムステップの初期状態では、EXAMPLE_STEP_OUTPUT という環境変数がOUTPUTとして指定されているのでこれを確認します。このようにカスタムステップを指定した場合はなぜかWeb UIの表示が変になってしまうので、ログをダウンロードして出力を確認する必要があります。

f:id:kouki_dan:20200630085347p:plain

ログは以下のようになり、カスタムステップが実行されていることがわかります。

+------------------------------------------------------------------------------+
| (2) path::$BITRISE_SOURCE_DIR                                                |
+------------------------------------------------------------------------------+
| id: $BITRISE_SOURCE_DIR                                                      |
| version:                                                                     |
| collection: path                                                             |
| toolkit: go                                                                  |
| time: 2020-06-29T23:51:01Z                                                   |
+------------------------------------------------------------------------------+
|                                                                              |
[36mINFO[0m[23:51:01]  * [32;1m[OK][0m Step dependency (git) installed, available. 
[36mINFO[0m[23:51:01]  * [32;1m[OK][0m Step dependency (wget) installed, available. 
This is the value specified for the input 'example_step_input': Default Value - you can leave this empty if you want to
|                                                                              |
+---+---------------------------------------------------------------+----------+
| [32;1m✓[0m | [32;1mpath::$BITRISE_SOURCE_DIR[0m                                     | 1.48 sec |
+---+---------------------------------------------------------------+----------+

                                          ▼

+------------------------------------------------------------------------------+
| (3) script@1                                                                 |
+------------------------------------------------------------------------------+
| id: script                                                                   |
| version: 1.1.6                                                               |
| collection: https://github.com/bitrise-io/bitrise-steplib.git                |
| toolkit: bash                                                                |
| time: 2020-06-29T23:51:03Z                                                   |
+------------------------------------------------------------------------------+
|                                                                              |
the value you want to share
|                                                                              |

これがステップの動作を確認するための典型的なbitrise.ymlになると思います。必要に応じて動作させるのに必要なステップを追加しましょう

3. 実際のワークフローに組み込むテスト

ステップが動くことを確認できたら実際のワークフローに組み込みます。 gitリポジトリを直接指定するか、StepLibに後悔する方法を選べます。StepLibへの公開は後ほど説明するので、ここではgitリポジトリを直接指定する方法について説明します。

gitリポジトリを指定して実行する方法はGUIからは操作できず、YMLファイルを編集する必要があります。以下のように記述します。

workflows:
 workflow_name:
    steps:
    - [yoursteps]
    - git::https://github.com/kouki-dan/bitrise-step-my-step.git@master:
    - [yoursteps]

すでにあるステップと同じ階層に、- git::[リポジトリのURL].git@[タグやブランチ]:のように指定してください。

+------------------------------------------------------------------------------+
| (3) git::https://github.com/kouki-dan/bitrise-step-my-step.git@master        |
+------------------------------------------------------------------------------+
| id: https://github.com/kouki-dan/bitrise-step-my-step.git                    |
| version: master                                                              |
| collection: git                                                              |
| toolkit: go                                                                  |
| time: 2020-06-29T23:55:44Z                                                   |
+------------------------------------------------------------------------------+
|                                                                              |
[36mINFO[0m[23:55:44]  * [32;1m[OK][0m Step dependency (git) installed, available. 
[36mINFO[0m[23:55:44]  * [32;1m[OK][0m Step dependency (wget) installed, available. 
This is the value specified for the input 'example_step_input': Default Value - you can leave this empty if you want to
|                                                                              |
+---+---------------------------------------------------------------+----------+
| [32;1m✓[0m | [32;1mgit::https://github.com/kouki-dan/bitrise-step-my-step.git@...[0m| 1.73 sec |
+---+---------------------------------------------------------------+----------+

                                          ▼

+------------------------------------------------------------------------------+
| (4) script@1                                                                 |
+------------------------------------------------------------------------------+
| id: script                                                                   |
| version: 1.1.6                                                               |
| collection: https://github.com/bitrise-io/bitrise-steplib.git                |
| toolkit: bash                                                                |
| time: 2020-06-29T23:55:47Z                                                   |
+------------------------------------------------------------------------------+
|                                                                              |
the value you want to share
|                                                                              |
+---+---------------------------------------------------------------+----------+
| [32;1m✓[0m | [32;1mscript@1[0m                                                      | 1.65 sec |
+---+---------------------------------------------------------------+----------+

ベンダリング

Golangでカスタムステップを作るときにライブラリを使いたいケースがあると思います。カスタムステップでの依存ライブラリの導入は少し特殊なため説明をしておきます。

このドキュメントで説明されている通り、ステップの実行に必要なものはリポジトリ内に含めることが推奨されています。 devcenter.bitrise.io

理由としては、ステップは堅牢ではなくてはいけませんが、ネットワークや認証などで問題が発生する可能性が増加してしまうためです。そのため、Golangのライブラリを使った場合も、リポジトリ内に含めることが求められます。

Golangではプロジェクトルートのvendorディレクトリを依存関係の解決先として扱うことができます。依存ツールがあるときはvendor以下に配置するようにして、vendorディレクトリをコミットするようにしましょう。公式のカスタムステップもそのように作られています。

例として、YAMLファイルを扱うライブラリ https://github.com/ghodss/yaml を導入してみましょう。

main.go を以下のように編集します。

package main

import (
    "fmt"
    "github.com/ghodss/yaml"
    "os"
    "os/exec"
)

func main() {
    j := []byte(`{"yaml": "test", "yaml2": test2}`)
    y, err := yaml.JSONToYAML(j)
    if err != nil {
        fmt.Printf("err: %v\n", err)
        return
    }
    cmdLog, err := exec.Command("bitrise", "envman", "add", "--key", "EXAMPLE_STEP_OUTPUT", "--value", string(y)).CombinedOutput()
    if err != nil {
        fmt.Printf("Failed to expose output with envman, error: %#v | output: %s", err, cmdLog)
        os.Exit(1)
    }

    os.Exit(0)
}

Go Module を使った例

Go Moduleはそのまま使うとvendorディレクトリには入りませんが、 go mod vendor コマンドを使うとvendorディレクトリを利用してくれます。そのため、こちらを使い、vendorをコミットすることでカスタムステップを実行することができます。

go mod init
go mod vendor
git add go.* vendor
git commit

dep を使った例

公式のカスタムステップではdepを使っているケースが多いため、depを使う方法についても記述しておきます。こちらも使うのは簡単で、以下のコマンドで実行できます。

dep init
dep ensure
git add go.* vendor
git commit

Go Moduleでライブラリを導入した例は以下のリポジトリで確認できます。

github.com

公開する

ここまでできればあとは開発を進めていくだけです。満足するものができたら、他の人にも使いやすいように公開しましょう。BitriseのステップはStepLibリポジトリで管理されています。

github.com

ここに公開することで、BitriseのGUI上からカスタムステップを選択できるようになります。実際にStepLibに公開するときはセマンティックバージョニングでバージョニングを行う必要があるので注意しましょう。

こちらもBitrise CLIで操作できます。以下のドキュメントを参考に公開しましょう。

devcenter.bitrise.io

まとめ

Bitriseを利用している方にはスクリプトステップなどで独自の処理を実行している方がいらっしゃると思います。その処理をカスタムステップ化すると、Golangを使って簡潔に記述できたり、入力や出力を明示することでわかりやすくなったりします。ぜひステップを作って利用、公開してみましょう!