|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# mise-node devcontainer feature — install step. |
| 4 | +# |
| 5 | +# Installs mise (https://mise.jdx.dev) for the remote user, wires up shell |
| 6 | +# activation, and ships postCreate.sh to /usr/local/share/mise-node/ where |
| 7 | +# it is invoked from devcontainer-feature.json's `postCreateCommand`. |
| 8 | + |
| 9 | +set -euo pipefail |
| 10 | + |
| 11 | + |
| 12 | +if [ "$(id -u)" -ne 0 ]; then |
| 13 | + echo "ERROR: Script must be run as root." |
| 14 | + exit 1 |
| 15 | +fi |
| 16 | + |
| 17 | +# Resolve target user. |
| 18 | +USERNAME="${_REMOTE_USER:-${USERNAME:-automatic}}" |
| 19 | +if [ "${USERNAME}" = "automatic" ] || [ -z "${USERNAME}" ]; then |
| 20 | + USERNAME="" |
| 21 | + for candidate in vscode node codespace "$(awk -v val=1000 -F ':' '$3==val{print $1}' /etc/passwd)"; do |
| 22 | + if [ -n "${candidate}" ] && id -u "${candidate}" >/dev/null 2>&1; then |
| 23 | + USERNAME="${candidate}" |
| 24 | + break |
| 25 | + fi |
| 26 | + done |
| 27 | + [ -z "${USERNAME}" ] && USERNAME="root" |
| 28 | +fi |
| 29 | + |
| 30 | +USER_HOME="$(getent passwd "${USERNAME}" | cut -d: -f6)" |
| 31 | +if [ -z "${USER_HOME}" ] || [ ! -d "${USER_HOME}" ]; then |
| 32 | + echo "ERROR: Could not resolve home directory for user '${USERNAME}'." |
| 33 | + exit 1 |
| 34 | +fi |
| 35 | +USER_GROUP="$(id -gn "${USERNAME}")" |
| 36 | + |
| 37 | +# Dependencies needed to fetch and run mise. |
| 38 | +apt_get_update() { |
| 39 | + if [ ! -d /var/lib/apt/lists ] || [ "$(find /var/lib/apt/lists -mindepth 1 2>/dev/null | wc -l)" = "0" ]; then |
| 40 | + apt-get update -y |
| 41 | + fi |
| 42 | +} |
| 43 | +check_packages() { |
| 44 | + if ! dpkg -s "$@" >/dev/null 2>&1; then |
| 45 | + apt_get_update |
| 46 | + apt-get install -y --no-install-recommends "$@" |
| 47 | + fi |
| 48 | +} |
| 49 | + |
| 50 | +export DEBIAN_FRONTEND=noninteractive |
| 51 | +check_packages curl ca-certificates git xz-utils |
| 52 | + |
| 53 | +su - "${USERNAME}" -c "curl -fsSL https://mise.run | sh" |
| 54 | + |
| 55 | +MISE_BIN="${USER_HOME}/.local/bin/mise" |
| 56 | +if [ ! -x "${MISE_BIN}" ]; then |
| 57 | + echo "ERROR: mise was not installed at ${MISE_BIN}." |
| 58 | + exit 1 |
| 59 | +fi |
| 60 | + |
| 61 | +# Activate mise in interactive bash and zsh. |
| 62 | +ensure_line() { |
| 63 | + local file="$1" line="$2" |
| 64 | + [ -e "${file}" ] || su - "${USERNAME}" -c "touch '${file}'" |
| 65 | + if ! grep -Fxq "${line}" "${file}"; then |
| 66 | + printf '%s\n' "${line}" >> "${file}" |
| 67 | + chown "${USERNAME}:${USER_GROUP}" "${file}" |
| 68 | + fi |
| 69 | +} |
| 70 | +ensure_line "${USER_HOME}/.bashrc" 'eval "$(~/.local/bin/mise activate bash)"' |
| 71 | +ensure_line "${USER_HOME}/.zshrc" 'eval "$(~/.local/bin/mise activate zsh)"' |
| 72 | + |
| 73 | +# Expose mise + shims for non-interactive / login shells. |
| 74 | +cat >/etc/profile.d/mise-node.sh <<EOF |
| 75 | +# Added by the mise-node devcontainer feature. |
| 76 | +export PATH="${USER_HOME}/.local/bin:${USER_HOME}/.local/share/mise/shims:\${PATH}" |
| 77 | +EOF |
| 78 | +chmod 0644 /etc/profile.d/mise-node.sh |
| 79 | + |
| 80 | +# Ship postCreate.sh and a small env file so it knows which user/options to use. |
| 81 | +SHARE_DIR="/usr/local/share/mise-node" |
| 82 | +mkdir -p "${SHARE_DIR}" |
| 83 | + |
| 84 | +FEATURE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 85 | +install -m 0755 "${FEATURE_DIR}/postCreate.sh" "${SHARE_DIR}/postCreate.sh" |
| 86 | + |
| 87 | +cat >"${SHARE_DIR}/env" <<EOF |
| 88 | +# Generated by mise-node install.sh — consumed by postCreate.sh. |
| 89 | +MISE_NODE_USER="${USERNAME}" |
| 90 | +MISE_NODE_USER_HOME="${USER_HOME}" |
| 91 | +
|
| 92 | +EOF |
| 93 | +chmod 0644 "${SHARE_DIR}/env" |
| 94 | + |
| 95 | +echo "mise-node feature installed." |
| 96 | +echo " mise: ${MISE_BIN}" |
| 97 | +echo " postCreate: ${SHARE_DIR}/postCreate.sh" |
| 98 | +echo " user: ${USERNAME}" |
0 commit comments