プロジェクトの設定ファイル使い回しを支える技術


私はQiitaや[ブログ](https://blog.ojisan.io) に記事を書くときは、なるべく動くサンプルコードを提示するようにしています。そのため記事を書くたびにサンプルコードが増えていき、それを管理しているレポジトリも増えました。

FYI: https://github.com/ojisan-toybox

レポジトリを作る頻度はかなり高いのですが、作るたびに環境構築に時間がかかってしまっており、なるべく早くプロジェクトの環境構築を済ませる方法を考えています。扱う分野によっては儀式的な設定をたくさんする必要があり、毎回セットアップするのは大変です。そこで既存プロジェクトからコピペするわけですが、どのプロジェクトがどの技術で作られていたかは覚えておらず、どれをコピペしたらいいかよくわからなくなりました。そういった環境構築のためのコピペを **労力のかからないシンプルな方法で** 少し効率的にする方法を紹介します。

## template を作成しておく

素早く環境を作る方法として、一つにはGitHubのtemplateを使う方法があります。GitHubにあらかじめテンプレートを登録しておけばそこからある程度設定された状態でプロジェクトを始めることができます。

テンプレートはレポジトリのSettingから作れます。ここでチェックを入れるとそのレポジトリのコードをテンプレートとして呼び出せるようになります。

![Screen Shot 2020-12-21 at 23.38.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89256/83f565cb-2f63-6226-1990-c4bf617766f6.png)

テンプレートはレポジトリを作成するときに呼び出せます。

![Screen Shot 2020-12-21 at 23.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89256/01d0d1e9-972a-6177-fe00-e434f2dfdf68.png)

あらかじめtemplateさえ作っておけば問題は解決しそうです。しかし、実際には templateだけで事が足りることは少なく、大抵の場合は細かい調整などをしていく必要があります。そこで既存プロジェクトから必要な設定だけをコピペできる仕組みも作ります。

## Topics でレポジトリにタグを付ける

既存プロジェクトからのコピペも素早い環境構築には有効な方法です。しかしたくさんプロジェクトを作っているとどのプロジェクトをどのような技術で作ったかを忘れています。たとえばこのような状態からどのレポジトリからコピペしたらいいかを判別できますか。

![Screen Shot 2020-12-22 at 1.35.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89256/23623b89-4b0e-4081-5bdf-3222dae1e29e.png)

リポジトリタイトル名からわかるものもあれば、そうでないものもあります。たとえばここから「TSで書かれていて、GitHub PagesにGitHub Actions経由でデプロイされるコードはどれか」と探せるでしょうか。とても探しにくいと思います。そこで、このようにタグを付けます。

![Screen Shot 2020-12-22 at 1.51.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89256/ab433252-2064-bdac-f7b5-657a9a86cca2.png)

これでどのレポジトリにどのような設定があるかわかるようになりましたね。この場合だと threejs-handle というレポジトリが良さそうです。この設定を使いたければ、あとはここからコピペするだけです。

このタグは レポジトリの Topic から設定できます。

![Screen Shot 2020-12-22 at 14.21.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89256/982b9b80-479b-62bb-cd16-25bbcc7f652c.png)


## インストールコマンドを抜き出すコマンドを作る

コピペで設定を作る事ができるようになりましたが、パッケージ情報まではコピーしたくないです。それはバージョンが古いライブラリが混じっていると脆弱性の問題があるからです。そのため利用パッケージのバージョンだけは最新に保ちたいです。

そこで既存プロジェクトで使われている設定ファイルから、インストールすべきライブラリをインストールするコマンドを生成するCLIを作りました。

それがこの npmingen というコマンドです。名前はnpm install generator の略です。今回はJavaScriptを対象にしていますが、他の言語でも似たようなものは作れると思います。

このコマンドの良いところは**設定ファイルが置かれてあるURLを指定するだけで、そのプロジェクトで使われているライブラリのインストールコマンドを出力できる**ところです。URLを指定するだけなので自分がターミナルでどの階層にいても欲しいライブラリを指定できます。

```sh
$ npmingen https://raw.githubusercontent.com/sadnessOjisan/HelloWorldEnterpriseEdition/master/package.json

npm install react react-dom

npm install -D @babel/cli @babel/core @babel/plugin-transform-react-jsx @babel/plugin-transform-typescript @babel/preset-env @commitlint/cli @commitlint/config-conventional @storybook/addon-actions @storybook/addon-essentials @storybook/addon-links @storybook/react @testing-library/jest-dom @testing-library/react @types/jest @types/react @types/react-dom @typescript-eslint/eslint-plugin @typescript-eslint/parser babel-loader css-loader eslint eslint-config-prettier eslint-plugin-react firebase-tools html-webpack-plugin husky jest lint-staged prettier react-is style-loader typescript webpack webpack-cli webpack-dev-server webpack-merge
```

URL経由でライブラリの一覧を取得する技として、GitHub上の Raw file を使います。たとえば、https://github.com/ojisan-toybox/test-template/blob/main/package.json の Raw は https://raw.githubusercontent.com/ojisan-toybox/test-template/main/package.json?token=ADIT2SZQOPVX3SV7HS4MJFS74BB62 であり、各ファイルにはRawファイルが存在しています。Raw はそのファイルのコードそのものをWeb上から取得できるエンドポイントです。ここにアクセスすることで、dependenciesとdevDependenciesを取得しています。

### JS版

では実装を紹介します。簡単に実行できるものとしてNodeJSのCLIとして用意しました。

```ts
import request from "request";

const url = process.argv[2];
if (url === undefined) {
  console.error("please give me a url.");
}

request(url, (error, response, body) => {
  // エラーチェック
  if (error !== null) {
    console.error("error:", error);
    return false;
  }

  // レスポンスコードとHTMLを表示
  if (response.statusCode !== 200) {
    console.error("invalid status code");
  }

  let bodyJson;

  try {
    bodyJson = JSON.parse(body);
    const dependencies = bodyJson.dependencies;
    const devDependencies = bodyJson.devDependencies;

    if (dependencies === undefined || devDependencies === undefined) {
      console.error("parsed data is not pacage.json");
    }

    const dependenciesArray = Object.keys(dependencies);
    const devDependenciesArray = Object.keys(devDependencies);

    console.log(`npm install ${dependenciesArray.join(" ")}\n`);
    console.log(`npm install -D ${devDependenciesArray.join(" ")}`);
  } catch (e) {
    console.error("body parse error.");
  }
});
```

```sh
npm i -g npmingen
```

```sh
$ npx npmingen https://raw.githubusercontent.com/sadnessOjisan/HelloWorldEnterpriseEdition/master/package.json

npm install react react-dom

npm install -D @babel/cli @babel/core @babel/plugin-transform-react-jsx @babel/plugin-transform-typescript @babel/preset-env @commitlint/cli @commitlint/config-conventional @storybook/addon-actions @storybook/addon-essentials @storybook/addon-links @storybook/react @testing-library/jest-dom @testing-library/react @types/jest @types/react @types/react-dom @typescript-eslint/eslint-plugin @typescript-eslint/parser babel-loader css-loader eslint eslint-config-prettier eslint-plugin-react firebase-tools html-webpack-plugin husky jest lint-staged prettier react-is style-loader typescript webpack webpack-cli webpack-dev-server webpack-merge
```

ソースコード: https://github.com/sadnessOjisan/npmingen-js

### Native版

JS版を作ったものの、ぼくは npm i -g するのがあまり好きではない(nodeのバージョンを変えたら入れ直しになるのが気に食わない)ので、ネイティブコマンドとしても用意しています。

```sh
brew install sadnessOjisan/npmingen
```

もし依存周りで怒られたらビルド済みのバイナリを直接実行してください。ビルド済みのファイルはこちらからもDLできます。

https://github.com/sadnessOjisan/npmingen/suites/1287014341/artifacts/20016240

ソースコード: https://github.com/sadnessOjisan/npmingen

## まとめ

このように素早く環境構築できる仕込みやツールを作っておくことで、モックやサンプルコードを作る効率が大幅に上がりました。いまでは環境構築で悩まなくなったので我ながら良い仕組みを作ったなと思っています。