scripts/bare-server-setup.sh
#!/bin/bash
set -eufx -o pipefail

# If the script has already run, exit early.
[ -f /tmp/bare-server-setup.sh.done ] && exit 0

PYTHON_VERSION=3.11.9
CUDA_VERSION=12.4
INSTALL_CUDA=true
INSTALL_PYENV=true
INSTALL_DOCKER=true
while [[ "$#" -gt 0 ]]
do
    case $1 in
        --cuda-version=*)
            CUDA_VERSION="${1#*=}"
            shift
            ;;
        --cuda-version)
            CUDA_VERSION="$2"
            shift 2
            ;;
        --python-version=*)
            PYTHON_VERSION="${1#*=}"
            shift
            ;;
        --python-version)
            PYTHON_VERSION="$2"
            shift 2
            ;;
        --no-cuda)
            INSTALL_CUDA=false
            shift
            ;;
        --no-pyenv)
            INSTALL_PYENV=false
            shift
            ;;
        --no-docker)
            INSTALL_DOCKER=false
            shift
            ;;
        *)
            echo "Unknown parameter passed: $1"
            exit 1
            ;;
    esac
done


_install_cuda() {
    CUDA_DISTRO="$(grep -oP '(?<=^ID=)\w+$' /etc/os-release)$(grep -oP '(?<=^VERSION_ID=")[\d.]+' /etc/os-release | sed 's/\.//g')"
    CUDA_REPO="https://developer.download.nvidia.com/compute/cuda/repos/${CUDA_DISTRO}/x86_64"
    CUDA_GPG_KEY=/usr/share/keyrings/nvidia-cuda.gpg
    CUDA_MAJOR_VERSION="${CUDA_VERSION%%.*}"

    wget -O- "${CUDA_REPO}/3bf863cc.pub" | gpg --dearmor | sudo tee "${CUDA_GPG_KEY}" > /dev/null
    echo "deb [signed-by=${CUDA_GPG_KEY} arch=amd64] ${CUDA_REPO}/ /" | sudo tee /etc/apt/sources.list.d/nvidia-cuda.list > /dev/null
    sudo apt-get update -y
    sudo apt-get install -yq --no-install-recommends \
            build-essential \
            cmake \
            cuda-libraries-${CUDA_VERSION} \
            cuda-nvcc-${CUDA_VERSION} \
            cuda-nvrtc-dev-${CUDA_VERSION} \
            cuda-nvtx-${CUDA_VERSION} \
            cuda-profiler-api-${CUDA_VERSION} \
            libcublas-dev-${CUDA_VERSION} \
            libcudnn9-dev-cuda-${CUDA_MAJOR_VERSION} \
            libcurand-dev-${CUDA_VERSION} \
            libcusolver-dev-${CUDA_VERSION} \
            libcusparse-dev-${CUDA_VERSION}

    cat <<EOF >> ~/.bashrc
LD_LIBRARY_PATH=/usr/local/cuda-${CUDA_VERSION}/lib64
NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility
export PATH=/usr/local/cuda-${CUDA_VERSION}/bin:\${PATH}
EOF
}

_install_pyenv() {
    rm -rf ~/.pyenv
    # This is what pyenv.run does, but sometimes we can't reach it with "Could not resolve host: pyenv.run"
    curl -s -S -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
    sudo apt update -y
    sudo apt install -y \
            libbz2-dev \
            libffi-dev \
            liblzma-dev \
            libncurses-dev \
            libreadline-dev \
            libsqlite3-dev \
            libssl-dev
    cat <<'EOF' >> ~/.bashrc
export PYENV_ROOT="${HOME}/.pyenv"
[[ -d ${PYENV_ROOT}/bin ]] && export PATH="${PYENV_ROOT}/bin:${PATH}"
eval "$(pyenv init -)"
EOF
    ~/.pyenv/bin/pyenv install ${PYTHON_VERSION}
    ~/.pyenv/bin/pyenv global ${PYTHON_VERSION}
    sudo sed -i 's|PATH="|PATH="'${HOME}'/.pyenv/shims:|' /etc/environment
}

_install_docker() {
    # Add Docker's official GPG key:
    sudo apt-get update
    sudo apt-get install -y --no-install-recommends \
        ca-certificates \
        curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc

    # Add the repository to Apt sources:
    echo \
        "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
        $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
        | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
    sudo apt-get install -y \
        containerd.io \
        docker-buildx-plugin \
        docker-ce \
        docker-ce-cli \
        docker-compose-plugin
    sudo apt install -y nvidia-docker2
    sudo systemctl restart docker

    sudo usermod -aG docker ${USER}
    sudo su -l ${USER} -c "docker run --rm --gpus all nvidia/cuda:${CUDA_VERSION}.0-base-ubuntu22.04 nvidia-smi"
}

_install_tailscale() {
    # cf https://askubuntu.com/a/1431746
    export NEEDRESTART_MODE=a && curl -fsSL https://tailscale.com/install.sh | sh
    sudo tailscale up --auth-key=$TAILSCALE_AUTH_KEY --hostname=$TAILSCALE_HOSTNAME --advertise-tags=$TAILSCALE_TAGS
}

[ "${INSTALL_CUDA}" = false ] && echo 'skipping cuda install' || _install_cuda
[ "${INSTALL_PYENV}" = false ] && echo 'skipping pyenv install' || _install_pyenv
[ "${INSTALL_DOCKER}" = false ] && echo 'skipping docker install' || _install_docker
echo "installing tailscale!"
_install_tailscale

# Test python availability in non-interactive shell
if ! echo 'python --version' | bash -s
then
    sudo apt install python-is-python3
fi

# Write a file to indicate the script has run successfully.
touch /tmp/bare-server-setup.sh.done
