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 を作成することになっています。

GitHub Actions ワークフローでジョブ毎のステータスを Slack 通知する

前回のフォローアップ的な内容です。

blog.kondoumh.com

複数ジョブからなるワークフローの最後に Slack などに通知する時、ワークフロー全体の成功・失敗だけでなく、どのジョブで失敗したかを通知したくなります。通知のリンクからワークフローの実行結果を開けば分かりますが、通知時点で分かる方がよいでしょう。

ジョブ毎にステータスをワークフローレベルの変数に記録して最後に通知したいところですが、そのようなグローバル変数は提供されていません。そのかわり、job.outputs を使えばジョブの出力を後続ジョブに伝搬できます。

https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idoutputs

各ジョブに必ず実行される最終スッテップを定義して、echo "::set-output name=<output_name>::<output of job>"job.status を記録します。ジョブの outputs${{ steps.<step_id>.outputs.<output_name> }} の形式で output を定義します。以下の例では、ジョブ名とジョブステータスを message という名前で記録して outputs/done に保持するようにしています。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ job.status }}"

全ジョブの結果出力用ジョブを書いてみます。needs で対象の全ジョブを指定します。${{ needs.<job_name>.outputs.<output_name> }} で記録したジョブの出力が取れます。

  Aggregate:
    if: ${{ always() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Aggregate
      run: |
        echo ${{ needs.Build.outputs.done }}
        echo ${{ needs.TestA.outputs.done }}
        echo ${{ needs.TestB.outputs.done }}
        echo ${{ needs.Deploy.outputs.done }}

出力結果。

f:id:kondoumh:20210130144146p:plain

文字だと地味なので Slack への通知は見やすいようにステータスを絵文字に変換してみます。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

Slack 通知用のジョブ。

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

実行結果。Deploy は Test B の失敗により実行されません。

f:id:kondoumh:20210130141441p:plain

Slack の通知です。Test B で失敗したことが分かります。

f:id:kondoumh:20210130140714p:plain

ワークフロー定義の全体を掲載しておきます。

name: Multi Stage Build
on:
  push:

jobs:
  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestA:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test A
    steps:
    - name: Test A
      run: echo Test A
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test A ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestB:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test B
    steps:
    - name: Test B
      run: |
        echo Test B
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test B ${{ (job.status == 'success' && '✅') || '❌' }}"

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Deploy
      run: echo Deploy
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Deploy ${{ (job.status == 'success' && '✅') || '❌' }}"

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

  Notify_failure:
    if: ${{ failure() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} failed'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_COLOR: danger
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

GitHub Actions ワークフローで複数のジョブ実行を制御する

GitHub Actions ワークフローでは、複数のジョブを順次実行・並列実行・条件実行することができます。各ジョブは異なるマシン(Runner) もしくは コンテナイメージで実行されます。

順次実行

何も指定しなければ、ジョブは並列に実行されます。順次実行するには、needs キーワードで、先行のジョブを指定します。

name: Sequential Jobs
on:
  push:

jobs:
  Build1:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    needs: Build1
    steps:
    - run: echo Build2

実行結果

f:id:kondoumh:20210122075032p:plain

並列実行 (フォーク)

ジョブを実行後に複数のジョブを並列実行するには、各ジョブの needs に分岐元のジョブを指定します。

name: Fork Jobs
on:
  push:

jobs:
  Setup:
    runs-on: ubuntu-latest
    steps:
    - run: echo Setup

  Build1:
    runs-on: ubuntu-latest
    needs: Setup
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    needs: Setup
    steps:
    - run: echo Build2

実行結果

f:id:kondoumh:20210122075441p:plain

並列実行 (ジョイン)

並列ジョブをジョインして最終の処理を行うには needs にジョインしたいジョブを列挙します。

name: Join Jobs
on:
  push:

jobs:
  Build1:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build2

  Teardown:
    runs-on: ubuntu-latest
    needs: [Build1, Build2]
    steps:
    - run: echo Teardown

実行結果

f:id:kondoumh:20210122075806p:plain

フォークとジョインの組合せ

CI でビルド後に複数のテストを並列実行して成功したらデプロイするような例です。

name: Deploy
on:
  push:
    paths:
    - .github/workflows/deploy.yml

jobs:
  Build:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build

  TestA:
    runs-on: ubuntu-latest
    needs: Build
    steps:
    - run: echo Test A

  TestB:
    runs-on: ubuntu-latest
    needs: Build
    name: Run Test B
    steps:
    - run: echo Test B

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    steps:
    - run: echo Deploy

実行結果

f:id:kondoumh:20210122085107p:plain

条件実行 (ワークフローの最後に結果を通知)

GitHub Actions ではワークフローの状態は内包するジョブの失敗・成功に依存します。1つでもジョブが失敗していれば、ワークフローの状態は失敗です。ワークフローの状態は success / failure などの関数で取得できるため、ジョブに if 条件文で実行条件を指定します。

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    steps:
    - name: Deploy
      run: echo Deploy

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: Deploy
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: Workflow Succeeded
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'

  Notify_failure:
    if: ${{ failure() }}
    runs-on: ubuntu-latest
    needs: Deploy
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: Workflow failed
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_COLOR: danger
        SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'

Slack への通知に、以下の Action を利用しました。

github.com

失敗時の実行結果

f:id:kondoumh:20210121235049p:plain

通知の例

f:id:kondoumh:20210122001019p:plain

成功時の実行結果

f:id:kondoumh:20210122000707p:plain

通知の例

f:id:kondoumh:20210122000916p:plain

野良 Scrapbox アプリ - Linux 用バイナリも Release に含めました。

これまで Windows と macOS のビルドを Release からダウンロードできるようにしていましたが、最近 Linux デスクトップも使うようになってきたので、Linux のバイナリを配布しようと思いました。

ひとまず起動確認。

f:id:kondoumh:20210119211652p:plain

ビルドして実行したところ GNOME のドック? にアイコンが出てません。

f:id:kondoumh:20210119211722p:plain

electron-builder を使ってるんですが、issue が立ってました。

github.com

この issue のコメントにあるコード書いたら一応アイコンが出ました。

f:id:kondoumh:20210119211741p:plain

配布形式としては、deb や rpm など特定のディストリビューションのパッケージではなく AppImage 形式のバイナリを配布することにしました。Ubuntu 20.0.4.1 と CentOS 8 では動作しました。

ダウンロードして利用するときは、実行用のパーミッションを付けます。

f:id:kondoumh:20210120001856p:plain

f:id:kondoumh:20210120001913p:plain

というわけでリリース。

github.com

Electron アプリもちゃんと動くので作業環境としては Linux デスクトップでも普段は生活していけそうです。

Apple Silicon のバイナリも作りたいですが、手元に M1 Mac ありません。GitHub Actions の Runner も今のところ Intel Mac しかないようです。AWS の EC2 インスタンスに Apple Silicon の Mac が来るのももう少し先なのでもうしばらく待ちですね。electron-builder も Pre-release では対応してます。

ワークアウトしたら Pixela に草を生やす PWA

テレワークで外出が減って、運動不足を自覚するようになりました。人間ドックでも数値がちょっと悪くなってました。そこで YouTube の Fit boxing チャンネルなどで室内運動するようになりました。外出した日は意識して歩数を伸ばすようにしたりしてます。

やはりモチベーション維持には、GitHub の Contribution graph のようなのあるといいなと思って Pixela にグラフを作りました。

pixe.la

数日は cURL コマンド叩いてましたが、スマホから更新できるように PWA にしてみました。

f:id:kondoumh:20210111180537g:plain

Boxing でも Walking でもやったら commit。1回のワークアウトで 20% 進捗するようにして、5回 commit したら輪が閉じます。commit と revert はそれぞれ Pixel の increment / decrement API を叩いているだけ。

まだ10日目。 f:id:kondoumh:20210111174305p:plain

2020 ふりかえり

今年は4月から完全テレワークになって外出が減り、外での飲食もなくなり、生活が大きく変わりました。

Job

クラウドネイティブなプロダクト開発に参加して丸2年が過ぎました。SRE チームとして活動しています。環境構築を自動化したり、可用性やセキュリティを高めるための設定を適用したりして安定運用を目指しています。Kubernetes 関連の技術を色々試せるのでいい経験です。

もう1つ少しだけ入ってたプロジェクトは離任して、その稼働を会社のマーケティング活動に使いました。

Mamezou-tech

会社の GitHub Organization で同僚の人とささやかな OSS 活動を始めました(業務外)。

github.com

僕は GitHub Actions をよく使うので Kubernetes のパッケージ管理やコンテナビルドの Action を公開してます。issue やプルリクもたまに来るので週末に対応したりしてました。

github.com

github.com

同僚の人は Concourse CI の Kubernetes resource を公開しています。

github.com

CI 関連の OSS は仕事でも使えるし、意外と使ってもらえたりするので題材としてはよかったです。

あと Scrapbox のグラフ構造を抽出する CLI ツールも公開してます。これは個人リポジトリでもよかったのですが、最初は OSS の数が少なかったので。

github.com

Personal Development

自分のリポジトリでは、野良 Scrapbox アプリもちょいちょいリリースしてました。Electron のリリースに追従するのが主でした。使っている NPM パッケージにプルリクを送ったりもしました。

github.com

追加した機能では、Markdown 変換が便利でけっこう使ってました。

blog.kondoumh.com

Scrapbox の更新情報をスマホで見たくて、Vue.js と Netlify Functions で PWA を作ってました。

github.com

グラフ構造可視化の画面も同じ PWA に組み込んでます。

blog.kondoumh.com

会社用の private プロジェクトを見るための PWA も同じ構造で作ってて便利に使ってます。

個人的な開発は Scrapbox を題材にしたものがほとんどでした。

Gadgets

テレワークになったので、リモートマシンに繋ぐためのシンクライアント端末と自分の MacBook を切り替えて使うようになりました。超久々にキーボード・マウス切り替え器を導入。

scrapbox.io

自宅の作業環境を最適化するため、客先にずっと置いてた REALFORCE と Microsoft Ergonomic Mouse 持って帰って使ってます。

scrapbox.io

scrapbox.io

ディスプレイも長年使ってる ASUS の WQHD 27inch。ベゼル太いです。

scrapbox.io

コロナ禍初期の頃はディスプレイも品切れが続いてました。

今年も 2015年の MacBook Pro リニューアルしてません。ずっとクラムシェルで使ってたせいかバッテリーが膨張してしまいました。仕事ではかなりハイスペックなマシンを使ってますが、自分の MacBook ではちょっとした Web アプリ作ったり Go で CLI を書く程度だったので結局買わずに過ごしてしまいました。仕事マシンで作業してる時間が圧倒的に長いのもあります。M1 Mac が出るまでは時折 Ryzen PC を物色してましたが Apple Silicon 凄いらしいので当面 Mac から離れられないのではと思ってます。

iPad Pro 11inch 2018 奥さんのを譲ってもらって使ってます。画面綺麗でヌルヌル動くし、Magic Keyboard / Trackpad とペアリングするとサブマシンとしてもかなり使えます。YouTube に可処分時間をかなり取られてますが、視聴は iPad です。MacBook だと CPU が高騰してファンが回るし、画面も占有されるのでマシンを分けてます。

blog.kondoumh.com

ThinkPad TrackPoint Keyboard 2 はなかなか良いですが、毎日長時間使ってると左小指がつりそうになるので、たまに自社のオフィスで作業する時に使うことにしました。

blog.kondoumh.com

Zoom や Teams 会議用にヘッドセットを追加。支給された iPhone で使ってます。

scrapbox.io

今年は「三体」をはじめ SF を読む時間が増え、Kindle の稼働が上がりました。

scrapbox.io

Software and Services

今年はやはり Zoom ですね。有償プラン契約して使ってます。1日何時間も会話しながら画面共有して作業してます。

zoom.us

Teams は Outlook と連動して会議の設定や参加が楽なところがよいです。チャットは高機能ですが動きがちょっともっさり、画面共有は Zoom の方が使いやすいです。

www.microsoft.com

Zenn 個人で開発されたサービスと聞いてサインアップだけしました。けっこう流行ってますね。本を書いて売ったりできるのもユニークです。

zenn.dev

Netlify は個人プロジェクト用によく使ってます。

www.netlify.com

GitHub の Arctic Code Vault プロジェクトも話題になりました。自分のしょぼいコードも北極に埋められるのか〜とちょっと面白かったです。

archiveprogram.github.com

GitHub は仕事でも個人でも365日使ってますが、GitHub Actions も随分使いやすくなりましたし、Bot で作業を自動化できたり、ナイトモードに対応したりと進化を続けてます。そんなに落ちなかったし。Microsoft による買収はいい方向に作用してると言えるのではないでしょうか。CodeSpaces のローンチも楽しみです。

Ingress は8周年。今年は外出が減ったので新機能の Dronenet であちこちにドローン飛ばしてました。Intel map も活用してました。

medium.com

YouTube Premium に入りました。広告ないのはとても快適ですね。登録しているチャンネルもかなり増えてしまいました。

Scrapbox は個人メモにも活用してます。

blog.kondoumh.com

Mos は macOS で Windows 用マウスでもスムーズスクロールを実現できるユーティリティ。これで、Windows でも macOS でも同じマウスを切り替えて使えるようになりました。

mos.caldis.me

最後に

来年は徐々に日常が回復していくことを祈ってます。

Scrapbox プロジェクトのグラフ構造可視化 - その後

Scrapbox のページ間リンクを可視化するやつです。

Graphviz バージョン blog.kondoumh.com

D3.js バージョン blog.kondoumh.com

D3.js バージョンを半年ぶりにちょっと触りました。と言っても Visualization の方式を変えたとかではなく、ノードをフィルタリングする UI を追加してみたっていう内容です。

Scrapbox API でページ毎の views と linked のカウントが取れます。これをフィルタリングの指標とします。スライダーの UI コンポーネントで2つの数値の Range を調整するようにしました。

vuetifyjs.com

views と linked を独立で調整できるようにしてます。

f:id:kondoumh:20201220220645p:plain

自分の Scrapbox プロジェクトを可視化してみたところです。

scrapbox.io


Scrapbox graph visualization

ハッシュタグのように使うページはやたらと 1-hop リンクが増えて行きます。文章内で言及されることによって自然とハブになるページとは違って、可視化した時はノイズに近いものになります。ハッシュタグかどうかの判定はデータからはできないので、リンク数で定量的に削る作戦です。Graph データを作るときにメタデータを出力するようにすれば UI でフィルターできそうですね。

個人 Scrapbox プロジェクトなので高々数100件しかありませんが、会社の Scrapbox は6000ページを超えており*1、全てのノードを描画するのは重すぎなので views で10件以上とかで絞り込めば閲覧に耐える速度になりました。

と言うことで、ノードの数を絞るのに views の Min を、ハッシュタグ的なノイズ的ページを隠すのに links の Max をうまく設定することでグラフ構造が浮き彫りになる感じです。

もっとピュンピュン動いて欲しいところですが、レンダリングアルゴリズムの見直しはまた今度。

*1:元データを作成するのに5分ぐらいかかります。(データ取得が大部分を占めていますが)。