AWS ECRのクロスアカウントレプリケーションを設定してみた

システム開発部Misocaチームエンジニアの id:mizukmb です。

今回は最近追加されたAWS ECRのクロスアカウントレプリケーション機能を実際に設定してみた話を書きたいと思います。

AWS ECRのクロスアカウントレプリケーション機能とは

ECRにDockerイメージがプッシュされたタイミングで異なるAWSアカウントのECRプライベートリポジトリに同じイメージを複製するといった機能です。アカウントだけでなく、同一アカウント内の異なるリージョンの複製もできるようです。

詳しくはAWS公式のブログを参照ください。

aws.amazon.com

これまでは異なるリージョン・アカウントに同じDockerイメージを配置するためにはレプリケーションを行うスクリプトを用意するといった運用が必要でしたが、AWS側でサポートされることでこうした運用が必要なくなります。

Misocaチームの課題

Misocaチームでは、本番環境と開発環境はAWSアカウントを分けて運用しています。 開発環境のECSリプレイスとterraformでのコード化 - 弥生開発者ブログ by Misocaチーム にある通り開発環境はECSで動いていて、本番環境でも一部Dockerコンテナを利用してるところがあります。両環境共に同じDockerイメージを利用する箇所があり、下図のようにそれぞれの環境でCodeBuildを使ってDockerイメージのビルドとECRプライベートリポジトリへのプッシュを行っています。見てわかると思いますがほぼ同じ処理をするCodeBuildを二重に動かすことで両環境にDockerイメージを配置しています。この複雑なビルド方法に対しては以前から課題に感じており、もっとシンプルにできないかと頭を悩ませていました。

f:id:mizukmb:20210305113439p:plain
同じDockerイメージを異なるAWSアカウントで作成していて煩雑になってしまっていた

そしてこのタイミングでクロスアカウントレプリケーション機能が使えるようになりました。この機能によってCodeBuildをどちらかの環境で動かすだけでビルドしたDockerイメージを両環境に配置できるため、ビルド構成が今よりシンプルになりました。

送信元アカウントで機能を有効にする

それでは設定していきます。送信元アカウントと送信先アカウントの両方で設定が必要です。まずは送信元アカウントでクロスアカウントレプリケーション機能を有効にします。

ECRのページから Registries をクリックし、レジストリページに遷移します。こんなページです

f:id:mizukmb:20210303183838p:plain

次に プライベート をクリックし、ラジオボタンを有効にした後、 編集 ボタンをクリックして、編集ページに遷移します。

クロスアカウントレプリケーションの有効・無効を切り替えるトグルボタンがあるので有効にします。送信先アカウント設定のフォームが出現するので送信先アカウントとリージョンを入力します。入力したら保存ボタンをクリックして保存をします。

f:id:mizukmb:20210303184801p:plain

保存すると、クロスアカウントレプリケーションの欄に送信先のAWSアカウントIDが表示されるようになります。これで、送信元アカウントの設定は完了です。次は送信先アカウントの設定になります。

f:id:mizukmb:20210303185451p:plain
送信先のAWSアカウントIDが表示されていたら設定完了

送信先アカウントでレジストリポリシーを設定する

レジストリポリシーを設定しますが、設定は 送信先アカウント で行いますので間違えないように気をつけてください。

まずは先ほどと同様にレジストリページに遷移します。次に プライベート をクリックし、ラジオボタンを有効にした後、今度は アクセス許可 ボタンをクリックして、プライベートレジストリのアクセス許可ページに遷移します。以下のような画面になります。

f:id:mizukmb:20210304145459p:plain

ステートメントを生成 ボタンをクリックして、ポリシーステートジェネレーターのモーダル画面を開きます。

ポリシーステートジェネレーターでは、ポリシーステートメント用のJSON文字列を自動生成し、設定します。直接JSONを書き込むこともできますがこちらの方が誤字等の設定ミスが少なくできるので今回はこちらを利用して設定します。以下のようにステートメントIDと送信元のAWSアカウントIDを入力し、 ポリシーに追加 ボタンをクリックして保存します。

f:id:mizukmb:20210304150829p:plain
ポリシーステートメントジェネレーター入力例

これで全ての設定が完了しました。

実際にレプリケーションするか確かめるには

レプリケーションは送信元のECRリポジトリに docker push されたタイミングで一度だけ実行されます。レプリケーションには数分の時間がかかっており、瞬時にレプケーションが完了するわけではない事に注意してください。

また、レプリケーション時にタグは上書きされることにも注意してください。例えば latest 運用していた場合にレプリケーションによって意図せずベースイメージが変わってしまう可能性があります。

その他レプリケーションに関する挙動については公式ドキュメントの『プライベートイメージのレプリケーションに関する考慮事項』をご確認ください。

docs.aws.amazon.com

おわりに

AWS ECRのクロスアカウントレプリケーションの設定に関する一連の流れを書きました。弊チームのようにDockerイメージのレプリケーション作業を自前で運用していた方は、こちらの機能を検討してみてはいかがでしょうか。

参考文献

EOL な Elasticsearch クラスターのバージョンアップを実施しました

はじめに

こんにちは、狩野と申します。 平沢進 氏の24曼荼羅(不死MANDALA)ライブ開催が決定されました。また第9曼荼羅のライブDVDの発売決定しましたので、ありがたく発売日を待っております。

⬆️ EOLなElasticsearchのバージョンアップ

先日Misocaで利用しているElasticsearchクラスターのバージョンアップと関連Gemのアップデートをリリースしました。

それまでのMisocaではEOLとなってしまった5系のバージョンを使用しておりました。 最新に追従できていない現状と、今後の機能追加・改善にElasticsearchを採用しにくくなっている懸念があったためElasticsearchのメジャーバージョンアップと関連Gemのアップデートを行いました。

アップグレード方針として5系から7系に直接上げず、一旦6系の最新にアップグレードしてから7系に上げました。

これはGemとElasticsearch本体のChangelogを調査した時に想像以上に変更が多く、問題発生時の原因切り分けが難しくなることが予想できたためです。 結果的な話ではありますが、6から7へアップグレードする際に切り戻し対応を行いましたので、その際に原因究明のスコープが小さくなり早急な対処に繋がったため良い方針だったと感じました。

本記事ではバージョンアップに際して工夫した以下の2点について記載します。

  • サービス無停止でのバージョンアップが可能なリリース方式の検討
  • 切り戻しを含めた詳細なリリース手順の作成とリハーサル

🦍 サービス無停止でのバージョンアップが可能なリリース方式の検討

MisocaではAmazon Elasticsearch Serviceを利用しています。 リリース方式の策定のために2つの方法を検討しました。

  • Amazon ESの「サービスソフトウェア更新」機能を利用する方式
  • 新旧クラスタを準備し切り替える方式

今回は「新旧クラスタを準備し切り替える方式」を採用しました。選んだ理由としては

「サービスソフトウェア更新」機能を利用する場合ESクラスターがアップグレードされるタイミングはAWS側に委ねられてしまいます。アプリケーションのGemバージョンとESクラスターのバージョンを一致させつつサービス無停止を実現するには「新旧クラスタを準備し切り替える方式」が必要という結論になりました。

🔄 新旧クラスタを準備し切り替える

リリース方式をかいつまんで説明します。リリース前の状態が下図になります。

現行

リリース事前準備として、通常時の検索であればElasticsearchからデータを取得しますが、一時的にMySQLを参照する機能をfeature flagによって実現しました。

feature flagの説明はこちらのMisocaのブログ記事をぜひ御覧ください!

またES6.xのクラスタを準備し事前に同期できるデータについては同期を実施しました。

移行直前

Elasticsearch側の準備はできたのでアプリケーションサーバ側の更新を行います。 またサーバ更新中に発生した差分を同期させます。

移行中

feature flagによって一時的に検索参照先をMySQLにしていたのを元に戻すことでバージョンアップ完了です。

移行仕上げ

上記の作業では5から6へアップグレードしていますが、同様のことを6から7へとアップグレード時にも実施し、現状ではAmazon Elasticsearch Serviceが提供している7系の最新に更新できました 🎉

📑 切り戻しを含めた詳細なリリース手順の作成とリハーサル

上記の方式ではリリースフェーズ毎にどちらのESクラスターにアプリが接続しているかや不整合なデータをユーザーに表示させていないかが分かりづらくなります。

なので当たり前のことですが入念に作業手順書をしっかりと書いておきリハーサルも行いました。

リリース時に想定しなかったエラーが起こってしまいましたので切り戻しを行いました。*1

作業手順書には切り戻し時の対応も準備しておきましたし、切り戻しのリハーサルも行っていたためユーザーへの影響無く切り戻しできました。

🌈 現状と今後

Amazon ESは今後のESバージョンのリリース予定日や古いバージョンのEOL日を提供していないため、Elastic社側のEOLやAmazon ESが新しいバージョンを提供したかを確認する日を半年に1回のペースでカレンダーで登録し、人力で気付けるようにしました。

ただもっと良い方法があると嬉しいな〜というきもちです。情報いただければ幸いです 🙏

今後は、よりよいチューニングやログ監視をしたいね、Amazon ESの「サービスソフトウェア更新」機能使えるなら使いたいね、という計画をしています。

📡 宣伝

Misoca開発チームではElasticsearchのことなら俺に任せろ!なエンジニアを募集しています! https://www.wantedly.com/projects/28443

*1:切り戻しの原因としては、インデックスへのデータインポート時にTimeoutエラーを起こしてしまうことでした。データインポート処理のバッチサイズを削減したり、非推奨とされているT2インスタンスを使わずT3インスタンスに切り替えることで解決しました。

Misocaメンバーの新年の抱負2021

はじめに

コロナ禍の厳しい年末年始でしたが、皆さんいかがお過ごしでしたでしょうか。 id:RKTMです。

私は子どもを連れてスキーをしていました。 良い雪があるのに、コロナのせいでスキー場もなかなか厳しいようです。

f:id:RKTM:20210110110501j:plain
長野県阿智村ヘブンスそのはらスキー場から登る富士見台高原の霧氷

今年のMisocaメンバーの抱負は?

弥生全体で大きなプロジェクトがスタートし、Misocaメンバーも何人かはそのプロジェクトに参戦中です。

www.talent-book.jp

そんなMisocaチームですが、今年の抱負を聞いてみたいと思います。

@suer

  • SwiftUI と Firebase でアプリを作る
  • Androidアプリもなにか作りたい
  • アウトプットを増やしたい(気持ちだけがある)
  • 去年は自粛続きで運動不足なので健康を取り戻す

@hidakatsuya

  • 今年こそ Thinreports v1.0.0 を出して、SectionReport 機能 を世に出す
  • Android アプリを作って公開する
  • TOEIC を受験する

@KawamataRyo

  • 今年こそGitHubに一面の草を生やす(去年は360/365)
  • SwiftUIで、AppleWatchのプランクアプリを作る
  • AtCoder始める
  • 懸垂以外の趣味を見つける

daaaaahara

  • 運動(リングフィットアドベンチャー)を週5日以上継続して行う。
  • Discordのbotの機能を増やす。
  • Androidアプリの勉強がてら、輝度を限界より暗くするアプリを作ってみる。
  • 平和に過ごす。

@RKTM

  • 去年はVueとTypeScriptをサポート受けながら書けるようになったので、今年はもう少しスッと書けるようになる。
  • 今年こそRustでなにか作りたい…。
  • バックカントリースキーで未圧雪の(緩い斜面を)気持ち良く滑られるようになる。
  • (マルチピッチ)クライミング頑張る。

id:mizukmb

  • 毎日同じ時間に起きる
  • 毎朝体重を測定する
  • AWSソリューションアーキテクトアソシエイト(SAA)試験に合格する

@kokuyouwind

  • リングフィットアドベンチャーとフィットボクシングを継続してやる
  • 死なない

@mugi_uno

  • Next.js完全に理解する
  • 2022年を元気に迎える

まとめ

全体的な傾向として、運動・健康に関心があるようです。 メンバー皆無事にこの1年を過ごせるよう願っています。

そんなMisocaを2021年もよろしくおねがいします!

新メンバーも募集しています!

www.wantedly.com

受注管理機能を支える技術 〜 VueCompositionAPIとGraphQLとAtomicDesignとScopedStyle〜

こんにちは、 @mugi_uno です。

少し前に背骨の手術を受けたら身長が伸びました。

🎉 受注管理機能をリリースしました

2020/12/10に、Misocaに新しく「受注管理機能」をリリースしました。

www.misoca.jp

f:id:mugi1:20201217165947p:plain

いままでは、請求書・見積書・納品書といった単位でのステータス管理が主でしたが、新たに追加された受注管理機能を使うことで、案件単位でステータスを管理しつつ、各文書への変換も簡単に行えるようになりました。

そして、同時にこの受注管理機能は、開発面においても様々な新しい技術面でのトライもありました。

  • Vue.js & Vue Composition API
  • GraphQL
  • Apollo Client & Vue Apollo
  • Atomic Design
  • Scoped Style

今回は、これらについてどういった対応をしたのかと、リリース後にふりかえってみてそれぞれよかった点、大変だった点についてのお話です。

前提 / 以前から利用している技術要素と受注管理機能の基本構成

受注管理に限らないMisoca全体の代表的な技術要素として、次のものを以前から利用しています。

  • バックエンド: Ruby on Rails
  • フロントエンド: TypeScript & Vue 2.x
  • API: REST
  • デザイン: SCSS (*.scss ファイルに記述)

新たに作成した受注管理機能はSPAで構成されており、次のような機能を実現しています。

  • ステータスの追加・変更・削除
  • 受注情報の新規作成・複製
  • 受注情報のステータス変更
  • 履歴の確認
  • コメントの追加
  • 検索

Vue.js (Vue Composition API)

受注管理機能の開発着手時点で、すでに Vue 3.x 系のリリースも見えている状態で、当時の選択肢としては2つ考えられました。

  • 従来の Vue 2.x 系の記法のまま実装する
  • Vue Composition API を利用する

Vue 3.x 以降も従来の記法はサポートされるので、無理に新しい記法とする必要はなかったのですが、「どうせ近い将来にすべて書き換えるなら、最初から Vue3フレンドリーな形で実装していこう」と、Vue Composition API を利用した形を主軸に実装しました。

github.com

結果としては、2020年9月時点で Vue3 が正式リリースされました。MisocaではIE対応の必要性から未導入ですが、後々必要となるマイグレーションを考えると、良い決断だったかなと思います。

👍 良かったこと

共通処理を Vue Composion API を使って簡単に切り出せるのは使い勝手が良かったです。

例として「新規作成と編集と複製って、ロジックがほぼ同じなんだけどちょっとだけ違うんだよな〜」といった際に、共通ロジックを useEditForm.ts として切り出す、といったことが簡単にできます。

また、Composition API は TypeScript との親和性が高く、機能追加・変更時も安全に手を加えていける点も良かったです。

🙈 大変だったこと

開発までの慣れ

いままでは Vue 2.x + Class Component で開発していたため、書き方が大きく変わる Composition API に開発メンバーが慣れるまでに少し時間がかかりました。ただ最初でこそ大変でしたが、慣れてくると「こっちのほうが書きやすい」という意見が多くなりました。

なお、開発と並行して @kawamataryoの主催で Vue Composition API のドキュメントを対象にした輪読会も開催されていました。そちらも、レビュアー・レビュイー双方の観点で良い効果があったと思います。

setup関数の肥大化

Composition API は setup 関数が肥大化しがちで、意識せずに処理を詰め込んでいくと、全体的に何をやってるか見通しが悪くなるのが課題でした。対策として、「大きくなりそうなら、処理は意味のある単位でコンポーネント内のローカル関数に切り出して、setup は小さく保つ」といったルールを共有していました。

<script lang="ts">

...

const useA = () => {
   // Aに関連する処理
}

const useB = () => {
   // Bに関連する処理
}

const useC = () => {
   // Cに関連する処理
}

export default defineComponent({
  setup() {
    return {
      ...useA(),
      ...useB(),
      ...useC()
    }
  }
})
</script>

関連ライブラリの影響

Vue Router や Vue Apollo などで一部機能が未対応だったり、Vue Composition API 側の変更の煽りを受けて挙動が壊れるというのを何度か踏みました。こればかりは新しい技術を突っ込んだ以上逃げられないものですので、ライブラリ側に修正パッチを送るなどして、わりと泥臭く対応してました。

GraphQL

SPAなのでデータの取得・更新はAPI経由ですが、すべてGraphQLで実装しています。

バックエンド(Rails)では、GraphQL Ruby でエンドポイントの実装とスキーマファイル出力を行い、フロントエンドでは、スキーマファイルを元に GraphQL Code Generator を利用して TypeScript の型定義ファイルを生成しています。

GraphQLを導入した背景としては、すでにMisocaでメイン利用されていた TypeScript との親和性が非常に高いことや、将来的にモバイルなど他の領域からの利用を考えた場合に柔軟に対応できる点が挙げられます。(※ただ正直なところ、エンジニアチームとして新しい技術にトライしたかったというのもあったかなと思います。)

👍 良かったこと

GraphQL による開発体験はとても良く、特に GraphQL Code Generator 経由で生成した型定義をフロントで利用できる点が非常に快適でした。

Misocaでは開発中の仕様変更も柔軟に受け入れていくスタイルですので、DB定義も含めてガンガン変更が入ります。その際に、GraphQL スキーマの変更から連動して型定義も変更されるため、ビルド時の静的検査の時点で検知でき、簡単かつ安全に対応していくことができました。

「バックエンドが変わったのにフロントエンドの変更が漏れてる」といったこともほぼ無く、品質面でも大きなメリットがあったように感じます。

ふりかえりなどでも GraphQL 超便利といった声が多数聞けました。導入して良かったです。

🙈 大変だったこと

ついうっかり N+1 を踏んでしまうことが何度かありました。「履歴データが多くなってくると一覧がタイムアウトします!!」みたいな感じです。

根本的な対処としては、Dataloader を利用してバッチ的にデータを取得する方法などが例として挙げられるかと思います。

しかし、Misocaの場合は発行されるGraphQLクエリがある程度は予測可能でしたので、単純に ActiveRecord 側で includes を足して対処しました。それでも、可能な限り無駄なくincludes を足すため、GraphQL クエリに基づいて自動的に includes に渡す引数を生成するヘルパーを自作しました。以下はヘルパーの利用イメージです。

RELATION_MAP = {
  comments: {
    user: {}
  },
  histories: {},
  items: {},
  status: {}
}.freeze

# クエリ内容とマッピング定義から includes に渡すべきパラメータを得る
includes_params = IncludesHelper.create_includes(
  lookahead, # GraphQLRubyが提供するクエリ検査用インタフェース
  RELATION_MAP
)

orders.includes(includes_params)

これにより、あるクエリ内で comments だけ取得している場合は comments のみが includes に含まれ、別クエリで itemsstatus を取得する場合には itemsstatus のみが includes に含まれるようになっています。

状態管理

Vue & TypeScript の組み合わせで迷いやすいのが状態管理の方法かなと思います。

今回のケースでは GraphQL のために Apollo Client を利用しており、基本的なデータについては Apollo Client のキャッシュ機構を利用して状態管理の代替としています。

同時に Vue Apollo も組み合わせているので、たとえば

  1. ユーザーが何か入力する
  2. 1 を Vue のリアクティブシステムで検知して GraphQL クエリを自動発行
  3. Apollo Client のキャッシュが更新
  4. 同一 GraphQL クエリに依存する箇所すべてが自動的に再描画

といった挙動もわりと簡単に実現できます。

ただ、画面上のすべての情報が Apollo Client で集約できるかというとそんなこともなく、UI操作上一時的にどこかに保持したい かつ 様々なコンポーネントから参照が必要になる値も存在します。モーダルの表示状態やドラッグ&ドロップの一時的なデータ保持などが代表例です。

Apollo Client に local stateとして管理させる方法もありましたが、シンプルさ重視で Vue Composition API の reactive を利用した簡易的なストアを作成しました。

👍 良かったこと

リフレッシュや多重リクエスト制御が自然といい感じになる

複数コンポーネントが Apollo Client のクエリ・ミューテーションと連動して自動的に再描画されていくのはなかなか便利です。

独自でクエリ発行→再描画を制御している場合、適宜処理の影響を受ける箇所でリフレッシュが必要になりますが、Vue Apollo がそのあたりを吸収してくれて、とても簡単に実装できました。

また、データが必要な箇所ではあまり深く考えずにクエリを実行しても、Apollo Client側で多重リクエストは自動的に抑制して捌いてくれます。(※設定によります)

開発時の「こうやって動いてくれたらいいのにな〜」という思いを、ほどよく汲みとって動作してくれるのは体験として良かったです。

reactive での簡易ストアは楽だった

reactive での簡易的な状態管理を作成しましたが、単純なオブジェクトを変更・参照していく形なのでとてもシンプルでした。TypeScriptとの相性も良かったです。最低限のガードとして参照側は Readonly 型としていたため、意図しないストア書き換えによる事故も発生しませんでした。

ただし、これは常にベストな選択肢では無さそうにも思います。一定以上の規模感になってくると、独自実装を増やすよりも、Vuexなどを利用して王道に沿った形にしていくほうが、長期的なメンテナンスコストは低くなるかもしれません。

🙈 大変だったこと

テストが書きづらい

Vue Test Utilsを利用したコンポーネントのテストを書く際に、Apollo Client のキャッシュに依存したテストが非常に厄介でした。

graphql-toolsを使うことでモック化自体は容易ですが、キャッシュを前提とした挙動のテストを書く場合、セットアップ段階でキャッシュを生成する必要があります。

ただ、実際にテストを実行していると、「キャッシュが無くてテストに落ちている」というのがエラーから気付くのがなかなか難しく、長時間ハマってしまうことも何度かありました。

暗黙的な挙動が増える&対処が複雑化しがち

Vue Apollo を組み合わせることで「Vueでの値の変更や別の箇所でのクエリ結果に連動して自動的に再描画してくれる」というのは、便利である反面、暗黙的な挙動となる箇所が増えていきます。また、クエリ発行のタイミング制御も難しくなり、画面上で複雑な操作・更新が発生する場合、それを実現するためのコードも同時に複雑になっていく傾向がありました。

Vue Apolloを利用せず、クエリ結果をストアに記録して明示的に制御するように書き換えるほうがシンプルになるかもしれないな..と思っている部分もありますが、もちろん恩恵を受けている箇所もあるため、今後様子を見ての判断になりそうです。

コンポーネント設計&デザイン

Atomic Design

コンポーネント設計は Atomic Design を採用しました。これは、全力で死守するものというより「判断基準を最初に設けたかった」というのが目的です。

以前からMisocaのフロントエンド開発時には、コンポーネントをどういった粒度で切るべきか / どこに配備すべきか、といった点が曖昧で迷ってしまい、フロントエンドチームが相談を受けることも度々ありました。

受注管理の機能開発時も同じ状況に陥ることが予想できたため、予め何らかの基準を設けるのが必要と考えました。

Scoped Style (Vue)

CSSは Vue.js の Scoped Style で書いていきました。

もともとMisocaのデザインは *.scss ファイル内に SCSS で書かれていますが、デザイナーの皆様の尽力もあり、いまではかなり整理されて、ファイル単位での影響範囲が明確になっていました。

tech.misoca.jp

さらに将来的な展望として Scoped CSS を利用した Vueコンポーネント化もあったため、まず手始めに今回の受注管理機能で実践してみた形です。

開発時のルールとしては、外部のCSSとの干渉や無駄な重複を避けるため、

  • Scoped Style を適用するためのクラスにはすべて _ を prefix として付与する
  • 色やフォントサイズ定義は既存の定義をimportして利用する

などがありました。

👍 良かったこと

迷いにくかった

「Atomic Design ベースでいきましょう」と合意をとったことで、当初の目論見どおり、コンポーネントの粒度・配備で迷うことは少なかったです。また、あくまでも判断基準のためで良くも悪くも緩い導入だったので、ルールに縛られすぎずに程よい付き合いができたのも良かった点でした。

CSSの変更が楽だった

Scoped Style ではコンポーネントに対応するスタイルは、当然ではありますが、そのコンポーネントの中に書いてあります。

整理されていたとはいえ、外部化されているCSSを触るときは「これってどこか他を壊しちゃうんじゃ…」という不安が心の片隅にありましたが、Scoped Style のように影響範囲がそのファイル内で閉じているのはなかなかに快適で、修正時の負担がかなり軽減されたように感じます。

🙈 大変だったこと

Scoped Style のスタイルの親子継承が厄介

影響範囲がファイル内で閉じていると書きましたが、実は Vue の Scoped Style には一部例外があり、子コンポーネントのルート要素だけは親要素のCSSを引き継いでしまうケースがあります。

スコープ付き CSS · vue-loader

たとえば、親コンポーネント側で _foo というクラスに対してスタイリングした場合に、子コンポーネントのルート要素に _foo というクラスを付与すると、親コンポーネント側で定義したスタイルが適用されてしまいます。

レイアウト用のスタイリング時に利用できる仕組みのようですが、どちらかというと意図しないCSS適用を引き起こすケースのほうが多かったです。

調べた限りではこれを無効化する仕組みは無いようで、コンポーネントのルート要素に Scoped Style 用のクラスを付与する場合には、できるだけ他で利用されにくい冗長な名前をつけて回避していました。

まとめ

今回は新しくリリースされた受注管理機能で利用した技術の話でした。

今後もずっと使い続けていけそうなノウハウも得られましたし、逆に「もっとこうすればよかったなぁ」という反省も勿論ありました。

いずれにしても試してみない限りは得られることのなかった知見かなと思うので、今後も(将来の開発者に迷惑をかけない範囲で)新しいことにチャレンジしていけるといいな〜と思います。

AndroidアプリにViewModelを導入しました

はじめまして。Misocaモバイルチームのtijinsです。

この記事は、弥生アドベントカレンダー14日目の記事です。

MVPからMVVMへ

Android版MisocaはModel-View-Presenter構造で作られていたのですが、Model-View-ViewModel構造へのリファクタリングが完了しました。

コードがスッキリしたので、リファクタリングの内容を紹介します。

MVPパターンとMVVMパターン

Model-View-Presenter(MVPパターン)

MVPパターンでは、PresenterがViewの参照(intrefaceで)を保持し、結果をViewに表示します。

Viewを直接参照せずにInterfaceにしておくことで、PresenterからViewの依存を排除しています。

@startuml
  interface PresenterResult
  class View implements PresenterResult
  {
    表示用データ
  }
  class Presenter
  class Model

  View --> Presenter: 読み込み・新規作成等の操作
  Presenter --> PresenterResult : 結果の表示
  Presenter --> Model
@enduml

Model-View-ViewModel(MVVMパターン)

MVVMパターンでは、ViewModelからViewの参照は不要です。

表示用のデータはViewModelが保持しており、ViewがViewModel上のデータを監視(Observe)して表示します。

@startuml
  class View
  note right of ViewModel : ViewModelは、操作結果により、\n保持する表示用データを更新する
  class ViewModel{
    表示用データ
  }
  class Model

  View --> ViewModel: 読み込み・新規作成等の操作\n変更の監視(変更があれば表示を更新する)
  ViewModel --> Model
@enduml

ViewModel化のメリット

ViewModelにも色々あるみたいなのですが、ここでのViewModelはデータを保持できるPresenterといった感じです。

PresenterからViewの参照を排除できる

ViewModel化前は、Presenterに非同期処理完了のコールバックとして、Viewの参照を(interfaceとしてですが)渡していました。

ViewModelを利用すると、ViewModelからViewへの参照は不要になります。

AndroidアプリでのViewModelのメリット

  • FragmentでもActivityライフサイクルでのデータ保持が可能

Fragmentにデータを保持していると、画面の回転等でFragmentが破棄された際に、データの読み込みが必要でした。

ActivityライフサイクルのViewModelを利用する事で、Fragmentが破棄されても、データの引き継ぎが可能になります。

  • ライフサイクル管理の単純化

Presenterが行う非同期処理の結果をFragmentで表示する場合、先にFragmentが破棄されていると、クラッシュしてしまう問題がありました。

ViewModelのライフサイクルはFragmentから分離されている為、Fragmentの状態を気にせずに、非同期処理が可能です。

リファクタリング前のコード

最初にリファクタリング前のコードです。

Viewを直接参照しないのは、PresenterからViewの依存を排除する為です。

こうしておくと、テスト用のダミークラスに差し替えたりできます。

@startuml

  interface ItemsResult
  {
    onItemLoaded(items)
    onFailed(message)
  }
  
  class ItemsFragment implements ItemsResult
  {
    items:List<Item>
  }
  class ItemsPresenter
  {
    loadItems(page)
  }
  class UseCase

  ItemsFragment --> ItemsPresenter: itemsの読み込み操作
  ItemsPresenter --> ItemsResult : 結果(items)
  ItemsPresenter --> UseCase


@enduml

ItemsViewInterface.kt

interface ItemsResult
{
    fun onItemsLoaded(items:List<Item>)
    fun onFailed(message:String)
}

ItemsPresenter.kt

class ItemsPresenter(
  private val context:Context,
  private val coroutineScope: CoroutineScope,
  private val view: ItemsResult) {

    private val useCase = ItemsUseCase()

    fun loadItems(page:Int) {
        coroutineScope.launch{
            try {
                val result = useCase.loadItems(context, page)
                view.onLoadItems(result)
            } catch (ex: Exception) {
                view.onFailed(ex.message)
            }
        }
    }
}

ItemsFragment.kt

class ItemsFragment:Fragment,ItemsResult
{
    // 請求書一覧のアダプターです
    private var adapter: ListAdapter<Item, RecyclerView.ViewHolder>?
    private var presenter: ItemsPresenter?
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        presenter = ItemsPresenter(requireContext(), viewLifecycleOwner.lifecycleScope, this)
        presenter?.loadItems(1)
    }
    
    override fun onItemsLoaded(items:List<Item>){
        adapter?.submitList(items)
    }
    
    override fun onFailed(message: String){
        // エラー表示する
    }
}

リファクタリング後のコード

ViewModelの実装

ViewModelからFragmentへの参照が無くなり、すっきりしていると思います。

@startuml

  class ItemsViewModel
  {
    - itemsBuffer:List<Item>
    + items:MutableLiveData<List<Item>>
    + networkState:MutableLiveData<NetworkState>
    laodItems(page)
  }

  note right of Fragment: Items, networkStateの変更を監視している
  class Fragment
  class UseCase

  Fragment --> ItemsViewModel : itemsの読み込み
  ItemsViewModel --> UseCase

@enduml

ItemsViewModel.kt

MisocaのAndroid版では、APIとの通信にContextが必要な為、AndroidViewModelを継承しています。

AndroidViewModelを継承することで、ApplicationContextをgetApplication()で参照可能になります。

ただし、ApplicationContextには言語設定や画面向きの変更が反映されない為、ViewModel内でUIに関する処理を行わないよう注意が必要です。

class ItemsViewModel(context: Context) : AndroidViewModel(context.applicationContext as Application){
    // 通信中状態、エラーの通知用のobserve可能なプロパティ
    val networkState = MutableLiveData<NetworkState>()    
    // 表示するデータの実体(LiveData経由でFragmentに渡す)
    private val itemsBuffer = ArrayList<Item>()
    // observe可能なプロパティ
    val items = MutableLiveData<List<Item>>()
    
    // データの一覧を読み込みます
    fun loadItems(page: Int) {
        viewModelScope.launch {
            try {
                networkState.postValue(NetworkState.LOADING)
                
                // loadItemsはDispatchers.IOで動作するsuspend functionです。
                val result = useCase.loadItems(getApplication(), page)
                itemsBuffer.addAll(result)
                
                // observeしているViewに更新を通知する
                items.postValue(itemsBuffer)
                networkState.postValue(NetworkState.LOADED)
            } catch (ex: Exception) {
                // エラーメッセージを表示
                networkState.postValue(NetworkState.error(ex.message))
            }
        }
    }
}

NetworkStateの実装は、下記のコードを参考にしました。

https://github.com/android/architecture-components-samples/tree/master/PagingWithNetworkSample

ViewModel上のデータ変更を監視する

ViewModelをFragmentから使う場合は、fragment-ktxにあるExtension(viewModels,activityViewModels)を使用すると便利です。

build.gradle

dependencies {
    implementation "androidx.fragment:fragment-ktx:$fragmentKtxVersion"
}

ItemsFragment.kt

class ItemsFragment:Fragment
{
    // fragment-ktxのactivityViewModelsでviewModelを取得します
    private val viewModel: ItemsViewModel by activityViewModels()
    // 請求書一覧のアダプターです
    private var adapter: ListAdapter<Item, RecyclerView.ViewHolder>?

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

     observeViewModel()
     // itemsの読み込みを開始します
     viewModel.loadItems(1)
   }

    private fun observeViewModel() {
        // itemsの更新を監視します
        viewModel.items.observe(viewLifecycleOwner) { items ->
            // List<Item>をそのまま通知しても、DiffUtilが効率的に再描画してくれるので気にしない
            adapter?.submitList(items)
        }
        
        // 通信中状態の更新を監視します
        viewModel.networkState.observe(viewLifecycleOwner) {
            when (it.status) {
                Status.RUNNING ->{
                    // プログレスバーを表示
                }
                Status.FAILED -> {
                    // エラー表示
                }
                Status.Success -> {
                    // プログレスバーを消す
                }
            }
        }
    }
}

おまけ

この画面は2つのタブがそれぞれFragmentになっているのですが、ViewModelを通じて2つのタブがデータを共有しています。

タブ間(Fragment間)でのデータの移動も、ViewModelを使用する事で、簡単に実現できています。

スクリーンショット 2020-11-13 10.37.55.png (47.6 kB)

まとめ

Viewの操作が不要になって、スッキリしました

宣伝

Misoca 開発チームでは、モバイルアプリエンジニア(iOS, Android)を募集しています。

www.wantedly.com

君のプログラミング言語は輝いているか

こんにちは、Misoca開発チームの黒曜(@kokuyouwind)です。

そしてこの記事は弥生 Advent Calendar 2020の 11日目の記事です。

昨日の担当はkosappiさんのCodePipelineのステージ間で変数を受け渡しするでした。

プログラミング言語について

Misoca開発チームでは主にサーバーサイド側言語としてRuby、クライアントサイド言語としてTypeScriptを利用しています。

一方で弥生製品開発チームは主にC#を利用しており、最近ではTypeScriptやGo言語も一部で利用しています。

そうなってくると、気になるのは「今後新しいプロダクトを作るとしたら、プログラミング言語は何にするべきなのか?」ということです。

今回はそれぞれの言語の特徴や向いている用途について、ざっくりまとめていきたいと思います。なお極力資料を探しながら正確に書こうと思いますが、個人的な印象やイメージが入ってしまう部分もあるかと思いますのでご了承ください。

なお誕生時期や特徴の主な出典はプログラミング言語図鑑に拠ります。

プログラミング言語図鑑

プログラミング言語図鑑

  • 作者:増井敏克
  • 発売日: 2018/04/13
  • メディア: Kindle版

目次

Ruby

言語の特徴

Misoca開発チームがバックエンド開発に使っている言語です。日本発で個人開発から始まったという、割とレアな出自を持っています。また公開が1995年と、実はC#より長い歴史を持つ言語です。

Rubyの特徴として、C++に代表されるクラスベースのオブジェクト指向よりも、Smalltalkに代表されるメッセージベースのオブジェクト指向を強く意識していることが挙げられます。

多くの言語では、数やそれ以外のプリミティブな型はオブジェクトではありません。 ですが、RubyはSmalltalkの影響を受け、すべての型がメソッドやインスタンス変数を与えられるようになっています。 これがRubyが使いやすい理由の一つです。 Ruby公式ページ - Rubyとは より

この特徴と、メッセージ呼び出しやブロック渡しでの柔軟な構文によって、Rubyでは自由度の高い言語内DSLを定義することができます。

例えばRailsの routes.rb ファイルは以下のように書くことができますが、これは妥当なRubyコードになっています。*1

Rails.application.routes.draw do
  resources :brands, only: [:index, :show] do
    resources :products, only: [:index, :show]
  end

  resource :basket, only: [:show, :update, :destroy]

  resolve("Basket") { route_for(:basket) }
end

他にも関数型プログラミングの要素が取り入れられていたり、オープンクラスとオーバーライドによる柔軟なクラス拡張が行えたりなど、とにかく「プログラマが自然に書きやすい」ことを重視したまつもとひろゆき氏の思想が伺えます。

向いている用途

最も普及している用途はRuby on RailsによるWebプログラミングで、特にスタートアップなどの小規模かつ迅速な開発で強みを発揮します。

他に言語内DSLの特性を活かしてVagrantChefなどの設定ファイルでの利用が有名です。またサーバ内で実行するバッチ処理などのスクリプト用途でも手軽に書きやすく重宝します。

一方で、自由度や動的な型システムによって大規模開発では混乱を生みやすい言語だといえるでしょう。また実行速度の面では遅い部類に入るため、処理速度が重要な場合はあまり向いていないと言えます。

なお12月25日(クリスマス!)にリリースされる予定のRuby 3.0*2では静的型検査システムの導入や実行速度の改善などが行われているため、これらの懸念点が多少緩和されるのではないかと期待しています。

C#

言語の特徴

弥生製品チームが主に利用している言語です。マイクロソフトが2000年に公開した、汎用目的のオブジェクト指向言語になっています。

一般に使われ始めたのは、Visual Studio .NETに組み込まれた2002年からになりそうです。それまでのネイティブビルドがターゲットだったVisual Studioシリーズ(6.0まで)から、.NET FrameworkをターゲットとしたVisual Studio .NETに移行するタイミングで追加されただけあり、.NET Frameworkの標準的な言語という扱いを受けています。*3

C++の構文をベースにしつつも、低レベルのメモリアクセスは基本的に制限したりガベージコレクションが標準で利用されるなど、自分の足を撃ち抜くことがないようになっています。型システムは.NETの共通型システムに依りますが、おおむねclassやinterfaceを用いたクラスベースのオブジェクト指向といえるでしょう。

またC# 3.0で追加されたLINQ用のクエリ式やC# 7.0で追加された関数型プログラミング由来のパターンマッチ機能など、積極的に様々な機能が導入されています。

特にクエリ式によって、データベースに対するクエリ発行を以下のように言語内で記述できる点は特筆すべきでしょう。*4

IEnumerable<string> highScoresQuery2 =
    from score in scores
    where score > 80
    orderby score descending
    select $"The score is {score}";

ほぼSQLと同じ内容を言語内で記述できるため、構文検査や静的な型検査もコンパイル時に行えるのは強みといえます。*5

向いている用途

.NET Frameworkの主力言語であるため、Windowsデスクトップ開発においては最有力候補と言えます。またUnityでのスクリプティングにおいてもC#が採用されています。

.NET ASPなどを利用したWebプログラミングも可能で、大企業やSIerなどの大規模プロジェクトで利用されているように思います。

一方で、やや文法が古くボイラープレートコードが多くなること・開発環境が実質的にVisual Studioに制限され自由度が少ないこと・稼働環境にWindowsサーバライセンスが必要になることなど諸々の問題から、スタートアップなど小規模なWebアプリケーションでの採用は稀という印象です。

またWindowsや.NET Framework自体が重厚なため、昨今のコンテナ化・マイクロサービス化の文脈ではコンテナサイズやメモリ使用量が膨らみやすいという点で他言語に軍配が上がりやすいと言えるでしょう。

こうした状況はMicrosoftも把握しているようで、.NET Coreのマルチプラットフォーム対応や.NET 5での単一の実行可能バイナリ生成など、着々と改善を重ねています。BlazorによるWebAssemblyアプリ作成などもあり、今後の展開によっては小規模Webサービスやマイクロサービス用途でも選択肢に挙がってくるかもしれません。

Go

言語の特徴

弥生基盤チームが一部で利用している言語です。Googleが2009年に公開した汎用目的のプログラミング言語です。

Goの基本的な言語仕様は単純で、個人的にはBetter Cという印象を受けます。ただしinterfaceを利用したオブジェクト指向機能と、GoRoutineを用いた並列処理機構の2つは非常に特徴的です。

Goのオブジェクト指向ではクラス定義を行うことはできず、どんなメソッドを持つかのinterfaceのみを定義することができます。そしてある型がinterfaceのメソッドを全て定義していれば、その型はinterfaceを実装しているとみなされます。

例えば、sort.InterfaceLen() int, Less(i, j int) bool, Swap(i, j int) の3つのメソッドを要求するため、これらを定義した型であればsort.Sortに渡して整列することができます。いわゆる構造的部分型ですね。Rubyでよく言われるダックタイピングのような振る舞いを記述できますが、コンパイル時にインターフェイスを満たしているか静的に検査できる点が優れています。

GoRoutineはマイクロスレッドを立ち上げるための言語機構です。Erlangのマイクロスレッドに近いですが、各プロセスがメッセージボックスを持つアクターモデルではなく、チャネルを通じて同期的に通信を行うCSPスタイルとなっています。

また標準でコードフォーマッターや単体テストライブラリなど組み込みのエコシステムが整備されており、コンパイルすると単一実行ファイルが生成されるため、コンテナベースのCI/CDといったモダンな開発プロセスに適応しやすくなっています。

向いている用途

Googleが自社向けに設計しただけあって、コンテナを利用したマイクロサービスシステムでは最初に名前が挙がる言語でしょう。ユーザが直接触れるWebアプリよりも、マイクロサービス内部のAPIサービスで特に強みを発揮するイメージです。また静的型システムを持ち並列処理機構も言語標準で持つため、Dockerなどミドルウェアの開発でも積極的に採用されています。

一般的なWebアプリでの利用も十分可能だと思いますが、Webフレームワークはデファクトが固まっておらず、フロントエンドも通してカバーできるTypeScriptに軍配が挙がっている印象です。もっとも、標準ライブラリのみで簡単なWebアプリなら作成できてしまうため、Webフレームワークの必要性が低いという事情もありそうです。

TypeScript

言語の特徴

Misoca開発チームがフロントエンド開発で利用している言語です。また弥生製品チームでも一部で利用されています。マイクロソフトが2012年に公開した、いわゆるAltJS言語の一種です。

JavaScriptに型検査システムやスコープ制限などの機能を追加した言語で、JavaScirptの厳密なスーパーセットになっているため既存のJavaScriptソースをそのまま利用でき移行が容易という点で他のAltJS言語と一線を画しています。

また型システムも非常に特徴的で、典型的なプリミティブ型・オブジェクト型などに留まらず、リテラルの値に依存するリテラル型や型レベル関数など意欲的な仕様が取り入れられています。

例えば、以下のように方位を表す "North", "East", "South", "West"のいずれかのみを取る型 CardinalDirection を定義することで、 move にそれ以外の文字列が渡されないことを型レベルで保証できます。*6

type CardinalDirection =
    | "North"
    | "East"
    | "South"
    | "West";

function move(distance: number, direction: CardinalDirection) {
    // ...
}

move(1,"North"); // Okay
move(1,"Nurth"); // Error!

一方で、型注釈を間違うと容易に型の整合性を崩せるなど健全性は諦めている部分があり、型検査を実用的なレベルで妥協するという割り切りが感じられる言語です。

向いている用途

現状のフロントエンド開発ではデファクトスタンダードと言えるでしょう。またサーバーサイドJavaScriptエンジンであるNode.jsを組み合わせることでサーバサイド開発でも利用することができます。

特にフロントエンド・サーバサイドを跨いだJavaScriptフレームワークであるNext.jsNuxt.jsなどは迅速な開発が必要なスタートアップなどで非常に人気があり、TypeScriptと組み合わせて採用するケースが増えているように思います。

静的型検査があるため開発規模が大きくなってもある程度はスケールしますが、コンパイルターゲットがJavaScriptであり速度面では他言語に劣ること、標準で利用されるnode_modulesが肥大化しやすくコンテナイメージが膨らみやすいことなどから、大規模開発では他の言語に分があるといえそうです。

とはいえTypeScript界隈はコミュニティが非常に活発であり、新たなツールでこうした課題が改善されていく可能性は十分ありそうに思えます。

その他の言語

他に、個人的に最近使われていると感じたり気になっている言語は以下のとおりです。

  • Swift : iOS開発開発のデファクトスタンダード。サーバーサイドSwiftという選択肢もあるが、事例は少なそうに感じる
  • Kotlin : Androidアプリ開発のデファクトスタンダード。サーバーサイドKotlinという選択肢もあるが、やはり事例は少なそうに感じる
  • Scala : オブジェクト指向と関数型プログラミングをいい感じに組み合わせたJVM言語。書ける人が書くと非常に生産性が高いが、設計方針が統一されていないとパラダイムが混ざってカオスになる。あとimplicit parameterを多様するとコンパイルがどんどん重くなる
  • Rust : 所有権管理が厳格なため、低レイヤー処理を安全に記述できる言語。ミドルウェアやドライバーなどハードウェア寄りのソフトウェアで特に強みを発揮するイメージ
  • F# : .NETに対応した関数型言語で、OCamlの文法をベースにしている。型プロバイダなど表現力が高いが、Scala以上に関数型寄りのパラダイムを持つためOCaml・Haskell辺りの経験がないと学習コストが高い

まとめ

いろいろなプログラミング言語がありますが、それぞれに設計者の思想や目的がある以上、用途に合わせてプログラミング言語を選ぶことで特徴を活かして輝かせたいですね。(雑なタイトル回収)*7

弥生 Advent Calendar 2020、明日の担当はmasuda_Mtenさんです。お楽しみに!

*1:routes.rbのコードはRailsガイド - Rails のルーティングから引用

*2:現時点ではRuby 3.0 preview 2がリリースされており、試すことができます。

*3:J#? 知らない子ですね…

*4:コードはC# - クエリ式から引用

*5:LINQやクエリ式はVB.NETやF#でも利用できるので、.NET系の言語共通の強みと言ったほうが正確かもしれません。

*6:コードはTypeScript Deep Dive 日本語版 - リテラル型から引用

*7:蛇足ですが、タイトルの元ネタは「君の青春は輝いているか」です。

CodePipelineのステージ間で変数を受け渡しする

この記事は弥生 Advent Calendar 2020 の10日目の記事です。

こんにちは。@kosappi です。最近は SRE としてインフラの改善に取り組んでいます。

Misoca 開発チームでは AWS を利用してインフラを構築しています。 AWS CodePipeline で使える変数の受け渡しが便利だったので、今回はこれを紹介します。

CodePipeline の基本機能について知りたい方は AWS による CodePipeline の紹介をご覧ください。

AWS CodePipeline(継続的デリバリーを使用したソフトウェアのリリース)

CodePipeline における変数の受け渡し

CodePipeline では各アクションの成果物をアーティファクトでやりとりできます。 近頃は、これに加えて「変数」という成果物も扱えるようになっています。

参考: AWS CodePipeline で実行中のアクション間での変数受け渡しが可能に

なお、AWS から提供されている変数についての資料は下記の2つです。

まとめ

  • アクションは変数を作成することができる
    • アクションによっては作成できない場合もある(後述)
  • 変数を作成するには名前空間を指定する必要がある
    • アクション毎に名前空間が必要
    • 名前空間が指定されていない場合は参照することができない
  • 作成された変数は #{NAMESPACE.VARIABLE_NAME} で参照できる

変数を作成することができるアクション

下記のアクションではユーザが変数を作ることができます。

  • CodeBuild
  • AWS CloudFormation
  • Lambda

上記以外のアクションではユーザは新たに変数を作ることはできず、定義済み変数のみ利用できます。 どのような変数が利用可能かは公式のドキュメントに一覧で載っています。

パイプラインアクションで使用できる変数

なお、定義済み変数を利用する場合でも名前空間の指定は必要です。

変数を下流ステージに提供する(CodeBuildの場合)

それでは、実際に CodeBuild で変数を下流ステージに提供していきましょう。 全体のイメージはこんな感じです。

f:id:rkosaka:20201207172506p:plain
パイプライン全体図

名前空間を指定する

名前空間を指定します。 コンソールからだと、ここにあります。

f:id:rkosaka:20201207173137p:plain
CodeBuild設定画面

CLI や terraform 等からもできますが、ここでは割愛します。 今回は CodeBuildVariables という名前空間にします。

buildspec 内で変数を作成する

buildspec 内で変数を作成するには env/exported-variables を利用します。 env では他にも変数を指定できますが、下流ステージから参照できるのは exported-variables のみです。

今回は BUILT_IMAGE_TAG という変数を作ります。 ここにビルドする際のタグ名を書いて、下流ステージでタグを使えるようにするイメージです。

version: 0.2

env:
  exported-variables:
    - BUILT_IMAGE_TAG
phases:
  pre_build:
    commands:
      - ...
      - TZ=Asia/Tokyo date +"%Y-%m-%d-%H-%M-%S" > ./TIMESTAMP
      - export BUILT_IMAGE_TAG=FUGAFUGA.`cat TIMESTAMP`
      - ...
  build:
    commands:
      - ...
      - docker build -t $BUILT_IMAGE_TAG -f ./Dockerfile .
      - docker push $BUILT_IMAGE_TAG
      - ...
  post_build:
    commands:
      - ...
artifacts:
  files:
    - ...

提供されている変数を参照する

下流のステージでは変数を参照するには #{NAMESPACE.VARIABLE_NAME} のように書きます。 今回は名前空間が CodeBuildVariables で変数名は BUILT_IMAGE_TAG なので #{CodeBuildVariables.BUILT_IMAGE_TAG} と書けば参照できます。

この書式で各種項目を書いてあげれば、動的に値を指定することができます。

例えば、CodeBuild の編集画面では、環境変数に上流から提供された変数を指定することができます。

f:id:rkosaka:20201208150938p:plain
上流ステージの変数を指定する

最後に

CodePipeline の隠れた(と個人的には思っている)便利機能である、変数を紹介しました。 ステージ間で情報をリレーしたいけど、アーティファクトでは立派すぎる...という場面で重宝するのではないでしょうか?

余談ですが、今回紹介した変数についてのドキュメントの日本語訳が非常に読み辛い状態になっています。 なんとなく、機械翻訳したものがそのまま使われているような気配を感じるのですが...。

変数の操作

宣伝

Misoca 開発チームでは AWS の便利な機能を使ってインフラを改善してくれるエンジニアを募集しています。