Add deploy script for scouting webserver

This patch lets folks do this to deploy the webserver:

    $ bazel run //scouting/deploy

It will copy the webserver to the scouting server, install it, and
start it. You can find a summary in the new README.md file.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: I997738db483dc0e7d01a336ef4dde150ccfed070
diff --git a/scouting/BUILD b/scouting/BUILD
index 0ed540b..a769426 100644
--- a/scouting/BUILD
+++ b/scouting/BUILD
@@ -33,6 +33,7 @@
         "//scouting/www:index.html",
         "//scouting/www:zonejs_copy",
     ],
+    visibility = ["//scouting/deploy:__pkg__"],
 )
 
 protractor_ts_test(
diff --git a/scouting/deploy/BUILD b/scouting/deploy/BUILD
new file mode 100644
index 0000000..ed4b9cd
--- /dev/null
+++ b/scouting/deploy/BUILD
@@ -0,0 +1,60 @@
+load("@rules_pkg//pkg:pkg.bzl", "pkg_deb", "pkg_tar")
+load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
+
+pkg_files(
+    name = "systemd_files",
+    srcs = [
+        "scouting.service",
+    ],
+    prefix = "etc/systemd/system",
+)
+
+pkg_tar(
+    name = "server_files",
+    srcs = [
+        "//scouting",
+    ],
+    include_runfiles = True,
+    package_dir = "opt/frc971/scouting_server",
+    strip_prefix = ".",
+)
+
+pkg_tar(
+    name = "deploy_tar",
+    srcs = [
+        ":systemd_files",
+    ],
+    deps = [
+        ":server_files",
+    ],
+)
+
+pkg_deb(
+    name = "frc971-scouting-server",
+    architecture = "amd64",
+    data = ":deploy_tar",
+    description = "The FRC971 scouting web server.",
+    # TODO(phil): What's a good email address for this?
+    maintainer = "frc971@frc971.org",
+    package = "frc971-scouting-server",
+    postinst = "postinst",
+    predepends = [
+        "systemd",
+    ],
+    prerm = "prerm",
+    version = "1",
+)
+
+py_binary(
+    name = "deploy",
+    srcs = [
+        "deploy.py",
+    ],
+    args = [
+        "--deb",
+        "$(location :frc971-scouting-server)",
+    ],
+    data = [
+        ":frc971-scouting-server",
+    ],
+)
diff --git a/scouting/deploy/README.md b/scouting/deploy/README.md
new file mode 100644
index 0000000..6d223da
--- /dev/null
+++ b/scouting/deploy/README.md
@@ -0,0 +1,37 @@
+Deploying the scouting application
+================================================================================
+The scouting application is deployed to `scouting.frc971.org` via `bazel`:
+```console
+$ bazel run //scouting/deploy
+(Reading database ... 119978 files and directories currently installed.)
+Preparing to unpack .../frc971-scouting-server_1_amd64.deb ...
+Removed /etc/systemd/system/multi-user.target.wants/scouting.service.
+Unpacking frc971-scouting-server (1) over (1) ...
+Setting up frc971-scouting-server (1) ...
+Created symlink /etc/systemd/system/multi-user.target.wants/scouting.service → /etc/systemd/system/scouting.service.
+Connection to scouting.frc971.org closed.
+```
+
+You will need SSH access to the scouting server. You can customize the SSH host
+with the `--host` argument.
+
+The Blue Alliance API key
+--------------------------------------------------------------------------------
+You need to set up an API key on the scouting server so that the scraping logic
+can use it. It needs to live in `/var/frc971/scouting/tba_config.json` and look
+as follows:
+```json
+{
+    "api_key": "..."
+}
+```
+
+Starting and stopping the application
+--------------------------------------------------------------------------------
+When you SSH into the scouting server, use `systemctl` to manage
+`scouting.service` like any other service.
+```console
+$ sudo systemctl stop scouting.service
+$ sudo systemctl start scouting.service
+$ sudo systemctl restart scouting.service
+```
diff --git a/scouting/deploy/deploy.py b/scouting/deploy/deploy.py
new file mode 100644
index 0000000..c9886fb
--- /dev/null
+++ b/scouting/deploy/deploy.py
@@ -0,0 +1,34 @@
+import argparse
+from pathlib import Path
+import subprocess
+import sys
+
+def main(argv):
+    """Installs the scouting application on the scouting server."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--deb",
+        type=str,
+        required=True,
+        help="The .deb file to deploy.",
+    )
+    parser.add_argument(
+        "--host",
+        type=str,
+        default="scouting.frc971.org",
+        help="The SSH host to install the scouting web server to.",
+    )
+    args = parser.parse_args(argv[1:])
+    deb = Path(args.deb)
+
+    # Copy the .deb to the scouting server, install it, and delete it again.
+    subprocess.run(["rsync", "-L", args.deb, f"{args.host}:/tmp/{deb.name}"],
+                   check=True, stdin=sys.stdin)
+    subprocess.run(f"ssh -tt {args.host} sudo dpkg -i /tmp/{deb.name}",
+                   shell=True, check=True, stdin=sys.stdin)
+    subprocess.run(f"ssh {args.host} rm -f /tmp/{deb.name}",
+                   shell=True, check=True, stdin=sys.stdin)
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff --git a/scouting/deploy/postinst b/scouting/deploy/postinst
new file mode 100644
index 0000000..a7a8b16
--- /dev/null
+++ b/scouting/deploy/postinst
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# This script runs after the frc971-scouting-server package is installed. This
+# script is responsible for making sure the webserver has everything it needs,
+# then starts the webserver.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+# Create a directory for the database to live in.
+mkdir -p /var/frc971/scouting/
+
+# Create an empty The Blue Alliance configuration file.
+if [[ ! -e /var/frc971/scouting/tba_config.json ]]; then
+    echo '{}' > /var/frc971/scouting/tba_config.json
+fi
+
+# Make sure it's all usable by the user.
+chown -R www-data:www-data /var/frc971/scouting/
+
+systemctl daemon-reload
+systemctl enable scouting.service
+systemctl start scouting.service || :
diff --git a/scouting/deploy/prerm b/scouting/deploy/prerm
new file mode 100644
index 0000000..31e3fbc
--- /dev/null
+++ b/scouting/deploy/prerm
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# This script gets run before the frc971-scouting-server package gets removed
+# or upgraded. This script is responsible for stopping the webserver before the
+# underlying files are removed by dpkg.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+systemctl stop scouting.service
+systemctl disable scouting.service
+systemctl daemon-reload
diff --git a/scouting/deploy/scouting.service b/scouting/deploy/scouting.service
new file mode 100644
index 0000000..2990f42
--- /dev/null
+++ b/scouting/deploy/scouting.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=FRC971 Scouting Server
+After=systemd-networkd-wait-online.service
+
+[Service]
+User=www-data
+Group=www-data
+Type=simple
+WorkingDirectory=/opt/frc971/scouting_server
+ExecStart=/opt/frc971/scouting_server/scouting/scouting \
+    -port 8080 \
+    -database /var/frc971/scouting/scouting.db \
+    -tba_config /var/frc971/scouting/tba_config.json
+Restart=always
+
+[Install]
+WantedBy=multi-user.target