Technote

by sizuhiko

オンラインイベントの盛り上がりを効果音で共有するサービス pong-swoosh を作成した

前回のBlog更新が1年半近く前の、最後のオフラインイベントだったでしょうか…. それから、書くまでもありませんが、イベントや仕事がすべてオンラインで行われるようになりました。

私たちインターネットに関連する仕事をしているものは、特に在宅でも同じように仕事を進めています(僕の場合は家の環境が劣悪というのはありますが)。

変わったのは勉強会やカンファレンス、コミュニティ活動がオンラインになったり、そもそも開催されなくなったりといったことがあります。

僕も昨年中はずっとモチベーションもダダ下がりで、ただ仕事をして何となくYoutubeで動画見るみたいな繰り返しを送っていました。 オンラインの勉強会やカンファレンスにも参加する気になれなかった、というのが本当のところです。

お前は懇親会とか飲み会メインだろ?とか言われると、まぁ否定もできないのですが、リアルな共感が得られない(廊下カンファレンスができない)とか、ちゃんとした理由もあったりします。 僕は必ず前方の席で参加するし、リアクションも他の人と比べるとある方かなーと思っています。 オンラインは、どうしても講義形式になりがちだし、参加している人同士がこういうことに関心があるんだとか、面白いと思ったり感動したりするんだな、ということは得られないな、と思っています。 もちろんチャットやTwitterなど、文字と盛り上がりが共有できたり、ログとして残ったりするんですが、どうしてもWeb会議システムで登壇者の顔、スライドを観ながらチャットも、となると注意力が分散したり、スクリーン数の限界とかにも達するなーと思っています。

目は2つしかない。

社内勉強会きっかけ

さて少し話題は変わりますが、僕は所属している会社でIoT勉強会というのを主催していて、これまでトイレ利用状況可視化サービスToilet Evolution、常駐先などから社員証で出退勤できるデバイスとサービスdakokuといったものを開発してきました。 IoT勉強会はデバイスなんかを作ることもあったので、会社で購入した工具なんかを共有で使ったりしていたわけですが、同様にオンラインとなると難しくなります。 そこで、何とかオンライン時代のサービスをIoTという文脈で作り出せないかな、と思い続けてきて今回の効果音共有サービスを開発するに至りました。 IoTはモノのインターネットですが、オンラインイベントに参加する盛り上がりや人の感情をモノと見立てて、インターネットで共有するのがオンラインイベントでは有益なのでは?と思っています。

pong-swoosh

在宅になって、ずっとFMラジオを聴きながら仕事をしていたりするのですが、DJの人が拍手なんかをサウンドマシーンで出すシーンがありますよね。 あれを ポン出し なんて言ったりします。 今回作ったサービスは、ポン(効果音)を投げる(チャットの文字送信みたいなイメージ)というところから、pong swoosh という名前にしました。 (日本酒が好きだからポンシュ、なんてことじゃないです。)

どんなサービスか

pong-swooshは、Web会議システムを利用しながら効果音を送受信して、盛り上がりを共有するシステムです。 使い方は簡単なので、まずは使ってみましょう。

まずはチャンネルを作成する

チャンネルとは効果音を共有する部屋の単位です。pong-swooshは効果音チャットシステムのようなものなので、チャットでよく使うチャンネルという単語を利用しています。

トップページにアクセスして、チャンネル名を入力して作成します。

そうすると、チャンネルのURLが表示されるので、これを参加者に共有します。 1URLだけなので、イベントサイトだったり、Web会議のチャットだったりで共有できますね。

コピーアイコンで簡単にクリップボードにもコピーできます。

参加者がアクセスする

共有されたURLにアクセスすると、エントランスページが表示されます。

pong-swooshには

  • コントローラー画面(効果音を送信する画面
  • スピーカー画面(効果音を再生する画面

というものがあります。 なぜ画面を分けたか?というのには理由があります。

オンラインでイベントに参加すると、参加者一覧の画面と、スライドなど画面共有された画面の2つを常に表示していると思います。 かなり大きなディスプレイを使っていれば、余裕があるかもしれないのですが、ノートPCとそれほど大きくない外部モニターだと、それぞれが最大表示されていて、画面には余白がないのでは?と思います(私もそうです)。

そこで、コントローラー画面はスマホでアクセスして利用しやすいように設計されています。 スピーカー画面は、最初に音量調整すれば特に前面に表示している必要がないので、Web会議システムと同じPCで開くことが想定されています。 もちろんWeb会議の音と混じるのが嫌な人もいると思うので、どちらもスマホからアクセスしやすいようにQRコードを表示するようにしています。

コントローラー画面

このスクショは、PullRequest中のものなので、この記事を読んですぐ試した方は違う画面になります(近いうちに変わります)。

コントローラー画面には効果音を参加者に共有(送信)するボタンと、実際にどんな音が鳴るのか送信せずに確認する 視聴 リンクがあります。

スピーカー画面

このスクショも、PullRequest中のものなので、この記事を読んですぐ試した方は違う画面になります(近いうちに変わります)。

ボリュームのスライダーがあるので、各自の環境に応じて音量を調整できます。 音量は参加者の人数と同じ効果音を同時(1秒以内)にリアクションした人の数で変化します。 計算式は以下のとおりです。

Math.sin(Math.PI * 90 * (同時にリアクションした人の数 / チャンネル参加者数) /180) * 2;

最初は直線的に音量が上がるようになっていたのですが、途中から円弧に沿って上がるように変更しています。 同時にリアクションした人の数を集計するのに、RedisのINCR/DECRコマンドを使っています。 チャンネル名と効果音IDをキーにINCRで人数を加算して、1秒後にDECRするといったことで音量が変化します。

技術的な話

前にも書きましたが、pong-swooshは効果音チャットシステムのようなものです。 サーバーとの送受信にはsocket.ioが使われていて、効果音が送信されてくるとチャンネル参加者にブロードキャストされます。 簡単なチャットプログラムを作ったことがあれば容易に想像できるシステムです。 バックエンドはNode.jsで書かれていて、現在はHeroku(無料プラン)にデプロイされています。 フロントエンドはSvelteを使っていて、GitHub Pagesにデプロイされています。 バックエンドは手動デプロイですが、フロントエンドはGitHub Actionsでmainにマージされると自動的にデプロイされるようになっています。

勉強会メンバーで開発していますが、ソースコードは公開されています。 https://github.com/its-succ/pong-swoosh

さいごに

pong-swooshは、現在自分が担当しているプロジェクトのスクラムイベントと、会社の部門メンバーが月一で集まるミーティングで利用して改善を重ねてきました。 スプリントレビューのデモや、ふりかえり、プレゼンテーションなどの場で効果音を使ってリアクションを共有しています。

それまでは画面越しに拍手をしてみたり(音は出さず映像で)だったのですが、pong-swooshを使うことで賑やかになったといったフィードバックをもらったりしています。

もちろんこういった共有の方法が合う場面、合わない場面などあるかもしれないのですが、もし興味があれば使ってみてください。 そんなに重い処理もないので、Herokuの無料枠で使ってもらっていても問題ないかな?と思っています。

もし使ってみてフィードバックがあれば、GitHubのissueなどに報告してもらえると嬉しいです。 もちろんPRという形で参加いただくのもWelcomeです。

いつかオフラインで昔のように勉強会やカンファレンスができるときが来ると良いのですが、 もうしばらくオンラインが続きそうな感じでもあります。 そんなとき pong-swoosh が少しでもお役に立てれば幸いです。

PHPerKaigi 2020で「E2Eテストに向き合う」ついて発表してきました

昨年に続いてPHPerKaigiに参加できました。 今年は 2/9(日), 2/10((月), 2/11(祝) の3日間の開催で、私はメインセッションが行われる10,11の2日間参加しました。

2日間の過ごし方は、概ね以下のような感じでした。

  • 2/10
    • 午前中は登壇とAsk The Speaker
    • 午後一はPHPの現場公開収録に参加
    • CI/CDのIRTに参加
    • ルームCで歓談
    • PHPer茶会に参加
  • 2/11
    • ルームCで歓談
    • ランチセッションに参加
    • @hnwさんのZend VMにおける例外の実装に参加
    • コードゴルフやったりアジャイルIRTやったりルームCで歓談
    • 懇親会に参加

ほとんどルームCで誰かと話していた感じですね。 でもPHPerKaigiのコンセプトには合っているはず。

「E2Eテストに向き合う」

E2Eテストに向き合うというタイトルで登壇しました。

Puppeteerについて、昨年も何回か登壇する機会があったのですが、 視点を変えてE2Eテストから見たときの考え方/ツールとしての可能性/有用性を話したかったのと、 テスティングピラミッドの背景みたいなところから自動テストについて理解が深まると良いなという思いでした。

最後のスライドでも紹介したPlaywrightのREADMEに書かれている Q: What about the WebDriver?の 下りは、私が特に感銘を受けているところです。

Webブラウザを使ったE2EテストやUIテストの自動化はWebDriverなしには、 ここまでにはなっていなかっただろうし、そういう意味で「普遍的な標準」であると私も思います。

一方でPWAなど新しい技術、新しいWebの登場により、自動テストが困難になっていることも事実です。

私たちWebに関わるプログラマは、こういった局面においてより多くの事実と事例を積み上げて標準にフィードバックするという姿勢が必要だと思っています。 なぜなら「みんなのインターネット」だから。

これはテスト自動化に関わらず、Webの標準についても同じだと思っています。 HTMLやCSS、はたまたブラウザに新しく追加される機能、どれも最初は少しユースケースが見えないものだったり、 遅くて使い物にならないものかもしれません。 でもその有用性や可能性、必要性がある人が結果を積み重ねることで、少しずつデファクトスタンダートに 近づいていく、これが現在のWebに関する標準の進み方だと思っています。

The Extensible Web Manifestoの宣言から7年。 フィードバックループにおける標準化の流れは現実になってきていると思っています。 いつかWebDriverにも同様のユースケースが取り入れられることを願って、今はそれぞれのWebアプリケーションに適したツールや環境を使い、アプリケーションの品質や、リリースサイクルの効率化ができると良いなと思います。

さいごに

PHPerKaigiは今年も楽しめました。 交流スペースで多くの人と、様々な話題について議論できました。

今年たくさんのノベルティがあって、その中にトレーディングカードがありました。

名札の右に立てて置いているものです。

私はTwitterもアカウント名だし、Publicにいろいろなことやっているからあまり気にならなかったのですが、 Twitterアカウントが実名だったり、写真も実写だったりする人もいますよね。

で、実際イベント期間中に PHPerKaigi の残念なところ(どういう風に利用されてしまうかわからず怖い)として Twitter に書き込みをされている方がいました (私はたまたまイベントのハッシュタグ付きで呟かれてたのをTL上で見たのですが)。

個人的にはトレーディングカード確かに面白いし、名刺代わり?になって便利だなーと思ったけど、 事前にあまり告知されていなかったこともあり、個人情報の利用という観点からはちょっと微妙かもなーと思いました。 申し込み時に拒否できたり、個人情報(写真や名前)が含まれないデフォルトカード?てきなものが選択できても良かったのかも。(とにかくみんな持っている/交換するみたいな雰囲気だったので、持ってないよりは良さそう、カード作成後に申し込みした人はそれに近い扱いだった模様)。

あと、トレカたくさん余っているので、これの使い道もどうしたら良いか考えたいw

M5stack の Soracom 拡張ボードを使って FeliCa リーダーで読み取った値を送信する

昨年 Google Cloud Next Tokyo'19 に出展しました でも書いた 勤怠システムを打刻するシステムですが、どこでも簡単に使えるようなデバイスを作ることにしました。

あたりを使います。FeliCaに関連する部材は、WiFi版のプロト開発でも使っていたので新しいのはM5StackとSORACOM拡張ボードあたりになります。 プロトモジュールはピッチ変換基板を取り付けるのに利用します。

Cloud Nextで展示したWiFi版からの変更として、以下のような作業を行いました。

  • M5Stackでの出退勤画面UI作成
  • M5StackとFeliCaリーダーの疎通
  • M5StackとSORACOM拡張ボードの疎通とインターネット接続確認

まぁこれだけやれば、あとは組み合わせるだけですね!

ハードウェアシリアルをどのように利用するか

3G拡張ボードも、FeliCaリーダーもUARTのポートを使って通信するので、リソースの割り当てを考えないといけません。 3G拡張ボードは M5Stack用3G拡張ボードを使う際、PIN(16,17)を利用するデバイスの対応 の記事でも紹介されているように、16,17固定で それ以外を使うのは困難なため、FeliCa側を

M5StackGPS.begin(9600, SERIAL_8N1, 13, 5)

みたいにして対応予定にしていました。

なぜかうまく動かない

M5Stackのプログラムのコンパイルも通過し、

  • 3G拡張ボードの初期化
  • IPアドレスの取得
  • FeliCaリーダーの初期化
  • FeliCaカードの読み取り

まで動作したのですが、ここでSORACOM Beamに接続しようとするとエラーになりました。

で、ここからATコマンドのデバッグをONにしたり、さまざまなログ出力をしてみたのですが、 さっぱりわからず、2日間ほどかけてしまって心が折れたところで愚痴Tweetしてしまったのです…

そしたら Max@ソラコムさん からレスもらってしまって、大変申し訳なくデバッグを続けることに。

結論としては解決

で、結論としては解決しました。

dakoku-m5.ino というファイルにメイン処理を書いていて、そこで3G拡張ボードの初期化をしていました。

TinyGsm modem(Serial2); /* 3G board modem */
TinyGsmClient ctx(modem);

次に RCS620S.cpp という FeliCa リーダー・ライター RC-S620S のArduinoライブラリのコードをローカルに展開するのですが、そこで以下のようにしていました。

/* --------------------------------
 * Variable
 * -------------------------------- */

HardwareSerial RCS620SSerial(2);

// 省略...

int RCS620S::initDevice(void)
{
    int ret;
    uint8_t response[RCS620S_MAX_RW_RESPONSE_LEN];
    uint16_t responseLen;

    RCS620SSerial.begin(115200, SERIAL_8N1, 22, 21);      // for RC-S620/S

FeliCaリーダーはコンフリクトを避けるために21/22番ポートを利用しています。

懸命な読者の皆様はすぐに気づきますかね。 まぁこの場所だけ抜き出すとすぐ気づくかもしれないのですが、たくさんのコードからこの問題に気づくのにはとても時間がかかりました。

時間がかかった原因

まず

  • 3G拡張ボードの初期化
  • IPアドレスの取得
  • FeliCaリーダーの初期化
  • FeliCaカードの読み取り

の動作が正常に動いたことで、どこが悪いのか検討もつかなくなったというのがあります。 たとえばFeliCaリーダーの初期化が失敗したり、IPアドレスが取得できなかったら、 もっと早く気がついたかも。

処理順番としては、まず3G拡張ボードの初期化が動いて、IPアドレス取得まで成功します。 その後でFeliCaリーダーの初期化が動くので、ここでシリアル2の接続を上書きしてしまいます。

最初はシリアル2は16/17番を向いているのですが、途中で21/22番に切り替わります。 なんですべての通信命令はFeliCaリーダーに送信されて、動かないと…

こんな感じでATコマンドのデバッグを入れていたのですが、まぁATコマンドとしてFeliCaリーダーに送信しているので、原因調査の切り口も良くなかった…

#define TINY_GSM_DEBUG Serial
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>

言い訳

M5Stackはスタックの名前のとおりボードを積み上げていくので、各ボードのポートに電気信号が流れているのか検知が難しいですね。 テスター使ったり、LED光らせるわけにもいかない。

ということで、自分で拡張ボード作るときはM5Stackの穴(ネジ穴?)みたいなところにLED入れて、利用状況を可視化したいなと思いました。

さいごに

RCS620S.cpp のシリアル番号を1に変更してうまく動作しました。 SORACOM BeamからGoogle IoT Coreにもうまく連携できた。これは簡単!(苦労は忘れました

/* --------------------------------
 * Variable
 * -------------------------------- */

HardwareSerial RCS620SSerial(1);

これでどこでもSIMを使った通信で勤怠打刻できるようになった。

Serverless Framework 1.61.3 以上で Alias プラグインが利用できない

Serverless Frameworkのバージョンも dependabot で追従してあげているのですが、 あるとき sls deploy がエラーになってしまいました。

Aliasのエラーが出ていたので、serverless-aws-alias を調査しようとしたら、ビンゴなissueを見つけました。

ServerlessError: Export ‘project-name-ServerlessAliasReference’ does not exist. #181

Serverless Framework 1.61.3 のコミットでAliasプラグインがうまく動かなくなることがあるようです。

私たちのプロジェクトでは 1.61.2 に戻して、dependabotから来るPRにはWIPをつけるようにしました。

どのようなときに問題がおきるかというと、

  • 1つのServerlessアプリケーションに複数のハンドラがある
  • 新しいハンドラが追加になった

ケースです。

複数のハンドラがあるけど、追加がないケースでは新しいバージョンを使っても問題は発生していません。 Aliasプラグイン便利なので、はやく修正されると良いなー(困った人はPRを出そうというのはわかるが、これは難しい問題のようだった

Serverless Frameworkでプライベートパッケージを利用するときの注意点

この記事はDependabotをGHEのプロジェクトに適用するの続編です。 少し前提となる条件があるので、読んでいただけるとスムーズに進みます。

ある日事件は起こった

毎朝出社すると、dependabotから出てくるPRをチェックしてマージしています。 プロジェクトでGHEにプライベートなnpmパッケージを作っていて、前日その更新をしたのでアプリ側にPRが来ていました。 修正内容は自分たちが実施したものなので、何も疑うことはなくマージ。テストも通過していました。

developブランチにマージすると、開発環境へデプロイするようになっているので、そのままデプロイされました。 特に問題はないように見えます。

あのー、サーバーからエラーが戻ってくるんですけど…

目の前にはネイティブアプリチームがいます。

「あのー、サーバーのAPIからエラーが戻ってくるんですけど、何かしました?」

あ、さっきデプロイしたけど、プライベートパッケージ更新しただけだぞ…

エラー通知が飛んでくる

ほどなく監視からエラーが。

{
    "errorType": "Runtime.ImportModuleError",
    "errorMessage": "Error: Cannot find module 'psl'",
    "stack": [
        "Runtime.ImportModuleError: Error: Cannot find module 'psl'",
        "    at _loadUserApp (/var/runtime/UserFunction.js:100:13)",
        "    at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)",
        "    at Object.<anonymous> (/var/runtime/index.js:45:30)",
        "    at Module._compile (internal/modules/cjs/loader.js:778:30)",
        "    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)",
        "    at Module.load (internal/modules/cjs/loader.js:653:32)",
        "    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)",
        "    at Function.Module._load (internal/modules/cjs/loader.js:585:3)",
        "    at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)",
        "    at startup (internal/bootstrap/node.js:283:19)"
    ]
}

psl というnpmパッケージを使っているのですが、それが見つからないよ、と。

この箇所はユニットテストも通過しているし、なんでだろう?と思い、S3にアップロードされているzipを展開すると確かに node_modules に psl は入っていません…

Serverless Framework におけるパッケージング

sls deploysls package を使ってzipを作成するとき、node_modules には dependencies だけが入り、 devDependencies は取り除かれます。 Lambdaなどはzipファイルのサイズに制限があるので、これはとても便利な機能です。

psl の利用箇所を調べるため、 package-lock.json を探してみます。

    "xxxx-node-fetch": {
      "version": "git+https://xxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxxx.com/xxxx/xxxx-node-fe
tch.git#22141d66ca58e25b9c6f9d5907004802f55afcc4",
      "from": "git+https://xxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxxx.com/xxxx/xxxx-node-fetch
.git#22141d66ca58e25b9c6f9d5907004802f55afcc4",
      "requires": {
        "psl": "^1.7.0",
        "winston": "^3.2.1",
        "winston-cloudwatch": "^2.3.0"
      }
    },

これはプライベートパッケージでGHEのリポジトリを参照しています。 これ以外の利用箇所を探してみました。

    "request": {
      "version": "2.88.0",
      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
      "dev": true,
      "requires": {
        "aws-sign2": "~0.7.0",
        "aws4": "^1.8.0",
        "caseless": "~0.12.0",
        "combined-stream": "~1.0.6",
        "extend": "~3.0.2",
        "forever-agent": "~0.6.1",
        "form-data": "~2.3.2",
        "har-validator": "~5.1.0",
        "http-signature": "~1.2.0",
        "is-typedarray": "~1.0.0",
        "isstream": "~0.1.2",
        "json-stringify-safe": "~5.0.1",
        "mime-types": "~2.1.19",
        "oauth-sign": "~0.9.0",
        "performance-now": "^2.1.0",
        "qs": "~6.5.2",
        "safe-buffer": "^5.1.2",
        "tough-cookie": "~2.4.3",
        "tunnel-agent": "^0.6.0",
        "uuid": "^3.3.2"
      },
      "dependencies": {
        "punycode": {
          "version": "1.4.1",
          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
          "dev": true
        },
        "tough-cookie": {
          "version": "2.4.3",
          "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
          "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ=
=",
          "dev": true,
          "requires": {
            "psl": "^1.1.24",
            "punycode": "^1.4.1"
          }
        }
      }

そうすると、 request パッケージが参照する tough-cookie が利用しているのがわかります。 "dev": true, になっているので、devDependenciesに指定されているということですね。

ただ、このようなケースはまぁまぁある(依存しているパッケージによっては dependencies だったり devDependencies であったりする)ので、Serverless Framework で良しなにしてくれるはずです。 実際、昨日までは問題なかったのに??

### DependabotのPRを見る

package-lock.json の変更だけで以下のようなdiffになっていました。

-     "version": "git+https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxx.com/xxxx/xxxx-node-fetch.git#112cf335236d974538ba02993d5ef7bbbbc3ec4c",
-     "from": "git+https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxx.com/xxxx/xxxx-node-fetch.git#develop",
+     "version": "git+https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxx.com/xxxx/xxxx-node-fetch.git#a2ad500e4345206e59d06e9112fab8e1ec7517d1",
+     "from": "git+https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxx.com/xxxx/xxxx-node-fetch.git#a2ad500e4345206e59d06e9112fab8e1ec7517d1",

package.json には "xxxx-node-fetch": "git+https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:x-oauth-basic@github.xxxx.com/xxxx/xxxx-node-fetch.git#develop", のように指定されていて、developから最新を取得するようになっています。

PRでは version 以外にも from#develop のようなブランチ指定から、コミットハッシュ指定に変わっているのがわかります。

dependencies ではブランチ指定をやめる

lockファイルの from 指定が変わり、package.json の dependencies と釣り合いが取れなくなることで、dependencies に必要のないライブラリと Serverless Framework が認識するようで zip ファイルに含まれなくなっていました。

この状態でも npm inpm ci ではパッケージはインストールできるので UnitTest は通過します。

このような事象から、私たちは一旦 package.json に記述するときにブランチ指定をやめて、コミットハッシュ指定に変更することとしました(つまり from を合わせるということです)。 この修正で無事 zip に psl が入って問題を解決することができました。

さいごに

本来であればOSSにできそうなものは普通にnpmにあげるとかした方が良いのですが、調整が困難なのと非常に短期間でプロジェクトが進んでいたのでOSSにはできませんでした。 また、OSSにできないプライベートなパッケージなどを作る場合は、ローカルレジストリ作った方が良いな、とも思いました(ブランチ指定とかコミットハッシュでなく、ちゃんとバージョン管理できるし)。

プライベートなnpmレジストリを作るには、独自の npm registry を使う が参考になりそうです。

また AWS を使っているのであれば、新しいクイックスタートを使用して JFrog Artifactory を AWS にデプロイ という公式記事があるので、参考になりそうです。