riju/doc/tutorial/install.md

11 KiB
Raw Permalink Blame History

Tutorial: install your language

Most likely, your language isn't installed by default in Ubuntu. If not, you'll need to add an install: block to your language configuration describing how to install it.

The easiest case is system packages, which are supported out of the box, for example:

install:
  apt:
    - ruby
  npm:
    - pug-cli
  pip:
    - bython
  gem:
    - solargraph
  cpan:
    - Acme::Chef
  opam:
    - ocamlformat

For more advanced configuration you need to understand how Debian packaging works. To build a package, we place files into a ${pkg} directory mirroring how they should be installed on the system, so for example a binary intended for /usr/local/bin/prettier would be placed at ${pkg}/usr/local/bin/prettier. Then the ${pkg} directory can be turned into a .deb, which can be installed on any system.

If you have trouble, see the tutorial on debugging package installation.

Download a binary or script

We prefer to put all binaries in /usr/local/bin rather than /usr/bin where they might conflict with default Ubuntu packages.

install:
  manual: |
    install -d "${pkg}/usr/local/bin"

    wget http://www.blue.sky.or.jp/grass/grass.rb
    chmod +x grass.rb
    cp -T grass.rb "${pkg}/usr/local/bin/grass"    

Get the latest version from GitHub

There is a predefined latest_release function that can be used to get the name of the latest tag in a GitHub repository.

install:
  manual: |
    install -d "${pkg}/usr/local/bin"

    ver="$(latest_release snoe/clojure-lsp)"
    wget "https://github.com/snoe/clojure-lsp/releases/download/${ver}/clojure-lsp"
    chmod +x clojure-lsp
    cp clojure-lsp "${pkg}/usr/local/bin/"    

Get the latest version from some other website

In the case that there's no "download the latest version" URL, we typically use disgusting grep pipelines to extract it in a semi-reliable way from a stable-seeming webpage.

install:
  manual: |
    install -d "${pkg}/usr/local/bin"

    path="$(curl -fsSL https://static.red-lang.org/download.html | grep -Eo '/dl/linux/[^"]+' | head -n1)"
    wget "https://static.red-lang.org${path}" -O red
    chmod +x red
    cp red "${pkg}/usr/local/bin/"    

Unpack a tarball

Sometimes tarballs can be extracted directly to /usr/local. However, unless there's an obviously correct way to do that, we typically put language distributions in /opt/<lang> and then put symlinks in /usr/local/bin for the binaries. The -C and --strip-components flags are very helpful for controlling the extraction.

Also, one thing that might be unintuitive is that we use system-global paths for the targets of symlinks (/opt/swift/bin/swiftc) but ${pkg} paths for the link names (${pkg}/usr/local/bin/swiftc). This is because while we are putting all files into ${pkg} during build, the eventual place they will be installed by the package is into the root filesystem, so any references to paths within files (including symlink targets) must not mention ${pkg}. This is a standard feature of all Linux packaging tools.

install:
  manual: |
    install -d "${pkg}/opt/swift"
    install -d "${pkg}/usr/local/bin"

    ver="$(latest_release apple/swift | grep -Eo '[0-9.]+')"
    wget "https://swift.org/builds/swift-${ver}-release/ubuntu2004/swift-${ver}-RELEASE/swift-${ver}-RELEASE-ubuntu20.04.tar.gz" -O swift.tar.gz
    tar -xf swift.tar.gz -C "${pkg}/opt/swift" --strip-components=2
    ln -s /opt/swift/bin/swiftc /opt/swift/bin/sourcekit-lsp "${pkg}/usr/local/bin/"    

Compile from source

Sometimes there is no binary distribution. In this case you want to clone/download the source code and run the appropriate compilation command. GCC and LLVM plus a number of scripting languages are already installed into the packaging image by default, but you might have to include a prepare block listing additional things to install to be able to compile the software.

For example, in this case the build.sh script provided by Zot happens to require some Qt development dependencies at compile time, and a non-development dependency at runtime.

install:
  prepare:
    apt:
      - qt5-qmake
      - qtscript5-dev
  apt:
    - libqt5script5
  manual: |
    install -d "${pkg}/usr/local/bin"

    git clone https://github.com/manyoso/zot.git
    pushd zot
    ./build.sh
    cp build/bin/zot "${pkg}/usr/local/bin/"
    popd    

Use a shared dependency

Your language may need to use software such as Prettier which is also used by other languages but does not have an Ubuntu package. In that case we package it as a Riju shared dependency (check the shared directory for a list), and you declare the dependency using the riju key.

install:
  riju:
    - prettier

Install custom scripts or config files

Sometimes you may need to add a wrapper script or config file somewhere on the filesystem. Riju has the scripts and files keys for this. The keys of these maps are the filesystem paths (scripts defaults to putting things in /usr/local/bin if relative paths are given).

install:
  scripts:
    teco-encode: |
      #!/usr/bin/env -S python3 -u

      import re
      import sys

      for line in sys.stdin:
          line = re.sub(r"\^(.)", lambda m: chr(ord(m.group(1)) ^ 0b1000000), line)
          line = line.replace("$", chr(27))
          print(line, end="")      
  files:
    "/opt/sqlite/sqls.yml": |
      connections:
        - driver: sqlite3
          dataSourceName: db.sqlite3      

Dealing with versioned APT packages

For some reason, some APT packages have version numbers in their names. For example, you can't sudo apt install lua; you have to sudo apt install lua5.4. The best way to deal with these situations is to use various grep-aptavail hacks to identify the latest available version programmatically. Check the man page as well as uses of grep-aptavail in Riju to understand the options.

install:
  apt:
    - $(grep-aptavail -XF Provides lua -a -XF Version "$(grep-aptavail -XF Provides lua -s Version -n | sort -Vr | head -n1)" -s Package -n | head -n1)

Custom APT repos

Some languages are distributed in third-party APT repositories. You can identify this by looking for installation directions that say to run add-apt-repository or add a file to /etc/apt/sources.list.d. Frequently you're also asked to import a custom signing key using apt-key.

Riju provides shorthands for these operations. The aptKey option can be either a URL or a hexadecimal string (both forms appear in commands you're asked to run). The aptRepo option is a line that can be added to /etc/apt/sources.list.d, which can be copied out of the suggested installation command.

Assuming these keys are specified, then all the packages in the custom APT repo(s) will be available for selection under the apt option, in addition to standard Ubuntu packages.

install:
  aptKey:
    - "https://keybase.io/crystal/pgp_keys.asc"
  aptRepo:
    - "deb [arch=amd64] https://dist.crystal-lang.org/apt crystal main"
  apt:
    - crystal

Getting the name of the current Ubuntu release

Sometimes you need to download from a URL or add an APT repository whose name depends on the version (e.g. 21.04) or name (e.g. hirsute) of the current Ubuntu release. These parameters are automatically available in the ${ubuntu_ver} and ${ubuntu_name} variables, which can be used in most option values:

install:
  aptKey:
    - "B4112585D386EB94"
  aptRepo:
    - "deb [arch=amd64] https://dl.hhvm.com/ubuntu ${ubuntu_name} main"
  apt:
    - hhvm

Dealing with broken certificate chains

Consider the following errors from curl and wget respectively:

curl: (60) SSL certificate problem: unable to get local issuer certificate
ERROR: cannot verify ioke.org's certificate, issued by CN=R3,O=Let's Encrypt,C=US:
  Unable to locally verify the issuer's authority.

These are often because of a server misconfiguration. Servers are supposed to send all intermediate certificates in their certificate chain, but sometimes they fail to do this. In that case, you need to manually download the missing intermediate certificate from its issuer, and install it.

You can get the information about which certificate is required from SSL Labs. Then, by Googling the name of the missing certificate that SSL Labs has found, you can typically find it as a download from the certificate issuer.

Riju has a cert option which takes URLs of certificates to download and install (note the use of prepare here because we need the certificate when building the package, not just when installing it):

install:
  prepare:
    cert:
      - "https://letsencrypt.org/certs/lets-encrypt-r3.pem"
  apt:
    - default-jdk
  manual: |
    install -d "${pkg}/opt/ioke"
    install -d "${pkg}/usr/local/bin"

    wget https://ioke.org/dist/ioke-ikj-latest.tar.gz -O ioke.tar.gz
    tar -xf ioke.tar.gz -C "${pkg}/opt/ioke" --strip-components=1
    ln -s /opt/ioke/bin/ioke "${pkg}/usr/local/bin/ioke"    

Setting up skeleton files

Sometimes your language may require some configuration files in addition to the actual file to be compiled. Or alternatively, your language may do a bunch of caching work the first time it starts up, which you don't want to happen every time someone launches it on Riju. Both of these problems can be solved by preparing files to be copied into the user's home directory at the start of a new Riju session (this is done using the setup key, which is explained later).

In the following example, we ask .NET to create a project template for us, and then compile the project. Then we take the files that were created for the project, plus cache directories in $HOME, and copy them into /opt so that they can be copied back later using setup. By convention, we name such directories skel.

install:
  prepare: &install-dotnet
    preface: |
      wget "https://packages.microsoft.com/config/ubuntu/${ubuntu_ver}/packages-microsoft-prod.deb"
      sudo --preserve-env=DEBIAN_FRONTEND apt-get install ./packages-microsoft-prod.deb
      sudo --preserve-env=DEBIAN_FRONTEND apt-get update      
    apt:
      - $(grep-aptavail -wF Package "dotnet-sdk-3\.[0-9.]+" -s Package -n | sort -Vr | head -n1)
  <<: *install-dotnet
  manual: |
    install -d "${pkg}/opt/qsharp/skel-home"
    install -d "${pkg}/opt/qsharp/skel-src"

    dotnet new -i Microsoft.Quantum.ProjectTemplates
    dotnet new console -lang Q# -o main
    dotnet run --project main

    shopt -s dotglob
    cp -R main "${pkg}/opt/qsharp/skel-src/"
    cp -R "${HOME}/.dotnet" "${HOME}/.nuget" "${pkg}/opt/qsharp/skel-home/"
    rm "${pkg}/opt/qsharp/skel-src/main/Program.qs"
    chmod -R a=u,go-w "${pkg}/opt/qsharp"    

setup: |
  shopt -s dotglob
  cp -R /opt/qsharp/skel-src/* ./
  cp -R /opt/qsharp/skel-home/* "${HOME}/"  

What about...?

Refer to jsonschema.yaml for the complete reference about what keys are accepted in install. See generate-build-script.js for how these are interpreted programmatically, and see the generated build.bash and install.bash scripts (in build/lang/<name>/) for examples of the outputs. And of course, check some of the other languages, since probably what you want to do has come up before at some point.