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
150
Cookpad Summer Internship 2019 Day4 Android
litmon
0
9.9k
クックパッドマートAndroid 徹底解剖
litmon
1
660
Google Play Consoleのリリーストラックを有効活用してリリースフローの最適化を行った話
litmon
2
5k
クックパッドアプリのリリースフローに関して
litmon
0
2.2k
AccessibilityServiceを使ってアプリの可能性を広げよう
litmon
1
2.6k
Other Decks in Technology
See All in Technology
AIのAIによるAIのための出力評価と改善
chocoyama
2
520
成立するElixirの再束縛(再代入)可という選択
kubell_hr
0
970
Windows 11 で AWS Documentation MCP Server 接続実践/practical-aws-documentation-mcp-server-connection-on-windows-11
emiki
0
770
Observability infrastructure behind the trillion-messages scale Kafka platform
lycorptech_jp
PRO
0
130
MySQL5.6から8.4へ 戦いの記録
kyoshidaxx
1
110
監視のこれまでとこれから/sakura monitoring seminar 2025
fujiwara3
10
3.3k
25分で解説する「最小権限の原則」を実現するための AWS「ポリシー」大全
opelab
11
2.3k
OAuth/OpenID Connectで実現するMCPのセキュアなアクセス管理
kuralab
5
910
Amplifyとゼロからはじめた AIコーディング 成果と展望
mkdev10
1
380
PostgreSQL 18 cancel request key長の変更とRailsへの関連
yahonda
0
110
TechLION vol.41~MySQLユーザ会のほうから来ました / techlion41_mysql
sakaik
0
160
Observability в PHP без боли. Олег Мифле, тимлид Altenar
lamodatech
0
310
Featured
See All Featured
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
4
200
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
31
1.2k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.6k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
50k
Making the Leap to Tech Lead
cromwellryan
134
9.3k
Scaling GitHub
holman
459
140k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
20k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
124
52k
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