Refactor pkg build process to not rely on couchdb-ci (#39)

diff --git a/.gitignore b/.gitignore
index 30fd9cf..8d7d35c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,6 @@
 debian/files
 debian/couchdb
 debian/tmp
-js/couch-*
-js/couch-*
 js/build
 rpm/BUILD
 rpm/BUILDROOT
@@ -14,11 +12,9 @@
 repo/db
 repo/dists
 repo/pool
-pkgs/
-couchdb/
 couchdb_*.snap
 parts/
 prime/
 snap/.snapcraft/
 stage/
-apache-couchdb*.tar.gz
+*.tar.gz
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c6abcf6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,41 @@
+dist: xenial
+
+language: minimal
+
+services:
+  - docker
+
+# Avoid double build on PRs (See https://github.com/travis-ci/travis-ci/issues/1147)
+branches:
+  only:
+    - master
+
+env:
+  global:
+    - ERLANGVER=19.3.6
+    - TARBALL_URL=https://dist.apache.org/repos/dist/release/couchdb/source/2.3.0/apache-couchdb-2.3.0.tar.gz
+    - TARBALL=apache-couchdb-2.3.0.tar.gz
+  matrix:
+    - TARGET="js debian-jessie"
+    - TARGET="couch debian-jessie ${TARBALL_URL}"
+    - TARGET="js debian-stretch"
+    - TARGET="couch debian-stretch ${TARBALL_URL}"
+    - TARGET="js ubuntu-trusty"
+    - TARGET="couch ubuntu-trusty ${TARBALL_URL}"
+    - TARGET="js ubuntu-xenial"
+    - TARGET="couch ubuntu-xenial ${TARBALL_URL}"
+    - TARGET="js ubuntu-bionic"
+    - TARGET="couch ubuntu-bionic ${TARBALL_URL}"
+    - TARGET="js centos-6"
+    - TARGET="couch centos-6 ${TARBALL_URL}"
+    - TARGET="js centos-7"
+    - TARGET="couch centos-7 ${TARBALL_URL}"
+
+before_install:
+  - docker --version
+
+script:
+  - ./build.sh ${TARGET}
+
+after_script:
+  - ls -laR pkgs/
diff --git a/Makefile b/Makefile
index f4f425b..d41f77d 100644
--- a/Makefile
+++ b/Makefile
@@ -20,19 +20,25 @@
 export DEBEMAIL="dev@couchdb.apache.org"
 
 # Debian default
-debian: find-couch-dist copy-debian update-changelog dpkg lintian
+debian: find-couch-dist copy-debian update-changelog dpkg lintian copy-pkgs
 
 # Debian 8
+debian-jessie: PLATFORM=jessie
+debian-jessie: DIST=debian-jessie
 debian-jessie: jessie
 jessie: debian
 
 # Debian 9
+debian-stretch: PLATFORM=stretch
+debian-stretch: DIST=debian-stretch
 debian-stretch: stretch
 stretch: debian
 
 # Ubuntu 12.04
+ubuntu-precise: PLATFORM=precise
+ubuntu-precise: DIST=ubuntu-precise
 ubuntu-precise: precise
-precise: find-couch-dist copy-debian precise-prep update-changelog dpkg
+precise: find-couch-dist copy-debian precise-prep update-changelog dpkg copy-pkgs
 
 precise-prep:
 	sed -i '/dh-systemd/d' $(DISTDIR)/debian/control
@@ -44,8 +50,10 @@
 # No lintian run because of bogus failure on
 # postrm-does-not-call-updaterc.d-for-init.d-script
 # See Ubuntu ufw changelog for why they disabled this check
+ubuntu-trusty: PLATFORM=trusty
+ubuntu-trusty: DIST=ubuntu-trusty
 ubuntu-trusty: trusty
-trusty: find-couch-dist copy-debian trusty-prep update-changelog dpkg
+trusty: find-couch-dist copy-debian trusty-prep update-changelog dpkg copy-pkgs
 
 # see changelog for ubuntu ufw package, this is safe
 trusty-prep:
@@ -53,19 +61,25 @@
 	sed -i '/erlang-*/d' $(DISTDIR)/debian/control
 
 # Ubuntu 16.04
+ubuntu-xenial: PLATFORM=xenial
+ubuntu-xenial: DIST=ubuntu-xenial
 ubuntu-xenial: xenial
 xenial: debian
 
 # Ubuntu 18.04
+ubuntu-bionic: PLATFORM=bionic
+ubuntu-bionic: DIST=ubuntu-bioniC
 ubuntu-bionic: bionic
 bionic: debian
 
 # RPM default
-centos: find-couch-dist link-couch-dist build-rpm
+centos: find-couch-dist link-couch-dist build-rpm copy-pkgs
 
+centos-6: DIST=centos-6
 centos-6: centos6
 centos6: make-rpmbuild centos
 
+centos-7: DIST=centos-7
 centos-7: centos7
 centos7: make-rpmbuild centos
 
@@ -140,13 +154,13 @@
 
 # ######################################
 copy-pkgs:
-	mkdir -p pkgs/$(PLATFORM)
-	-cp ../rpmbuild/RPMS/x86_64/*.rpm pkgs/$(PLATFORM)
-	-cp ../couchdb/*deb pkgs/$(PLATFORM)
-	-chmod -R a+rwx pkgs/$(PLATFORM)
+	-chmod a+rwx ../rpmbuild/RPMS/x86_64/couchdb-* ../couchdb/couchdb_* 2>/dev/null
+	-mkdir -p pkgs/couch/$(DIST) && chmod 777 pkgs/couch/$(DIST)
+	-cp ../rpmbuild/RPMS/x86_64/couchdb-* pkgs/couch/$(DIST) 2>/dev/null
+	-cp ../couchdb/couchdb_* pkgs/couch/$(DIST) 2>/dev/null
 
 clean:
-	rm -rf couchdb_2.0_amd64.snap parts prime snap/.snapcraft stage js/build
+	rm -rf parts prime stage js/build
 
 # ######################################
 couch-js-clean:
diff --git a/README.md b/README.md
index 71df666..9ec06d3 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,76 @@
-# CouchDB Packaging support files
+# CouchDB Packaging support repo
 
-Quickstart:
+The main purpose of this repository is to provide packaging support files for Apache CouchDB and its SpiderMoneky 1.8.5 dependency, for a number of well-known and used packaging formats, namely:
+
+* `.deb` files, as used by Debian, Ubuntu, and derivatives
+* `.rpm` files, as used by CentOS, RedHat, and derivatives
+* `snapcraft` files, as used by the Ubuntu Snappy package manager
+
+# Usage
+
+## On a system with all necessary build-time dependencies:
+
+### SpiderMonkey 1.8.5
+
+#### rpms
 
 ```shell
-$ cd .. && git clone https://github.com/apache/couchdb
-$ cd couchdb-pkg && make build-couch $(lsb_release -cs) PLATFORM=$(lsb_release -cs)
+make couch-js-rpms
 ```
 
+#### debs
+
+```shell
+make couch-js-debs PLATFORM=$(lsb_release -cs)
+```
+
+### CouchDB
+
+#### rpms or debs from `master` branch:
+
+```shell
+cd .. && git clone https://github.com/apache/couchdb
+cd couchdb-pkg && make build-couch $(lsb_release -cs) PLATFORM=$(lsb_release -cs)
+```
+
+#### rpms or debs from a release tarball:
+
+```shell
+make copy-couch $(lsb_release -cs) COUCHTARBALL=path/to/couchdb-#.#.#.tar.gz PLATFORM=$(lsb_release -cs)
+```
+
+-----
+
+## Building inside the `couchdbdev` docker containers
+
+You must first pull down the image or images you need from Docker Hub, or build the images using the [apache/couchdb-ci](https://github.com/apache/couchdb-ci) repository. Example:
+
+```shell
+docker pull couchdbdev/<osname>-<codename>-erlang-<erlang-version>
+```
+
+A full list of supported environments is at https://hub.docker.com/u/couchdbdev/ .
+
+### SpiderMonkey 1.8.5
+
+```shell
+./build.sh js <os>-<codename>    # for example, debian-stretch, ubuntu-bionic or centos-7.
+```
+
+### CouchDB
+
+```shell
+./build.sh couch <os>-<codename> path/to/couchdb-#.#.#.tar.gz
+```
+
+or, if you want to build directly from the Apache distribution repository,
+
+```shell
+./build.sh couch <os>-<codename> https://dist.apache.org/repos/dist/release/couchdb/source/#.#.#/apache-couchdb-#.#.#.tar.gz
+```
+
+-----
+
 # Building packages for a release
 
 ## Prerequisites
@@ -25,13 +89,27 @@
 
 Run:
 
-    $ ./make-packages path/to/apache-couchdb-VERSION.tar.gz
+    $ ./build.sh couch-all path/to/apache-couchdb-VERSION.tar.gz
 
 or
 
-    $ ./make-packages http://url/to/apache-couchdb-VERSION.tar.gz
+    $ ./build.sh couch-all http://url/to/apache-couchdb-VERSION.tar.gz
 
-Packages will be placed in the `pkgs/` subdirectory.
+Packages will be placed in the `pkgs/couch` subdirectory.
+
+A similar `js-all` target exists, should the SpiderMonkey packages need to be regenerated.
+
+## Uploading the packages
+
+If you have Apache Bintray credentials (set your `BINTRAY_USER` and `BINTRAY_API_KEY` environment variables appropriately), after building all CouchDB packages above, simply run:
+
+    ./build.sh couch-upload-all
+
+Or, for the SpiderMonkey packages:
+
+    ./build.sh js-upload-all
+
+-----
 
 # Building snaps
 
@@ -42,7 +120,7 @@
 
 ## How to do it
 
-1. Edit `snap/snapcraft.yaml` to point to the correct tag (e.g. `2.1.0`)
+1. Edit `snap/snapcraft.yaml` to point to the correct tag (e.g. `2.3.0`)
 1. `snapcraft`
 
 # Feedback, Issues, Contributing
diff --git a/bin/build-couchdb-pkg.sh b/bin/build-couchdb-pkg.sh
new file mode 100755
index 0000000..e7a30e8
--- /dev/null
+++ b/bin/build-couchdb-pkg.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing,
+#   software distributed under the License is distributed on an
+#   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#   KIND, either express or implied.  See the License for the
+#   specific language governing permissions and limitations
+#   under the License.
+
+# This shell script builds and tests CouchDB on the current host.
+# It assumes the build environment is already set up correctly.
+# It needs no special privileges.
+
+# stop on error
+set -e
+
+SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+. ${SCRIPTPATH}/detect-os.sh
+
+redhats='(rhel|centos|fedora)'
+debians='(debian|ubuntu)'
+
+cd /home/jenkins
+
+if [[ $1 ]]; then
+  # use copied package
+  mkdir couchdb
+  tar -xf ${SCRIPTPATH}/../$1 -C couchdb
+  cp ${SCRIPTPATH}/../$1 couchdb
+else
+  # use master branch
+  git clone https://github.com/apache/couchdb
+  cd couchdb
+  ./configure -c
+  make dist
+  cd ..
+fi
+
+# now, build the package
+cd couchdb-pkg
+platform=${ID}-${VERSION_CODENAME}
+make $platform PLATFORM=${VERSION_CODENAME}
+
+# and save the output
+if [[ ${ID} =~ ${redhats} ]]; then
+  mv ../rpmbuild/RPMS/* ${SCRIPTPATH}/../couch/$platform
+elif [[ ${ID} =~ ${debians} ]]; then
+  mv ../couchdb/*.deb ${SCRIPTPATH}/../couch/$platform
+else
+  echo "Sorry, we don't support this Linux (${ID}) yet."
+  exit 1
+fi
+# and make sure we can delete it if necessary
+chmod -R a+rwx  ${SCRIPTPATH}/../couch/$platform/*
diff --git a/bin/build-js.sh b/bin/build-js.sh
new file mode 100755
index 0000000..7968867
--- /dev/null
+++ b/bin/build-js.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing,
+#   software distributed under the License is distributed on an
+#   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#   KIND, either express or implied.  See the License for the
+#   specific language governing permissions and limitations
+#   under the License.
+
+# This shell script builds a CouchDB-compatible SpiderMonkey 1.8.5
+# and assumes the build environment is already set up correctly.
+# It expects to be run as root.
+
+# stop on error
+set -e
+
+# This works if we're not called through a symlink
+# otherwise, see https://stackoverflow.com/questions/59895/
+SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ARCH=$(arch)
+
+# Check if running as root
+if [[ ${EUID} -ne 0 ]]; then
+  echo "Sorry, this script must be run as root."
+  echo "Try: sudo $0 $*"
+  exit 1
+fi
+
+. ${SCRIPTPATH}/detect-os.sh
+
+cd ${SCRIPTPATH}/..
+
+redhats='(rhel|centos|fedora)'
+debians='(debian|ubuntu)'
+if [[ ${ID} =~ ${redhats} ]]; then
+  # it will always place the build path at /root/rpmbuild :(
+  cp -r ${SCRIPTPATH}/.. /root/couchdb-pkg
+  cd /root/couchdb-pkg
+  make couch-js-rpms
+  mkdir -p ${SCRIPTPATH}/../pkgs/js/${ID}-${VERSION_CODENAME}
+  mv /root/rpmbuild/RPMS/${ARCH}/couch-js-* ${SCRIPTPATH}/../pkgs/js/${ID}-${VERSION_CODENAME}
+elif [[ ${ID} =~ ${debians} ]]; then
+  make couch-js-debs PLATFORM=${VERSION_CODENAME}
+  mkdir -p pkgs/js/${ID}-${VERSION_CODENAME}
+  mv js/couch-lib* pkgs/js/${ID}-${VERSION_CODENAME}
+else
+  echo "Sorry, we don't support this Linux (${ID}) yet."
+  exit 1
+fi
+chmod a+rw ${SCRIPTPATH}/../pkgs/js/*
diff --git a/bin/detect-os.sh b/bin/detect-os.sh
new file mode 100755
index 0000000..f664f33
--- /dev/null
+++ b/bin/detect-os.sh
@@ -0,0 +1,137 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing,
+#   software distributed under the License is distributed on an
+#   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#   KIND, either express or implied.  See the License for the
+#   specific language governing permissions and limitations
+#   under the License.
+
+# This shell script detects the OS we're on, and sets a number
+# of environment variables the calling script can use.
+
+# It uses the systemd standard of variables, and attempts to
+# populate some of the variables manually if systemd is not
+# present on the underlying OS.
+
+# Example systemd /etc/os-release file:
+# NAME="Ubuntu"
+# VERSION="16.04.4 LTS (Xenial Xerus)"
+# ID=ubuntu
+# ID_LIKE=debian
+# PRETTY_NAME="Ubuntu 16.04.4 LTS"
+# VERSION_ID="16.04"
+# HOME_URL="http://www.ubuntu.com/"
+# SUPPORT_URL="http://help.ubuntu.com/"
+# BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
+# VERSION_CODENAME=xenial
+# UBUNTU_CODENAME=xenial
+
+# While these scripts are primarily written to support building CI
+# Docker images, they can be used on any workstation to install a
+# suitable build environment.
+
+# stop on error
+set -e
+
+case "${OSTYPE}" in
+  linux*)
+    echo "Detected OS: Linux"
+    # Try the new, systemd-based way first
+    . /etc/os-release 2>/dev/null || true
+    # then try lsb_release, which might be installed
+    # os-release doesn't actually give us everything we want...
+    lsb_plat=$(lsb_release -d 2>/dev/null | awk -F"\t" '{print $2}' 2>/dev/null)
+    if [[ ${lsb_plat} ]]; then
+      if [[ ${lsb_plat} =~ "^Debian" ]]; then
+        ID=${ID:-debian}
+        VERSION_ID=${VERSION_ID:-$(echo ${lsb_plat} | awk '{print $3}' | awk -F'.' '{print $1}')}
+        VERSION_CODENAME=${VERSION_CODENAME:-$(echo ${lsb_plat} | awk '{print $4}' | sed 's/[\(\)]//g')}
+        DISTRIB_CODENAME=${DISTRIB_CODENAME:-${VERSION_CODENAME}}
+      elif  [[ ${lsb_plat} =~ "^Ubuntu" ]]; then
+        ID=${ID:-ubuntu}
+        VERSION_ID=${VERSION_ID:-$(echo ${lsb_plat} | awk '{print $2}' | awk -F'.' '{print $1}')}
+        VERSION_CODENAME=${VERSION_CODENAME:-$(lsb_release -cs)}
+        DISTRIB_CODENAME=${DISTRIB_CODENAME:-${VERSION_CODENAME}}
+      fi
+    fi
+    # and finally some rough heuristics
+    if [[ -f /etc/redhat-release ]]; then
+      # /etc/redhat-release is so inconsistent, we use rpm instead
+      rhelish=$(rpm -qa '(redhat|sl|slf|centos|oraclelinux)-release(|-server|-workstation|-client|-computenode)' 2>/dev/null | head -1)
+      if [[ $rhelish ]]; then
+        ID=${ID:-$(echo ${rhelish} | awk -F'-' '{print tolower($1)}')}
+        VERSION_ID=${VERSION_ID:-$(echo ${rhelish} | sed -E 's/([^[:digit:]]+)([[:digit:]]+)(.*)/\2/' )}
+        VERSION_CODENAME=${VERSION_CODENAME:-${VERSION_ID}}
+        DISTRIB_CODENAME=${VERSION_CODENAME:-${VERSION_ID}}
+      fi
+    elif [[ -f /etc/debian_version ]]; then
+      # Ubuntu keeps changing the format of /etc/os-release's VERSION, and
+      # it's numeric, not the codename. Boo.
+      # Also, Debian doesn't supply VERSION_CODENAME. Double Boo.
+      if [[ ${PRETTY_NAME} =~ "Ubuntu" ]]; then
+        # Ubuntu keeps changing the format of /etc/os-release's VERSION, and
+        # the codename is buried. Boo. Let's use a fancy regex.
+        VERSION_CODENAME=${VERSION_CODENAME:-$(echo ${VERSION} | sed -E 's/([0-9.]+)\W+([A-Za-z\,]+)\W+\(?(\w+)(.*)/\L\3/')}
+        DISTRIB_CODENAME=${DISTRIB_CODENAME:-${VERSION_CODENAME}}
+      elif [[ ${PRETTY_NAME} =~ "Debian" ]]; then
+        VERSION_CODENAME=${VERSION_CODENAME:-$(echo ${VERSION} | sed -E 's/(.*)\(([^\]+)\)/\2/')}
+        DISTRIB_CODENAME=${DISTRIB_CODENAME:-${VERSION_CODENAME}}
+      else
+        echo "Unknown Debian-like OS ${PRETTY_NAME}, aborting..."
+        exit 1
+      fi
+    fi
+    if [[ ${ID} && ${VERSION_ID} && ${VERSION_CODENAME} ]]; then
+      echo "Detected distribution: ${ID}, version ${VERSION_ID} (${VERSION_CODENAME})"
+    else
+      echo "Unable to determine Linux distribution! Aborting."
+      exit 1
+    fi
+    ;;
+
+  freebsd*)
+    echo "Detected OS: FreeBSD"
+    # use userland version
+    VERSION=$(freebsd-version -u | cut -d '-' -f1)
+    ;;
+  *bsd*)
+    # TODO: detect netbsd vs. freebsd vs. openbsd?
+    echo "Detected OS: BSD - UNSUPPORTED"
+    exit 1
+    ;;
+  darwin*)
+    # TODO
+    echo "Detected OS: macOS (OSX) - UNSUPPORTED"
+    exit 1
+    ;;
+  solaris*)
+    # TODO
+    echo "Detected OS: Solaris-like"
+    exit 1
+    ;;
+  msys*)
+    # TODO
+    echo "Detected OS: Windows (msys)"
+    exit 1
+    ;;
+  cygwin*)
+    # TODO
+    echo "Detected OS: Windows (cygwin)"
+    exit 1
+    ;;
+  *)
+    echo "Unknown OS detected: ${OSTYPE}"
+    exit 1
+    ;;
+esac
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..6cc59ac
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,286 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing,
+#   software distributed under the License is distributed on an
+#   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#   KIND, either express or implied.  See the License for the
+#   specific language governing permissions and limitations
+#   under the License.
+
+# This is the master shell script to build Docker containers
+# for CouchDB 2.x.
+
+# stop on error
+set -e
+
+# This works if we're not called through a symlink
+# otherwise, see https://stackoverflow.com/questions/59895/
+SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+# TODO derive these by interrogating the Docker repo rather tha
+# hard coding the list
+DEBIANS="debian-jessie debian-stretch"
+UBUNTUS="ubuntu-trusty ubuntu-xenial ubuntu-bionic"
+debs="(debian-jessie|debian-stretch|ubuntu-trusty|ubuntu-xenial|ubuntu-bionic)"
+
+CENTOSES="centos-6 centos-7"
+rpms="(centos-6|centos-7)"
+
+BINTRAY_API="https://api.bintray.com"
+ERLANGVERSION=${ERLANGVERSION:-19.3.6}
+
+
+build-js() {
+  # TODO: check if image is built first, if not, complain
+  # invoke as build-js <plat>
+  docker run \
+      --mount type=bind,src=${SCRIPTPATH},dst=/home/jenkins/couchdb-pkg \
+      couchdbdev/$1-base \
+      sudo /home/jenkins/couchdb-pkg/bin/build-js.sh
+}
+
+build-all-js() {
+  rm -rf ${SCRIPTPATH}/pkgs/js/*
+  for plat in $DEBIANS $UBUNTUS $CENTOSES; do
+    build-js $plat
+  done
+}
+
+bintray-check-credentials() {
+  if [[ ! ${BINTRAY_USER} || ! ${BINTRAY_API_KEY} ]]; then
+    echo "Please set your Bintray credentials before using this command:"
+    echo "  export BINTRAY_USER=<username>"
+    echo "  export BINTRAY_API_KEY=<key>"
+    exit 1
+  fi
+}
+
+bintray-upload() {
+  echo "Uploading ${PKG}..."
+  local ret="$(curl \
+      --request PUT \
+      --upload-file $PKG \
+      --user ${BINTRAY_USER}:${BINTRAY_API_KEY} \
+      --header "X-Bintray-Package: ${PKGNAME}" \
+      --header "X-Bintray-Version: ${PKGVERSION}" \
+      --header "X-Bintray-Publish: 1" \
+      --header "X-Bintray-Override: 1" \
+      --header "X-Bintray-Explode: 0" \
+      "${HEADERS[@]}" \
+      "${BINTRAY_API}/content/apache/${REPO}/${RELPATH}")"
+  if [[ ${ret} == '{"message":"success"}' ]]; then
+    echo "Uploaded successfully."
+  else
+    echo "Failed to upload $PKG, ${ret}"
+    exit 1
+  fi
+}
+
+upload-js() {
+  # invoke with $1 as plat, expect to find the binaries under pkgs/js/$plat/*
+  bintray-check-credentials
+  # Debian packages first
+  PKGNAME="spidermonkey"
+  PKGVERSION="1.8.5"
+  for PKG in $(ls pkgs/js/$1/*.deb 2>/dev/null); do
+    # Example filename: couch-libmozjs185-1.0_1.8.5-1.0.0+couch-2~bionic_amd64.deb
+    # TODO: pull this stuff from buildinfo / changes files, perhaps? Not sure it matters.
+    REPO="couchdb-deb"
+    fname=${PKG##*/}
+    DIST=$(echo $fname | cut -d~ -f 2 | cut -d_ -f 1)
+    PKGARCH=$(echo $fname | cut -d_ -f 3 | cut -d. -f 1)
+    RELPATH="pool/s/spidermonkey/${fname}"
+    HEADERS=("--header" "X-Bintray-Debian-Distribution: ${DIST}")
+    HEADERS+=("--header" "X-Bintray-Debian-Component: main")
+    HEADERS+=("--header" "X-Bintray-Debian-Architecture: ${PKGARCH}")
+    bintray-upload
+  done
+  for PKG in $(ls pkgs/js/$1/*.rpm 2>/dev/null); do
+    # Example filename: couch-js-1.8.5-21.el7.x86_64.rpm
+    REPO="couchdb-rpm"
+    fname=${PKG##*/}
+    # better not put any extra . in the filename...
+    DIST=$(echo $fname | cut -d. -f 4)
+    PKGARCH=$(echo $fname | cut -d. -f 5)
+    RELPATH="${DIST}/${PKGARCH}/${fname}"
+    HEADERS=()
+    bintray-upload
+  done
+}
+
+cannot-find-tarball() {
+    echo Must supply path to tarball, either:
+    echo '  - path/to/couchdb-VERSION.tar.gz or'
+    echo '  - http(s)://url/to/couchdb-VERSION.tar.gz'
+    echo
+    exit 1
+}
+
+get-couch-tarball() {
+  if [ $# -ne "1" ]
+  then
+    cannot-find-tarball
+  fi
+  ARG=$1
+  if [ -f $ARG ]
+  then
+    # file
+    cp $ARG . 2>/dev/null || true
+    COUCHTARBALL=$(basename ${ARG})
+  else
+    if [[ $ARG =~ ^http.*$ ]]
+    then
+      #url
+      # thank you, advanced bash scripting guide
+      curl -O $ARG
+      COUCHTARBALL=${ARG##*/}
+    else
+      usage
+    fi
+  fi
+  echo Using ${COUCHTARBALL} to build packages...
+  chmod 777 ${COUCHTARBALL}
+}
+
+build-couch() {
+  # $1 is plat, $2 is the optional path to a dist tarball
+  docker run \
+      --mount type=bind,src=${SCRIPTPATH},dst=/home/jenkins/couchdb-pkg \
+      -w /home/jenkins/couchdb-pkg \
+      couchdbdev/$1-erlang-${ERLANGVERSION} \
+      make copy-couch $1 COUCHTARBALL=${COUCHTARBALL}
+}
+
+build-all-couch() {
+  rm -rf ${SCRIPTPATH}/pkgs/couch/*
+  for plat in $DEBIANS $UBUNTUS $CENTOSES; do
+    build-couch $plat $*
+  done
+}
+
+upload-couch() {
+  # invoke with $1 as plat, expect to find the binaries under pkgs/couch/$plat/*
+  bintray-check-credentials
+  # Debian packages first
+  PKGNAME="CouchDB"
+  for PKG in $(ls pkgs/couch/$1/*.deb 2>/dev/null); do
+    # Example filename: couchdb_2.3.0~jessie_amd64.deb
+    # TODO: pull this stuff from buildinfo / changes files, perhaps? Not sure it matters.
+    fname=${PKG##*/}
+    REPO="couchdb-deb"
+    DIST=$(echo $fname | cut -d~ -f 2 | cut -d_ -f 1)
+    PKGARCH=$(echo $fname | cut -d_ -f 3 | cut -d. -f 1)
+    PKGVERSION=$(echo $fname | cut -d_ -f 2 | cut -d~ -f 1)
+    RELPATH="pool/C/CouchDB/${fname}"
+    HEADERS=("--header" "X-Bintray-Debian-Distribution: ${DIST}")
+    HEADERS+=("--header" "X-Bintray-Debian-Component: main")
+    HEADERS+=("--header" "X-Bintray-Debian-Architecture: ${PKGARCH}")
+    bintray-upload
+  done
+  for PKG in $(ls pkgs/couch/$1/*.rpm 2>/dev/null); do
+    # Example filename: couchdb-2.3.0-1.el7.x86_64.rpm.asc
+    fname=${PKG##*/}
+    REPO="couchdb-rpm"
+    # better not put any extra . in the filename...
+    DIST=$(echo $fname | cut -d. -f 4)
+    PKGARCH=$(echo $fname | cut -d. -f 5)
+    PKGVERSION=$(echo $fname | cut -d- -f 2)
+    RELPATH="${DIST}/${PKGARCH}/${fname}"
+    HEADERS=()
+    bintray-upload
+  done
+}
+
+
+case "$1" in
+  clean)
+    # removes built pkgs for all platforms
+    shift
+    rm -rf ${SCRIPTPATH}/pkgs/js/* ${SCRIPTPATH}/pkgs/couch/*
+    ;;
+  js)
+    # Build js packages for a given platform
+    shift
+    build-js $1
+    ;;
+  js-all)
+    # build all supported JS packages
+    shift
+    build-all-js
+    ;;
+  js-upload)
+    shift
+    upload-js $1
+    ;;
+  js-upload-all)
+    shift
+    for dir in $(ls pkgs/js); do
+      upload-js $dir
+    done
+    ;;
+  couch)
+    # build CouchDB pkgs for <plat>
+    shift
+    get-couch-tarball $2
+    build-couch $*
+    ;;
+  couch-all)
+    # build CouchDB pkgs for all platforms
+    shift
+    get-couch-tarball $1
+    build-all-couch
+    ;;
+  couch-upload)
+    shift
+    upload-couch $1
+    ;;
+  couch-upload-all)
+    shift
+    for dir in $(ls pkgs/couch); do
+      upload-couch $dir
+    done
+    ;;
+  *)
+    if [[ $1 ]]; then
+      echo "Unknown target $1."
+      echo
+    fi
+    cat << EOF
+$0 <command> [OPTIONS]
+
+Recognized commands:
+  clean                 Remove all built package artefacts.
+
+  js <plat>             Builds the JS packages for <plat>.
+  js-all                Builds the JS packages for all platforms.
+  *js-upload <plat>     Uploads the JS packages for <plat> to bintray.
+  *js-upload-all        Uploads the JS packages for all platforms to bintray.
+
+  couch <plat> <src>    Builds CouchDB packages for <plat>.
+  couch-all <src>       Builds CouchDB packages for all platforms.
+  *couch-upload <plat>  Uploads the JS packages for <plat> to bintray.
+  *couch-upload-all     Uploads the JS packages for all platforms to bintray.
+
+  <src> is either
+    - a path/to/a/couchdb.tar.gz, or
+    - a URL to http(s)://domain.com/to/couchdb.tar.gz
+
+  Commands marked with * require BINTRAY_USER and BINTRAY_API_KEY env vars.
+EOF
+    if [[ $1 ]]; then
+      exit 1
+    fi
+    ;;
+esac
+
+exit 0
diff --git a/make-releases.sh b/make-releases.sh
deleted file mode 100755
index 71798dd..0000000
--- a/make-releases.sh
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/bin/bash
-IMAGES=(
-  centos-6-erlang-19.3.6 centos6 \
-  centos-7-erlang-19.3.6 centos7 \
-  debian-jessie-erlang-19.3.6 jessie \
-  debian-stretch-erlang-19.3.6 stretch \
-  ubuntu-trusty-erlang-19.3.6 trusty \
-  ubuntu-xenial-erlang-19.3.6 xenial \
-  ubuntu-bionic-erlang-19.3.6 bionic
-)
-
-usage() {
-  echo $0 takes exactly one argument, either:
-  echo '  - path/to/couchdb-VERSION.tar.gz or'
-  echo '  - http://url/to/couchdb-VERSION.tar.gz'
-  echo
-  exit
-}
-
-if [ $# -ne "1" ]
-then
-  usage
-fi
-
-ARG=$1
-
-if [ -f ${ARG} ]
-then
-  # file
-  cp ${ARG} . 2>/dev/null || true
-  FILE=$(basename ${ARG})
-else
-  if [[ ${ARG} =~ ^http.*$ ]]
-  then
-    # url
-    # thank you, advanced bash scripting guide
-    curl -O ${ARG}
-    FILE=${ARG##*/}
-  else
-    usage
-  fi
-fi
-
-echo Using ${FILE} to build packages...
-chmod 777 ${FILE}
-
-mkdir -p pkgs && chmod 777 pkgs
-
-image_count=${#IMAGES[@]}
-index=0
-
-while [ "$index" -lt "$image_count" ]
-do
-  img=${IMAGES[$index]}
-  ((index++))
-  plat=${IMAGES[$index]}
-  ((index++))
-  docker run -it -w /tmp/couchdb-pkg -v $(readlink -f .):/tmp/couchdb-pkg couchdbdev/$img make copy-couch $plat copy-pkgs PLATFORM=$plat COUCHTARBALL=${FILE}
-done
diff --git a/pkgs/couch/.gitignore b/pkgs/couch/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/pkgs/couch/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/pkgs/js/.gitignore b/pkgs/js/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/pkgs/js/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore