Node.js で Google Fit のデータを取得する

今年になってから今更ですがドラクエウォーク始めました。

www.dragonquest.jp

通勤しないけど、このおかげでけっこう外歩きするようになってるので、歩数データと体重データの相関取ったりしてみたいなあと思いました。Google Fit って Web アプリは廃止されてて、スマホアプリの UI しかないんですが、REST API でデータを読み書きはできます。そこで REST API で Fit のデータを取得する方法を調べました。

Node.js で書きたいので googleapis の NPM パッケージを使うことに。

www.npmjs.com

API Key もいいんですが、よりセキュアな OAuth2 認証使うため local-auth パッケージもインストール

www.npmjs.com

OAuth2 を使って、Google の People API を呼び出すサンプルがありました。

https://github.com/googleapis/google-api-nodejs-client/blob/master/samples/oauth2.js

ローカル認証のために、localhost:3000 の http server を起動しリダイレクトして、oauth2Client から token を取得しています。 コマンドを叩くたびに、リダイレクト用ページがブラウザで開き、スマホにセキュリティ通知が来るのがうざいですがちゃんと動きます。

Fit の REST API を使うために、Google Cloud Platform のプロジェクトで Fitness API を有効化します。

https://console.cloud.google.com/apis/library/fitness.googleapis.com

認証情報で OAuth 2.0 クライアント ID を作成してダウンロードし oauth2.keys.json として保存して秘密のディレクトリに格納しておきます。

https://console.cloud.google.com/apis/credentials

Fitness REST API の Aggregate の仕様は以下で見れます。

Users.dataset: aggregate  |  Google Fit  |  Google Developers

歩数と体重を読むだけなので OAuth の認可スコープは、以下でよさそうです。

const scopes = [
  'https://www.googleapis.com/auth/fitness.activity.read',
  'https://www.googleapis.com/auth/fitness.body.read'
];

集計の軸は、dataTypeNamedataSourceId で指定します。ここにサンプルがありました。

Read the Daily Step Total  |  Google Fit  |  Google Developers

以下の指定でよいようです。

{
  "dataTypeName": "com.google.step_count.delta",
  "dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}

Fit のアプリで登録した体重データは以下のように指定すれば取れました。

{
  "dataTypeName": "com.google.weight.summary",
  "dataSourceId": "derived:com.google.weight:com.google.android.gms:merge_weight"
}

ということで、このサンプルの runSample() 関数を書き換えます。

https://github.com/googleapis/google-api-nodejs-client/blob/master/samples/oauth2.js

async function runSample() {
  const res = await fitness.users.dataset.aggregate({
    userId: 'me',
    requestBody: {
      "aggregateBy": [
        {
          "dataTypeName": "com.google.step_count.delta",
          "dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
        },
        {
          "dataTypeName": "com.google.weight.summary",
          "dataSourceId": "derived:com.google.weight:com.google.android.gms:merge_weight"
        },
      ],
      "bucketByTime": { "durationMillis": 86400000 },
      "startTimeMillis": 1624658400000,
      "endTimeMillis": 1624698000000,
    },
  });

  const steps = res.data.bucket[0].dataset[0].point[0];
  console.log(steps.originDataSourceId);
  console.log(formatDate(steps.startTimeNanos));
  console.log(formatDate(steps.endTimeNanos));
  console.log(steps.dataTypeName);
  console.log(steps.value[0].intVal);

  const weight = res.data.bucket[0].dataset[1].point[0];
  console.log(weight.originDataSourceId);
  console.log(formatDate(weight.startTimeNanos));
  console.log(formatDate(weight.endTimeNanos));
  console.log(weight.dataTypeName);
  console.log(weight.value[0].fpVal);
}

function formatDate(timestamp) {
  let date = new Date();
  date.setTime(timestamp / 1000000);
  const params = {
    year: 'numeric', month: 'numeric', day: 'numeric',
    hour: 'numeric', minute: 'numeric', second: 'numeric',
    hour12: false
  };
  return date.toLocaleString("ja", params);
}

リクエストでは、取得したい期間 (startTimeMillis, endTimeMillis)をエポック時間で指定します。bucketByTime は集計に必要な期間を指定します (上記の read-daily-step-total サンプルに合わせてます)。userId は Google のアカウント名とかではなく me を指定します。

レスポンスの data.bucket[0].dataset は配列になっていて、歩数、体重の順に格納されてます。データの時刻はナノ秒単位で入っているので、フォーマットしました。実行すると以下のようにデータが取れます。

raw:com.google.step_count.cumulative:Google:Pixel 3 XL:caa195e4620531ba:Step Counter
2021/6/26 7:25:33
2021/6/26 15:22:25
com.google.step_count.delta
9422
raw:com.google.weight:com.google.android.apps.fitness:user_input
2021/6/26 8:02:26
2021/6/26 8:02:26
com.google.weight.summary
67.0999984741211

時期によっては、前使ってた Nexus 6P や Moto 360 のデータも入っていました。

Argo Workflow の機能と記法

前回デプロイと動作確認をしたので続きです。

blog.kondoumh.com

Steps

順次実行 (コードはリンク先に)。

f:id:kondoumh:20210611131108p:plain

- - で先行ステップを表現するのはややわかりづらい気がします。

でも DAG っていう GitHub Actions とかでもお馴染みの記法もサポートされてます。

DAG

f:id:kondoumh:20210611131224p:plain

Artifacts

後続のステップに成果物を受け渡せます。サンプルでは [MinIO] のストレージをマウントしてますが、S3 とか色々サポートされてます。

Scripts & Results

source タグに直接コード書けます。Node.js とか Python のイメージ持ってくれば、ベタっとスクリプト書けて便利そうです。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: scripts-bash-
spec:
  entrypoint: bash-script-example
  templates:
  - name: bash-script-example
    steps:
    - - name: generate
        template: gen-random-int-bash
    - - name: print
        template: print-message
        arguments:
          parameters:
          - name: message
            value: "{{steps.generate.outputs.result}}"  # The result of the here-script

  - name: gen-random-int-bash
    script:
      image: debian:9.4
      command: [bash]
      source: |                                         # Contents of the here-script
        cat /dev/urandom | od -N2 -An -i | awk -v f=1 -v r=100 '{printf "%i\n", f + r * $1 / 65536}'

  - name: gen-random-int-python
    script:
      image: python:alpine3.6
      command: [python]
      source: |
        import random
        i = random.randint(1, 100)
        print(i)

  - name: gen-random-int-javascript
    script:
      image: node:9.1-alpine
      command: [node]
      source: |
        var rand = Math.floor(Math.random() * 100);
        console.log(rand);

  - name: print-message
     inputs:
      parameters:
      - name: message
    container:
      image: alpine:latest
      command: [sh, -c]
      args: ["echo result was: {{inputs.parameters.message}}"]

Loops

繰り返し。パラメータとかに List や Map を書いて Matrix build 的なこともできます。いろんな環境で並列にテストするとかが簡単です。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: loops-maps-
spec:
  entrypoint: loop-map-example
  templates:
  - name: loop-map-example
    steps:
    - - name: test-linux
        template: cat-os-release
        arguments:
          parameters:
          - name: image
            value: "{{item.image}}"
          - name: tag
            value: "{{item.tag}}"
        withItems:
        - { image: 'debian', tag: '9.1' }       #item set 1
        - { image: 'debian', tag: '8.9' }       #item set 2
        - { image: 'alpine', tag: '3.6' }       #item set 3
        - { image: 'ubuntu', tag: '17.10' }     #item set 4

  - name: cat-os-release
    inputs:
      parameters:
      - name: image
      - name: tag
    container:
      image: "{{inputs.parameters.image}}:{{inputs.parameters.tag}}"
      command: [cat]
      args: [/etc/os-release]

実行結果。並列で動いてます。

f:id:kondoumh:20210611131255p:plain

Conditional

条件分岐は when タグで書きます。Coin flip のサンプルです。実行されたステップとされなかったステップが識別できます。

f:id:kondoumh:20210611131313p:plain

Retrying Failed or Errord Steps

エラーのリトライも指定できます。特徴的なのは、nodeAntiAffinity の設定で、失敗した時とは別のワーカーノードで実行されることで環境要因のエラーが切り分けられる可能性があります。Kubernetes の特性を活かしてる感があります。

retry-backoff.yaml

 apiVersion: argoproj.io/v1alpha1
 kind: Workflow
 metadata:
   generateName: retry-backoff-
 spec:
   entrypoint: retry-backoff
   templates:
   - name: retry-backoff
     retryStrategy:
       limit: 10
       retryPolicy: "Always"
       backoff:
         duration: "1" # Must be a string. Default unit is seconds. Could also be a Duration, e.g.: "2m", "6h", "1d"
         factor: 2
         maxDuration: "1m" # Must be a string. Default unit is seconds. Could also be a Duration, e.g.: "2m", "6h", "1d"
       affinity:
         nodeAntiAffinity: {}
     container:
       image: python:alpine3.6
       command: ["python", -c]
       # fail with a 66% probability
       args: ["import random; import sys; exit_code = random.choice([0, 1, 1]); sys.exit(exit_code)"]

f:id:kondoumh:20210611131331p:plain

Exit handlers

ワークフローが成功しても失敗しても実行したいステップ(結果を通知するなど) を記述するときは Exit handler を使います。

f:id:kondoumh:20210611131346p:plain

Suspending

suspend タグで停止のステップを入れることが可能です。duration で時間も指定できます。Argo CLI の resume サブコマンドで外部からレジュームすることも可能です。

 $ argo resume WORKFLOW

Daemon Containers

template のスコープで、指定したコンテナを Daemon として起動、破棄できます。そのスコープでは常に使用できるので、コンテナ化した Web アプリのテストをするとか、一時的に Docker レジストリとか NPM のパッケージレジストリを建ててテスト中だけ使うなどの用途が考えられます。

Sidecars

メインのコンテナと同一 Pod で起動するサイドカーコンテナを指定することもできます。

サイドカーを使って DinD な処理も書けます。

Docker-in-Docker Using Sidecars

Kubernetes Resources

Kubernetes のマニフェストを直接 workflowに記述して適用することも可能です。

以上のように Kubernetes ネイティブでかつ CI パイプラインの記述でお馴染みの書き方でワークフローを作れる Argo Workflow なかなかイイですね。色々なコンテナでコードもぱぱっとかけちゃうし。

CI にも使いたいぜって思いますが、今のところ git push で trigger できないです。でも、

but we plan to do so in the near future.

と近い将来対応されるようなので、それまでは cron ジョブとかで起動してねとのことです。

Kubernetes native なワークフローエンジン Argo workflow を動かす

Argo workflow は Cloud Native Computing Foundation にホストされている Kubernetes native なワークフローエンジンです。

github.com

Kubeflow でも機械学習のパイプラインエンジンとして使用されています。

blog.kondoumh.com

公式の manifest でインストールできます。

https://github.com/argoproj/argo-workflows/tree/master/manifests

Helm charts もコミュニティベースでメンテナンスされています。

argo-helm/charts/argo at master · argoproj/argo-helm · GitHub

今回は Docker Desktop の Kubernetes 環境に argo namespace を作成して quick-start 用の manifest を適用してみました。

$ kubectl create namespace argo
$ kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-workflows/stable/manifests/quick-start-postgres.yaml

argo-server と workflow-controller が起動し、ストレージ用の minio と DB サーバ postgres も起動されました。

$ kubectl get po,deploy,svc -n argo            
NAME                                      READY   STATUS    RESTARTS   AGE
pod/argo-server-5b86d9f84b-vjhhq          1/1     Running   4          23m
pod/minio-58977b4b48-ts9zn                1/1     Running   0          23m
pod/postgres-6b5c55f477-ss24g             1/1     Running   0          23m
pod/workflow-controller-d9cbfcc86-jkf8j   1/1     Running   2          23m

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argo-server           1/1     1            1           23m
deployment.apps/minio                 1/1     1            1           23m
deployment.apps/postgres              1/1     1            1           23m
deployment.apps/workflow-controller   1/1     1            1           23m

NAME                                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/argo-server                   ClusterIP   10.106.203.74   <none>        2746/TCP   23m
service/minio                         ClusterIP   10.96.161.227   <none>        9000/TCP   23m
service/postgres                      ClusterIP   10.98.163.40    <none>        5432/TCP   23m
service/workflow-controller-metrics   ClusterIP   10.102.138.81   <none>        9090/TCP   23m

CRD もインストールされました。

$ kubectl get crd | grep argo
NAME                                   CREATED AT
clusterworkflowtemplates.argoproj.io   2021-05-06T03:32:28Z
cronworkflows.argoproj.io              2021-05-06T03:32:28Z
workfloweventbindings.argoproj.io      2021-05-06T03:32:28Z
workflows.argoproj.io                  2021-05-06T03:32:28Z
workflowtemplates.argoproj.io          2021-05-06T03:32:28Z

Argo Server の Web UI を使うために port-forward します。

kubectl -n argo port-forward deployment/argo-server 2746:2746

Chrome で https://localhost:2746 に接続すると NET::ERR_CERT_INVALID と自己証明書のエラーで接続できないので、thisisunsafe の呪文をタイプして接続しました。

f:id:kondoumh:20210507083016p:plain

まだワークフローは何も登録されていない状態です。

Argo workflow は kubectl コマンドでも操作できますが、Argo CLI に便利コマンドが用意されているのでインストールします。

$ curl -sLO https://github.com/argoproj/argo/releases/download/v3.0.2/argo-linux-amd64.gz
$ gunzip argo-linux-amd64.gz
$ chmod +x argo-linux-amd64
$ mv ./argo-linux-amd64 /usr/local/bin/argo

サンプルワークフローをデプロイしてみます。hello-world- というプレフィクス付きのワークフローを生成します。whalesay という template が定義されており、コンテナイメージ whalesay を起動して cowsay コマンドを叩くものです。メモリと CPU リソースの limit も指定しています。この template を entrypoint に指定することで起動します。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: hello-world-
  labels:
    workflows.argoproj.io/archive-strategy: "false"
spec:
  entrypoint: whalesay
  templates:
  - name: whalesay
    container:
      image: docker/whalesay:latest
      command: [cowsay]
      args: ["hello world"]
      resources:
        limits:
          memory: 32Mi
          cpu: 100m
$ argo submit -n argo --watch https://raw.githubusercontent.com/argoproj/argo-workflows/master/examples/hello-world.yaml

Name:                hello-world-mg6x7
Namespace:           argo
ServiceAccount:      default
Status:              Succeeded
Conditions:          
 PodRunning          False
 Completed           True
Created:             Tue May 11 12:27:50 +0900 (1 minute ago)
Started:             Tue May 11 12:27:50 +0900 (1 minute ago)
Finished:            Tue May 11 12:29:25 +0900 (now)
Duration:            1 minute 35 seconds
Progress:            1/1
ResourcesDuration:   35s*(100Mi memory),35s*(1 cpu)

STEP                  TEMPLATE  PODNAME            DURATION  MESSAGE
 ✔ hello-world-mg6x7  whalesay  hello-world-mg6x7  1m 

1分ほどでコンテナ作成から実行までが完了しました。

最新のジョブの状態やログを Argo CLI で確認できます。

$ argo list -n argo @latest
NAME                STATUS      AGE   DURATION   PRIORITY
hello-world-mg6x7   Succeeded   23m   1m         0

$ argo logs -n argo @latest
hello-world-mg6x7:  _____________ 
hello-world-mg6x7: < hello world >
hello-world-mg6x7:  ------------- 
hello-world-mg6x7:     \
hello-world-mg6x7:      \
hello-world-mg6x7:       \     
hello-world-mg6x7:                     ##        .            
hello-world-mg6x7:               ## ## ##       ==            
hello-world-mg6x7:            ## ## ## ##      ===            
hello-world-mg6x7:        /""""""""""""""""___/ ===        
hello-world-mg6x7:   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~   
hello-world-mg6x7:        \______ o          __/            
hello-world-mg6x7:         \    \        __/             
hello-world-mg6x7:           \____\______/

もちろん Web UI でも結果を閲覧できます。

f:id:kondoumh:20210511123323p:plain

f:id:kondoumh:20210511123343p:plain

ジョブの詳細画面からログ出力を確認できます。

f:id:kondoumh:20210511124600p:plain

f:id:kondoumh:20210511124623p:plain

タスクの依存関係を dependencies で指定するサンプルです。

# The following workflow executes a diamond workflow
# 
#   A
#  / \
# B   C
#  \ /
#   D
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: dag-diamond-
spec:
  entrypoint: diamond
  templates:
  - name: diamond
    dag:
      tasks:
      - name: A
        template: echo
        arguments:
          parameters: [{name: message, value: A}]
      - name: B
        dependencies: [A]
        template: echo
        arguments:
          parameters: [{name: message, value: B}]
      - name: C
        dependencies: [A]
        template: echo
        arguments:
          parameters: [{name: message, value: C}]
      - name: D
        dependencies: [B, C]
        template: echo
        arguments:
          parameters: [{name: message, value: D}]

  - name: echo
    inputs:
      parameters:
      - name: message
    container:
      image: alpine:3.7
      command: [echo, "{{inputs.parameters.message}}"]

CLI の実行結果

 ✔ dag-diamond-vqswz  diamond                                             
 ├─✔ A                echo      dag-diamond-vqswz-4245884217  16s         
 ├─✔ B                echo      dag-diamond-vqswz-4195551360  6s          
 ├─✔ C                echo      dag-diamond-vqswz-4212328979  6s          
 └─✔ D                echo      dag-diamond-vqswz-1249778     5s 

Web UI での表示

f:id:kondoumh:20210515083827p:plain

以上、ローカルでの Argo 環境構築と動作確認でした。

GitHub CLI に GitHub Actions 用コマンドが追加された

1.0 では対応してませんでした。

blog.kondoumh.com

今月の中旬にリリースされた v1.9.0 で run, workflow, actions のコマンド群が提供されました。

github.blog

actions でコマンド一覧が表示できます。

$ gh actions

f:id:kondoumh:20210429150834p:plain

run はワークフロー実行結果や詳細を見るためのコマンド。workflow はワークフローファイルの管理や起動のめのコマンドのようです。

リポジトリのワークフロー実行結果一覧を取得するには run list サブコマンドを使用します。

$ gh run list

f:id:kondoumh:20210429143053p:plain

ワークフロー起動時のコミット、ワークフロー名、ブランチ、トリガー、run ID が取得されます。

実行中のワークフローをウォッチするには run watch サブコマンドを使用します。

$ gh run watch

f:id:kondoumh:20210429145055p:plain

実行中のジョブを選択できて、選択するとワークフローのジョブごとにステップの実行結果が順次出力されていきます。Web UI だとステップごとの結果はジョブをクリックしないと見れませんので一覧性は高くなっています。

f:id:kondoumh:20210429145247p:plain

run view サブコマンドで run ID を指定して実行結果の詳細を取得できます。

$ gh run view <run ID>

f:id:kondoumh:20210429150136p:plain

Web UI もよくできているのででエラー箇所を見るのは簡単ですが、CLI を使うと grep などと組み合わせて出力を加工できるのでこれはこれで便利です。

この他ワークフローを再実行する run rerun や ワークフロー実行により生成された Artifact をダウンロードする run download などのサブコマンドも提供されています。

ワークフローファイルの一覧は workflow list サブコマンドで取得できます。

$ gh workflow list

f:id:kondoumh:20210429152131p:plain

ワークフロー名を指定して実行できます。

$ gh workflow run <workflow name>

f:id:kondoumh:20210429152523p:plain

実行後は run watch で実行状態を監視できます。

今のところ実行できるのは workflow_dispatch トリガーを持つワークフローだけです。このトリガーを持たないワークフローを実行しようとするとエラーになります。

f:id:kondoumh:20210429153048p:plain

実行後は run watch で実行状態を監視できます。

ブラウザでポチポチしなくてもワークフローの状態を調べることができるようになって便利になりました。

ワークフロー一覧はもう少し属性情報を出して欲しいですね。workflow view サブコマンドで詳細が出ますが、YAML の解析が大変なのかも? あとworkflow_dispatch だけでなく webhook イベントでトリガーするrepository_dispatch の実行にも対応して欲しいところです。

GitHub リリースページのドラフトを自動生成してくれる Release Drafter を導入する

リリースページに ChangeLog というかリリースノートを書くのはかなり面倒です。 Release Drafter を使うと PR 単位でいい感じに リリースページのドラフトを生成してくれます。

Release Drafter の GitHub App は簡単に導入できますが、何やら Deprecated にしようという議論があるようです。

GitHub Apps - Release Drafter · GitHub

github.com

GitHub Actions で release-drafter 用のワークフローを使う方が無難かなと思います。

github.com

README にある通り、設定用の release-drafter.yml とワークフロー用の release-drafter.yml をそれぞれリポジトリの .github.github/workflows に配置するだけ。

設定用の release-drafter.yml。PR にラベルをつけておくと、Features や Bug Fixes に振り分けてくれます。ChangeLog に入れたくないような修正については除外用のラベルを指定することもできます。

name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
  - title: '🧰 Maintenance'
    labels:
      - 'chore'
change-title-escapes: '\<*_&'
exclude-labels:
  - 'exclude from changelog'
template: |
   ## Changes

   $CHANGES

ワークフロー用の release-drafter.yml です。

name: Release Drafter

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, reopened, synchronize]

jobs:
  update_release_draft:
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

PR が作成・マージされる度に Draft が更新されていきます。

f:id:kondoumh:20210326150153p:plain

template に 'v$NEXT_PATCH_VERSION' を指定しているので、0.1.0 がリリースされている状態だと 0.1.1 のドラフトができます。

f:id:kondoumh:20210326150331p:plain

Draft を Edit して実際に作成したリリースページ。

f:id:kondoumh:20210326150353p:plain

手作業でリリース時にあたふたやってたことが継続的にオートメーション化された感じですね。PR のタイトルをちゃんと付けるようにすれば、ほとんど修正は不要でしょう。

テレワーク環境 Early 2021

去年は色々試行錯誤してましたが、ようやく落ち着いてきました。

blog.kondoumh.com

ずっと ASUS の 27inch WQHD ディスプレイ使ってます。

scrapbox.io

8年も前のモデルで HDMI 入力端子が1個しかないので、テレワーク端末のノート PC と MacBook Pro をスイッチするのに HDMI セレクタを導入しました。

REALFORCE と Microsoft Ergonomic Mouse を共用するため USB 切り替え器も久々に導入。

ノート PC も MacBook もクラムシェルにして画面を閉じ、入出力デバイスは同一という環境になりました。

WQHD は Chrome と VS Code を並べて表示するのに十分な解像度で、1画面だけなので首もほとんど動かさずに作業できます。客先でもWUXGA (1920 x 1200) 程度のディスプレイが貸与されることがほとんどなので、自宅の方が画面は広いです。PC では StrokePlus、MacBook では BetterTouch tool で仮想画面の切り替えをマウスでできるように設定してますが、ほとんど切り替えることはありません。

ディスプレイ横には iPad Pro 11inch を高めの位置にマウントしてます。このアームスタンドを導入してみました。iPad Pro 11 だと重さでけっこう揺れるので机ではなく別の台に固定してます。

作業しながら登録している YouTube チャネルを垂れ流したりしてますが、Magic Keyboard / Trackpad をペアリングして、Slack や Twitter なども iPad でやるようにしてます。ChangeLog メモも Textastic で書くようにしたのでテレワーク端末で作業しててもすぐ書けます。

iPad で映画見るときとかゲームする時は USB-C / DisplayPort 変換ケーブルを使ってます。iPad がコントローラー的な画面になり、コンテンツが 27inch で見れてなかなかいいです。

ということで、仕事してるのか個人プロジェクトやってるのか遊んでるのかは側から見ると全く変わらない感じになってしまってます。

Teams や Zoom は支給された iPhone 7 から使うことが多いですが、画面共有して共同作業する時は PC 画面を占有されないよう iPad でやることもあります。ワイヤレスヘッドセットが増えました。

f:id:kondoumh:20210304083932j:plain

Netlify Functions の Zero configuration と Netlify CLI

Netlify Functions は AWS Lambda を基盤とした API のプラットフォームです。

blog.kondoumh.com

利用は非常に簡単ですが、最近さらに Zero configuration という仕組みが導入されました。規定のディレクトリ (netlify/functions) にコードを配置するだけで、コミットしたら Netlify が自動的に Functions をビルド・デプロイしてくれます。

www.netlify.com

これまでは、netlify-lambda を devDependencies に追加して、netlify.toml で build command や functions のディレクトリなどを指定していました。

[build]
  command = "npm run build"
  functions = "resource/api"

試しに functions のコードを netlify/functions に移動し、netlify.toml を消し、package.json の build script は 文字列を echo するだけのものにして git commit / push してみました。

npm run build は実行されますが、これとは別に Functions bundling が開始され、netlify/functions 配下の hello.js が検出され bundle されました。

f:id:kondoumh:20210224074821p:plain

ちゃんとデプロイされました。

f:id:kondoumh:20210224123158p:plain

これで、Zero configuration の動作は確認できました。

これまで、Functions のローカル開発は netlify-lambda serve <dir> を実行することで行っていました。しかしこの実行には netlify.toml の作成が必須であり、Zero configuration と矛盾します。*1

netlify-lambda のドキュメントを読んでみました。

github.com

netlify-lambda serve (legacy command)

This command is pretty much superceded by Netlify Dev. We only keep it around for legacy/backward compatibility support reasons.

どうやら serve は legacy command という位置付けのようです(create-react-app などと共に使用するようなユースケースのため)。 そして Netlify Dev に置き換えられるとのことです。

www.netlify.com

Netlify Dev ってなんぞっていうところですが、文字通り Netlify を使った開発をローカルで行うための環境でした。

docs.netlify.com

Netlify CLI をインストールして Netlify にログインした状態で使います。

インストール

% npm install netlify-cli -g

ログイン

% netlify login

ブラウザが開くので Authorize をクリックするとログイン状態になります。

f:id:kondoumh:20210224203415p:plain

プロジェクトディレクトリで netlify dev を叩くと

f:id:kondoumh:20210224203921p:plain

localhost:8888 でプロジェクトのコンテンツが Serving されているようです。Netlify Functions の規定のパスは、.netlify/functions/ なので、API を呼び出してみると結果が取得できました。

f:id:kondoumh:20210224213107p:plain

Netlify CLI には他にも多くのサブコマンドが実装されています。package.json に何も書かなくてもよいので、まさに Zero configuration です。

Vue を使う開発でも、Vue の開発用サーバと netlify dev を別 port で動かし proxy の設定だけしておけば、netlify-lambda は要らないだろうなと思っています。

*1:netlify/functions 以外のディレクトリに配置したい場合飲み netlify.toml を作成することになっています。