selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

TypeScriptで書いたcliのnpm packageをリリースする方法

背景

人生初のnpm packageをリリースしました。

画像を所定のフォーマットで圧縮してくれるCLIです。現在はシンプルに画像を圧縮するだけですが、おいおい未圧縮の画像を検知する機能であったり、gitのpre-commitにhookして自動で圧縮する機能などを追加していこうと考えています。なお、このpackageはTypeScriptで書かれています。

本日のブログでは、npm packageをリリースする方法、TypeScript(altJS 全般で使えそう)でnpm packageを作る際の方法、npm packageでcliを作る際の注意点について記載していきます。

なお、今回の npm package が僕の初めてのリリースですので、間違った部分や至らない部分があるかもしれません。そこを意識して見て頂ければ幸いです。誤りなどありましたら、コメントを頂けると喜びます!

npm packageをリリースする

シンプルな npm packageであれば、下記の手順を踏めば簡単に npm packageをpublishすることが可能です。

  • npmjs にユーザー登録する
  • npm config にnpmjsに登録したユーザー情報を設定する
  • npm の2要素認証設定をする
  • npm packageをpublishする

以降、図を使いながらより詳細に説明していきます。

npmjsにユーザー登録する

https://www.npmjs.com/signup

上記URLにアクセスして npmjs.com に自分のユーザーを作ります。説明どおりにやれば大丈夫です。

f:id:selmertsx:20180111192236p:plain

npm configにユーザー情報を設定する

terminal から npm login と叩いて、npmjs に登録したユーザー情報を入力します。 問題なく設定できてたら、npm whoami と叩くと自分のユーザー名が返されます。

$ npm login
Username: ${yourName}
Password:
Email: (this IS public) ${yourEmail}
Logged in as shuhei.morioka on https://registry.npmjs.org/.
$ npm whoami
${yourName}

参考URL: Redirecting…

2要素認証をする

自分のpackageに悪さされないように、2要素認証も入れておきましょう。

Requires npm version 5.5.1 or greater

2要素認証は、 バージョンが5.5.1以上のnpmから対応されているため、必要であればnpmのバージョンを上げましょう。

$ npm -v
3.10.10
$ npm update -g npm
/nodepath/node/v6.10.3/lib
└── npm@5.6.0
$ npm -v
5.6.0
$ npm profile enable-2fa

するとバーコードがterminal上で表示されるので、表示されたバーコードを使って2要素認証の設定ができるようになります。

参考URL: Redirecting…

npm packageをpublishする

$ npm publish --access public (--otp xxxx)

上記コマンドで作成したpackageをリリースすることができます。ただし2要素認証の設定をしていた場合、--otpのあとにotpcodeを入力することを忘れないで下さい。

TypeScriptでnpm packageを作る

nodejsのシンプルなコードであれば、上記の手順でnpm packageをpublishすることができます。ただしTypeScriptなどのaltJSを使って開発をしている場合、build後のコードはGitでは管理しないけれども、packageには含めたくなります。

gitでは管理していないコードをpackagesに含めるための方法は2つあって、.npmignoreを使う方法と、packages.jsonのfiles fieldを利用する方法です。ざっくり書くと .npmignoreはブラックリストであって、packages.jsonのfiles fieldはホワイトリストになります。確認が容易なものではないので、何を入れるのか明示的に指定したほうが事故がないと判断し、今回は packages.json の files fieldを採用しました。

packages.json の files field

ioptにおける files field の設定内容は下記のように、たった2行になります。

# packages.json
  "files": [
    "build",
    "bin"
  ],

iiopt/package.json at master · speee/iiopt · GitHub

files fieldはデフォルトで、package.json、README、CHANGES、LICENSE、NOTICE 等のファイルを含めます。なので、ここに記載するコードは少なくてすみます。

https://docs.npmjs.com/files/package.json#files

altJSを使った npm packageをリリースする上で、必要だった作業は僕がやった限りこれだけでした。

npm packageでCLIを作る上での注意点

CLIツールのコマンドは、参考資料に示すように bin fieldに実行コードへのパスを指定し、yarn linkすることによって使えるようになります。

# packages.json
  "bin": {
    "iiopt": "./bin/cli"
  },
$ yarn link
yarn link v1.3.2
success Registered "@speee/iiopt".
info You can now run `yarn link "@speee/iiopt"` in the projects where you want to use this module and it will be used instead.
✨  Done in 0.14s.

yarn linkを実行した際に、/usr/local/bin/iiopt に対して、bin fieldsで指定したコードのパスへのリンクを貼ることで、コマンドが使えるようになります。

$ ls -l /usr/local/bin/iiopt
/usr/local/bin/iiopt -> /path_to_iiopt/iiopt/bin/cli

その他

packages.jsonの整理や抜け漏れチェックには fixpackが便利だった

nodejsで開発をしていると、packages.jsonの要素の順番がごちゃごちゃしてきたりします。 また初めてのnpm package開発だと、packages.jsonに何を記載すれば良いのか分からないと思います。 fixpackは、その問題に対する解決方法になります。

$ fixpack
missing main
package.json already clean!

上が、fixpackの実行結果になります。packages.jsonの中に記載されていない情報があれば、通知してくれます。また、中の要素もfixpack指定の順番に並べてくれます。

最後

以上、npm packageをリリースする方法を記載しました。

参考URL

公式ドキュメント以外で参照したページです。お世話になりました。

2018年の抱負 (4月まで)

2017年は、ふわっと目標を定めたので、ふわっと終わってしまいました。2018年は、もう少し具体的に目標を定めて、言語化し、達成してきたいと思っています。

目標のざっくり全体像

2018年は、一言で言うとエンジニアとしての基礎力を上げるということをテーマとしてやっていこうと思います。より具体的に言うと、下記の内容です。

  • 自らがサービスで利用しているコアな技術に関して、選定理由を明確に語ることができる
    • どの条件が変われば、他の技術の方に軍配が上がるのかも説明できる
  • サーバーサイドエンジニアの領域に関して、現在のトレンドを幅広く把握している
  • 英語での読み書きが開発に支障無い範囲内で行える

具体例で言うと、ゆううきさんみたいに、深い技術的な理解に基づいた文章が書けるエンジニアにあこがれています。

もうちょっと具体的な目標

  • 継続してキャッチアップするもの
  • ちゃんとやってみるもの
    • serverless
    • nodejs
  • 一通り触ってみるもの
    • docker & k8s
    • react
  • その他

4月までの詳細な目標

とりあえず、4月までの目標をざざっと書いてみます。 細ければ細かいほど良いと思うので、思いつく限り書きまくります。 OKRの考え方にもとづいて、全体の70%くらい実現できたらいいなと。 業務のことはとりあえず、置いといてます。

  • serverlessについて語れるようになる
    • https://martinfowler.com/articles/serverless.html これ読んでブログにまとめる
    • micro service について把握する
      • serverlessはmicro serviceからの流れを組むものだと思っているので、その流れなどを語れるようになる
    • https://github.com/selmertsx/serverless-prpolice これを作り切る
      • PR policeを AWS SAM Localを使ってリプレイスしたもの
    • serverless-prpoliceに関して、エラー監視をできるようにする
    • serverless-prpoliceに関して、トラフィック監視をできるようにする
    • serverless-prpoliceに関して、適切なデプロイ戦略を考えることができる
    • これらの知識をまとめて技術書展で本を出す
  • 英語頑張る
    • 1日1時間は勉強する
    • 1日1時間半勉強する
    • TOEIC 受ける
    • TOEIC 700点
    • TOEIC 800点
  • その他
    • ふつうのLINUXプログラミングを読了する
    • ふつうのLINUXプログラミングをブログにまとめる
    • 週2~3本はブログ書く
    • 週1本は、ちゃんと頭を使い切ったブログを書く

どうして serverlessを選んだのか

個人的には serverlessは将来、webサービス開発における選択肢として、十分に挙がってくるものになると考えています。

今のserverlessでは、コードをコンテナにデプロイし、使い終わったら破棄するという仕組みのため、大きなコードを用意してしまったらデプロイに時間が掛かってしまい早いレスポンスが返せない。サービスが拡大したとき、functionの関連性を管理することが難しい。などなどデメリットも多くあります。

しかしながら、AWSを見ていると大きなメモリやコードを許容できるようにする動きもありますし、awslabにて expressフレームワークで作った webサービスをlambdaで動かす実験的なコードも生まれてきました。 GitHub - awslabs/aws-serverless-express: Run serverless applications and REST APIs using your existing Node.js application framework, on top of AWS Lambda and Amazon API Gateway

今ある serverlessの欠点の多くは、仕組み以上に、エコシステムに起因する部分が大きいのではないかと感じていて、AWSはそこの解決に力を入れているように見えています。そこをやりきった後には、今とは違う形の serverlessの世界が見えてくるのでは無いかと思ってちゃんとキャッチアップしてみることにしました。

今のところまだまだ感覚で話してはいますが、3ヶ月後はこれについて自信を持って話せるようになりたいですな。 それでもって、良い所感を持てたら、この分野でOSS等にコミットしていきたいと思ったり思わなかったり。

後半力尽きましたが、2018年前半の抱負はこんな感じでいこうと思います!

2017年棚卸し

今の会社のエンジニアの人と、2017年の棚卸しをしようじゃないかという話しになりまして、この記事にまとめました。

2017年の目標

2017年は、下記3点の目標を持って仕事していました。

  • Ruby以外の言語でもサービスを作れるようにする
    • 2016年当時は、Ruby以外は正直なところちょいちょい摘む程度だった
  • AWSのインフラを使って、サービスを自分1人で作れるようにする
    • 2016年当時、ほとんど触ったことが無かった
  • OSSに関して、ちょっとばかり参加する
    • 2016年時点では、gemを3つ作ったくらい

Ruby以外の言語に関してはnodejsでサービスを1つ作りましたし、AWSについてもちょいちょい勉強会でお話しさせて頂くことができました。また、OSSについては、自作のOSSを一つ作ったのと、ドキュメント修正をちょいちょいやったりしました。

※ 自作OSS

※ ドキュメント修正 (一部抜粋)

読んだ技術書

他にも10冊くらい読んだ気がするのですが、読んでる最中に飽きてしまって読みきれませんでした。 上にあるのは、ちゃんと読み切ったやつです。上からおすすめ順になってます。

ビジネス書

サービスとして何を作るべきか悩んでいた時期に、このあたりの本を読んだりしていました。 上2つについては、まだ手元に置いてます。

発表

AWS Startup tech meetup

speakerdeck.com

【開催報告】第10回 AWS Startup Tech Meetup | Amazon Web Services ブログ

Rails Developers Meetup

speakerdeck.com

Rails Developers Meetup #3 (東京会場) - connpass

JAWS UG night

speakerdeck.com

ASCII.jp:JAWS-UG NightはパネルやLT、クイズまで欲張らナイト!|JAWS-UG広報ユカリンの部屋

備考

2017年おすすめ本です。よかったらポチってください :pray:

JestにおけるbeforeAllの実行タイミングについて

TL:DR

Jestにおいて、describe内外にbeforeAllを複数用意した場合の評価タイミングを確認した。その結果、beforeAllは上から順番に実行されていることが分かった。

モチベーション

beforeAllを上書き指定できれば、綺麗に書けるテストを書いていた。 そのため、beforeAllの優先順位や実行順序などが気になって検証した。

実行内容

コード

beforeAll(()=>{
  console.log("out of describe");
});

describe('sample describe 1', () => {
  beforeAll(()=>{
    console.log("inside of describe 1");
  });

  describe("sample describe 2", () => {
    beforeAll(() => {
      console.log('inside of describe 2');
    });

    test('sample test', () => {
      expect('hoge').toEqual('hoge');
    });
  })
});

ターミナル

$ yarn test __tests__/test.spec.ts
yarn run v1.3.2
$ yarn build && yarn jest __tests__/test.spec.ts
$ yarn tsc
$ /path_to_dir/node_modules/.bin/tsc
$ /path_to_dir/node_modules/.bin/jest __tests__/test.spec.ts
 PASS  __tests__/test.spec.ts
  sample describe 1
    sample describe 2
      ✓ sample test (5ms)

  console.log __tests__/test.spec.ts:2
    out of describe

  console.log __tests__/test.spec.ts:6
    inside of describe 1

  console.log __tests__/test.spec.ts:10
    inside of describe 2

というように、上から順番に実行されていることが分かった。

parameter storeで環境変数管理したら便利だった

モチベーション

GitHubで管理できないパラメータの管理。めんどうですよね。昔は.envrc.localで管理していたんですけれども、チームで開発するときは、ちょっといじると周りに共有しなければならなくて手間でした。そうしなくて済む方法をawsparameter storeで作ってみました。

Parameter Storeってなんだ

AWS のサービスの一つで、パスワード、データベース文字列、ライセンスコードなどのデータをセキュアに扱うためのものです。

Systems Manager パラメータストア - Amazon EC2 Systems Manager

Parameter Storeへのデータ設定方法

下記のコマンドを叩いて、parameter storeにデータを突っ込んでいきます。

aws ssm put-parameter --name SlackToken --type String --value xxxxx 
aws ssm put-parameter --name IAMRole --type String --value xxxxx 

put-parameter — AWS CLI 1.14.16 Command Reference

コード

#!/bin/sh
SlackToken=(`aws ssm get-parameters --names SlackToken --query "Parameters[*].{Value: Value}"  --output text`)
IAMRole=(`aws ssm get-parameters --names IAMRole --query "Parameters[*].{Value: Value}"  --output text`)

echo "export SlackToken=${SlackToken}" >> .envrc.local
echo "export IAMRole=${IAMRole}" >> .envrc.local

ホントなら一回のリクエストで済ませたいのですが、複数パラメータを扱おうとすると、jsonを扱ったりする必要があって、ちょい手間なのでこんな感じですませちゃいました。

あと、.envrc.local を .gitignoreに追加しておきましょう。

実行

bin/setup.sh

を実行すると、下記のように .envrc.local が生成されます。

export SlackToken=xxxx
export IAMRole=xxxxx

という感じで、簡単にパスワード管理ができるようになりました。

おひとりさま開発基盤グループでの業務の進め方

はじめに

この記事は Speee Advent Calender の23日目の記事です。

こんにちは!ロボット大好き系エンジニア、開発基盤グループ所属の森岡です。 ガンダムF91とクロスボーン以外はほぼ全て視聴済み、フロントミッションも携帯ゲーム以外は全てコンプリート。フロントミッションアーマードコアの最新作をずっと待ち続けています。好きなMSはリックドムです。

さて、僕が所属している開発基盤グループですが、グループという名前が付いているものの、実際に所属しているのは現在森岡1人だけで、完全におひとりさまグループとなっております。この記事では、1人で開発基盤グループを進めていくためにやっていることについて、ご紹介させていただきます。

開発基盤グループのお仕事

開発基盤グループは Speee全社におけるwebサービスの技術的な品質向上 をミッションとして業務を行っています。具体的な業務としては、下記のようなものが挙げられます。

  • 全社的に利用されているDatadog, Sentry, Sidekiq Proなどのツールの導入・管理
  • 開発に関するガイドラインの作成
  • 全社のサービス品質を向上するための各種ツールの作成

tech.speee.jp ※ 1部、Speee Cafe Meetupでお話しさせていただきました

現在、このような業務を進めている開発基盤グループですが、過去は異なるミッションを持って仕事を進めていました。

"全社的な開発効率の向上"から"サービスの技術的な品質向上"へ

先月(2017年11月)まで、開発基盤グループのミッションは、全社的な開発効率の向上でした。 そのミッションに従って、僕たちはRevieeeという確認環境の自動構築ツールを作っていました。

tech.speee.jp

しかしながら、このツールは現在開発を停止しています。その理由は下記の通りです。

  • 人の異動などで、恩恵を受けられるプロダクトのエンジニアが少なくなった
  • AWSのインフラが大きな役割を持つwebサービス増え、Revieeeでそれらをカバーすることが難しくなった

また、弊社で採用している言語、フレームワークが多様化してきたこともあり、Revieeeのような Railsに特化した確認環境構築ツールは、現在のSpeeeにおいて 多くのエンジニアに価値があるとは言い難い ものになってしまいました。

このように開発効率の向上というミッションは、採用する技術や、そのときの人の配置に依存する部分が大きく、開発基盤グループが1人という現状では達成することが難しいものでした。そこで、2017年11月にミッションの見直しを開始しました。

新しく作るミッションは、人の入れ替わりや利用している技術に依存せずに価値を出せる必要があります。それが何かと考えたときに、弊社が運用している 全てのサービスの技術的な品質向上 が適切であると考えました。

このミッションを進めていく手順は下記のようになっています。

f:id:selmertsx:20171222204619p:plain

これだけでは具体的なイメージが湧かないと思うので、現在行っている業務内容を例に説明させていただきます。

画像圧縮の全自動化

現在、開発基盤グループでは、Speeeにおける全てのwebサービスにおいて、ユーザーにとって最適な画像を配信する ことを目的として、デザイナーやフロントエンドエンジニアの人たちと一緒に、その仕組みづくりを進めています。

なぜ 画像の最適化 を最初の施策として選んだのかと言うと、現状パフォーマンスの向上に最も寄与し、画像を用いる全てのサービスにおいて有効で、ページの表示速度を早くしたいという事業の意図とも合致するものであったからです。 画像の最適化 ができる仕組みを作るために、下記の内容で仕事を進めています。

  1. 画像取扱ガイドラインの作成
  2. サイトの評価ツールの作成
  3. 画像を自動圧縮するツールの作成
  4. CIでの未圧縮画像検知

画像取扱ガイドライン

ガイドラインの作成にあたっては、Googleの資料を参考にさせて頂いています。

Image Optimization  |  Web Fundamentals  |  Google Developers

ガイドラインには下記の項目を記載しています。

  • JPG、PNGSVG、GIFの選定基準
  • JPG、PNGの圧縮ツールおよび、圧縮レベル

Googleの資料においては、PNGではなく WebPの使用を勧めていますが、現状の対応ブラウザ状況を見ると、WebPへの完全移行を進めることは難しい状態にあります。そのあたりは僕たちで判断しガイドラインの作成をしております。その詳細については、またどこかのタイミングで発表させていただこうと思います。

未圧縮画像の検知ツールの作成

さて、ガイドラインが出来たら次に必要になってくるのは、僕たちのサイトの評価ツールです。 評価ツールは、slackのslash commandがインターフェースとなっており、下記のような方法で利用できます。

f:id:selmertsx:20171223184619p:plain

実行すると、slack上で改善点や、改善した場合にどの程度容量を圧縮できるか教えてくれます。 slackを利用した理由は、エンジニアだけでなく全ての職種の人に結果を見てほしかったからです。

f:id:selmertsx:20171223185232j:plain

この機能は AWS API GatewayAWS Lambdaを使って実現しています。 Lambdaの中で headless Chromeを使ってページを閲覧し、分析を行っています。 詳細については、また別の機会に説明させていただきます。 もう少し調整が出来たら、OSSとして公開しようかなと考えています。

その他、開発予定のツールについて

ここから先はまだ未着手のものですので、構想だけのお話しになります。 ここまでで、ガイドライン、そして評価ツールを作りました。 次に必要になってくるのは、手軽に画像を圧縮できるツールと、未圧縮の画像がコミットされたときに、それを検知できるCIツールです。rubocopもrspecも、CIで実行されるようにならなければ、きっとこれほど価値を出すことができなかったでしょう。

画像の圧縮については、Gitフックを使って実装しようと考えています。コミット時に自動で圧縮されるので、特に意識せずとも圧縮した画像をGitHubで扱えるようになるかと思います。

WE ARE HIRING

以上、「お一人様開発基盤グループでの業務の進め方」について記載させて頂きました。まだまだ歩き初めたばかりのミッションなので、荒削りな部分や修正するべき点などもあるかとは思います。人が増えて、ミッションの形も変わってくることもあるかもしれません。

開発基盤グループでは、全社的なサービスの品質向上に一緒に取り組んでくれる、またはより良いミッションについて一緒に考えてくれる、そんなエンジニアを絶賛募集中です!!!ご興味を持って頂けた方がいましたら、ぜひwantedlytwitterなどで連絡をください!ご連絡お待ちしております!

www.wantedly.com https://twitter.com/

selmertsx (@selmertsx) | Twitter

24日目の記事は、iida-hayato より、「MercariARハッカソンで優勝したので技術的なまとめ」です!

おまけ

この取組みに関する考え方は SRE本の4章から拝借しました。

parcelでd3jsを扱うことが出来ない (v1.2.1 で解決済み)

エラー内容

➜  image-guideline git:(d3js) ✗ yarn parcel jpg_report.html
yarn run v1.3.2
$ /Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/.bin/parcel jpg_report.html
⏳  Building...
Server running at http://localhost:1234
🚨  /Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/xmlhttprequest/lib/XMLHttpRequest.js: Could not statically evaluate fs     at evaluate (/Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/parcel-bundler/src/visitors/fs.js:164:11)
    at CallExpression.path.get.map.arg (/Users/shuhei_morioka/project/dev_opt/design-guideline/image-guideline/node_modules/parcel-bundler/src/visitors/fs.js:31:21)

原因

🐛 d3 import: Could not statically evaluate fs call · Issue #298 · parcel-bundler/parcel · GitHub

npm libraries can specify alternative main fields in their package.json, we resolve the "module" and "jsnext:main" in priority of "browser" to get the full dependency tree. libraries like d3.js specifies node.js specific files in the "main" which breaks the build

対処方法

Resolver.jsを直接書き換えて回避した。

# parcel-builder/src/Resolver.js

    return resolver(filename, {
      filename: parent,
      paths: this.options.paths,
      modules: builtins,
      extensions: extensions,
      packageFilter(pkg, pkgfile) {
        // Expose the path to the package.json file
        pkg.pkgfile = pkgfile;

        // npm libraries can specify alternative main fields in their package.json,
        // we resolve the "module" and "jsnext:main" in priority of "browser" to get the full dependency tree.
        // libraries like d3.js specifies node.js specific files in the "main" which breaks the build
        const main = pkg.module || pkg['jsnext:main'] || pkg.browser;

        if (main) {
          pkg.main = main;
        }

        return pkg;
      }
    });

追記

現在では、下記のPRがマージされ、v1.2.1 でリリースされているので、問題無く扱うことができる。

Resolve "module", "jsnext:main" and "browser" before "main" by fathyb · Pull Request #299 · parcel-bundler/parcel · GitHub Comparing v1.2.1...master · parcel-bundler/parcel · GitHub