You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

563 lines
13 KiB

  1. #!/bin/sh
  2. set -eu
  3. # code-server's automatic install script.
  4. # See https://github.com/cdr/code-server/blob/master/doc/install.md
  5. usage() {
  6. arg0="$0"
  7. if [ "$0" = sh ]; then
  8. arg0="curl -fsSL https://code-server.dev/install.sh | sh -s --"
  9. else
  10. not_curl_usage="The latest script is available at https://code-server.dev/install.sh
  11. "
  12. fi
  13. cath << EOF
  14. Installs code-server for Linux, macOS and FreeBSD.
  15. It tries to use the system package manager if possible.
  16. After successful installation it explains how to start using code-server.
  17. Pass in user@host to install code-server on user@host over ssh.
  18. The remote host must have internet access.
  19. ${not_curl_usage-}
  20. Usage:
  21. $arg0 [--dry-run] [--version X.X.X] [--method detect] \
  22. [--prefix ~/.local] [--rsh ssh] [user@host]
  23. --dry-run
  24. Echo the commands for the install process without running them.
  25. --version X.X.X
  26. Install a specific version instead of the latest.
  27. --method [detect | standalone]
  28. Choose the installation method. Defaults to detect.
  29. - detect detects the system package manager and tries to use it.
  30. Full reference on the process is further below.
  31. - standalone installs a standalone release archive into ~/.local
  32. Add ~/.local/bin to your \$PATH to use it.
  33. --prefix <dir>
  34. Sets the prefix used by standalone release archives. Defaults to ~/.local
  35. The release is unarchived into ~/.local/lib/code-server-X.X.X
  36. and the binary symlinked into ~/.local/bin/code-server
  37. To install system wide pass ---prefix=/usr/local
  38. --rsh <bin>
  39. Specifies the remote shell for remote installation. Defaults to ssh.
  40. - For Debian, Ubuntu and Raspbian it will install the latest deb package.
  41. - For Fedora, CentOS, RHEL and openSUSE it will install the latest rpm package.
  42. - For Arch Linux it will install the AUR package.
  43. - For any unrecognized Linux operating system it will install the latest standalone
  44. release into ~/.local
  45. - For macOS it will install the Homebrew package.
  46. - If Homebrew is not installed it will install the latest standalone release
  47. into ~/.local
  48. - For FreeBSD, it will install the npm package with yarn or npm.
  49. - If ran on an architecture with no releases, it will install the
  50. npm package with yarn or npm.
  51. - We only have releases for amd64 and arm64 presently.
  52. - The npm package builds the native modules on postinstall.
  53. It will cache all downloaded assets into ~/.cache/code-server
  54. More installation docs are at https://github.com/cdr/code-server/blob/master/doc/install.md
  55. EOF
  56. }
  57. echo_latest_version() {
  58. # https://gist.github.com/lukechilds/a83e1d7127b78fef38c2914c4ececc3c#gistcomment-2758860
  59. version="$(curl -fsSLI -o /dev/null -w "%{url_effective}" https://github.com/cdr/code-server/releases/latest)"
  60. version="${version#https://github.com/cdr/code-server/releases/tag/}"
  61. version="${version#v}"
  62. echo "$version"
  63. }
  64. echo_npm_postinstall() {
  65. echoh
  66. cath << EOF
  67. The npm package has been installed successfully!
  68. Please extend your path to use code-server:
  69. PATH="$NPM_BIN_DIR:\$PATH"
  70. Please run with:
  71. code-server
  72. EOF
  73. }
  74. echo_standalone_postinstall() {
  75. echoh
  76. cath << EOF
  77. Standalone release has been installed into $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION
  78. Please extend your path to use code-server:
  79. PATH="$STANDALONE_INSTALL_PREFIX/bin:\$PATH"
  80. Then you can run:
  81. code-server
  82. EOF
  83. }
  84. echo_systemd_postinstall() {
  85. echoh
  86. cath << EOF
  87. To have systemd start code-server now and restart on boot:
  88. sudo systemctl enable --now code-server@\$USER
  89. Or, if you don't want/need a background service you can run:
  90. code-server
  91. EOF
  92. }
  93. main() {
  94. if [ "${TRACE-}" ]; then
  95. set -x
  96. fi
  97. unset \
  98. DRY_RUN \
  99. METHOD \
  100. STANDALONE_INSTALL_PREFIX \
  101. VERSION \
  102. OPTIONAL \
  103. ALL_FLAGS \
  104. RSH_ARGS \
  105. RSH
  106. ALL_FLAGS=""
  107. while [ "$#" -gt 0 ]; do
  108. case "$1" in
  109. -*)
  110. ALL_FLAGS="${ALL_FLAGS} $1"
  111. ;;
  112. esac
  113. case "$1" in
  114. --dry-run)
  115. DRY_RUN=1
  116. ;;
  117. --method)
  118. METHOD="$(parse_arg "$@")"
  119. shift
  120. ;;
  121. --method=*)
  122. METHOD="$(parse_arg "$@")"
  123. ;;
  124. --prefix)
  125. STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
  126. shift
  127. ;;
  128. --prefix=*)
  129. STANDALONE_INSTALL_PREFIX="$(parse_arg "$@")"
  130. ;;
  131. --version)
  132. VERSION="$(parse_arg "$@")"
  133. shift
  134. ;;
  135. --version=*)
  136. VERSION="$(parse_arg "$@")"
  137. ;;
  138. --rsh)
  139. RSH="$(parse_arg "$@")"
  140. shift
  141. ;;
  142. --rsh=*)
  143. RSH="$(parse_arg "$@")"
  144. ;;
  145. -h | --h | -help | --help)
  146. usage
  147. exit 0
  148. ;;
  149. --)
  150. shift
  151. # We remove the -- added above.
  152. ALL_FLAGS="${ALL_FLAGS% --}"
  153. RSH_ARGS="$*"
  154. break
  155. ;;
  156. -*)
  157. echoerr "Unknown flag $1"
  158. echoerr "Run with --help to see usage."
  159. exit 1
  160. ;;
  161. *)
  162. RSH_ARGS="$*"
  163. break
  164. ;;
  165. esac
  166. shift
  167. done
  168. if [ "${RSH_ARGS-}" ]; then
  169. RSH="${RSH-ssh}"
  170. echoh "Installing remotely with $RSH $RSH_ARGS"
  171. curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" "$RSH" "$RSH_ARGS" sh -s -- "$ALL_FLAGS"
  172. return
  173. fi
  174. VERSION="${VERSION-$(echo_latest_version)}"
  175. METHOD="${METHOD-detect}"
  176. if [ "$METHOD" != detect ] && [ "$METHOD" != standalone ]; then
  177. echoerr "Unknown install method \"$METHOD\""
  178. echoerr "Run with --help to see usage."
  179. exit 1
  180. fi
  181. STANDALONE_INSTALL_PREFIX="${STANDALONE_INSTALL_PREFIX-$HOME/.local}"
  182. OS="$(os)"
  183. if [ ! "$OS" ]; then
  184. echoerr "Unsupported OS $(uname)."
  185. exit 1
  186. fi
  187. distro_name
  188. ARCH="$(arch)"
  189. if [ ! "$ARCH" ]; then
  190. if [ "$METHOD" = standalone ]; then
  191. echoerr "No precompiled releases for $(uname -m)."
  192. echoerr 'Please rerun without the "--method standalone" flag to install from npm.'
  193. exit 1
  194. fi
  195. echoh "No precompiled releases for $(uname -m)."
  196. install_npm
  197. return
  198. fi
  199. if [ "$OS" = "freebsd" ]; then
  200. if [ "$METHOD" = standalone ]; then
  201. echoerr "No precompiled releases available for $OS."
  202. echoerr 'Please rerun without the "--method standalone" flag to install from npm.'
  203. exit 1
  204. fi
  205. echoh "No precompiled releases available for $OS."
  206. install_npm
  207. return
  208. fi
  209. CACHE_DIR="$(echo_cache_dir)"
  210. if [ "$METHOD" = standalone ]; then
  211. install_standalone
  212. return
  213. fi
  214. case "$(distro)" in
  215. macos)
  216. install_macos
  217. ;;
  218. debian)
  219. install_deb
  220. ;;
  221. fedora | opensuse)
  222. install_rpm
  223. ;;
  224. arch)
  225. install_aur
  226. ;;
  227. *)
  228. echoh "Unsupported package manager."
  229. install_standalone
  230. ;;
  231. esac
  232. }
  233. parse_arg() {
  234. case "$1" in
  235. *=*)
  236. # Remove everything after first equal sign.
  237. opt="${1%%=*}"
  238. # Remove everything before first equal sign.
  239. optarg="${1#*=}"
  240. if [ ! "$optarg" ] && [ ! "${OPTIONAL-}" ]; then
  241. echoerr "$opt requires an argument"
  242. echoerr "Run with --help to see usage."
  243. exit 1
  244. fi
  245. echo "$optarg"
  246. return
  247. ;;
  248. esac
  249. case "${2-}" in
  250. "" | -*)
  251. if [ ! "${OPTIONAL-}" ]; then
  252. echoerr "$1 requires an argument"
  253. echoerr "Run with --help to see usage."
  254. exit 1
  255. fi
  256. ;;
  257. *)
  258. echo "$2"
  259. return
  260. ;;
  261. esac
  262. }
  263. fetch() {
  264. URL="$1"
  265. FILE="$2"
  266. if [ -e "$FILE" ]; then
  267. echoh "+ Reusing $FILE"
  268. return
  269. fi
  270. sh_c mkdir -p "$CACHE_DIR"
  271. sh_c curl \
  272. -#fL \
  273. -o "$FILE.incomplete" \
  274. -C - \
  275. "$URL"
  276. sh_c mv "$FILE.incomplete" "$FILE"
  277. }
  278. install_macos() {
  279. if command_exists brew; then
  280. echoh "Installing from Homebrew."
  281. echoh
  282. sh_c brew install code-server
  283. return
  284. fi
  285. echoh "Homebrew not installed."
  286. install_standalone
  287. }
  288. install_deb() {
  289. echoh "Installing v$VERSION deb package from GitHub releases."
  290. echoh
  291. fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server_${VERSION}_$ARCH.deb" \
  292. "$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
  293. sudo_sh_c dpkg -i "$CACHE_DIR/code-server_${VERSION}_$ARCH.deb"
  294. echo_systemd_postinstall
  295. }
  296. install_rpm() {
  297. echoh "Installing v$VERSION rpm package from GitHub releases."
  298. echoh
  299. fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server-$VERSION-$ARCH.rpm" \
  300. "$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
  301. sudo_sh_c rpm -i "$CACHE_DIR/code-server-$VERSION-$ARCH.rpm"
  302. echo_systemd_postinstall
  303. }
  304. install_aur() {
  305. echoh "Installing from the AUR."
  306. echoh
  307. sh_c mkdir -p "$CACHE_DIR/code-server-aur"
  308. sh_c "curl -#fsSL https://aur.archlinux.org/cgit/aur.git/snapshot/code-server.tar.gz | tar -xzC $CACHE_DIR/code-server-aur --strip-components 1"
  309. echo "+ cd $CACHE_DIR/code-server-aur"
  310. if [ ! "${DRY_RUN-}" ]; then
  311. cd "$CACHE_DIR/code-server-aur"
  312. fi
  313. sh_c makepkg -si
  314. echo_systemd_postinstall
  315. }
  316. install_standalone() {
  317. echoh "Installing standalone release archive v$VERSION from GitHub releases."
  318. echoh
  319. fetch "https://github.com/cdr/code-server/releases/download/v$VERSION/code-server-$VERSION-$OS-$ARCH.tar.gz" \
  320. "$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
  321. sh_c="sh_c"
  322. if [ ! -w "$STANDALONE_INSTALL_PREFIX" ]; then
  323. sh_c="sudo_sh_c"
  324. fi
  325. if [ -e "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION" ]; then
  326. echoh
  327. echoh "code-server-$VERSION is already installed at $STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
  328. echoh "Remove it to reinstall."
  329. exit 0
  330. fi
  331. "$sh_c" mkdir -p "$STANDALONE_INSTALL_PREFIX/lib" "$STANDALONE_INSTALL_PREFIX/bin"
  332. "$sh_c" tar -C "$STANDALONE_INSTALL_PREFIX/lib" -xzf "$CACHE_DIR/code-server-$VERSION-$OS-$ARCH.tar.gz"
  333. "$sh_c" mv -f "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION-$OS-$ARCH" "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION"
  334. "$sh_c" ln -fs "$STANDALONE_INSTALL_PREFIX/lib/code-server-$VERSION/bin/code-server" "$STANDALONE_INSTALL_PREFIX/bin/code-server"
  335. echo_standalone_postinstall
  336. }
  337. install_npm() {
  338. if command_exists yarn; then
  339. sh_c="sh_c"
  340. if [ ! -w "$(yarn global bin)" ]; then
  341. sh_c="sudo_sh_c"
  342. fi
  343. echoh "Installing with yarn."
  344. echoh
  345. "$sh_c" yarn global add code-server --unsafe-perm
  346. NPM_BIN_DIR="$(yarn global bin)" echo_npm_postinstall
  347. return
  348. elif command_exists npm; then
  349. sh_c="sh_c"
  350. if [ ! -w "$(npm config get prefix)" ]; then
  351. sh_c="sudo_sh_c"
  352. fi
  353. echoh "Installing with npm."
  354. echoh
  355. "$sh_c" npm install -g code-server --unsafe-perm
  356. NPM_BIN_DIR="$(npm bin -g)" echo_npm_postinstall
  357. return
  358. fi
  359. echoh
  360. echoerr "Please install npm or yarn to install code-server!"
  361. echoerr "You will need at least node v12 and a few C dependencies."
  362. echoerr "See the docs https://github.com/cdr/code-server#yarn-npm"
  363. exit 1
  364. }
  365. os() {
  366. case "$(uname)" in
  367. Linux)
  368. echo linux
  369. ;;
  370. Darwin)
  371. echo macos
  372. ;;
  373. FreeBSD)
  374. echo freebsd
  375. ;;
  376. esac
  377. }
  378. # distro prints the detected operating system including linux distros.
  379. # Also parses ID_LIKE for common distro bases.
  380. #
  381. # Example outputs:
  382. # - macos -> macos
  383. # - freebsd -> freebsd
  384. # - ubuntu, raspbian, debian ... -> debian
  385. # - amzn, centos, rhel, fedora, ... -> fedora
  386. # - opensuse-{leap,tumbleweed} -> opensuse
  387. # - alpine -> alpine
  388. # - arch -> arch
  389. #
  390. # Inspired by https://github.com/docker/docker-install/blob/26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c/install.sh#L111-L120.
  391. distro() {
  392. if [ "$OS" = "macos" ] || [ "$OS" = "freebsd" ]; then
  393. echo "$OS"
  394. return
  395. fi
  396. if [ -f /etc/os-release ]; then
  397. (
  398. . /etc/os-release
  399. if [ "${ID_LIKE-}" ]; then
  400. for id_like in $ID_LIKE; do
  401. case "$id_like" in debian | fedora | opensuse)
  402. echo "$id_like"
  403. return
  404. ;;
  405. esac
  406. done
  407. fi
  408. echo "$ID"
  409. )
  410. return
  411. fi
  412. }
  413. # os_name prints a pretty human readable name for the OS/Distro.
  414. distro_name() {
  415. if [ "$(uname)" = "Darwin" ]; then
  416. echo "macOS v$(sw_vers -productVersion)"
  417. return
  418. fi
  419. if [ -f /etc/os-release ]; then
  420. (
  421. . /etc/os-release
  422. echo "$PRETTY_NAME"
  423. )
  424. return
  425. fi
  426. # Prints something like: Linux 4.19.0-9-amd64
  427. uname -sr
  428. }
  429. arch() {
  430. case "$(uname -m)" in
  431. aarch64)
  432. echo arm64
  433. ;;
  434. x86_64)
  435. echo amd64
  436. ;;
  437. amd64) # FreeBSD.
  438. echo amd64
  439. ;;
  440. esac
  441. }
  442. command_exists() {
  443. command -v "$@" > /dev/null
  444. }
  445. sh_c() {
  446. echoh "+ $*"
  447. if [ ! "${DRY_RUN-}" ]; then
  448. sh -c "$*"
  449. fi
  450. }
  451. sudo_sh_c() {
  452. if [ "$(id -u)" = 0 ]; then
  453. sh_c "$@"
  454. elif command_exists sudo; then
  455. sh_c "sudo $*"
  456. elif command_exists su; then
  457. sh_c "su -c '$*'"
  458. else
  459. echoh
  460. echoerr "This script needs to run the following command as root."
  461. echoerr " $*"
  462. echoerr "Please install sudo or su."
  463. exit 1
  464. fi
  465. }
  466. echo_cache_dir() {
  467. if [ "${XDG_CACHE_HOME-}" ]; then
  468. echo "$XDG_CACHE_HOME/code-server"
  469. elif [ "${HOME-}" ]; then
  470. echo "$HOME/.cache/code-server"
  471. else
  472. echo "/tmp/code-server-cache"
  473. fi
  474. }
  475. echoh() {
  476. echo "$@" | humanpath
  477. }
  478. cath() {
  479. humanpath
  480. }
  481. echoerr() {
  482. echoh "$@" >&2
  483. }
  484. # humanpath replaces all occurances of " $HOME" with " ~"
  485. # and all occurances of '"$HOME' with the literal '"$HOME'.
  486. humanpath() {
  487. sed "s# $HOME# ~#g; s#\"$HOME#\"\$HOME#g"
  488. }
  489. # We need to make sure we exit with a non zero exit if the command fails.
  490. # /bin/sh does not support -o pipefail unfortunately.
  491. prefix() {
  492. PREFIX="$1"
  493. shift
  494. fifo="$(mktemp -d)/fifo"
  495. mkfifo "$fifo"
  496. sed -e "s#^#$PREFIX: #" "$fifo" &
  497. "$@" > "$fifo" 2>&1
  498. }
  499. main "$@"