Technology Engineering

178inaba の技術ブログ

LinuxにNode.js入れる時はPermissionのためにnvm使ったほうがいい

前回の記事ではyumでNode.jsをインストールしたが、それはServerlessをインストールしようと思ったためであった。

しかし、Node.jsのインストール後、Serverless公式に書いてあったコマンド
npm install serverless -g
を実行したらPermissionで怒られた。

npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
npm ERR!  { Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/local/lib/node_modules' }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.

Permissionで怒られたのでsudoつけて実行したら以下のようなエラーが出た。

┌───────────────────────────────────────────────────┐
│          serverless update check failed           │
│        Try running with sudo or get access        │
│       to the local update config store via        │
│ sudo chown -R $USER:$(id -gn $USER) /root/.config │
└───────────────────────────────────────────────────┘

インストールはできていてコマンドは使えるようだったが、このようなエラーを放置するのはちょっと気持ち悪いのでnpmのPermission周りについて色々調べた。

すると以下のページを発見。

Resolving EACCES permissions errors when installing packages globally

ここの『Reinstall npm with a node version manager』に以下の記述がある。

This is the best way to avoid permissions issues.

node version managerでnpmをインストールする事がPermission問題を回避する最善の方法らしい。
そのままリンクを辿っていくとversion managerの紹介がされていた。

Using a Node version manager to install Node.js and npm

ここでnとnvmが紹介されていたのだが、nvmの方がスター数が多かったため今回はnvmを選択した。

nvmのインストール方法は割愛する。
以下nvmレポジトリの Installation を参照してください。

github.com

nvmのインストール後、 nvm install node で最新のNode.jsがインストールされる。
その後Serverlessをインストールするとエラーは出なかった🎉

まとめ

LinuxにNode.js入れる時はPermissionのためにnvm使ったほうがいい!

Amazon Linuxで古いNode.jsがインストールされる時の解決方法

Amazon Linuxで何度Node.js v11をインストールしようとしてもv6が入ってしまう現象が起こったのでその解決策をメモ。

現象

v11用RPMのセットアップをする。

$ curl -sL https://rpm.nodesource.com/setup_11.x | sudo bash -

その後、yum installしても 2:6.14.4-1nodesource がインストールされる。

$ sudo yum install -y nodejs
...
Installed:
  nodejs.x86_64 2:6.14.4-1nodesource

解決策

以下のコマンドでRPMのキャッシュを消す。

$ sudo yum remove -y nodesource-release* nodejs
$ yum clean all
$ sudo rm -rf /var/cache/yum/*
$ sudo rm /etc/yum.repos.d/nodesource-el.repo

その後、再度RPMセットアップコマンドを流してyum installすればよい。

$ curl -sL https://rpm.nodesource.com/setup_11.x | sudo bash -
...
$ sudo yum install -y nodejs
...
Installed:
  nodejs.x86_64 2:11.1.0-1nodesource

v11が入りました。

参考

github.com

削除済みリモートブランチを追跡しているローカルブランチを1発で全削除するコマンド

リモートブランチはマージ後GitHubが削除するか聞いてくれるのでその時点で削除するようにしている。

f:id:i178inaba:20181103173338p:plain

だが、そのリモートブランチを追跡しているローカルブランチの削除を忘れて溜まっていることがよくある。

$ git branch -v
* develop           fef1cdd Merge pull request #30 from abcdef/fix/bazbarfoo
  feature/foo       e57408f [gone] Fix deploy
  feature/bar       a9b73ad [gone] Upload image
  feature/baz       b833113 [gone] Fix test
  feature/foobar    703e2ad [gone] Add foobar.c
  feature/foobaz    1587581 [gone] Fix foobaz.rb
  feature/barfoo    37265cb [gone] Add barfoo.php
  feature/barbaz    3c121ca [gone] Fix barbaz.py
  feature/bazfoo    cb69767 [gone] Add bazfoo.go
  feature/bazbar    bb8575f [gone] Fix bazbar.pl
  feature/foobarbaz 66d2807 [gone] Fix README.md
  fix/foobazbar     84a781e [gone] Add gitignore
  fix/bazfoobar     cd36a6b [gone] Fix ci
  master            0a41ce8 Merge pull request #20 from abcdef/develop

[gone] が付いているのがリモートブランチへの追跡が切れているローカルブランチ。

いつも git branch -d の後ろに1つずつブランチ名をコピペして削除していた。

$ git branch -d feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar
Deleted branch feature/foo (was e57408f).
Deleted branch feature/bar (was a9b73ad).
Deleted branch feature/baz (was b833113).
Deleted branch feature/foobar (was 703e2ad).
Deleted branch feature/foobaz (was 1587581).
Deleted branch feature/barfoo (was 37265cb).
Deleted branch feature/barbaz (was 3c121ca).
Deleted branch feature/bazfoo (was cb69767).
Deleted branch feature/bazbar (was bb8575f).
Deleted branch feature/foobarbaz (was 66d2807).
Deleted branch fix/foobazbar (was 84a781e).
Deleted branch fix/bazfoobar (was cd36a6b).

面倒なので1発コマンド打つだけで全部消えるコマンドを組もうと思い調べた。

コマンド

git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)' | xargs git branch -d

解説

最初は git branch コマンドを使って作ろうとしていた。
だが、先の記事にも書いた通り、 git branch を使ってスクリプトを書かないほうがいいらしい。

inaba.hatenablog.com

なので git for-each-ref を使って書いた。
これはGitレポジトリの各refの情報を条件に従って出力していくコマンド。

Git - git-for-each-ref Documentation

何点か解説を書いてみる。

ブランチ名

ブランチ名は %(refname:short) で取得。
short をつけないと refs/heads がついてしまい、ブランチ名として認識されないので注意が必要。

# `refs/heads` がついてしまう。
$ git for-each-ref --format '%(refname)'
refs/heads/develop
refs/heads/feature/foo
refs/heads/feature/bar
refs/heads/feature/baz
refs/heads/feature/foobar
refs/heads/feature/foobaz
refs/heads/feature/barfoo
refs/heads/feature/barbaz
refs/heads/feature/bazfoo
refs/heads/feature/bazbar
refs/heads/feature/foobarbaz
refs/heads/fix/foobazbar
refs/heads/fix/bazfoobar
refs/heads/master
refs/remotes/origin/HEAD
refs/remotes/origin/develop
refs/remotes/origin/feature/aaa
refs/remotes/origin/feature/bbb
refs/remotes/origin/ccc
refs/remotes/origin/master
refs/remotes/origin/try/ddd
refs/remotes/origin/try/eee

# shortをつければ正しいブランチ名が取得できる。
$ git for-each-ref --format '%(refname:short)'
develop
feature/foo
feature/bar
feature/baz
feature/foobar
feature/foobaz
feature/barfoo
feature/barbaz
feature/bazfoo
feature/bazbar
feature/foobarbaz
fix/foobazbar
fix/bazfoobar
master
origin/HEAD
origin/develop
origin/feature/aaa
origin/feature/bbb
origin/ccc
origin/master
origin/try/ddd
origin/try/eee

gone

削除対象は [gone] が付いたブランチなのでこれを取得する。

upstream の項で以下のように説明されている。

:track also prints "[gone]" whenever unknown upstream ref is encountered.

試すと

$ git for-each-ref --format '%(upstream:track)'

[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]
[gone]








[gone] のみでは分かりづらいと思うのでブランチ名をつけてみる。

$ git for-each-ref --format '%(upstream:track) %(refname:short)'
 develop
[gone] feature/foo
[gone] feature/bar
[gone] feature/baz
[gone] feature/foobar
[gone] feature/foobaz
[gone] feature/barfoo
[gone] feature/barbaz
[gone] feature/bazfoo
[gone] feature/bazbar
[gone] feature/foobarbaz
[gone] fix/foobazbar
[gone] fix/bazfoobar
 master
 origin/HEAD
 origin/develop
 origin/feature/aaa
 origin/feature/bbb
 origin/ccc
 origin/master
 origin/try/ddd
 origin/try/eee

リモートへの追跡が切れていない develop, master やリモートにのみ存在しているブランチには [gone] がついておらず、リモートへの追跡が切れたローカルブランチにのみ [gone] がついている事がわかる。

if

git for-each-ref ではif文( %(if)…​%(then)…​%(end) )が使える。
if文を使い、 [gone] があるときだけブランチ名を表示するようにする。

$ git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)'

feature/foo
feature/bar
feature/baz
feature/foobar
feature/foobaz
feature/barfoo
feature/barbaz
feature/bazfoo
feature/bazbar
feature/foobarbaz
fix/foobazbar
fix/bazfoobar








xargs

取得できたブランチ名をxargsを使って git branch -d に流す。 ちなみにxargsだけ実行すると git branch -d にどんな値が渡っているのかわかる。

$ git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)' | xargs
feature/foo feature/bar feature/baz feature/foobar feature/foobaz feature/barfoo feature/barbaz feature/bazfoo feature/bazbar feature/foobarbaz fix/foobazbar fix/bazfoobar

こんな感じ。

別解

サブコマンドとして実行してもよい。

git branch -d $(git for-each-ref --format '%(if)%(upstream:track)%(then)%(refname:short)%(end)')

$(git branch)でカレントディレクトリのファイル一覧も出てしまう時の対処法

とあるシェルでgitのブランチ一覧を取りたくて echo $(git branch) と書いたのだが、なぜかファイル一覧も出てしまい戸惑った。

前提

下記のようなディレクトリがあって、それがgit管理されていたとする。

$ ls
README.md   main.go     main_test.go

そして以下のようなブランチが存在する。

$ git branch
  develop
  feature/bar
  feature/baz
  feature/foo
* master

事象

echo $(git branch) を実行すると下記のようにブランチ一覧の間にファイル一覧が差し込まれてしまう。

$ echo $(git branch)
develop feature/bar feature/baz feature/foo README.md main.go main_test.go master

原因

勘のいい人はもうお分かりかもしれませんが、 git branch した時にmasterの前に * がついていますよね。
それが echoワイルドカードとみなされてファイル一覧が出てしまいます。

$ echo *
README.md main.go main_test.go

と同じになるわけです。

対処法

ダブルクオートで囲めばワイルドカードが展開されずに出力されます。

$ echo "$(git branch)"
  develop
  feature/bar
  feature/baz
  feature/foo
* master

ブランチ一覧を1行で取得したい場合

tr を使って * をスペースに変換したリストをダブルクオート無しで echo すれば取得可能です。

$ echo $(echo "$(git branch | tr '*' ' ')")
develop feature/bar feature/baz feature/foo master

でも、そもそも git branch を使ってスクリプトを書かないほうがいいらしい。

stackoverflow.com

上記によれば、Gitはスクリプトで使用するために明示的に設計されたplumbingインターフェースを提供しているとのこと。
なので自分はplumbingインターフェースを使ってみようと思います。

参考

stackoverflow.com

特定のディレクトリ内にあるjavascriptを全てMinifyする

例えば static/js ディレクトリにjsが置いてあるとする。

$ tree
.
└── static
    └── js
        ├── bar.js
        ├── baz.js
        └── foo.js

そのディレクトリ内にあるjavascriptを全てMinifyしたい場合のbashスクリプト

for f in $(ls static/js/*.js); do
  uglifyjs -cm -o "${f/.js/.min.js}" "${f}"
done

Minifyには uglify-js を使用する。
オプションの c が単純なソース圧縮、 m が変数名の圧縮(1文字にする)、 o はアウトプットするファイルを指定。

これを minify.sh というファイル名で作成し、実行する。
注意点としては ${f/.js/.min.js}bashの文字列置換を使用しているのでbashで実行すること。

$ bash minify.sh

すると全てのjavascriptがMinifyされる。

$ tree
.
├── minify.sh
└── static
    └── js
        ├── bar.js
        ├── bar.min.js
        ├── baz.js
        ├── baz.min.js
        ├── foo.js
        └── foo.min.js

ちなみにMinifyしたjavascriptをgit管理したくない場合は .gitignore*.min.js を入れると無視してくれる。

もうひと工夫

シバンを入れて実行権限をつけるとスッキリして良い。
実行権限をつけるのでファイル名も minify にすると綺麗かも。

#!/bin/bash

for f in $(ls static/js/*.js); do
  uglifyjs -cm -o "${f/.js/.min.js}" "${f}"
done
$ mv minify.sh minify
$ chmod u+x minify
$ ./minify
$ tree
.
├── minify
└── static
    └── js
        ├── bar.js
        ├── bar.min.js
        ├── baz.js
        ├── baz.min.js
        ├── foo.js
        └── foo.min.js

参考

CircleCIのYAMLの仕様が変わった

CircleCIを使っている。
今週水曜(2018年10月10日)なぜか本番デプロイができなくなっていた。

調査すると設定を書いているYAMLの仕様が変更されたようで、キーの上書きができなくなっていた。

元々の仕様

デプロイにはFabricを使用している。

fab dev deploy で開発環境、 fab prod deploy で本番環境にデプロイされるように設定ファイルを書いている。

references:
  env_dev: &env_dev
    environment:
      ENV: dev

  env_prod: &env_prod
    environment:
      ENV: prod

jobs:
  deploy_dev: &deploy
    docker:
      - image: circleci/python:3.6
        environment:
          AWS_DEFAULT_REGION: ap-northeast-1
    steps:
      - checkout
      - run:
          name: Install python package
          command: sudo pip install -r requirements.txt
      - run:
          name: Deploy
          command: fab $ENV deploy
    <<: *env_dev

  deploy:
    <<: *deploy
    <<: *env_prod

Anchor(&name)とAlias(*name)を使用してコンパクトに書いている。

CircleCIは各jobページのConfigurationタブで展開されたYAMLを確認することができる。

f:id:i178inaba:20181014050410p:plain

展開されたYAMLを確認すると下記のようになっている。

jobs:
  deploy_dev:
    environment:
    - ENV: dev
    steps:
    - checkout
    - run:
        name: Install python package
        command: sudo pip install -r requirements.txt
    - run:
        name: Deploy
        command: fab $ENV deploy
    docker:
    - image: circleci/python:3.6
      environment:
        AWS_DEFAULT_REGION: ap-northeast-1
  deploy:
    environment:
    - ENV: prod
    docker:
    - image: circleci/python:3.6
      environment:
        AWS_DEFAULT_REGION: ap-northeast-1
    steps:
    - checkout
    - run:
        name: Install python package
        command: sudo pip install -r requirements.txt
    - run:
        name: Deploy
        command: fab $ENV deploy

ちゃんと environmentENV が上書きされて展開されているのがわかります。

仕様変更後

水曜日、本番デプロイのCIが失敗することで仕様変更を知る。
仕様変更後に展開されたYAMLを確認すると deploy jobの ENV が上書きされず、 dev のままになってしまっていた。

jobs:
  ...
  deploy:
    ...
    environment:
    - ENV: dev

Twitterでつぶやかれている方も居た。

修正

AnchorとAlias周りがおかしいのではないかという事はすぐわかったので、以下のように修正を行った。

   deploy:
-    <<: *deploy
-    <<: *env_prod
+    <<: [*env_prod, *deploy]

元々は *deploy を先に書き、そこに *env_prod を上書きするという意図で書いていた。
しかし、上書きされないため書く順番を逆にし、 *env_prod を先に書き、 *deploy を後に書いた。
この時、Aliasの書き方もCircleCIで推奨されている [] を使用した書き方に変更した。

すると下記のように以前の想定通り展開されました。

jobs:
  ...
  deploy:
    environment:
    - ENV: prod
    docker:
    - image: circleci/python:3.6
      environment:
        AWS_DEFAULT_REGION: ap-northeast-1
    steps:
    - checkout
    - run:
        name: Install python package
        command: sudo pip install -r requirements.txt
    - run:
        name: Deploy
        command: fab $ENV deploy

そして無事デプロイされるようになった。

ブログ再始動

お久しぶりです。

178inabaです。

久しぶりにブログを書いています。

最近、技術書典で技術書を販売した方のブログを見ました。

shu223.hatenablog.com

このブログを見て

(自分もいつかは本が書けたら楽しいだろうな)
(沢山の人が来場していて交流できるし、報酬も得られる!)

などと思い、

でも本を出すにはまず、休止状態のブログを再始動して技術的な記事とか書くべきだろうと思いこのブログを書いています。

Kyash始めてみた

Kyashは同僚と食事に行った時に割り勘代金を渡すために入れてみたのですが便利です。

上の技術書典のブログを読んだときに、KyashのAndroidエンジニアのこにふぁーさんのブログを思い出しました。

konifar.hatenablog.com

このこにふぁーさんのブログには投げ銭の話が書いてあって、上の技術書典の話と 『技術情報の発信で報酬を得る』 という所で通ずるなと思いました。

なので真似してKyashのIDとQRコードを貼ってみます。

kyash_id: 178inaba

f:id:i178inaba:20181012005104j:plain

今後

今後は定期的に技術情報をブログに書いていけたらと思います。
よろしくです!