PrivateリポジトリのSwiftPMをCIから使う

最近はモバイルアプリのリポジトリのプロジェクト整理として、これまではPrivateなPodSpecsやgit submoduleを使って解決していた依存を置き換えたり、単体での取り回しが効く程度にリポジトリを分けたりなどをしている。

iOSアプリならPackage.swift を用意して、Swift Package Managerで解決
Androidアプリなら GitHub PackagesMavenとして利用して、Gradleで解決
という方針で進めている。

CI as a Service (以下、CI) にて、PrivateリポジトリをSwift Package Managerで依存解決するのに、ちょっとてこずったので記しておく。
AndroidアプリでのGitHub Packages利用についても、あとでまとめたい。

本題

本題の回答としては、Appleドキュメントに記載があって、Xcode 12現在はこれで解決ができるようになっている。
Apple Developer Documentation

If you’re using the xcodebuild command directly, use SSH–based Git URLs for your packages and configure your SSH credentials. Set up your known_hosts file in the ~/.ssh directory of the macOS user that runs your CI tasks. xcodebuild honors your SSH configuration — there’s no additional setup required.

  • https ではなく、SSH形式のGit URLを使う
    • 例えば git@github.com:niw/KeyboardGuide.git
  • ~/.ssh/known_hostsSSHの設定を置く

この設定をしていない場合、 xcodebuild で依存解決ができずに、ビルドができない。

...
Resolving Swift Package Manager dependencies...
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies
...
Resolve Package Graph
Fetching git@github.com:user/repository.git
xcodebuild: error: Could not resolve package dependencies:
  The server SSH fingerprint failed to verify.
...

具体的な策

BitriseのWorkflowで、 Activate SSH key (RSA private key) 後に、Script stepとして、以下を実行しておくことで解決した。

for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true

前述の‪Apple‬ドキュメントにある通り、 ~/.ssh/known_hostsgithub.comへの接続を追加した形だ。

ここからは、この対応をする上で半日を潰した罠を書き残しておく。

1. 情報が少ない

上記のドキュメントに書いてある通りにやればいいだけ。
それだけなのだが、それにしても話題にしている人が少ない。

取り組み始めた段階で、すぐにほぼ答えの回答にたどり着いたが、なぜか設定後のビルドが失敗したので、諦めて別の方法を探していた。

discuss.bitrise.io

Circle CIのヘルプにも、同じ話題が上がっていた。

support.circleci.com

しかし、 IDEPackageSupportUseBuiltinSCM について調べても、情報が少ない。
ヘルプの記載と名前から推測すると、 xcodebuild でシステムのGitを参照させるためのオプションだろうか。

xcodebuild does not conform to the system ssh config and does not access the keys CircleCI stores in ssh-agent. We can work around this by requesting Xcode uses the main system version of ssh which will behave as expected.

ちなみに、Xcode 12では解決しているのか、 IDEPackageSupportUseBuiltinSCM YES にせずに、通るようになった。

2. SSH公開鍵/秘密鍵の用意

普段GitHubと連携する前提だと、多くのことがGitHubPersonal Access Token で解決できるのだけど、SwiftPMの場合にはGitそれ自体のサポートのため、SSHによる認証が採用されている。
幸いに、今回の環境ではBitriseでCocoaPodsのPrivate PodSpecsを使うために、他リポジトリにもアクセス権があるSSH鍵を設定していたので、この構成を調整する必要はなかった。
BitriseにAppを追加するフローで設定できるSSH鍵のままでは、リポジトリへの権限問題で、認証を通過できないかもしれない。

また、GitHub Actionsなどの他CIでは、解決方法が異なるかもしれない。

3. Bitrise Workflowの同時編集

これはSwiftPMと全く関係ない話題 だが、BitriseのWorkflowを複数人で編集すると、上書きしあう可能性がある。
自分が本題で詰まっていたため、途中からチームメンバーにも検証を手伝ってもらっていたが、どうにも自分が書いた設定が巻き戻っていることがあった。
それぞれ別々にWorkflowを用意して作業していたつもりであったが、おそらく他の人がWorkflowエディタを開いた段階の設定が、自分の変更を上書きして、巻き戻りが発生していたのだと思う。
やってみれば、 bitrise.yml は1枚なので、そうなると理解できるが、できればdiffだけを保存してもらいたい...

まとめ

以上、紆余曲折あって半日溶かしたものの、結局はAppleドキュメントにある通りに、パッケージをSSH形式のURL指定し、 ~/.ssh/known_hostsSSH設定を追加する

for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true

で解決できた。
1日お疲れ様でした。

SPAJAM 2020 第2回予選大会に「おひっこし」として参加して、優秀賞に選ばれました #SPAJAM

9/26 (土)、27 (日) に行われたSPAJAM 2020 の第2回予選大会に、 @_bannzai_@gaopin1534@koooootake@noa_design51と共に参加し、優秀賞に選出されました。

https://history.spajam.jp/competition-result2/

www.youtube.com

やったこと

  • Firebase Cloud Firestore / Strageのデータベース関連の処理
  • 画像の合成処理の実装
  • 写真を撮った後のプレビュー画面
  • Figmaで作られたデザインを当てたり
  • デモ動画の演者

感想

ものとしては、24時間でそれなりに動くし、見た目も丁寧に作れたのでよかった。
特にプレゼン資料など、デザイナーって (@noa_design51 が) すごいと感じてた。

今回自分以外は東京集まっていたので、常時Discordで繋いでいたのはよかった。
ただ完全に同じ環境にはもちろんならなくて、食事とか打ち上げとかの面ではちょっと寂しい感じになった。
オンラインだから同じ予選会のチームで出れたのではあるのだけど。

あと、めちゃくちゃ久しぶりにハッカソンに参加して、徹夜が非常に辛かった。

優秀賞ということで、本選に行けるかは、全ての予選終了後の復活にかかっている状態。
今回のメンバーのバランスがよかったので、本選に行って、次はみんなと同じ場所で一緒にやりたい。
たのむ!!!!

iOSDC Japan 2020に参加 & LT登壇しました #iosdc

前々回: iOS Japan 2018に参加 & LT登壇しました #iosdc - がんばってなんか書く
去年は書いてなかったことが発覚した。

全体

今年は時世もあり、オンライン開催ということだった。

iosdc.jp

👍

  • 暑い中の移動が無
  • ノベルティを持ち帰らなくていいのが、非常に楽
    • 宿泊を伴うと、荷物が面倒なので
  • 複数トラックの並行視聴が可能
    • 自分は途中で諦めた
  • 起床、即参加が可能

👎

  • 京都に住んでいると、iOSDCだけに限らず、カンファレンス参加は出張チャンスなのだけど、今年は家で一人
  • Ask the Speakerに行かないと、トークを待っている時間が長く感じる
  • 休憩ルームで人と話すことがメインで参加しているので、オンラインだとその点を完全には補完する術がない
    • Discordの雑談チャンネルはよかったけど、喋っているのは大体固定のメンバー (かつ、大体いつもカンファレンスで話している人たち) で、「自分たちの会話を顔の見えない不特定多数に聞かれている」という感覚が、2.5日では慣れることができなかった
  • コーヒーを自分で淹れる必要がある
  • 昼食タイミングが難しい

LT

fortee.jp

xcrun Essentialsという題目で話した。 ギリギリまで5分に収まるように調整していたり、そもそもオンラインでの登壇も初めてだったので、ここ最近で最もプレッシャーがあった。

LT待機室では、他の登壇者の様子も見えていたけど、自分ばかり話していた気がする。 *1

やっぱり、準備した全ての内容を5分で話すことはできなかったので、Ask the Speakerで話させてもらった。 アップロードしたスライドには、完全版として掲載している

speakerdeck.com

👍

  • 一人でいるので、LTの練習がしやすい
  • カンペ見放題
  • LT中の焦りが、視聴者からは見えづらい
  • ニコ生を意識した煽り

👎

  • 今年はLTのみがライブだったので、周りに共感者が少ない
  • 自分はZoomが好きではなく *2 、慣れていなかったためにセットアップでの不安やZoomを通すとKeynoteのTransitionsアニメーションがもたつく *3 ので、非常にストレスフルだった
  • カメラの目線が難しい
  • 自分のワイプが配信に載る時に、左右反転することを先に知りたかった
    • 内側を向くようにカメラを調整したつもりだった

f:id:yutailang0119:20200923142232j:plain
タイムシフトからいいの撮れた

最後に

今年もお疲れさまでした

*1:スタッフ側の画面に自分ばかり映ると言われた

*2:Macに修正パッチ出させたあれの印象が強い

*3:諦めてアニメーション切った

リポジトリに含まれるコード量/比率をMackerelに記録する with GitHub Actions & action-mackerel-api

リポジトリを管理していると、何かの拍子にリポジトリのコードベースの変遷を可視化したくなる場合があります。
例えば、以下の場面です。

  • ある時からのリポジトリの成長
  • 実装言語を置き換える際のコードベースの遷移

やり方はいろいろありますが、今回は GitHub ActionsGitHub API v3 、そして Mackerel API を使って、Mackerelのサービスメトリックに記録してみます。

実行サンプル

Posting language lists to Mackerel with GitHub Ac…

GitHub APIの仕様上、特定ファイルの除外ができないことは、ここでは考慮していません。
JavaScriptは得意じゃないので、37行目からのscriptを効率化できる案があれば、教えてもらえると助かります。

グラフ例

Mackerel上でのおすすめのグラフ定義は、以下です。

  • 単位: bytes
  • グラフの種類: 積み上げ

f:id:yutailang0119:20200713021940p:plain
グラフ定義は bytes と 積み上げ を設定

利用しているグッズ

actions/github-script はyml内にJavaScriptが書けて、異常な便利さがあります。

yutailang0119/action-mackerel-api

上記の例でさらっと使っていますが、GitHub ActionsからMackerel APIを使用するために、Mackerel APIGitHub Actionsを作りました。
Mackerel APIの薄いWrapperです。

github.com

Marketplaceにも 公開 しています。
Marketplaceを検索する と、Mackerel と名のつくGItHub Actionsは他に公開されていないようです。*1

とりあえず動くものをと作ったので、まだ足りない部分がありますが、今後追加にご期待ください。

余談

shellが苦手すぎて、README/Usage が actions/github-script を使って、 body を作るサンプルになっています。
単にエスケープした文字列を入れればいいだけのはずなので、回答のPull Requestをお待ちしています。

*1:GitHub内検索 でもほとんどない

fastlane-plugin-mackerel_apiを作って、fastlaneにコントリビューションもした

rubygems.org

github.com

これはなに

Mackerel APIインターフェイスを、fastlane 向けに提供するPluginです。
実装は fastlane/actions/github_api.rb を元として、Mackerel API向けに調整しました。
Actions.shcurl を実行するだけでも事足りるのですが、せっかくなのでPlugin化しました。

例) ビルドにかかった時間をトラッキングする

発端は、はてなブックマークアプリでの開発環境にまつわる情報をトラッキングするために、MackerelのService Metricsを使おうと考えたことでした。
Service Metricsについては こちら

以下のように使うことで、処理にかかった時間を、Mackerelに投稿します。*1

start_time = Time.new.to_i
# scanなどの処理
end_time = Time.new.to_i
duration = (end_time.to_f - start_time.to_f) / 60 # 単位を分に変換

result = mackerel_api(
    api_key: ENV['MACKEREL_API_KEY'],
    http_method: "POST",
    path: "/api/v0/services/#{ENV['MACKEREL_SERVICE_NAME']}/tsdb",
    body: [
        {
            "name": "#{ENV['MACKEREL_GRAPH_NAME']}.#{ENV['MACKEREL_METRIC_NAME']}",
             "time": end_time,
             "value": duration
        }
    ]
)

結果

f:id:yutailang0119:20200522152407p:plain
社に了解を得て掲載しています

緑のグラフが跳ねているのは、Xcode 11.5を利用するように変えた時に、CIのキャッシュを破棄したからです。
こういった情報をグラフで記録できます!

ビルド時間の他にも、テストカバレッジの変動の記録にも使用しています。

感想

GWにゲームしながら、合間に作っていました。
fastlaneを使い始めて、早いもので3年強経ちますが、Pluginを作ったのは初めてでした。
最近はfastlaneのDSLよりも、Rubyらしく書けるようになりたいと思っていて、 fastlane new_action でActionとして作ることが多くなっています。
Pluginの実装は、Actionを作るのとほぼ同じように作れるため、何から始めればよいかはわかりやすかったです。

GitHub Actions for Android Lintを作ってる - がんばってなんか書く を作った時にも感じたことですが、テンプレートから生成して、そこにGitHub ActionsやTravis CIなどのテスト環境が用意されていると、ちゃんとテスト書くようになるのでよいですね。
特に慣れていないプログラミング言語のプロジェクトだと、実行結果があっているのかの自信もないので、自然とテストを書くようになれます。

fastlaneにコントリビューションもした

github.com

最近のCI環境は、もっぱらGitHub Actionsで暮らしています。
この fastlane-plugin-mackerel_api でも、GitHub Actions上でテストを行なっています。
これまで fastlane new_plugin からは、Travis CIとCircle CIでの実行テンプレートが生成されていました。
fastlane-plugin-mackerel_apiGitHub Actionsでの知見を培ったので、Pluginテンプレートに追加するPull Requestを出し、無事マージされました🎉
fastlane pluginの作成を始める開発者は、CI環境としてこれまでのTravis CIとCircle CIに追加して、新たにGitHub Actionsを選びやすくなりました。
v2.148.1 でリリースされています。

今回の変更の恩恵は、fastlaneのエンドユーザーでは感じることがないので、ぜひPluginを作ってみてください!

大変だったこと

1つ目は、テストの書き方やrakeなどのRubyを扱うそのもの。
Rubyに慣れていないので、Error throwsのテストの書き方がわからずに、めっちゃ調べました。

2つ目は、RubyGemsへの公開。
fastlaneのドキュメント Create your own fastlane pluginの中のPublishing your plugin には、

bundle install
rake install
rake release

で公開すると書かれていて、GitHub Actionsでやろうとしましたが、 rake release がgitのタグを打とうとする挙動を知らずに、はまりました。
bundle exec gem push pkg/*.gem とすることにしました。

その他に、GitHub ActionからのRubyGems公開には、OTP codeを突破できないという課題が残っています。
それを除けば、たぶん release.yml で動くと思うのだけど、OTPの回避方法を知っている人が教えてください。

まとめ

ぜひ、ご利用ください!

*1:実際にはこの処理をさらにWrapしたActionを作っています

GitHub Actions for Android Lintを作ってる

github.com

f:id:yutailang0119:20200413222656p:plain
Marketplace

Danger x Android Lint

自分は仕事ではAndroidアプリの開発もしています。
Android Lintも使っていて、danger/dangerloadsmart/danger-android_lintを組み合わせて、Pull RequestにLint結果を表示するという、一般にも使われていそうな方法で利用しています。

danger-android_lintの問題点

Dangerはすごく便利なのだけど、Lintからの指摘とDangerfileに指定したルールからの指摘とが混在してしまうのは、情報が多くなりがちで、目が滑るのが気がかりでした。
Dangerからの指摘には、Project specificな情報やレビュー上で確認必須なルールをDangerfileに指定して、Lintの情報は別にまとめたい。

GitHub Action for SwiftLint

ところで、最近はGitHub Actionsが時流らしいし、自分もaction.ymlを書くのにだいぶ慣れてきました。
Swiftだとnorio-nomura/action-swiftlintを使うと、GitHub Actionsで簡単にrealm/SwiftLintが実行できます。

norio-nomura/action-swiftlintのいいところは、実行が簡単なだけではありません。
Workflow commandswarningerrorを使って、Review CommentのようにPull Requestの対応コードにLint結果を表示します。
これが最高に便利。

ということで、Android Lint版を作った

github.com

やっていることは単純で、Android Lintで生成されたxmlをparseして、Workflow commandの形式に変換して echo しているだけです。
mobileposse/github-android-lint-action *1を参考にさせてもらいつつ、Annotationの表示にはWorkflow commandsを使うようにすることで、GitHubaccess tokenが不要になっています。

追加機能の予定

Android Lintが生成する lint-results.xml に載っている情報を全部使えているわけではないので、もう少しはアップデートしようと思っています。
id とか priority のフィルタリング機能とか。

あとは、このActionだけでAndroid Lintの実行までを行うようにすると嬉しいのかどうか...

まとめ

フィードバックをぜひお願いします!
みなさんもぜひ便利なGitHub Actionsを作って、公開してください!!!

[おまけ] GitHub Actionを作る

今回のActionは、xmlのparseして、 echo するだけで、環境依存はありません。
よって、Javascript actionで作りました。
かつ、型がある方が嬉しい*2ので、actions/typescript-actionをTemplateにして、作成を始めました。
TypeScriptを久々に触りながら、出来上がりを優先して作っているので、クオリティはいまいちかもしれませんが、テストはちゃんと書いているので、ある程度の品質は担保できているはず。

READMEとaction.ymlの情報で、Marketplaceに公開されます。
Update action.yml for branding · yutailang0119/action-android-lint@f183f3c · GitHub

f:id:yutailang0119:20200413183312p:plain
GitHub Marketplaceに公開するために、iconとcolorを指定するのおもしろい

*1:残念なことにアーカイブされてしまっている😐

*2:結果的にxmlのパース部分は、型定義ファイルがないので、 any を扱うことになったけど

WEB+DB PRESS Vol.116 特集1 「はじめてのトラブルシューティング」に寄稿しました #wdpress

宣伝

2020/04/24 (金) に発売予定WEB+DB PRESS Vol.116 *1 に寄稿しました!
担当は、特集1 「はじめてのトラブルシューティング」の第4章「モバイルアプリ」です。
id:Soudai さん、 id:rukiadia0401 さん、@maeponさんとの特集共著です。

一部の大手書店様では、4/16 (木) から、先行販売もあるようです。
外に出づらいご時世ですが、電子書籍版の販売もありますので、合わせてよろしくお願いします!

f:id:yutailang0119:20200410105814j:plain:w300
表紙
WEB+DB PRESS|gihyo.jp … 技術評論社

iOSAndroidの両方の解説で、誌面の関係で掲載を断念せざるを得なかった内容も多くあります。
以下を意識しています。

  • 4月発刊の新人歓迎号と銘打っているので、基礎の基礎から説明
  • 限られた誌面の中でも、できるだけ広い領域を拾えるように
  • 日頃からトラブルへの準備をできるように
  • プロフェッショナルな皆様にも、いくつかは学びがあるように

経緯

今回の寄稿は id:Soudai さんより、声をかけてもらいました。
たしか、去年の12/20 (金) *2
id:Soudaiid:yutailang0119 の組み合わせといえば、なつかしのマグロですね。

yutailang0119.hatenablog.com

Web+DB PRESSid:yutailang0119

読者としては、初めてWeb+DB PRESSを読んだのかが、いつかは覚えていないのですが、 builderscon tokyo 2017で「WEB+DB PRESS 100号記念特別企画」に関わらせてもらいました。

https://builderscon.io/tokyo/2017/session/17d5767a-4f26-11e7-aa42-42010af00d0a

www.youtube.com

このbuilderscon tokyo 2017開催レポートを、101号に寄稿させてもいただきました。

そういえば、「4月は新人歓迎号」という話も、この時に聞きましたね。
2年半経って、自分もついに技術記事で寄稿の機会をいただくことができたし、なんと 特集1 !!!

謝辞

同じ特集1の共著者メンバーの id:Soudai さん、 id:rukiadia0401 さん、@maeponさん、編集の稲尾さん、いろいろ大変な時期でしたが、お疲れ様でした!
ありがとうございました!

はてなの同僚の id:cockscomb id:ikesyo id:itokjp の皆様には、レビューと称して、かなり校正もしてもらいました。
ありがとうございました!

最後に

2020/04/24 (金) 発売予定 WEB+DB PRESS Vol.116 を、よろしくお願いします!

*1:URLはまだ404だけど、たぶんこれのはず

*2:忘年会中にDM来た