寝ても覚めてもこんぴうた

プログラム書いたり、ネットワーク設計したり、サーバ構築したり、車いじったり、ゲームしたり。そんなひとにわたしはなりたい。 投げ銭は kyash_id : chidakiyo マデ

#Flutter やってみる : (2) Flutterでサンプルアプリケーションを動かしてみる

f:id:chidakiyo:20190722150152j:plain

こちらは、ほぼ この 記事の通りの内容です。

以前の Flutter開発環境の構築 の続きになります。

コマンドラインからFlutter appを新規作成します

flutter create my_app

シミュレータで実行します

cd my_app
flutter run

とおもったら以下のようなメッセージが表示されうまく起動してくれません。

No connected devices.

Run 'flutter emulators' to list and start any available device emulators.

If you expected your device to be detected, please run "flutter doctor" to diagnose
potential issues, or visit https://flutter.dev/setup/ for troubleshooting tips.

バイスにつながってないよーという感じなので、 事前にシミュレータを起動しておく必要がありそうです。

コマンドでシミュレータを起動する

open -a Simulator

再度シミュレータで実行する

flutter run

以下のようなメッセージが表示され、 エミュレータでサンプルアプリケーションが実行されます

🔥  To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on iPhone Xʀ is available at: http://127.0.0.1:65356/p50We9onbd8=/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".

サンプルのアプリケーションは、 + ボタンを押すたびに画面上の数字が増えていきます。

f:id:chidakiyo:20190722150308p:plain

おまけ : dartのインストールを忘れていたので今更実行

brew tap dart-lang/dart
brew install dart

#Flutter やってみる : (1) Flutter開発環境の構築

f:id:chidakiyo:20190722145735j:plain

Flutterのインストール

https://flutter.dev/docs/get-started/install/macos

zipをダウンロードして解凍してPATH通すだけ。

flutter doctor コマンドでチェックする

>> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.3, on Mac OS X 10.14.5 18F132, locale ja-JP)[f:id:chidakiyo:20190722145735j:plain]
[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/setup/#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, set ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

[!] Xcode - develop for iOS and macOS (Xcode 10.2.1)
    ! CocoaPods out of date (1.6.0 is recommended).
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your
        plugin usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To upgrade:
        brew upgrade cocoapods
        pod setup
[✗] iOS tools - develop for iOS devices
    ✗ libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:
        brew update
        brew install --HEAD usbmuxd
        brew link usbmuxd
        brew install --HEAD libimobiledevice
        brew install ideviceinstaller
    ✗ ios-deploy not installed. To install:
        brew install ios-deploy
[!] Android Studio (not installed)
[!] IntelliJ IDEA Ultimate Edition (version 2019.1.2)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.36.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[!] Connected device
    ! No devices available

こんな感じなのでそれぞれ用意していく

Android toolchain のためにAndroid Studioをインストールする

https://developer.android.com/studio/index.html にアクセスし、ダウンロードする。 自分は新しもの好きなので、 Preview Releases の方を選択してみたかったけど、うまくダウンロードされないので諦めた。

cocoapods の最新化

cocoapods がそもそもインストールされていないので、インストールする

brew install cocoapods

と適当に実行したらインストールされたようなのでそのままインストールが完了するのを待つ。

エラーが発生したので、

brew link --overwrite cocoapods

を実行していいい感じに。

pod setup

を実行してpodを利用できる状態にする。

iOS tools を整える

支持されてたように以下を実行する

brew update
brew install --HEAD usbmuxd
brew link usbmuxd
brew install --HEAD libimobiledevice
brew install ideviceinstaller

そのあと

brew install ios-deploy

もう一度 flutter doctor を実行する

結果は

flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.7.8+hotfix.3, on Mac OS X 10.14.5 18F132, locale ja-JP)
[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/setup/#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, set ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

[✓] Xcode - develop for iOS and macOS (Xcode 10.2.1)
[✓] iOS tools - develop for iOS devices
[!] Android Studio (version 3.4)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] IntelliJ IDEA Ultimate Edition (version 2019.1.2)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.36.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[!] Connected device
    ! No devices available

Android toolchain がokになっていないので、一度Android Studioを起動する

起動し、一通り初期のモジュールのインストールが終わったら、以下を実行しライセンスをacceptする。

flutter doctor --android-licenses

Andoroid Studio にFlutter pluginとDart pluginをインストールする

PreferenceのPluginsからFlutterのpluginを検索してインストールする。 Flutterのプラグインをインストールすると依存としてDartプラグインも一緒に入る模様。

ここまでですでにだいぶ疲れる。

モノレポ(mono repository)内でmodで管理しているアプリケーションをGAEにデプロイするために必要な2つのポイント

f:id:chidakiyo:20190719161116j:plain

前回は モノレポ(mono repository)内でgoのmoduleを相対パスで利用する方法 という記事を書きましたが、今回はさらにモノレポで管理しているAppengineの複数のサービスをデプロイする方法になります。

なぜGAEのデプロイの記事が必要?

前回の記事を試した方がいるかいないかは不明ですが、モノレポ内でmodで依存を解決したものをデプロイした場合、modを利用したデプロイ(GO111MODULE=on)ではGCP上のCloudBuild上でコンパイル(依存の解決)を行うため、相対的に管理しているモジュールを取得することができないためエラーになり、デプロイが完了しません。

なので、今回はサンプルも作り、丁寧に説明したいと思います。 サンプルのソースコードはこちらになります。

https://github.com/chidakiyo/benkyo/tree/master/appengine-relative-mod

デプロイするために必要な設定2つ

  1. デプロイ時にはvendorモードでデプロイする
  2. デプロイする際にgo.modファイルが存在してはだめ

上記の2点の対策が必要です。

vendorモードでデプロイするとは?

普通にmodを利用したAppengineのデプロイは以下のようなコマンドを実行します。

GO111MODULE=on gcloud app deploy ./app.yaml

gcloudコマンドをmodで管理する GO111MODULE=on という環境変数の元実行します。 先程の話のように、相対的にモジュールを管理している場合、この方法ではデプロイは失敗します

成功するためには

vendorモードでデプロイする必要があるため、以下のような手続きでコマンドを実行します。

GO111MODULE=on go mod vendor                 // vendorディレクトリを作成する
GO111MODULE=off gcloud app deploy ./app.yaml // Appengineのデプロイを実行
rm -rf ./vendor                              // 一時的に作成したvendorディレクトリを削除する(optional)

つまり、デプロイの際に、vendorディレクトリ配下に相対的に管理しているモジュールのソースコードを持ってきて、 そのファイルごと依存としてGCP側のデプロイ用のCloudBuildにまるごと渡します。

もう一つの嬉しい作用として、vendorモードでデプロイするとGAEのデプロイが若干早くなるようです。

注意: この1点目だけではうまくいきません。

go.modファイルを除外する

デプロイする際に、デプロイするアプリケーションのディレクトリ内にgo.modファイルが存在する場合、 GO111MODULE=off でデプロイしてもCloudBuild側でmodを利用した依存解決をしようとします。

そのため、glcoud app deployコマンドでgo.modファイルを持っていかないように .gloudignore ファイル内に

go.mod

と記述します。 これでCloudBuildのデプロイプロセスではvendorモードとして動作し、Appengineへのデプロイが成功します。

参考まで

Github にモノレポで相対パスでmod管理し、かつ、Appengineにデプロイできる簡単なサンプルを作成してみました。

シンプルなテンプレートエンジンのenvsubstをインストールせずに使う

f:id:chidakiyo:20190719143057j:plain

motivation

色々な仕組みを作るときにテンプレートエンジンが欲しくなることがあると思いますが、 あまり仰々しい感じの仕組みを利用したくない、(or インストールしたくない)ということはあると思います。

kubernetes(k8s)の設定ファイルのパラメータを環境ごとに入れ替えたり、 Appengineのapp.yamlの値を環境ごとに差し替えたい、ということもあるかもしれません。

そんなときに気軽に使えるのがenvsubst。 k8sのconfigにはkustomize( https://kustomize.io/ )という手もありますが。。

envsubstの使い方

envsubstコマンドの基本としては以下のように利用します

テンプレートファイルは以下のイメージです

cat template.txt

${HELLO} ${WORLD}

実行コマンドは以下のようになります

export HELLO=hello
export WORLD=world
envsubst < template.txt // hello world

という感じで、環境変数に定義していた値でtemplateファイルのプレースホルダを置き換えます。

使い方は簡単ですが、envsubstコマンドのインストールが必要です。

インストールせずにenvsubstを利用する

envsubstをインストールしたくないのでDockerをインストールするのか!という突っ込みはありそうですが、 CI/CD環境などでの利用を想定しています。

今回利用するコンテナは supinf/envsubst というものを使ってみます。 DockerhubのURLは https://hub.docker.com/r/supinf/envsubst で、 Githubでは https://github.com/supinf/dockerized-tools/tree/master/cli-tools/envsubst/versions/0.x でどのようなものか確認できます。

利用1 : 環境変数もファイルとして値を与える

以下のようなテンプレートファイルを用意します。

cat template.txt

${A}
  ${B}

環境変数ファイルとして以下のようなファイルを用意します

cat env.txt

A=foo
B=bar

Dockerコマンドでenvsubstを実行すると以下のような結果が得られます。

% docker run --rm -it -v $(pwd):/tmp  supinf/envsubst /tmp/template.txt /tmp/env.txt
foo
  bar

こちら のentrypoint.shを見るとわかりますが、第一引数にはテンプレートファイルを取り、第二引数が存在する場合にはenvファイルを利用するというふるまいになっています。

利用2 : 環境変数を直接渡す

先程のテンプレートファイルをそのまま利用します。

Dockerコマンドで環境変数を渡しながらenvsubstコマンドを以下のように実行します。

% docker run --rm -it -v $(pwd):/tmp -e A=FOO -e B=HOGE supinf/envsubst /tmp/template.txt
FOO
  HOGE

-e オプションで渡した環境変数の値がテンプレートに差し込まれて出力されていることがわかります。

追記 : docker実行したenvsubstから生成ファイルを取得する

docker run --rm -it -v $(pwd):/tmp -e A=FOO -e B=HOGE supinf/envsubst /tmp/template.txt >| config.txt

まとめ

このような形で、環境ごとに設定ファイルのパラメータの変更をしたいなどという場合に、envsubstをインストールせずとも(or インストールできない環境でも)テンプレートファイルの処理が簡単に行なえます。

コンテナでgcloudコマンドを実行するとERROR: gcloud crashed (UnicodeDecodeError): 'ascii' codec can't decode byte 0xe8 in position 1: ordinal not in range(128) みたいなエラーが出る

f:id:chidakiyo:20190719134440j:plain

タイトルの通りで、 コンテナ環境で gcloud コマンドを実行した際 ERROR: gcloud crashed (UnicodeDecodeError): 'ascii' codec can't decode byte 0xe8 in position 1: ordinal not in range(128) のようなエラーが発生してうまく実行できない。

なぜ

glcoudコマンドの中でpythonを実行しているようなのですが、処理するファイルに非asciiな文字列が含まれている場合、pythonのレイヤーでエラーが出ているようです。

これはgcloudコマンド特有の問題ではなく、コンテナ内で各種コマンドを実行した際のあるあるです。 (昔一度ハマったのにまたハマったので記事にしました)

対策

解決方法としては、一番手っ取り早いのは

export LANG=C.UTF-8

のような形で環境変数にLANGを指定してあげれば良いです。

LANG="ja_JP.UTF-8" のような形で設定することもできますが、 その場合には apt-get -y install language-pack-ja みたいな形でlanguage packのインストールが必要そうです。

インストールしていない場合には -bash: warning: setlocale: LC_ALL: cannot change locale (ja_JP.UTF-8) のようなエラーメッセージが出ます

モノレポ(mono repository)内でgoのmoduleを相対パスで利用する方法

f:id:chidakiyo:20190703173656j:plain

goの依存管理ツールはいろいろありますが、最新はGo Modules(mod)を利用することが多くなってきたと思います。 dep(vendor)を利用した依存解決の場合にはgopath配下にあればある程度柔軟に相対的に依存を解決できましたが、 modは普通に利用するとリポジトリにコミットをした状態を期待しているような振る舞いをします。

モノレポで以下のように複数のモジュールを管理している場合には、modでは利用しづらいように思えましたが、 参照しているモジュールをコミットした状態ではなくとも参照したいという要望があったので解決してみました。

project
  ├ a_module
  │  ├ go.mod
  │  └ main.go
  ├ b_module
  │  ├ go.mod
  │  └ hoge
  │     └ say.go
  ...

各モジュールをmod化する

各モジュールをmod化します。 mod init する際に以下のように module 名を設定します。

// a_moduleをmod化
$ cd path/to/project/a_module
$ go mod init a_module

// b_moduleをmod化
$ cd path/to/project/a_module
$ go mod init b_module

作成されたmodファイルは以下のようになります。

a_module/go.mod

module a_module

go 1.12
b_module/go.mod
module b_module

go 1.12

a_moduleからb_moduleを参照する

a_moduleからb_moduleを利用したい場合、a_moduleのmodファイルを以下のように追記します

a_module/go.mod

module a_module

go 1.12

require (
    b_module v0.0.0
)

replace b_module => ../b_module

b_moduleを依存として利用します(require)という記述と、 b_moduleは ../b_module という相対位置にありますという記述が必要になります。 modの相対位置の関係はgo.modファイルがある位置になるようです。

コード側からb_moduleの関数を利用する

先程の記述は依存関係を定義したので、実際にa_moduleのファイルからb_moduleの関数を利用してみます。

a_module/main.go

package main

import (
    "b_module/hoge"
)

func main() {
    hoge.Say() // b_moduleで実装した関数 // Hello
}

main.goが作成できたら go run main.go などで実行します。 実行の際に依存関係の取得が行われます。

ちなみに b_module/hoge/say.go の中身はこんな感じになるかと思います。

package hoge

func Say() string {
    return "Hello"
}

まとめ

このようにmodファイルを構成することでモノレポで管理しているプロジェクトでも 共通のユーティリティモジュールなどを柔軟に参照することができます。

一時 GO111MODULE=off を利用し、vendor(dep) を引き続き利用していましたが、 こちらの方法でも同じように管理できるようになったのでまとめました。

ツッコミどころなどあればコメントください。

次回作の予告

上記のmodでの相対的なモジュール管理はGAEで利用しようとすると、Appengineのbuildのプロセスでは相対的に取得できるはずのモジュールが取得できないため、デプロイがエラーになります。 その部分も回避策があるため次回の記事で書きます。

参考

cmd/go: clarify go.mod documentation for trivial relative modules · Issue #27274 · golang/go · GitHub

go build keeps complaining that: go.mod has post-v0 module path - Stack Overflow

Docker build時にキャッシュを利用しない

Docker build する際に、基本的にはCacheを利用されますが、 それでは困る(毎回ちゃんと処理したい)という場合もあると思います。

そんなときは --no-cache オプションを docker build 時に付与すれば良い。

ま、それだけ。

docker build | Docker Documentation