いまいち分かりづらいBitriseの環境変数周りの仕様を整理する

# はじめに
最近Bitriseのワークフローをガッツリいじる機会があったのですが、環境変数周りの仕様について勘違いしていたところがあり、結構嵌って時間を取られてしまいました。

ググってもわかりやすい記事が意外と出てこなくて、簡単なワークフローを自分で作っていろいろ試してやっと理解できたので、ユースケースごとに整理してみます。

なお、以下では`Secrets`と`Env Vars`という用語を使い、総称として環境変数と表現します。

# `Env Vars`を上書きしたい
`Env Vars`に`SLACK_CHANNEL`を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/a3b44cd0-244b-24c0-b176-70fef403ffd1.png)

Slackにメッセージを送信するステップにおいて、`$SLACK_CHANNEL`を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/4a2e62e5-ba0e-d25a-3a7f-283f15fa1d63.png)

ビルド時に`Custom Environment Variables`を以下のように指定し、別のチャンネルにメッセージを送信するようにしてみましょう。
ところが、`SLACK_CHANNEL`の値は上書きされず、`#default-channel`にメッセージが送信されてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/82ff3648-eb52-3dcf-112b-109898061086.png)

実は環境変数には以下の評価順序があり、`Custom Environment Variables`のあとに`Env Vars`が評価されるので、値を上書きできないのです。

`Secrets` -> `Custom Environment Variables` -> `Env Vars`

> **Don't use the same key as an App Env Var**
> App Environment Variables have precedence over custom Environment Variables! This means that if you define a custom Env Var with the same key as an App Env Var, the build will use the value of the App Env Var.

https://devcenter.bitrise.io/en/builds/environment-variables.html#using-env-vars-in-the-value-of-an-env-var

値を上書きするためには、Slackステップの前にScriptステップを配置し、以下のようなスクリプトを入れておきます。

```bash
#!/usr/bin/env bash
set -ex

if [ ! -z "$CUSTOM_SLACK_CHANNEL" ]; then
  envman add --key SLACK_CHANNEL --value "$CUSTOM_SLACK_CHANNEL"
fi

# もしくはワンライナーで
envman add --key SLACK_CHANNEL --value ${CUSTOM_SLACK_CHANNEL:-$SLACK_CHANNEL}
```

https://discuss.bitrise.io/t/how-to-override-environment-variables-through-the-build-trigger-api/191

これで`Custom Environment Variables`に`CUSTOM_SLACK_CHANNEL`を追加することで、Slackのメッセージの送信先チャンネルを変更することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/2528f502-27d9-cf6d-20ec-0a881af131cf.png)

# Secretsを上書きしたい
環境変数には以下の評価順序があると説明しました。

`Secrets` -> `Custom Environment Variables` -> `Env Vars`

つまり、`Secrets`はビルド時に`Custom Environment Variables`を同名で追加することで上書きが可能です。

`Secrets`に`SLACK_CHANNEL`を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/c75f1b83-1d4d-5373-276e-b2b8510825a2.png)

`Custom Environment Variables`で`SLACK_CHANNEL`に`#custom-channel`を指定します。
これで`#custom-channel`にメッセージが送信されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/82ff3648-eb52-3dcf-112b-109898061086.png)


# `Env Vars`、`Secrets`で変数展開したい
環境変数の中で別の環境変数を使用したいケースがあります。
そんなときは`Env Vars`と`Secrets`どちらでも使える変数展開の機能を使用します。

`Env Vars`に以下のように環境変数を追加します。
`SLACK_CHANNEL`の値に別の環境変数である`$ENV_PRODUCTION`を使用しています。
変数展開して値を`#ios-prod`となるようにするには、`Replace variables in inputs?`のチェックをオンにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/928e9004-8c21-42d5-6d97-c97549a5cea4.png)

`Secrets`も同様に変数展開可能なんですが、`Secrets`の場合は**環境変数が登録されている順番が重要**らしく、以下のように`ENV_PRODUCTION`が`SLACK_CHANNEL`よりも下にあると参照できないようでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/90bebe18-4e16-6bd3-8c14-89c0f1a3f836.png)

## `Env Vars`と`Secrets`間で変数を参照し合うことはできるのか？
結論は以下の通りです。

- `Env Vars`から`Secrets`の環境変数を参照して展開することはできる
- `Secrets`から`Env Vars`の環境変数を参照して展開することはできない

前述の評価順序に従えば納得ですね。

## `Custom Environment Variables`を参照して変数展開することはできるのか？
`Env Vars`で`SLACK_CHANNEL`を以下のように追加しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/d2bba28d-2aa7-449b-d08b-2feeed5e523c.png)

`Custom Environment Variables`で`ENV_PRODUCTION`に`prod`を指定します。
こうすると変数展開されて、`SLACK_CHANNEL`は`#ios-prod`になります。
※下図において、`Replace variables in input?`のチェックはここではどちらでも関係ありません

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/5270191b-3b7d-23e2-8c96-1730c167aae6.png)

なお、`Secrets`で`Custom Environment Variables`を受け取って変数展開することはできませんでした。
これも評価順序を理解していれば想定通りです。

## `Custom Environment Variables`で`Env Vars`を参照して変数展開することはできるのか？
`Env Vars`に`ENV_PRODUCTION`を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/607ed873-100a-38b5-0906-e774fe346141.png)

そして`Custom Environment Variables`で変数展開するように指定してみましたが、これはできませんでした。
値が`#ios-`になってしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/4d6d987b-5d43-72d0-ca7d-dd080afff6c2.png)

## `Custom Environment Variables`同士で参照して変数展開することはできるのか？
`Custom Environment Variables`に以下の2つを追加すると、`SLACK_CHANNEL`の値は`#ios-prod`になります。
変数展開したい方の環境変数では`Replace variables in input?`のチェックをオンにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81715/6ed70a87-b640-9351-84f5-89dc4034f2af.png)

なお、このケースでは`SLACK_CHANNEL`を先に追加してしまうと`ENV_PRODUCTION`を参照できないので、追加順序に気をつけてください。

# あとがき
ユニットテストやビルドの作成に限らず、Bitriseを使うと様々なことが自動化できて便利です。
使い込み出すと、環境変数を変えてワークフローの挙動を制御したりしたくなってくるのですが、環境変数がうまく上書きされなかったりして困ることが結構ありました。

特に`Replace variables in input?`を盛大に勘違いしていて、これのチェックをオンにしてれば`Custom Environment Variables`で上書きできるんだろうと思っていたのですが、全然違いました。。

調べてみると、割と深く理解してないと環境変数をうまく使いこなせないことがわかり、今回時間をかけて整理をしてみました。
同じように困ってる人の助けになれば幸いです。