Let's Encrypt で HTTPS な nginx 構築

おおまかな流れ

nginx を HTTPS 対応するおまかな流れは以下の通りです。

  • Let’s Encript が提供するツール “Certbot” のインストール
  • 上記ツールを利用して SSL/TSL 証明書を取得
  • 取得した証明書を nginx に設定して HTTPS に対応

ちなみに、Let’s Encript のサイトはこちらです。

letsencrypt.org

また、日本語ポータルサイトもあるので、適宜参考にすること。

letsencrypt.jp

Certbot のインストール

基本的には、Certbot 本家サイトに従えばインストールできます。

certbot.eff.org

自分のサイトは、OS:Ubuntu 16.04(Xenial) 、Webサーバ:nginx である。この場合、インストール手順は下記のとおり(本家サイトの説明通り)。

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx 

証明書取得

ここからは、Certbot 本家のサイトに書いてあるのとちょっと違う。なぜなら、nginxサーバが Dockerコンテナの入っていたりゴニュゴニュしているためです。*1

今回は、3つのサーバに対応した証明書を発行してもらいます。具体的には、"-d" オプションで複数のサーバ名を指定すればOK。これについては、以下のサイトが参考になります。

tnamao.hatenablog.com

また、"–standalone" オプションを指定。これにより、Certbot クライアントに内蔵されている簡易的なウェブサーバが機能が一時的に使われるため、自分サイトの nginx は一旦停止しておく必要があります。

ともあれ、実行したコマンドのログを貼り付けておきます。

### 証明書の取得 ###

$ sudo certbot certonly --standalone -d my-domain.jp -d www.my-domain.jp -d app.my-domain.jp

### メールアドレスの指定(初回のみらしい) ###

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel):mysccount@my-domain.jp

### なんか同意する ###

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf. You must agree
in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: N

### 証明書が発行される!! ###

Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for my-domain.jp
tls-sni-01 challenge for www.my-domain.jp
tls-sni-01 challenge for app.my-domain.jp
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/my-domain.jp/fullchain.pem. Your cert
   will expire on 2017-11-11. To obtain a new or tweaked version of
   this certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

ここまで無事に成功したら、"/etc/letsencript/live/[domein name]/“ の下に証明書ファイルが生成されているはず。 ちなみに、証明書はいくつかのファイルがあり、それぞれシンボリックリンクされている。

# cd /etc/letsencrypt/live/my-domain.jp

# ls -l
lrwxrwxrwx 1 root root   44 Aug 13 12:57 cert.pem -> ../../archive/my-domain.jp/cert1.pem
lrwxrwxrwx 1 root root   45 Aug 13 12:57 chain.pem -> ../../archive/my-domain.jp/chain1.pem
lrwxrwxrwx 1 root root   49 Aug 13 12:57 fullchain.pem -> ../../archive/my-domain.jp/fullchain1.pem
lrwxrwxrwx 1 root root   47 Aug 13 12:57 privkey.pem -> ../../archive/my-domain.jp/privkey1.pem
-rw-r--r-- 1 root root  543 Aug 13 12:57 README

nginx 設定

証明書を入手できたので、これを nginx に仕込みます。
具体的には、次の2つを設定すれば OK です。

  • listen のポートを 443 にする
  • ssl_certificate” と ”ssl_certificate_key" に証明書ファイルを指定する

ということで、以下のような感じになりました。

server {
    listen  443 ssl;
    server_name  my-domain.jp www.my-domain.jp;
    ssl_certificate         /etc/letsencrypt/live/my-domain.jp/cert.pem;
    ssl_certificate_key     /etc/letsencrypt/live/my-domain.jp/privkey.pem;

    access_log  /var/log/nginx/default.ssl.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page  404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

以上です。さらに、80番ポートでアクセスされた場合は、443ポートにリダイレクトするような設定を追加するとバッチリかと思います。

今後の運用

Let’s Encript 証明書の有効期限は 90日間なので、今後は、90日ごとに更新手続きを行う必要があります。こちらについても、"Certbot" を使って簡単に実行できますので、そのころまたレポートを追加したいと思います。

*1:ノーマルな nginx サーバであれば、本家 Certbot のサイトに書いてあるように、"sudo certbot –nginx" もしくは “sudo certbot –nginx certonly” とやれば、自サイトの nginx 設定を見ながらいい感じに証明書を発行してくれるようです。

node express + react 開発環境作成手順(自分メモ)

はじめに

サーバアプリは “node” + “express"、クライアントアプリには "React” を使用した場合の開発環境を構築した時の手順を記載します。あくまでも自分用メモです。

大体の構成

  • サーバ:
    ”node" + “express"。

  • クライアント:
    “React"。クライアントのビルドに "webpack"。JSトランスパイラは "babel"。その他 CSS のビルドに "style-loader"、また、ビルドしたReact JSファイルを読み込む index.htmlを生成してくれるプラグインには "html-webpack-plugin” を利用する。

大まかな流れ

  1. node + express-generator をインストール
  2. express 開発環境を作成
  3. react インストール
  4. webpack 導入
  5. babel 導入
  6. style-loader 導入
  7. html-webpack-plugin 導入

サーバアプリ開発環境の構築

1) node + express-generator をインストール

最初に node をインストール。nodebrew を使用すると吉。
続いて、express-generator をグローバルにインストール。

$ npm install express-generator -g

2) express 開発環境を構築

express-generator でサーバアプリ開発環境の雛形を作成。
今回は、HTMLテンプレートエンジンに “pug” を指定。(実際は使用しないかもしれないけど一応あると便利かも)

$  express [app-name] --view=pug

これで、[app-name] ディレクトリ配下に、node + express 開発環境の雛形が生成され、最低限のサーバアプリ開発環境ができた。あとは、自分の必要に応じて、".gitignore" などを作ったり、EsLint のセットアップをすること。

クライアントアプリ開発環境を追加する

3) reacインストール

$ npm install --save react react-dom

4) webpack 導入

$ npm install --save-dev webpack

5) Babel を導入

# webpack から babel を利用する場合の基本モジュール
$ npm install --save-dev babel-loader babel-core

# React をとトランスパイルするモジュール
$ npm install --save-dev babel-cli babel-preset-react

# Es2015 をトランスパイルするためのもの
$ npm install --save-dev babel-preset-es2015

babel の設定ファイル “.babel.rc” を作成

$ echo '{ "presets": ["react", "es2015"] }' > .babelrc
$ cat .babelrc

{ "presets": ["react", "es2015"] }

ここで、babel で js をトランスパイルするための webpack 設定をおこなう。

$ vim webpack.config.js

ここまでの webpack.config.js の中身は次のとおり。

var path = require('path');

module.exports = {
  entry: './client/src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'public')
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
};

6) style-loader 導入

$ npm install --save css-loader

webpack.config.js に下記の loader を追加。

module: {
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: ['style-loader', 'css-loader'],
      },
    ],
  },

7) html-webpack-plugin 導入

$ npm install -save-dev html-webpack-plugin

index.html の元となるテンプレ “client/template/index.ejs” を作成。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id='app'></div>
  </body>
</html>

あとは、webpack.config.js に html-webpack-plugin の設定を追加する。

  plugins: [
    new HtmlWebpackPlugin({
      title: 'ngzm uploader',
      template: './client/template/index.ejs',
    }),
  ],

まとめ

最終的な webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './client/src/App.jsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'public'),
  },
  devtool: 'inline-source-map',
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: ['style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'ngzm uploader',
      template: './client/template/index.ejs',
    }),
  ],
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

なお今回は、"webpack-dev-server" は利用していませんが、これも便利そうなので次回は導入したいと思います。

参考

以上。

Karabiner-Elements - 左右commandキー単独をかな英数キーにGUIで設定できるようになってた!

はじめに

MacOS Sierra になってそれまでお世話になった Karabiner さんが動かなくなっていろいろ試行錯誤がありました。本ブログでも、2016年12月に、当時最もナイスな方法と思われた wwwjfy さんの「Stand Alone Key」PR版を使う方法をご紹介したりしました。

ngzm.hateblo.jp

そしてついに、本家 Karabiner-Elements で 左右Commandキー単独でかな英数切り替えがサポートされたということで、さっそく試してみましたよ。

Karabiner-Elements のセットアップ方法とか

今回インストールしたのは Karabiner-Elements 0.91.5 です。
本家サイトの Usage にて、大変丁寧なインストール方法や設定方法が書いてあるため、基本的なセットアップ作業はそれに従えばぜんぜん大丈夫と思います。

github.com

待望の機能は「Complex Modifications」というものです。これにより、複数キーの組み合わせをあるキーにマッピングしたり、逆に、あるキーを複数のキーにマッピングする Many to Many Mapping であるとか、キーを単独で押した場合だけ、別のキーに割り当てる Stand Alone Key の機能が使えるようになりました。

なるほど、これで「左右Command」キーを単独で押した場合に「かな」や「英数」にマッピングすることができるわけですね。

しかも試してみて分かったのですが、全てGUI画面で設定できるようになっていました!
とっても簡単ですね。ありがたや。

私の設定メモ

参考までに、私が行った大まかな設定内容をメモっておきます。

1. キーボードの種類を設定

私は日本語キーボードを使用しているので、Karabiner-Elements の Virtual Keyboard 画面で、キーボードタイプを JIS に変更します。なお Karabiner-Elementsデフォルトは ANSI (英語)キーボードです。

f:id:ngzm:20170708174923p:plain

2. 英数キーを左Commandキーに変換

私は「英数」キーと「左Command」 キーを良く打ち間違えるのでこの設定をします。「そんなの間違えないよ!」「だせえ」とお思いの方々は、本設定は不要です。これは Karabiner-Elements の Simple Modifications 画面で次のように設定します。

f:id:ngzm:20170708175843p:plain

3. Command キー単独で日本語切り替え

この設定が今回のメインです。Karabiner-Elements の Complex Modifications 画面で、あらかじめ用意された Complex Modifications ルールから適当なものを選択するだけで OKです!

左下の「Add Rule」ボタンをおしてあらかじめ用意された Complex Modifications ルールを表示します。

f:id:ngzm:20170708182148p:plain

あいにく Example にはCommandキーと日本語切り替えのものは無かったので、上の「Import more rules from the Internet」から Karabiner-Elements のサイトに用意されているルール集から探してみます。

すると「For Japanese (日本語環境向けの設定)コマンドキーを単体で押したときに、英数・かなキーを送信する。(左コマンドキーは英数、右コマンドキーはかな)」というルールがありましたので、こいつをインポートします。

f:id:ngzm:20170708182743p:plain

これで Karabiner-Elements に インポートしたルールが表示されます。「Enable」ボタンを押してさっさと有効にしちゃいます。

f:id:ngzm:20170708192111p:plain

成功です。コマンドキーを単体で押したときに、英数・かなキーを送信する、のルールが有効になりました!

f:id:ngzm:20170708183457p:plain

これで設定は完了です。無茶簡単ですね。キーボード入力してみて問題がないかどうか試してみてください。

4. Karabiner-Elements 設定ファイル

Karabiner-Elements の設定内容は、~/.config/karabiner/karabiner.json というファイルに保存されます。

参考までに、今回設定した内容が、どのように karabiner.json に定義されているか? ということで、以下にその内容を記載して終わりです。

{
    "global": {
        "check_for_updates_on_startup": true,
        "show_in_menu_bar": true,
        "show_profile_name_in_menu_bar": false
    },
    "profiles": [
        {
            "complex_modifications": {
                "parameters": {
                    "basic.to_if_alone_timeout_milliseconds": 1000
                },
                "rules": [
                    {
                        "description": "コマンドキーを単体で押したときに、英数・かなキーを送信する。(左コマンドキーは英数、右コマンドキーはかな)",
                        "manipulators": [
                            {
                                "from": {
                                    "key_code": "left_command",
                                    "modifiers": {
                                        "optional": [
                                            "any"
                                        ]
                                    }
                                },
                                "to": [
                                    {
                                        "key_code": "left_command"
                                    }
                                ],
                                "to_if_alone": [
                                    {
                                        "key_code": "japanese_eisuu"
                                    }
                                ],
                                "type": "basic"
                            },
                            {
                                "from": {
                                    "key_code": "right_command",
                                    "modifiers": {
                                        "optional": [
                                            "any"
                                        ]
                                    }
                                },
                                "to": [
                                    {
                                        "key_code": "right_command"
                                    }
                                ],
                                "to_if_alone": [
                                    {
                                        "key_code": "japanese_kana"
                                    }
                                ],
                                "type": "basic"
                            }
                        ]
                    }
                ]
            },
            "devices": [],
            "fn_function_keys": {
                "f1": "display_brightness_decrement",
                "f10": "mute",
                "f11": "volume_decrement",
                "f12": "volume_increment",
                "f2": "display_brightness_increment",
                "f3": "mission_control",
                "f4": "launchpad",
                "f5": "illumination_decrement",
                "f6": "illumination_increment",
                "f7": "rewind",
                "f8": "play_or_pause",
                "f9": "fastforward"
            },
            "name": "Default profile",
            "selected": true,
            "simple_modifications": {
                "japanese_eisuu": "left_command"
            },
            "virtual_hid_keyboard": {
                "caps_lock_delay_milliseconds": 0,
                "keyboard_type": "jis"
            }
        }
    ]
}

15年前に作ったアプリを React + Sinatra でリメイクしました

昔々、、ひょんなことから作った人工無脳アプリを、最近の Web アプリ風に作り直してみました!

ということで、この開発内容を以下のスライドにまとめましたので、よかったらご覧ください。

www.slideshare.net

せっかくスライド作ったので、いつもお世話になっている kawasaki.rb で LT させてもらいました。

medium.com

kawasaki.rb の皆さんありがとうございます。

React Router v4 でルーティング先の component に Props を渡す方法

「React Router v4 でルーティング先の component に Props を渡す方法」について調査したので記事にします。

基本的なReact Router v4 の基本的なルーティング

React Router v4 の基本的なルーティングは次のような感じでコーディングします。

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render((
  <BrowserRouter>
    <AppFrame />
  </BrowserRouter>
  ),
  document.getElementById('root'),
);

class AppFrame extends Component {
  render() {
    return (
      <main>
        <Switch>
          <Route path='/login'   component={Login} />
          <Route path='/getbook' component={GetBooks} />
          <Route path='/addbook' component={Addbook} />
        </Switch>
      </main>
    );
  }
}

今回は “component={xxxx}” で起動する React component に props を渡す方法を考えます。

React component に props を渡す

変数を props として渡す方法

上記 /getbook の URI でルーティングされる GetBook component に、ある変数(ここでは書籍ID(bookID)とします) を props として渡したい!となった場合、、その時は、以下のように書くと良いでしょう。

class AppFrame extends Component {
  render() {
    this.bookId = 2;    // とりあえず仮に書籍ID = 2 と設定
    return (
      <main>
        <Switch>
          <Route path='/login' component={Login} />
          // GetBook component に、bookID を props として渡します 
          <Route
            path='/getbook'
            render={props => <GetBook bookId={this.bookId} {...props} />}
          />
          <Route path='/addbook' component={Addbook} />
        </Switch>
      </main>
    );
  }
}

“component” ではなく “render” を使うところがミソですね。

function を props として渡す方法

続いて、/login の URI でルーティングされる Login component に、認証処理を行う function auth() を props として渡たしたい!となったらどうするか?

同じく render を使用して渡してあげれば大丈夫です。

class AppFrame extends Component {
  auth(userInfo) {
    // 何かの認証処理
  }
  render() {
    return (
      <main>
        <Switch>
          // Login component に、認証処理を行う function auth() を props として渡します 
          <Route
              path='/login'
              render={props => <Login onSubmit={userinfo => this.auth(userinfo)} {...props} />}
          />
          <Route path='/getbook' component={GetBooks} />
          <Route path='/addbook' component={Addbook} />
        </Switch>
      </main>
    );
  }
}

参考となった情報

いろいろ調査しましたが、特に、下記の Github issue が参考になりました。様々なアプローチが議論されており見るだけで勉強になります。

github.com

まとめ?

最近、React Router が V4 になっていろいろ変わって大変だ!との嘆きを耳にしました。ですが、React 初心者である私は「へー」とか「ふーん」と実感も伴わずに聞き流していましたが、最近、実際にコードを書く段になって「大変」の意味がわかりました。

今回の調査もそうでしたが、とにかくまだ情報が少なく、あっても英語なことが多いので、どうしても時間がかかってしまします。また、v4に関する情報かどうかを見極めるのも(特に初心者は v2 や v3 の情報と混同しがち)以外に大変だったりしました。

ということで「英語は重要だ!」というまとめを以って終了です。

React 始めたので vim の jsHint を ESLint に変更した話

いまさらですが、React 書き始めました。すると、これまで利用していた jsHint ではエラーや警告が誤検出されるので、この対策をした時のメモになります。

React なら ESLint ですわ

React は JSX など独特な書き方がありますし、これからは ES6 になりますので、これらをうまく解釈できるものはないか? と調査すると jsHint で JSX をチェックする JSXHint というラッパーがありました。

GitHub - STRML/JSXHint: Wrapper around JSHint for linting JSX files. 100% compatible with existing tools using JSHint.

よし、これを試すか、、と思ったらのっけから

Don’t use JSXHint anymore if you can switch to ESLint.

ということで、これまでお世話になった jsHint を辞めて ESLint に Switch しましょう。どうも現代では、React で書くなら ESLint を使うのが当たり前のようです。*1

ESLint セットアップ

私の開発環境(MAC OS Sierra)に ESLint を導入します。

eslint.org

導入方針

ESLint についていろいろ調べたところ、グローバルにインストールするのではなく、アプリケーション毎にローカルにインストールして、それぞれ専用の設定をして使用することができるようです。確かに、React 使う時や Angular 使う時やその他でコンフィグレーション内容が大きく変わりそうなので、ローカルにインストールする方が理に適っています。

また、ESLint がローカルにインストールされていれば、それをグローバル的に利用可能とする “eslint-cli” というツールを発見、便利そうなのでこれも併せて導入します。

www.npmjs.com

(1) ESLint をローカルにインストール

前提として node.js がインストールされている必要があります。ちなみに、私の場合は、nodebrew で利用する node.js のバージョンを選択できるようにしています。

$ node -v
v7.8.0

$ npm -v
4.2.0

以下のようにローカルに ESLint をインストールします。今回 React が前提なので eslint-plugin-react も必要となります。

$ npm install --save-dev eslint eslint-plugin-react

ちなみにインストールした ESLint のバージョンは 3.19.0 でした。

$ eslint -v
v3.19.0

(2) ESLint 初期設定

ESLint を使用するには、予めルールなどを定義した設定ファイルを作成する必要がありますが、自作するのは結構メンドくさそうです。ですが最近では “eslint –init” で簡単に設定ファイルを作成できるようです。

というか、本家の Getting Start のページを見ると、むしろこの手順で初期化することが前提となっていますね。

Getting Started with ESLint - ESLint - Pluggable JavaScript linter

さっそくやってみましょう。
今回は対話(質問に答える)形式で初期化する方法で実行してみました。

$ ./node_modules/.bin/eslint --init
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? No
? Do you use JSX? Yes
? Do you use React? Yes
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JavaScript
Successfully created .eslintrc.js file in /Users/naoki/DEV/SANDBOX/react

成功です。下記のようにESLint の設定ファイル “.eslintrc.js” ができています。

$ cat .eslintrc.js

module.exports = {
    "env": {
        "browser": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        },
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
};

(3) eslint-cli をグローバルにインストール

ここで、冒頭で説明した “eslint-cli” をインストールしておきます。これで、わざわざ “./node_module/.bin/eslit” とせずに “eslint” だけで実行できるようになります。

$ mpn install eslint-cli -g

(4) ESLint の動作確認テスト

ESLint 実行環境が整ったので動作確認します。テスト対象としたコードは、React Tutorial で作成した “index.js” です。

$ eslint src/index.js

/Users/naoki/DEV/SANDBOX/react/src/index.js
    5:10  error  'Square' is defined but never used            no-unused-vars
    6:3   error  Expected indentation of 4 spaces but found 2  indent
   13:7   error  'Board' is defined but never used             no-unused-vars
   14:3   error  Expected indentation of 4 spaces but found 2  indent
   15:5   error  Expected indentation of 6 spaces but found 4  indent
   17:3   error  Expected indentation of 4 spaces but found 2  indent
   18:5   error  Expected indentation of 6 spaces but found 4  indent
   40:7   error  'Game' is defined but never used              no-unused-vars
   41:3   error  Expected indentation of 4 spaces but found 2  indent
   42:5   error  Expected indentation of 6 spaces but found 4  indent

   ... 省略 ...

  128:5   error  Expected indentation of 6 spaces but found 4  indent
  129:5   error  Expected indentation of 6 spaces but found 4  indent
  130:7   error  Expected indentation of 8 spaces but found 6  indent
  133:3   error  Expected indentation of 4 spaces but found 2  indent

✖ 48 problems (48 errors, 0 warnings)

ご覧の通りたくさんのエラーが出てしまいました。よく見ると、そのほとんとがインデント数が変だと言っていますが、これは誤検出です。むしろ設定ファイルのタブ数の設定に問題がありますので “.eslintrc.js” の “rules.indent” を 4 から 2 に変更します。

   "indent": [
      "error",
      2
   ],

再度実行してみます。

$ eslint src/index.js

/Users/naoki/DEV/SANDBOX/react/src/index.js
   5:10  error  'Square' is defined but never used  no-unused-vars
  13:7   error  'Board' is defined but never used   no-unused-vars
  40:7   error  'Game' is defined but never used    no-unused-vars

✖ 3 problems (3 errors, 0 warnings)

だいぶマシになりましたが、3箇所エラーが検出されてしまいました。うむむ。
こちらは ES6 のプログラム的には問題ない(never used となっていますが JSXから参照しています)ので、やはり ESLint の誤検出で、設定に問題があります。

(5) no-unused-vars 問題を解決する

こちらに解決策がありました。ありがたや。

ESLintでReactを扱う - Qiita

また、eslint-plugin-react のページでも、no-unused-vars を prevent するルールについての記述がありますね。

eslint-plugin-react

ということで、次のルールを “.eslintrc.js” に加します。

  "rules": {
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
  }

これで再実行して Lint エラー(誤検出)が全て無くなりました。OKです。

vim のチェッカを jsHint から ESLint に変更

最終的な目的は、vim で自動的に Lint チェックしてもらうことなので、.vimrc の設定を変更します。

具体的には、下記の通り “let g:syntastic_javascript_checkers” の値を “jshint” から “eslint” に変更するだけです。

let g:syntastic_mode_map = { 'mode': 'active',
  \ 'active_filetypes': [],
  \ 'passive_filetypes': [] }
let g:syntastic_javascript_checkers = ['eslint']
let g:syntastic_check_on_wq = 0

これで、vim で編集する javascript の コードは、ESLint でチェックすることが可能となりました。これでとりあえずの作業は全て完了です。

まとめ

  • React なら ESLint を使え
  • ローカルインストールすれば、アプリケーション毎に専用の設定ができる
  • ESLint の設定ファイルを一から自作するのはちょっと大変だが、"eslint –init" でこの作業を簡単にすることができる

今回はこれくらいです。 これからは ES6 必須ですし、しばらくは ESLint のお世話になりそうです。ということは、さらにきめ細かな設定方法や、様々なツールやエディタとの連携方法なども習得していく必要がありますね。

*1:世間の速さに追いついていないです。キャッチアップしないとだめです

Ruby で カウントダウン Twitter BOT 作って Scaleway の Docker で動かす- 後半(完成まで)

大事な日までのカウントダウンを毎日 tweet してくれる BOTRuby で作成して Scaleway サーバで動かそうと思います。こんな感じでつぶやきます。

前半のおさらい

このエントリは、先日書いた以下の記事の続きです。

ngzm.hateblo.jp

前半は下記のことを行ないました。

  1. カウントダウン Twitter BOTRuby で作成しました
  2. Scaleway に Docker コンテナが動く仮想サーバを立てました
  3. 上記仮装サーバの Docker 上で Ruby:latest コンテナが動くことを確認しました。

今回はその続きで、実際に Scaleway の Docker で動かすまでの話です。

カウントダウン Twitter BOT の Docker コンテナを作成

1) Docker Ruby イメージについて

まず最初は、今回の Docker コンテナのベースとなる Ruby イメージについて、その基本的な使い方や構成などを確認しておく必要があります。これらは、 DockerHub の Ruby ページ で確認できます。

どうも Ruby アプリケーションは /usr/src/app/ にデプロイして動かせば良いようです。

2) Dockerfile などコンテナ環境の準備

さて、Twitter BOT アプリケーション用の Docker イメージを作成するための Dockerfile を作成します。

今回、この Docker ファイルは、Twitter BOT アプリケーション(countdown_bot.rb など)と同じディレクトリに作成しました。

FROM ruby:latest
MAINTAINER ngzm<nk.ngzm@gmail.com>
ADD . /usr/src/app
RUN cd /usr/src/app && bundle install
RUN cd /usr/src/app && chmod u+x countdown_bot.sh
CMD ["/usr/src/app/countdown_bot.sh"]

countdown_bot.sh というのはこのアプリを起動するだけの簡単なシェルスクリプトです。次のような感じです。これもアプリケーションと同じディレクトリに保存します。

#!/bin/bash
cd /usr/src/app
if [ $? -eq 0 ]; then
    ruby countdown_bot.rb
fi

なお、作成した Docker ファイルや、起動スクリプト countdown_bot.sh は、アプリケーションコードと同様、github のページ に置いてありますので適宜参照してください。

3) Docker コンテナを作成

準備が完了したので、docker build で Twitter BOT アプリケーション用の Docker イメージを作成します。

# docker build -t countdown .

Sending build context to Docker daemon 68.61 kB
Step 1 : FROM ruby:latest
 ---> d2cee8adb148
Step 2 : MAINTAINER ngzm<nk.ngzm@gmail.com>
 ---> Using cache
 ---> 276ca2d970a3
Step 3 : ADD . /usr/src/app
 ---> f7a2c4909d42
Removing intermediate container 8730da1e725f
Step 4 : RUN cd /usr/src/app && bundle install
 ---> Running in 7895f7170efc
Fetching gem metadata from https://rubygems.org/........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Installing public_suffix 2.0.5
Installing buftok 0.2.0
Installing unf_ext 0.0.7.4 with native extensions
Installing equalizer 0.0.11
Installing multipart-post 2.0.0
Installing http-form_data 1.0.1
Installing http_parser.rb 0.6.0 with native extensions
Installing thread_safe 0.3.6
Installing naught 1.1.0
Installing simple_oauth 0.3.1
Using bundler 1.14.6
Installing addressable 2.5.1
Installing unf 0.1.4
Installing faraday 0.11.0
Installing memoizable 0.4.2
Installing domain_name 0.5.20170404
Installing http-cookie 1.0.3
Installing http 2.2.1
Installing twitter 6.1.0
Bundle complete! 1 Gemfile dependency, 19 gems now installed.
Bundled gems are installed into /usr/local/bundle.
 ---> 6842c8ee3be4
Removing intermediate container 7895f7170efc
Step 5 : RUN cd /usr/src/app && chmod u+x countdown_bot.sh
 ---> Running in b14e152beca3
 ---> b581ef596c71
Removing intermediate container b14e152beca3
Step 6 : CMD /usr/src/app/countdown_bot.sh
 ---> Running in 28768d88b292
 ---> 40c6fe50a43f
Removing intermediate container 28768d88b292
Successfully built 40c6fe50a43f

成功です。Twitter BOT アプリケーションファイルをコピーして、bundle install もうまく行っていますね。docker images で確認すると、きちんとDocker イメージが生成されていました。

続いて、 Docker イメージから、Docker コンテナの作成 + 起動を行います。

# docker run --name countdown_bot countdown

エラーなく終了しました。ここで Twitter を見てみると、無事につぶやきが投稿されていました。OKです。カウントダウン Twitter BOT の Docker コンテナができました。

cron で定期的に実行させる

ここまできたら、後は、カウントダウン Twitter BOT コンテナを、定期的に自動実行できれば良い訳です。とりあえず、今回は、cron に仕込んで実行させることにします。

# crontab -e

0 9 * * * docker start countdown_bot

私の生活リズム都合で、毎朝 9時につぶやいてもらうように設定しました。

その後追記

と言う訳で、翌日の朝9時になりました。が、その時間になっても一向につぶやきませんでした。むむむ。

どこか不具合があったかぁ、、と考えたら、はっと問題点に気づきました。私がセットしたのは確かに 9:00AM ですが、それは日本時間ではありません。そうです Scaleway サーバは Paris にあるのです。早速サーバにログインして、タイムゾーンを確認すると UTC でした。なるほど。

ということで対策を行います。今回は、せっかくUTCで動いているので、タイムゾーンはそのままで、Twitter BOT アプリケーションの起動時間を -9時間するように cron を設定し直しました。

# crontab -e

3 0 * * * docker start countdown_bot

なお、ジャスト0時に起動するのもなんなので*1、0時3分に起動するようにして完了です。翌日 9時3分に無事につぶやきを確認してとりあえずおしまいデス。

まとめ

  • Scaleway の VC1s + Docker で Ruby アプリケーションが割と簡単に動かせる
  • 海外のサーバは JST じゃない(当然ですが)ので気をつけろ
  • 毎朝 9時3分にこのつぶやきを見ながら、その日に向かってしっかり頑張ろう!!

*1:経験的にこのような境界値に思わぬ問題が潜んでいることが多いので、今回は簡単に少しだけずらしてリスクを回避します