dockerでsinopiaを試したメモ

`docker`と`sinopia`を使ってnpmのprivate repositoryが使えるか検証したメモです。

## まとめ

* dockerを使うことで簡単に導入できる
* 設定の変更がうまくいかない
* sinopiaの開発状況には注意が必要

### sinopia

- sinopia
  - https://github.com/rlidwka/sinopia
- docker-sinopia
  - https://github.com/kfatehi/docker-sinopia

## setup

dockerを事前にインストールしておきます。docker machineとmacの組み合わせで試しています。

### docker run

docker hubにあるものをそのまま使います。
https://hub.docker.com/r/keyvanfatehi/sinopia/

sinopiaの起動portには4873が割り当てられています。ここでは8000番へport fowardします。

```bash
$ docker run --name sinopia -d -p 8000:4873  keyvanfatehi/sinopia
Unable to find image 'keyvanfatehi/sinopia:latest' locally
latest: Pulling from keyvanfatehi/sinopia
...
```

しばらく待つとinstallが終わって起動します。psとimagesで確認します。

```bash
$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                    NAMES
42ac6fc4c4d6        keyvanfatehi/sinopia   "/opt/sinopia/start.s"   12 minutes ago      Up 12 minutes       0.0.0.0:8000->4873/tcp   sinopia

$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
keyvanfatehi/sinopia   latest              05e4f7416026        6 months ago        704.2 MB
```



### 動作確認

curlを使って、レスポンスが来ることを確認します。

```bash
$ curl $(docker-machine ip default):8000
<!doctype html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <title>Sinopia</title>
...
```

ブラウザからもアクセスして確認します。

![スクリーンショット 2016-04-09 17.47.08.png](https://qiita-image-store.s3.amazonaws.com/0/542/82ceb2f5-45bf-a4fd-5db0-cb0130e3a86c.png)

pacakgeがなにもない状態ですが、動いていることが確認できます。

### 

## npm コマンドを試す

### `registry`の切り替え

ブラウザからアクセスした際に、ヘッダに表示されているコマンドを使って、npmの`registry`を切り替えます。

```bash
$ npm set registry http://192.168.99.100:8000

# 設定を確認
$ npm config list
...
registry = "http://192.168.99.100:8000/"
```

### `user`の追加

pacakageをpublishするために、registoryに`user`を追加します。
初期設定では、sinopia側の制限が何もないので自由に追加できます。

```bash
$ npm adduser
Username: test
Password:
Email: (this IS public) test@example.com

# ログインユーザを確認
$ npm whoami
test
```

ブラウザ上の右上のボタンからログインすることもできます。

![スクリーンショット 2016-04-09 18.22.41.png](https://qiita-image-store.s3.amazonaws.com/0/542/9c9eb50a-3b38-1506-aa8d-41efa2fa297c.png)

アクセス制限が何もない状態ですと、名前の部分の表示が変わるだけのようです。

### packageの`publish`

pacakgeを適当に作成して、reositoryへ`publish`します

```bash
$ mkdir sinopia_test
$ cd sinopia_test
$ npm init
...

$ npm publish
+ sinopia_test@1.0.0
```

ブラウザから追加されていることを確認できます。

![スクリーンショット 2016-04-09 20.47.00.png](https://qiita-image-store.s3.amazonaws.com/0/542/d1f2d3c7-b98e-5868-bf8f-0dd35975e6fb.png)

同じバージョンのままpublishを行うと拒否されます。

```bash
$ npm publish
...
npm ERR! publish fail Cannot publish over existing version.
npm ERR! publish fail Update the 'version' field in package.json and try again.
npm ERR! publish fail
npm ERR! publish fail To automatically increment version numbers, see:
npm ERR! publish fail     npm help version
````

バージョンアップすることで再度`publish`できます

```bash
$ npm version patch
v1.0.1

$ npm publish
+ sinopia_test@1.0.1
```

npm本家とは違い、readme.md以外の情報はブラウザ上で閲覧できないようです。

![スクリーンショット 2016-04-09 20.59.50.png](https://qiita-image-store.s3.amazonaws.com/0/542/715cd4b4-e576-58f3-2719-8ecb70826910.png)

### install

resigryに登録された物以外を`install`するとき、`sinopia`がproxyとして動作します。

```bash
$ npm install null --save
```

動作は通常のものと変わりありませんが、node_modules内のpacakge.jsonを確認すると`sinopia`経由で`install`されたのが確認できます。

```bash
$ vi node_modules/null/package.json
{
  "name": "null",
  "version": "0.0.0",
  "dependencies": {},
  "devDependencies": {},
  "_id": "null@0.0.0",
  "engines": {
    "node": "*"
  },
  "_engineSupported": true,
  "_npmVersion": "1.0.1",
  "_nodeVersion": "v0.4.5",
  "_defaultsLoaded": true,
  "dist": {
    "shasum": "4744b3fb86f3fd1b455feac414a4f50f82a3d06d",
    "tarball": "http://192.168.99.100:8000/null/-/null-0.0.0.tgz"
  },
  "scripts": {},
  "directories": {},
  "_shasum": "4744b3fb86f3fd1b455feac414a4f50f82a3d06d",
  "_resolved": "http://192.168.99.100:8000/null/-/null-0.0.0.tgz",
  "_from": "null@*"
}
```

publishしたものを使って`install`してみます。

```bash
$ mkdir install_test
$ cd install_test/
$ npm init
$ npm install sinopia_test --save
npm WARN package.json install_test@1.0.0 No description
npm WARN package.json install_test@1.0.0 No repository field.
npm WARN package.json install_test@1.0.0 No README data
sinopia_test@1.0.3 node_modules/sinopia_test
└── null@0.0.0
```

今回は事前にregistryが切り替わっていますが、`install`の時だけregistryを切り替えることもできます。

```bash
$ npm install sinopia_test --registry=http://192.168.99.100:8000/
# ok
$ npm install sinopia_test --registry=https://registry.npmjs.org/
# fail
```


### packageを`unpublish`する

```bash
# `--force`をつけない場合は失敗する
$ npm unpublish
npm ERR! Refusing to delete entire project.
npm ERR! Run with --force to do this.
npm ERR! npm unpublish <project>[@<version>]
```

`unpublish`する際には`--force`オプションが必要です。

```bash
# pakage名を入れない場合、特定バージョンがunpublish
$ npm unpublish --force
npm WARN using --force I sure hope you know what you are doing.
- sinopia_test@1.0.3
$ npm unpublish --force
npm WARN using --force I sure hope you know what you are doing.
- sinopia_test@1.0.3

# package名を指定すると全体がunpublish
$ npm unpublish sinopia_test --force
npm WARN using --force I sure hope you know what you are doing.
- sinopia_test
```


なお、本家npmではpublish後、24時間経つとunpublishできなくなる変更が、最近加わりました。

- changes to npm’s unpublish policy
  - http://blog.npmjs.org/post/141905368000/changes-to-npms-unpublish-policy
- 2016-04-04のJS: Node.js 5.10.0、npm security fix、Safari Technology Preview
  - http://jser.info/2016/04/04/nodejs-5100npm-security-fixsafari-technology-preview/

### registryからの`logout`

```bash
$ npm logout
npm ERR! registry error parsing json

...

npm ERR! Unexpected token C
npm ERR! Cannot DELETE /-/user/token/....
```

`logout`は失敗します。対応するエンドポイントが実装されていないようです。
　(Implement logout endpoint to allow npm delete access token:https://github.com/rlidwka/sinopia/pull/297 )

他に方法があるかもしれませんが、configファイルから認証情報を直接削除して対応しました。

```bash
# configの場所を確認
$ npm config list
...
; userconfig /Users/oooo/.npmrc

# tokenが含まれる行を削除
$ vi /Users/oooo/.npmrc
registry=http://192.168.99.100:8000
//192.168.99.100:8000/:_authToken="..."

# ログイン状態を確認
$ npm whoami
...
npm ERR! need auth this command requires you to be logged in.
npm ERR! need auth You need to authorize this machine using `npm adduser`
```

`https://registry.npmjs.org/`にresitoryを切り替えるだけでも良いのですが、再度resitoryをsinopia側へ設定するとログインしたままになっています。


## scoped moduleと組み合わせる

- npmで名前空間を持ったモジュールを公開する方法(scoped modules)
  - http://efcl.info/2015/04/30/npm-namespace/

npmではユーザ名をscope、name spaceとして扱うことができます。詳しい扱いは上記の記事を参照してください。


またregistryの扱いをscopeごとに切り替えることができます。
あらかじめscoped moduleとしておくことで、npmで提供されている`private repository`と`sinopia`と組み合わせると柔軟に公開範囲と運用を切り替えられるのではないかと考えています。

```bash
# see https://docs.npmjs.com/misc/scope#associating-a-scope-with-a-registry
$ npm config set @sinopia:registry http://192.168.99.100:8000/

# sinopia から ｉｎｓｔａｌｌ される
$ npm install @sinopia/test

# https://registry.npmjs.org/ から install される
$ npm install null
```


### 想定する方法

- 事前にnpm本家のユーザを登録する`@foo`
- packageを作る際には`@foo/bar`のように、scopeを区切っておく
- LANまたは限定された空間でのアクセスのみにしたい
  - -> `sinopia`に登録
- どこからでもアクセスできるが、アクセスできるのは限定したユーザ 
  - -> `private repository`として登録
- 誰からでもどこからでもアクセスできる
  -  -> `public repository`として登録

## 設定

docker-sinopiaのReadmeに記載されている２つの方法を試してみましたが、どちらも上手く行きません。
dockerに詳しくなったら再度挑戦しようかと思います。

#### Original Method

```
# https://github.com/kfatehi/docker-sinopia#original-method
docker run --volumes-from sinopia -it --rm ubuntu vi /opt/sinopia/config.yaml
```

`vi`が立ち上がるのですが、修正を行おうと表示がずれていき上手く変更できません。

##### Alternative Method

```bash
# https://github.com/kfatehi/docker-sinopia#alternative-method
curl -L https://raw.githubusercontent.com/rlidwka/sinopia/master/conf/default.yaml -o  config.yaml
docker run -v "$PWD/config.yaml":/opt/sinopia/config.yaml --name sinopia -d -p 8000:4873 keyvanfatehi/sinopia
```

dockerのcontainerは立ち上があるのですが、siniopiaへのアクセスができません。

### 認証と認可

TBD

## 運用について

### バックアップ

https://github.com/kfatehi/docker-sinopia#backups

バックアップ,リストアが出来ることを確認しました。


### 開発状況

- Project no longer maintained? #376
  - https://github.com/rlidwka/sinopia/issues/376

sinopia本家のrepositoryを見ると、issueやpull requestが多く溜まっている状態になっています。最終更新は6ヶ月前(2015年10月)で、今後保守され続けるのかは不安視されているようです。

## 代替

### publicなpackageにしてしまう

publicにして問題ないようなものであれば、publicにしてしまうのがやはり簡単そうです。

### npm側で用意しているprivate packageを使う

https://www.npmjs.com/enterprise/

npm側が公式で用意している`private pacakge`や`enterprize`向けの有料プランがあるため、お金を工面できれば利用できます。

### npmを建てる

npmの仕組み自体もOSSであるため、自分自身で運用するのであればresitoryも作成できます。

- Running npm On-Site in AWS
  - http://blog.npmjs.org/post/138936231510/running-npm-on-site-in-aws

### `git+ssh`プロトコルを使う

package.jsonで指定するpackageには`git+ssh`プロトコルのURLやGithubのURLを指定することできます。
しかし、セマンティックバージョンニングによる指定の恩恵を受けられないこと、`npm install`する度に再度installがされるため少々面倒です。
特にセマンティックバージョニングによる`"^1.1.0"`などの記述ができないため、ブランチやタグ、コミットハッシュで指定する必要があり、packageが多くなっていくと依存関係の管理が難しくなっていきます。

### 他のOSSを使う

- https://github.com/dickeyxxx/elephant

現在開発が進んでいるようです。sinopiaよりも認証機能を強化するようです。


## 参考

- SinopiaでPrivateなnpmレポジトリを作成する方法
  - http://qiita.com/Quramy/items/2d019474bdcf692f7c1a
- CREATING A PRIVATE NPM REGISTRY WITH SINOPIA
  -  https://blog.dylants.com/2014/05/10/creating-a-private-npm-registry-with-sinopia/