Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
alpha-release-automation
Search
もん
August 21, 2018
Technology
2
4.9k
alpha-release-automation
2018-08-21 Cookpad.apk #1
Fukuo Kadota
もん
August 21, 2018
Tweet
Share
More Decks by もん
See All by もん
令和に始めるcode generation
litmon
0
140
Cookpad Summer Internship 2019 Day4 Android
litmon
0
9.9k
クックパッドマートAndroid 徹底解剖
litmon
1
650
Google Play Consoleのリリーストラックを有効活用してリリースフローの最適化を行った話
litmon
2
4.9k
クックパッドアプリのリリースフローに関して
litmon
0
2.2k
AccessibilityServiceを使ってアプリの可能性を広げよう
litmon
1
2.5k
Other Decks in Technology
See All in Technology
Amazon Q Developer 他⽣成AIと⽐較してみた
takano0131
1
120
パスキー導入の課題と ベストプラクティス、今後の展望
ritou
7
1.2k
移行できそうでやりきれなかった 10年超えのシステムを葬るための戦略
ryu955
2
540
Engineering Managementのグローバルトレンド #emoasis / Engineering Management Global Trend
kyonmm
PRO
6
990
Redefine_Possible
upsider_tech
0
300
SpannerとAurora DSQLの同時実行制御の違いに想いを馳せる
masakikato5
0
570
LINE Notify互換のボットを作った話
kenichirokimura
0
180
PostgreSQL Unconference #52 pg_tde
nori_shinoda
1
230
頻繁リリース × 高品質 = 無理ゲー? いや、できます!/20250306 Shoki Hyo
shift_evolve
0
160
ISUCONにPHPで挑み続けてできるようになっ(てき)たこと / phperkaigi2025
blue_goheimochi
0
140
Javaの新しめの機能を知ったかぶれるようになる話 #kanjava
irof
3
4.9k
20250326_管理ツールの権限管理で改善したこと
sasata299
1
390
Featured
See All Featured
Bash Introduction
62gerente
611
210k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
118
51k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7.1k
Automating Front-end Workflow
addyosmani
1369
200k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.8k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
29
2k
Making Projects Easy
brettharned
116
6.1k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.4k
Code Reviewing Like a Champion
maltzj
522
39k
Transcript
Cookpad.apk #1 alpha release automation 技術部 モバイル基盤グループ 門田
自己紹介 • かどたふくお • 技術部モバイル基盤グループ ◦ 主な仕事:Kotlin大臣、リリース自動化など • twitter: @_litmon_
github: @litmon speakerdeck: @litmon
今までのリリースフローおさらい
クックパッドのリリースフロー • 実は以前にも紹介したことがある ◦ 「クックパッド リリースフロー」で検索したら出てきます ◦ https://speakerdeck.com/litmon/kutukupatudoapurifalseririsuhuroniguan-site • 今回は、ここで話していた「自動化」周りの話をします
• fastlane/supply は知っている前提で話します ◦ 分からない人はこの機会に調べてみてください ◦ 雑に言うと「Google PlayにリリースするAPIをいい感じにラップしてくれてる便利ツール」
「クックパッドアプリのリリースフローに関して」P25 から抜粋
軽くおさらい v18.4 開発開始 リリース コードフリーズ 開発期間 テスト期間 リリース期間 v18.5 開発開始
軽くおさらい v18.4 開発開始 リリース コードフリーズ 開発期間 テスト期間 リリース期間 v18.5 開発開始
• あるバージョンの開発が開始し、 masterブランチに対して新規機能開発やバグ 修正などを行う ◦ 例: v18.4 の開発が開始 • バージョン名はそのまま Github Enterprise上でマイルストーンとして管理され、 追加したいfeatureごとにissueが切られて管理される ◦ そのマイルストーンの issueを見ればどういう機能が入るバージョンなの かが明確になる
軽くおさらい リリース コードフリーズ テスト期間 リリース期間 v18.5 開発開始 • あるバージョンの開発が一通り終了し、リリース前のテスト期間へと移行する •
その際、ブランチを RC-<version> として切ることで、masterへは次のバージョ ンの新機能開発をマージ可能な状態にする ◦ コードフリーズ • テスト期間ではQITチームによる品質チェックを行い、 テスト期間中に見つかった不具合やバグなどは RCブランチに順次修正を加え ていく • ある程度修正が落ち着いたら RCブランチの差分をmasterに適用する
軽くおさらい リリース リリース期間 v18.5 開発開始 • テスト期間が終了し、リリース可能な品質であることが確認できたらそのバー ジョンを公開する • リリース用の署名を付けた
apkはJenkins Jobによって作成 • fastlane/supply をラップしたRubyスクリプトを使用し、 開発者のPCを使って apkをアップロードする • 20, 50, 100% と段階的に公開率を操作し、新たな不具合が見つかったら必要 に応じてリリースを中止したりパッチリリースを行う
以前のリリースフローの問題点 • fastlane/supply をラップしたRubyスクリプトを使用し、 ◦ メンテナンスが大変 ◦ Rubyスクリプト内でコマンドラインツールとしての supplyを実行していてひたすら冗長 •
開発者のPCを使って apkをアップロードする ◦ apkはJenkins Jobでビルドしているのに、わざわざ開発者の手元に持ってくるのが手間 ◦ Google Publishing APIを叩くためのAPI Key(Publisher.json)をリリースを行う開発者が持つ必 要があり危険 ◦ ミスタイプとかしそうでヒューマンエラーが怖い
難しい・・・
他にも問題はある • パッチリリースを行うときに、バージョンの更新を行う必要がある ◦ 1回1回は大したことない作業だが重なると大変 ◦ 大規模な修正を加えたときはパッチリリースが増えがちなので、毎度毎度面倒が発生する ◦ masterにマージする際にコンフリクトが発生しがち
他にも問題はある • RCブランチを運用すると、masterとRCで差分が生じた際に面倒になる ◦ RCに入れるべき変更を masterに間違えて投げてしまったり、 RCの差分をmasterに適用しよう としたときにコンフリクトが発生したり、 …… ◦
マイルストーンごとにこの作業を行う人を立てているが、負担が大きい • 各マイルストーンに入れたい施策のために開発時期を調整するオペレーション が何度も発生していた ◦ 1イテレーションが2週間ほどなので、「どうしてもこの時期に出したいが、開発がちょっと間に合 わない」みたいな場合に調整が発生する
そもそも人間が 作業するのは面倒!!!
こうしたい • apkアップロード作業は特定のタイミングに勝手に実行されていればいい • 品質チェックが通った時点で、リリース作業は公開ボタンを押すだけに したい • RCブランチを廃止したい
自動でリリースする
自動アルファリリースを導入する • さまざまな手作業からの開放 ◦ リリース用apk作成のためのJenkins Job実行 ◦ リリースのためのPublishing.jsonファイルの用意 ◦ リリース用apkを手元にダウンロード
◦ リリース用スクリプトの実行 ◦ バージョンの更新作業 • 逆に言うと、これらを機械的に実行できるよう調整する必要がある
自動アルファリリースを導入する Before • RCブランチにリリースに必要なものが揃ってからapkをビルド • リリースするときに手動でスクリプトを実行しリリース After • RCブランチに変更があった時点でapkをビルドし自動でアップロード •
リリースの判断をしたときにPlay Consoleから手作業で昇格
自動アルファリリースを導入するために • 一番の技術的な障害は「versionCode」の取扱い ◦ Google Playの仕様上、各トラックにアップロードする apkは以前アップロードしたものよりも大き い必要がある • 現在リリースされているversionCodeはAPIで取得することができるため、ビルド
時にversionCodeを渡すようにした
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } }
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } gradle実行時にpropertyを渡すようにする 実行時は `./gradlew assembleRelease -PversionCode=180501001` のように渡せる
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } versionMajor, versionMinorなどの値は以下のように決まっている - versionMajor: 年度 - versionMinor: 年度内のリリース回数 (パッチリリース含まない ) - versionBuild100, versionBuild: パッチリリース回数 - marketId: リリース先(以前は色々あったが現在は 1つのみ) versionCodeは <versionMajor(2桁)><versionMinor(2桁)><versionBuild100(2桁)><versionBuild(2桁)><marketId>(1桁) で構成されている 例: 180500011
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } versionMajor, versionMinorなどの値は以下のように決まっている - versionMajor: 年度 - versionMinor: 年度内のリリース回数 (パッチリリース含まない ) - versionBuild100, versionBuild: パッチリリース回数 - marketId: リリース先(以前は色々あったが現在は 1つのみ) versionNameも同様に “v<versionMajor(2桁)>.<versionMinor(2桁)>.<versionBuild100(2桁)>.<versionBuild(2桁)>” となっている 例: v18.5.0.1
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } versionMajor, versionMinorの値は今までのリリースフロー通りの値を使うようにしたので、引数 に渡ってきた値とこれからリリースしたいバージョンの大小を比較しビルドするようにした
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } versionNameは前述どおりの仕様なので versionCodeから組み立てられるのでそれ用のメソッド を用意
build.gradleの変更 android { defaultConfig { if (project.hasProperty("versionCode")) { def newerVersionCode
= Math.max( Integer.parseInt(project.property("versionCode")), getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) ) def newerVersionName = getVersionNameFromVersionCode(String.valueOf(newerVersionCode)) println("versionCode: " + newerVersionCode) println("versionName: " + newerVersionName) versionCode newerVersionCode versionName newerVersionName } else { versionCode getVersionCode(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) versionName getVersionName(versionMajor, versionMinor, versionBuild100, versionBuild, marketId) } } } versionCodeを引数から与えない場合 (開発時など)はデフォルトのversionCode, versionNameを 指定するようにしている
fastlane/supplyでビルドに必要なversionCodeを取得 platform :android do lane :retrieve_newer_version_from_google_play do alpha_version_codes = google_play_track_version_codes(
json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', ) alpha_version_code = alpha_version_codes.max&.to_i rollout_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'rollout' ) rollout_version_code = rollout_version_codes.max&.to_i production_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', ) production_version_code = production_version_codes.first [alpha_version_code, rollout_version_code, production_version_code].compact.max&.+10 || 0 end end
fastlane/supplyでビルドに必要なversionCodeを取得 platform :android do lane :retrieve_newer_version_from_google_play do alpha_version_codes = google_play_track_version_codes(
json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', ) alpha_version_code = alpha_version_codes.max&.to_i rollout_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'rollout' ) rollout_version_code = rollout_version_codes.max&.to_i production_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', ) production_version_code = production_version_codes.first [alpha_version_code, rollout_version_code, production_version_code].compact.max&.+10 || 0 end end fastlane/supplyには元々versionCodeを取得するためのlaneがあるので、それを使って取り出す ( https://docs.fastlane.tools/actions/supply/#retrieve-track-version-codes ) 引数にはAPI KeyになるJSONファイルのPATHと、対象になるアプリの packageName、あとは取 り出したいtrackを指定する アルファリリースを自動化するためには、 alphaトラックに上がっている apkのversionCodeよりも 大きくないといけないので、 alphaトラックに上がっている versionCodeの中で最も大きいものをま ず取り出す
fastlane/supplyでビルドに必要なversionCodeを取得 platform :android do lane :retrieve_newer_version_from_google_play do alpha_version_codes = google_play_track_version_codes(
json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', ) alpha_version_code = alpha_version_codes.max&.to_i rollout_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'rollout' ) rollout_version_code = rollout_version_codes.max&.to_i production_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', ) production_version_code = production_version_codes.first [alpha_version_code, rollout_version_code, production_version_code].compact.max&.+10 || 0 end end alphaリリースを自動化するために versionCodeを上げる必要があるため、 versionCode + 10 の値を返却するようにする + 10にしているのは、前述した marketIdは固定値で下1桁目に位置しているためその値を除いた 値を変える必要があるから 例: v18.5のアルファリリース時の versionCode 180500011 → 180500021 → 180500031 → ...
fastlane/supplyでビルドに必要なversionCodeを取得 platform :android do lane :retrieve_newer_version_from_google_play do alpha_version_codes = google_play_track_version_codes(
json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', ) alpha_version_code = alpha_version_codes.max&.to_i rollout_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'rollout' ) rollout_version_code = rollout_version_codes.max&.to_i production_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', ) production_version_code = production_version_codes.first [alpha_version_code, rollout_version_code, production_version_code].compact.max&.+10 || 0 end end さらにクックパッドでは段階リリースも行っているた め、たとえば180500011のversionCodeのアプリを alphaトラックからrolloutトラックに昇格した場合、 - alphaトラック: 公開中apkなし - rolloutトラック: 公開中apkあり(180500011) の状態になり、この状態でパッチリリースを行うため のビルドを行おうとしても alpha_version_codes # => [] となってしまう
fastlane/supplyでビルドに必要なversionCodeを取得 platform :android do lane :retrieve_newer_version_from_google_play do alpha_version_codes = google_play_track_version_codes(
json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', ) alpha_version_code = alpha_version_codes.max&.to_i rollout_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'rollout' ) rollout_version_code = rollout_version_codes.max&.to_i production_version_codes = google_play_track_version_codes( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', ) production_version_code = production_version_codes.first [alpha_version_code, rollout_version_code, production_version_code].compact.max&.+10 || 0 end end そのため、 - rolloutトラックに存在するversionCode - alphaトラックに存在するversionCode - productionトラック(rollout 100%)に存在する versionCode の中から最も高いものを選択して使用する必要があ る
fastlane/supplyを使ってビルド→アップロード lane :build_newer_version_release_apk do version_code = retrieve_newer_version_from_google_play gradle(task: 'assembleProdExternalRelease', properties:
{ 'versionCode': version_code }) end lane :upload_newer_version_apk_to_google_play_alpha do version_code = retrieve_newer_version_from_google_play gradle(task: 'copyChangelog', properties: { 'versionCode': version_code }) supply( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', apk: 'cookpad/build/outputs/apk/cookpad-prod-external-release.apk', mapping: 'cookpad/build/outputs/mapping/prodExternal/release/mapping.txt', skip_upload_images: true, skip_upload_screenshots: true, ) gradle(task: 'releng', properties: { 'versionCode': version_code }) end
fastlane/supplyを使ってビルド→アップロード lane :build_newer_version_release_apk do version_code = retrieve_newer_version_from_google_play gradle(task: 'assembleProdExternalRelease', properties:
{ 'versionCode': version_code }) end lane :upload_newer_version_apk_to_google_play_alpha do version_code = retrieve_newer_version_from_google_play gradle(task: 'copyChangelog', properties: { 'versionCode': version_code }) supply( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', apk: 'cookpad/build/outputs/apk/cookpad-prod-external-release.apk', mapping: 'cookpad/build/outputs/mapping/prodExternal/release/mapping.txt', skip_upload_images: true, skip_upload_screenshots: true, ) gradle(task: 'releng', properties: { 'versionCode': version_code }) end versionCodeは先程定義した retrieve_newer_version_from_google_play を使って取得 assembleタスクにプロパティを渡すようにして、新しいバージョンの apkをビルドする
fastlane/supplyを使ってビルド→アップロード lane :build_newer_version_release_apk do version_code = retrieve_newer_version_from_google_play gradle(task: 'assembleProdExternalRelease', properties:
{ 'versionCode': version_code }) end lane :upload_newer_version_apk_to_google_play_alpha do version_code = retrieve_newer_version_from_google_play gradle(task: 'copyChangelog', properties: { 'versionCode': version_code }) supply( json_key: ENV.fetch('GOOGLE_PLAY_PUBLISHER_PATH'), package_name: 'com.cookpad.android.activities', track: 'alpha', apk: 'cookpad/build/outputs/apk/cookpad-prod-external-release.apk', mapping: 'cookpad/build/outputs/mapping/prodExternal/release/mapping.txt', skip_upload_images: true, skip_upload_screenshots: true, ) gradle(task: 'releng', properties: { 'versionCode': version_code }) end supplyを素直に使ってalphaトラックへアップロード アップロードするapkは先ほどビルドしたものを使用する クックパッドではアップロード前に apkの軽いチェックを挟んでいるため、 laneを分けている
実行するときはこんな感じ $ bundle exec fastlane android build_newer_version_release_apk $ bundle exec
fastlane android upload_newer_version_apk_to_google_play_alpha fastlane便利・・・ あとはこれをJenkins Jobで実行するようにトリガの設定などを行うだけ
導入してみてどうだったか • apkのビルド・アップロードを人間が意識しなくて良くなったので、 リリースの作業を行うエンジニア(自分)が楽になった ◦ 人間に依存しないようになったので属人性が排除された • コンソール上から公開率の操作を行うだけになったので、非エンジニアでも簡単 に変更が加えられるようになった ◦
エンジニアが作業する必要がなくなったため、開発に集中できるように ◦ なったらいいなぁ
今後どうしていきたいか • まだまだ人間が管理している箇所が多い ◦ versionMajor, versionMinorは人間が管理している ◦ コードフリーズ・RCブランチの運用など • 「人間がスケジュールを管理する」のではなく
「スケジュールによって人間が動く」未来を作りたい ◦ リリースフローに関する人間の作業を徹底的に排除 ◦ これによって、調整業や面倒な仕事から脱却したい
余談 • 段階リリースを中止しているときにalphaリリースを実行しようとすると、APIから 謎のエラーが返ってきて出来ない ◦ なにか知っている人いたら教えてください :pray:
余談 • 段階リリースを中止しているときにalphaリリースを実行しようとすると、APIから 謎のエラーが返ってきて出来ない ◦ なにか知っている人いたら教えてください :pray:
余談 • 段階リリースを中止しているときにalphaリリースを実行しようとすると、APIから 謎のエラーが返ってきて出来ない ◦ なにか知っている人いたら教えてください :pray: • アルファリリース自動化を導入後、Playコンソールから段階リリースを操作して 間違って100%リリースをしてしまったことがあった
◦ 事故怖い。コンソール怖い。 ◦ 今後はアルファリリースからの昇格も Slack bot上で完結できるようにしたい ……
おわり!
次回予告 • Dangerを使ってKotlinのコンパイル時エラーや警告を表示してみた 〜プロジェクトkyoto-sensei〜 • Kotlinをクックパッドアプリに導入したときのあれこれ • Android版クックパッドアプリで採用している技術の現状確認 2018年版 inspired
by https://techlife.cookpad.com/entry/2015/06/25/093507