Sinopia: a private NPM registry

Not all of our packages can be pushed to the public NPM repository. Proprietary code and uninteresting code we want to keep internal, but until recently the package distribution story for this code has been worse than open-sourcing it. You either modularise it and publish to the world, or you have a bad time.

Or you modularise and share via git dependencies, which isn’t a great solution. You lose versioning, all the pre-publish hook loveliness, and become quite limited by where our package is being used (no preprocessors for you!).

In the early days NPM wasn’t designed with multiple registries in mind, so hosting your own internal one meant either proxying/mirroring the public registry or manually adding public packages to your private registry and using it for everything. Thanks to improvements in NPM and several open-source efforts, it’s now much easier than that to host your own internal NPM registry.

NPM have recently started offering private modules, which looks very interesting. There’s also a pay-for Enterprise option from NPM which is worth thinking about once you scale.

If private cloud hosting isn’t your thing, the host-your-own options are worth exploring. Sinopia is what we’ll setup.

What is Sinopia?

Sinopia is a simple NPM registry, which has zero dependencies (that means no CouchDB for those who’ve done this before); it has support for most day-to-day NPM features, such as scopes, command line publishing, and authentication. Sinopia is a good candidate for a low-to-medium utilised NPM registry, such as company private registries.

Running Sinopia on your machine

If you just want to run Sinopia locally and aren’t interested in how it works, you can:

git clone https://github.com/jagregory/sinopia-ansible.git
cd sinopia-ansible
ansible-galaxy install -r requirements.txt
vagrant up

Once this completes Sinopia will be available on port 4873.

Vagrant setup

Vagrant uses a Vagrantfile to define how your Virtual Machine (or machines) will be configured. You can run vagrant init to create a Vagrantfile. Once you have that file, we’ll make some changes inside the config block.

The Ubuntu boxes provided by the Phusion team are a good set of boxes to start from. We’re using their 14.04 TLS box.

config.vm.box = "ubuntu-14.04-amd64-vbox"
config.vm.box_url = "https://oss-binaries.phusionpassenger.com/vagrant/boxes/latest/ubuntu-14.04-amd64-vbox.box"

When Sinopia starts it will listen on port 4873 in the guest machine, but we need to forward that port to a port on our host. For convinience, we’ll just use the same port on both.

config.vm.network "forwarded_port", guest: 4873, host: 4873

Finally, Vagrant will run Ansible on the Virtual Machine. Ansible will download and configure Sinopia using the sinopia-ansible role.

config.vm.provision "ansible" do |ansible|
  ansible.playbook = "site.yml"
end

Ansible playbook

Our Vagrantfile delegates machine setup to Ansible, which is driven by a Playbook. Our playbook is pretty simple, but it’s worth having a look.

Here’s the directory structure we have for Ansible. There’s very little to it.

.
├── Vagrantfile
├── hosts
├── requirements.txt
└── site.yml

site.yml: For our Vagrantfile to run we need a site.yml Ansible playbook, which tells Ansible which roles our host is supposed to have.

In our site.yml we give the playbook a name, specify that it should run on all of our hosts (we only have one), that our commands should be run as sudo, and then we specify which roles our machine should have.

---
- name: Install Sinopia
  hosts: all
  sudo: True
  roles:
    - nodesource.node
    - jagregory.sinopia

requirements.txt: Ansible has a dependency management system/tool called Ansible Galaxy which is growing in popularity for sharing roles. We’ll use Ansible Galaxy to download the roles we use for this machine, rather than copying them into our repository.

Ansible Galaxy uses a requirements.txt to list which dependencies to install. We just have two, a NodeJS role and our Sinopia role.

nodesource.node
jagregory.sinopia

To install these dependencies you can run: ansible-galaxy install -r requirements.txt

hosts: The last thing of interest is the Ansible inventory file, which declares the machines we’re letting Ansible manage. In our case it’s just one host in our inventory.

npm.jagregory.com

Sinopia in the Cloud

If you want to run a Sinopia instance in the cloud it’s as easy as launching an instance in EC2 (or your preferred provider), adding it’s public IP address to the inventory file and running Ansible.

ansible-playbook site.yml -i hosts

This will run Ansible against all the hosts in our inventory (hosts file) and execute the site.yml playbook.

You can read more about running Sinopia on their website.