netpoetica
netpoetica

Reputation: 3425

Packing a node bash CLI for execution from the /usr/local/bin/ folder

Does anyone know how to achieve being to execute a node bash proram from the command line, for example the way you might call "brew install" - normally, you need to call "node NAME_OF_CLI".

The problem is that I can't just throw my program into the /bin folder because I would need to put the node_modules folder in there as well - and that just does not seem acceptable. Is there a known way to package all of these things together so it will be an executable program?

Also, I do not want this to be in npm - this is not a public module.

Upvotes: 0

Views: 1161

Answers (2)

Peter Lyons
Peter Lyons

Reputation: 146154

Here's what I would do.

Distribute your program in a traditional archive

  • package your program into a distributable archive such as .tar.bz2 (preferred), .tar.gz, or .zip (if it needs to support windows)
  • In the archive include node itself, your fully-populated node_modules folder, and all your other javascript modules, files, whatnot
  • Because node is platform-dependent, you will thus need a different archive distribution for each target architecture (linux/windows/osx)
  • These above are consistent with the 12 Factor App Build/Release/Run principles.
  • Include an executable shell script that is your program's entry point that can do something like

Include an executable shell script wrapper

#!/bin/sh
DIR=$(dirname "${0}")
exec "${DIR}/node/bin/node" "${DIR}/main.js" "${@}"

Install via curl command line copy/paste

I noticed in the comments you want a homebrew-style install-via curl command line. In that case, all of the above still applies but your curl command downloads a shell script that does:

  • download the distribution archive
  • extract it in place (/usr/local/programname would be reasonable)
  • If necessary, set up symlinks or copy files into a bin directory in the user's PATH (/usr/local/bin/programname would be reasonable)

So your curl command might be curl http://example.com/myprogram/install.sh | sh

Again, this is consistent with the 12 Factor App principles, which are sound. In particular:

  • Do not expect the target user to already have node installed
  • Do not expect the target user to install node by following your directions
  • Do not bother trying to support varying versions of node unless you really have a requirement to do so
  • It is OK to bundle node with your app. It makes it easier to install, easier to support, and less prone to errors.

Misc Tips

  • Make sure your installer code is idempotent
  • You may want to explicitly set the wrapper shell script executable on the target machine with chmod in the install.sh script as zip archives and tar under some circumstances won't preserve that
  • Watch out for OS X's crappy old tar. Use gnutar instead.

Reference material

Refer to homebrew's go ruby program for ideas/inspiration https://raw.github.com/mxcl/homebrew/go

Here are some examples taken from build script in the github repo for my web site

install_node() {
  local VERSION=${1-0.10.7}
  local PREFIX=${2-node}
  local PLATFORM=$(uname | tr A-Z a-z)
  case $(uname -p) in
      i686)
          ARCH=x86
      ;;
  esac
  mkdir -p "${PREFIX}"
  curl --silent \
    "http://nodejs.org/dist/v${VERSION}/node-v${VERSION}-${PLATFORM}-${ARCH}.tar.gz" \
    | tar xzf - --strip-components=1 -C "${PREFIX}"
}


task:dist() {
  cd "${CODE_PATH}"
  local GIT_REF="${1-master}"
  local BUILD_DIR="build"
  local DIST_DIR="dist"
  local PREFIX="${SITE}-${GIT_REF}"
  dirs "${BUILD_DIR}" "${DIST_DIR}"
  echo doing git archive
  git archive --format=tar --prefix="${PREFIX}/" "${GIT_REF}" | \
    #extract that archive into a temporary build directory
    "${TAR}" --directory "${BUILD_DIR}" --extract
  #install node
  NODE_VERSION=$(./bin/jsonpath.coffee engines.node)
  echo installing node
  install_node "${NODE_VERSION}" "${BUILD_DIR}/${PREFIX}/node"
  #Note we use npm from the build platform (OS X) here instead of
  #the one for the run platform as they are incompatible
  echo install npm packages
  (cd "${BUILD_DIR}/${PREFIX}" && npm install --silent --production)
  echo creating archive
  "${TAR}" --directory "${BUILD_DIR}" --create --bzip2 --file "${DIST_DIR}/${PREFIX}.tar.bz2" .
}

Upvotes: 1

furydevoid
furydevoid

Reputation: 1401

Add a file to the ./bin folder

$ cd myproject
$ mkdir bin

bin/my_bin_file:

#!/usr/bin/env node
require('../main_file.js');

Add a "bin" option to your package.json:

"bin": {
    "my_bin_file": "./bin/my_bin_file"
}

Make it executable

$ chmod +x bin/my_bin_file

Install globally:

npm install -g

EDIT: npm can install your package globally to the system without it being public, and there is a shebang in the bin file that tells the system how to invoke it (no need to call node on the cli explicitly)

Upvotes: 0

Related Questions