...

Text file src/oss.terrastruct.com/d2/install.sh

Documentation: oss.terrastruct.com/d2

     1#!/bin/sh
     2set -eu
     3
     4# *************
     5# DO NOT EDIT
     6#
     7# install.sh was bundled together from
     8#
     9# - ./ci/sub/lib/rand.sh
    10# - ./ci/sub/lib/temp.sh
    11# - ./ci/sub/lib/log.sh
    12# - ./ci/sub/lib/flag.sh
    13# - ./ci/sub/lib/release.sh
    14# - ./ci/release/_install.sh
    15#
    16# The last of which implements the installation logic.
    17#
    18# Generated by ./ci/release/gen_install.sh.
    19# *************
    20
    21#!/bin/sh
    22if [ "${LIB_RAND-}" ]; then
    23  return 0
    24fi
    25LIB_RAND=1
    26
    27pick() {
    28  seed="$1"
    29  shift
    30
    31  seed_file="$(mktempd)/pickseed"
    32
    33  # We add 32 more bytes to the seed file for sufficient entropy. Otherwise both Cygwin's
    34  # and MinGW's sort for example complains about the lack of entropy on stderr and writes
    35  # nothing to stdout. I'm sure there are more platforms that would too.
    36  #
    37  # We also limit to a max of 32 bytes as otherwise macOS's sort complains that the random
    38  # seed is too large. Probably more platforms too.
    39  (echo "$seed" && echo "================================") | head -c32 >"$seed_file"
    40
    41  while [ $# -gt 0 ]; do
    42    echo "$1"
    43    shift
    44  done \
    45    | sort --sort=random --random-source="$seed_file" \
    46    | head -n1
    47}
    48#!/bin/sh
    49if [ "${LIB_TEMP-}" ]; then
    50  return 0
    51fi
    52LIB_TEMP=1
    53
    54ensure_tmpdir() {
    55  if [ -n "${_TMPDIR-}" ]; then
    56    return
    57  fi
    58  _TMPDIR=$(mktemp -d)
    59  export _TMPDIR
    60}
    61
    62if [ -z "${_TMPDIR-}" ]; then
    63  trap 'rm -Rf "$_TMPDIR"' EXIT
    64fi
    65ensure_tmpdir
    66
    67temppath() {
    68  while true; do
    69    temppath=$_TMPDIR/$(</dev/urandom od -N8 -tx -An -v | tr -d '[:space:]')
    70    if [ ! -e "$temppath" ]; then
    71      echo "$temppath"
    72      return
    73    fi
    74  done
    75}
    76
    77mktempd() {
    78  tp=$(temppath)
    79  mkdir -p "$tp"
    80  echo "$tp"
    81}
    82#!/bin/sh
    83if [ "${LIB_LOG-}" ]; then
    84  return 0
    85fi
    86LIB_LOG=1
    87
    88if [ -n "${TRACE-}" ]; then
    89  set -x
    90fi
    91
    92tput() {
    93  if should_color; then
    94    TERM=${TERM:-xterm-256color} command tput "$@"
    95  fi
    96}
    97
    98should_color() {
    99  if [ -n "${COLOR-}" ]; then
   100    if [ "$COLOR" = 1 -o "$COLOR" = true ]; then
   101      _COLOR=1
   102      __COLOR=1
   103      return 0
   104    elif [ "$COLOR" = 0 -o "$COLOR" = false ]; then
   105      _COLOR=
   106      __COLOR=0
   107      return 1
   108    else
   109      printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
   110    fi
   111  fi
   112
   113  if [ -t 1 -a "${TERM-}" != dumb ]; then
   114    _COLOR=1
   115    __COLOR=1
   116    return 0
   117  else
   118    _COLOR=
   119    __COLOR=0
   120    return 1
   121  fi
   122}
   123
   124setaf() {
   125  fg=$1
   126  shift
   127  printf '%s\n' "$*" | while IFS= read -r line; do
   128    tput setaf "$fg"
   129    printf '%s' "$line"
   130    tput sgr0
   131    printf '\n'
   132  done
   133}
   134
   135_echo() {
   136  printf '%s\n' "$*"
   137}
   138
   139get_rand_color() {
   140  if [ "${TERM_COLORS+x}" != x ]; then
   141    TERM_COLORS=""
   142    export TERM_COLORS
   143    ncolors=$(TERM=${TERM:-xterm-256color} command tput colors)
   144    if [ "$ncolors" -ge 8 ]; then
   145      # 1-6 are regular
   146      TERM_COLORS="$TERM_COLORS 1 2 3 4 5 6"
   147    elif [ "$ncolors" -ge 16 ]; then
   148      # 9-14 are bright.
   149      TERM_COLORS="$TERM_COLORS 9 10 11 12 13 14"
   150    fi
   151  fi
   152  pick "$*" $TERM_COLORS
   153}
   154
   155echop() {
   156  prefix="$1"
   157  shift
   158
   159  if [ "$#" -gt 0 ]; then
   160    printfp "$prefix" "%s\n" "$*"
   161  else
   162    printfp "$prefix"
   163    printf '\n'
   164  fi
   165}
   166
   167printfp() {(
   168  prefix="$1"
   169  shift
   170
   171  _FGCOLOR=${FGCOLOR:-$(get_rand_color "$prefix")}
   172  should_color || true
   173  if [ $# -eq 0 ]; then
   174    printf '%s' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")"
   175  else
   176    printf '%s: %s\n' "$(COLOR=$__COLOR setaf "$_FGCOLOR" "$prefix")" "$(printf "$@")"
   177  fi
   178)}
   179
   180catp() {
   181  prefix="$1"
   182  shift
   183
   184  should_color || true
   185  sed "s/^/$(COLOR=$__COLOR printfp "$prefix" '')/"
   186}
   187
   188repeat() {
   189  char="$1"
   190  times="$2"
   191  seq -s "$char" "$times" | tr -d '[:digit:]'
   192}
   193
   194strlen() {
   195  printf %s "$1" | wc -c
   196}
   197
   198echoerr() {
   199  FGCOLOR=1 logp err "$*"
   200}
   201
   202caterr() {
   203  FGCOLOR=1 logpcat err "$@"
   204}
   205
   206printferr() {
   207  FGCOLOR=1 logfp err "$@"
   208}
   209
   210logp() {
   211  should_color >&2 || true
   212  COLOR=$__COLOR echop "$@" | humanpath >&2
   213}
   214
   215logfp() {
   216  should_color >&2 || true
   217  COLOR=$__COLOR printfp "$@" | humanpath >&2
   218}
   219
   220logpcat() {
   221  should_color >&2 || true
   222  COLOR=$__COLOR catp "$@" | humanpath >&2
   223}
   224
   225log() {
   226  FGCOLOR=5 logp log "$@"
   227}
   228
   229logf() {
   230  FGCOLOR=5 logfp log "$@"
   231}
   232
   233logcat() {
   234  FGCOLOR=5 logpcat log "$@"
   235}
   236
   237warn() {
   238  FGCOLOR=3 logp warn "$@"
   239}
   240
   241warnf() {
   242  FGCOLOR=3 logfp warn "$@"
   243}
   244
   245warncat() {
   246  FGCOLOR=3 logpcat warn "$@"
   247}
   248
   249sh_c() {
   250  FGCOLOR=3 logp exec "$*"
   251  if [ -z "${DRY_RUN-}" ]; then
   252    eval "$@"
   253  fi
   254}
   255
   256sudo_sh_c() {
   257  if [ "$(id -u)" -eq 0 ]; then
   258    sh_c "$@"
   259  elif command -v doas >/dev/null; then
   260    sh_c "doas $*"
   261  elif command -v sudo >/dev/null; then
   262    sh_c "sudo $*"
   263  elif command -v su >/dev/null; then
   264    sh_c "su root -c '$*'"
   265  else
   266    caterr <<EOF
   267Unable to run the following command as root:
   268  $*
   269Please install doas, sudo, or su.
   270EOF
   271    return 1
   272  fi
   273}
   274
   275header() {
   276  FGCOLOR=${FGCOLOR:-4} logp "/* $1 */"
   277}
   278
   279bigheader() {
   280  set -- "$(echo "$*" | sed "s/^/ * /")"
   281  FGCOLOR=${FGCOLOR:-6} logp "/****************************************************************
   282$*
   283 ****************************************************************/"
   284}
   285
   286# humanpath replaces all occurrences of " $HOME" with " ~"
   287# and all occurrences of '$HOME' with the literal '$HOME'.
   288humanpath() {
   289  if [ -z "${HOME-}" ]; then
   290    cat
   291  else
   292    sed -e "s# $HOME# ~#g" -e "s#$HOME#\$HOME#g"
   293  fi
   294}
   295
   296hide() {
   297  out="$(mktempd)/hideout"
   298  capcode "$@" >"$out" 2>&1
   299  if [ "$code" -eq 0 ]; then
   300    return
   301  fi
   302  cat "$out" >&2
   303  return "$code"
   304}
   305
   306hide_stderr() {
   307  out="$(mktempd)/hideout"
   308  capcode "$@" 2>"$out"
   309  if [ "$code" -eq 0 ]; then
   310    return
   311  fi
   312  cat "$out" >&2
   313  return "$code"
   314}
   315
   316echo_dur() {
   317  local dur=$1
   318  local h=$((dur/60/60))
   319  local m=$((dur/60%60))
   320  local s=$((dur%60))
   321  printf '%dh%dm%ds' "$h" "$m" "$s"
   322}
   323
   324sponge() {
   325  dst="$1"
   326  tmp="$(mktempd)/sponge"
   327  cat > "$tmp"
   328  cat "$tmp" > "$dst"
   329}
   330
   331stripansi() {
   332  # First regex gets rid of standard xterm escape sequences for controlling
   333  # visual attributes.
   334  # The second regex I'm not 100% sure, the reference says it selects the US
   335  # encoding but I'm not sure why that's necessary or why it always occurs
   336  # in tput sgr0 before the standard escape sequence.
   337  # See tput sgr0 | xxd
   338  sed -e $'s/\x1b\[[0-9;]*m//g' -e $'s/\x1b(.//g'
   339}
   340
   341runtty() {
   342  case "$(uname)" in
   343    Darwin)
   344      script -q /dev/null "$@"
   345      ;;
   346    Linux)
   347      script -eqc "$*"
   348      ;;
   349    *)
   350      echoerr "runtty: unsupported OS $(uname)"
   351      return 1
   352  esac
   353}
   354
   355capcode() {
   356  set +e
   357  "$@"
   358  code=$?
   359  set -e
   360}
   361
   362strjoin() {
   363  (IFS="$1"; shift; echo "$*")
   364}
   365#!/bin/sh
   366if [ "${LIB_FLAG-}" ]; then
   367  return 0
   368fi
   369LIB_FLAG=1
   370
   371# flag_parse implements a robust flag parser.
   372#
   373# For a full fledge example see ../examples/date.sh
   374#
   375# It differs from getopts(1) in that long form options are supported. Currently the only
   376# deficiency is that short combined options are not supported like -xyzq. That would be
   377# interpreted as a single -xyzq flag. The other deficiency is lack of support for short
   378# flag syntax like -carg where the arg is not separated from the flag. This one is
   379# unfixable I believe unfortunately but for combined short flags I have opened
   380# https://github.com/terrastruct/ci/issues/6
   381#
   382# flag_parse stores state in $FLAG, $FLAGRAW, $FLAGARG and $FLAGSHIFT.
   383# FLAG contains the name of the flag without hyphens.
   384# FLAGRAW contains the name of the flag as passed in with hyphens.
   385# FLAGARG contains the argument for the flag if there was any.
   386#   If there was none, it will not be set.
   387# FLAGSHIFT contains the number by which the arguments should be shifted to
   388#   start at the next flag/argument
   389#
   390# flag_parse exits with a non zero code when there are no more flags
   391# to be parsed. Still, call shift "$FLAGSHIFT" in case there was a --
   392#
   393# If the argument for the flag is optional, then use ${FLAGARG-} to access
   394# the argument if one was passed. Use ${FLAGARG+x} = x to check if it was set.
   395# You only need to explicitly check if the flag was set if you care whether the user
   396# explicitly passed the empty string as the argument.
   397#
   398# Otherwise, call one of the flag_*arg functions:
   399#
   400# If a flag requires an argument, call flag_reqarg
   401#   - $FLAGARG is guaranteed to be set after.
   402# If a flag requires a non empty argument, call flag_nonemptyarg
   403#   - $FLAGARG is guaranteed to be set to a non empty string after.
   404# If a flag should not be passed an argument, call flag_noarg
   405#   - $FLAGARG is guaranteed to be unset after.
   406#
   407# And then shift "$FLAGSHIFT"
   408flag_parse() {
   409  case "${1-}" in
   410    -*=*)
   411      # Remove everything after first equal sign.
   412      FLAG="${1%%=*}"
   413      # Remove leading hyphens.
   414      FLAG="${FLAG#-}"; FLAG="${FLAG#-}"
   415      FLAGRAW="$(flag_fmt)"
   416      # Remove everything before first equal sign.
   417      FLAGARG="${1#*=}"
   418      FLAGSHIFT=1
   419      return 0
   420      ;;
   421    -)
   422      FLAGSHIFT=0
   423      return 1
   424      ;;
   425    --)
   426      FLAGSHIFT=1
   427      return 1
   428      ;;
   429    -*)
   430      # Remove leading hyphens.
   431      FLAG="${1#-}"; FLAG="${FLAG#-}"
   432      FLAGRAW=$(flag_fmt)
   433      unset FLAGARG
   434      FLAGSHIFT=1
   435      if [ $# -gt 1 ]; then
   436        case "$2" in
   437          -)
   438            FLAGARG="$2"
   439            FLAGSHIFT=2
   440            ;;
   441          -*)
   442            ;;
   443          *)
   444            FLAGARG="$2"
   445            FLAGSHIFT=2
   446            ;;
   447        esac
   448      fi
   449      return 0
   450      ;;
   451    *)
   452      FLAGSHIFT=0
   453      return 1
   454      ;;
   455  esac
   456}
   457
   458flag_reqarg() {
   459  if [ "${FLAGARG+x}" != x ]; then
   460    flag_errusage "flag $FLAGRAW requires an argument"
   461  fi
   462}
   463
   464flag_nonemptyarg() {
   465  flag_reqarg
   466  if [ -z "$FLAGARG" ]; then
   467    flag_errusage "flag $FLAGRAW requires a non-empty argument"
   468  fi
   469}
   470
   471flag_noarg() {
   472  if [ "$FLAGSHIFT" -eq 2 ]; then
   473    unset FLAGARG
   474    FLAGSHIFT=1
   475  elif [ "${FLAGARG+x}" = x ]; then
   476    # Means an argument was passed via equal sign as in -$FLAG=$FLAGARG
   477    flag_errusage "flag $FLAGRAW does not accept an argument"
   478  fi
   479}
   480
   481flag_errusage() {
   482  caterr <<EOF
   483$1
   484Run with --help for usage.
   485EOF
   486  return 1
   487}
   488
   489flag_fmt() {
   490  if [ "$(printf %s "$FLAG" | wc -c)" -eq 1 ]; then
   491    echo "-$FLAG"
   492  else
   493    echo "--$FLAG"
   494  fi
   495}
   496#!/bin/sh
   497if [ "${LIB_RELEASE-}" ]; then
   498  return 0
   499fi
   500LIB_RELEASE=1
   501
   502ensure_os() {
   503  if [ -n "${OS-}" ]; then
   504    # Windows defines OS=Windows_NT.
   505    if [ "$OS" = Windows_NT ]; then
   506      OS=windows
   507    fi
   508    return
   509  fi
   510  uname=$(uname)
   511  case $uname in
   512    Linux) OS=linux;;
   513    Darwin) OS=macos;;
   514    FreeBSD) OS=freebsd;;
   515    *) OS=$uname;;
   516  esac
   517}
   518
   519ensure_arch() {
   520  if [ -n "${ARCH-}" ]; then
   521    return
   522  fi
   523  uname_m=$(uname -m)
   524  case $uname_m in
   525    aarch64) ARCH=arm64;;
   526    x86_64) ARCH=amd64;;
   527    *) ARCH=$uname_m;;
   528  esac
   529}
   530
   531ensure_goos() {
   532  if [ -n "${GOOS-}" ]; then
   533    return
   534  fi
   535  ensure_os
   536  case "$OS" in
   537    macos) export GOOS=darwin;;
   538    *) export GOOS=$OS;;
   539  esac
   540}
   541
   542ensure_goarch() {
   543  if [ -n "${GOARCH-}" ]; then
   544    return
   545  fi
   546  ensure_arch
   547  case "$ARCH" in
   548    *) export GOARCH=$ARCH;;
   549  esac
   550}
   551
   552gh_repo() {
   553  gh repo view --json nameWithOwner --template '{{ .nameWithOwner }}'
   554}
   555
   556manpath() {
   557  if command -v manpath >/dev/null; then
   558    command manpath
   559  elif man -w 2>/dev/null; then
   560    man -w
   561  else
   562    echo "${MANPATH-}"
   563  fi
   564}
   565
   566is_writable_dir() {
   567  mkdir -p "$1" 2>/dev/null
   568  # directory must exist otherwise -w returns 1 even for paths that should be writable.
   569  [ -w "$1" ]
   570}
   571
   572ensure_prefix() {
   573  if [ -n "${PREFIX-}" ]; then
   574    return
   575  fi
   576  # The reason for checking whether lib is writable is that on macOS you have /usr/local
   577  # owned by root but you don't need root to write to its subdirectories which is all we
   578  # need to do.
   579  if ! is_writable_dir "/usr/local/lib"; then
   580    # This also handles M1 Mac's which do not allow modifications to /usr/local even
   581    # with sudo.
   582    PREFIX=$HOME/.local
   583  else
   584    PREFIX=/usr/local
   585  fi
   586}
   587
   588ensure_prefix_sh_c() {
   589  ensure_prefix
   590
   591  sh_c="sh_c"
   592  # The reason for checking whether lib is writable is that on macOS you have /usr/local
   593  # owned by root but you don't need root to write to its subdirectories which is all we
   594  # need to do.
   595  if ! is_writable_dir "$PREFIX/lib"; then
   596    sh_c="sudo_sh_c"
   597  fi
   598}
   599#!/bin/sh
   600set -eu
   601
   602
   603help() {
   604  arg0="$0"
   605  if [ "$0" = sh ]; then
   606    arg0="curl -fsSL https://d2lang.com/install.sh | sh -s --"
   607  fi
   608
   609  cat <<EOF
   610usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix path]
   611  [--tala latest] [--force] [--uninstall] [-x|--trace]
   612
   613install.sh automates the installation of D2 onto your system. It currently only supports
   614the installation of standalone releases from GitHub and via Homebrew on macOS. See the
   615docs for --detect below for more information
   616
   617If you pass --edge, it will clone the source, build a release and install from it.
   618--edge is incompatible with --tala and currently unimplemented.
   619
   620\$PREFIX in the docs below refers to the path set by --prefix. See docs on the --prefix
   621flag below for the default.
   622
   623Flags:
   624
   625-d, --dry-run
   626  Pass to have install.sh show the install method and flags that will be used to install
   627  without executing them. Very useful to understand what changes it will make to your system.
   628
   629--version vX.X.X
   630  Pass to have install.sh install the given version instead of the latest version.
   631  warn: The version may not be obeyed with package manager installations. Use
   632        --method=standalone to enforce the version.
   633
   634--edge
   635  Pass to build and install D2 from source. This will still use --method if set to detect
   636  to install the release archive for your OS, whether it's apt, yum, brew or standalone
   637  if an unsupported package manager is used.
   638
   639  To install from source like a dev would, use go install oss.terrastruct.com/d2. There's
   640  also ./ci/release/build.sh --install to build and install a proper standalone release
   641  including manpages. The proper release will also ensure d2 --version shows the correct
   642  version by embedding the commit hash into the binary.
   643
   644  note: currently unimplemented.
   645  warn: incompatible with --tala as TALA is closed source.
   646
   647--method [detect | standalone | homebrew ]
   648  Pass to control the method by which to install. Right now we only support standalone
   649  releases from GitHub but later we'll add support for brew, rpm, deb and more.
   650
   651  - detect will use your OS's package manager automatically.
   652    So far it only detects macOS and automatically uses homebrew.
   653  - homebrew uses https://brew.sh/ which is a macOS and Linux package manager.
   654  - standalone installs a standalone release archive into the unix hierarchy path
   655     specified by --prefix
   656
   657--prefix path
   658  Controls the unix hierarchy path into which standalone releases are installed.
   659  Defaults to /usr/local or ~/.local if /usr/local is not writable by the current user.
   660  Remember that whatever you use, you must have the bin directory of your prefix path in
   661  \$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then
   662  my \$PATH must contain /usr/local/bin.
   663  You may also need to include \$PREFIX/share/man into \$MANPATH.
   664  install.sh will tell you whether \$PATH or \$MANPATH need to be updated after successful
   665  installation.
   666
   667--tala [latest]
   668  Install Terrastruct's closed source TALA for improved layouts.
   669  See https://github.com/terrastruct/tala
   670  It optionally takes an argument of the TALA version to install.
   671  Installation obeys all other flags, just like the installation of d2. For example,
   672  the d2plugin-tala binary will be installed into \$PREFIX/bin/d2plugin-tala
   673  warn: The version may not be obeyed with package manager installations. Use
   674        --method=standalone to enforce the version.
   675
   676--force:
   677  Force installation over the existing version even if they match. It will attempt a
   678  uninstall first before installing the new version. The installed release tree
   679  will be deleted from \$PREFIX/lib/d2/d2-<VERSION> but the release archive in
   680  ~/.cache/d2/release will remain.
   681
   682--uninstall:
   683  Uninstall the installed version of d2. The --method and --prefix flags must be the same
   684  as for installation. i.e if you used --method standalone you must again use --method
   685  standalone for uninstallation. With detect, the install script will try to use the OS
   686  package manager to uninstall instead.
   687  note: tala will also be uninstalled if installed.
   688
   689-x, --trace:
   690  Run script with set -x.
   691
   692All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change
   693path of the cached assets. Release archives are unarchived into \$PREFIX/lib/d2/d2-<VERSION>
   694
   695note: Deleting the unarchived releases will cause --uninstall to stop working.
   696
   697You can rerun install.sh to update your version of D2. install.sh will avoid reinstalling
   698if the installed version is the latest unless --force is passed.
   699
   700See https://github.com/terrastruct/d2/blob/master/docs/INSTALL.md#security for
   701documentation on its security.
   702EOF
   703}
   704
   705main() {
   706  while flag_parse "$@"; do
   707    case "$FLAG" in
   708      h|help)
   709        help
   710        return 0
   711        ;;
   712      d|dry-run)
   713        flag_noarg && shift "$FLAGSHIFT"
   714        DRY_RUN=1
   715        ;;
   716      version)
   717        flag_nonemptyarg && shift "$FLAGSHIFT"
   718        VERSION=$FLAGARG
   719        ;;
   720      tala)
   721        shift "$FLAGSHIFT"
   722        TALA=${FLAGARG:-latest}
   723        ;;
   724      edge)
   725        flag_noarg && shift "$FLAGSHIFT"
   726        EDGE=1
   727        echoerr "$FLAGRAW is currently unimplemented"
   728        return 1
   729        ;;
   730      method)
   731        flag_nonemptyarg && shift "$FLAGSHIFT"
   732        METHOD=$FLAGARG
   733        ;;
   734      prefix)
   735        flag_nonemptyarg && shift "$FLAGSHIFT"
   736        export PREFIX=$FLAGARG
   737        ;;
   738      force)
   739        flag_noarg && shift "$FLAGSHIFT"
   740        FORCE=1
   741        ;;
   742      uninstall)
   743        flag_noarg && shift "$FLAGSHIFT"
   744        UNINSTALL=1
   745        ;;
   746      x|trace)
   747        flag_noarg && shift "$FLAGSHIFT"
   748        set -x
   749        export TRACE=1
   750        ;;
   751      *)
   752        flag_errusage "unrecognized flag $FLAGRAW"
   753        ;;
   754    esac
   755  done
   756  shift "$FLAGSHIFT"
   757
   758  if [ $# -gt 0 ]; then
   759    flag_errusage "no arguments are accepted"
   760  fi
   761
   762  REPO=${REPO:-terrastruct/d2}
   763  ensure_os
   764  ensure_arch
   765  ensure_prefix
   766  CACHE_DIR=$(cache_dir)
   767  mkdir -p "$CACHE_DIR"
   768  METHOD=${METHOD:-detect}
   769  INSTALL_DIR=$PREFIX/lib/d2
   770
   771  case $METHOD in
   772    detect)
   773      case "$OS" in
   774        macos)
   775          if command -v brew >/dev/null; then
   776            log "detected macOS with homebrew, using homebrew for installation"
   777            METHOD=homebrew
   778          else
   779            warn "detected macOS without homebrew, falling back to --method=standalone"
   780            METHOD=standalone
   781          fi
   782          ;;
   783        linux|windows)
   784          METHOD=standalone
   785          ;;
   786        *)
   787          warn "unrecognized OS $OS, falling back to --method=standalone"
   788          METHOD=standalone
   789          ;;
   790      esac
   791      ;;
   792    standalone) ;;
   793    homebrew) ;;
   794    *)
   795      echoerr "unknown installation method $METHOD"
   796      return 1
   797      ;;
   798  esac
   799
   800  if [ -n "${UNINSTALL-}" ]; then
   801    uninstall
   802    if [ -n "${DRY_RUN-}" ]; then
   803      bigheader "Rerun without --dry-run to execute printed commands and perform install."
   804    fi
   805  else
   806    install
   807    if [ -n "${DRY_RUN-}" ]; then
   808      bigheader "Rerun without --dry-run to execute printed commands and perform install."
   809    fi
   810  fi
   811}
   812
   813install() {
   814  case $METHOD in
   815    standalone)
   816      install_d2_standalone
   817      if [ -n "${TALA-}" ]; then
   818        # Run in subshell to avoid overwriting VERSION.
   819        TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )"
   820      fi
   821      ;;
   822    homebrew)
   823      install_d2_brew
   824      if [ -n "${TALA-}" ]; then install_tala_brew; fi
   825      ;;
   826  esac
   827
   828  FGCOLOR=2 bigheader 'next steps'
   829  case $METHOD in
   830    standalone) install_post_standalone ;;
   831    homebrew) install_post_brew ;;
   832  esac
   833  install_post_warn
   834}
   835
   836install_post_standalone() {
   837  log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
   838  if [ -n "${TALA-}" ]; then
   839    log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX"
   840  fi
   841  log "Rerun this install script with --uninstall to uninstall."
   842  log
   843  if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then
   844    logcat >&2 <<EOF
   845Extend your \$PATH to use d2:
   846  export PATH=$PREFIX/bin:\$PATH
   847Then run:
   848  ${TALA:+D2_LAYOUT=tala }d2 --help
   849EOF
   850  else
   851    log "Run ${TALA:+D2_LAYOUT=tala }d2 --help for usage."
   852  fi
   853  if ! manpath 2>/dev/null | grep -qF "$PREFIX/share/man"; then
   854    logcat >&2 <<EOF
   855Extend your \$MANPATH to view d2's manpages:
   856  export MANPATH=$PREFIX/share/man:\$MANPATH
   857Then run:
   858  man d2
   859EOF
   860  if [ -n "${TALA-}" ]; then
   861    log "  man d2plugin-tala"
   862  fi
   863  else
   864    log "Run man d2 for detailed docs."
   865    if [ -n "${TALA-}" ]; then
   866      log "Run man d2plugin-tala for detailed TALA docs."
   867    fi
   868  fi
   869}
   870
   871install_post_brew() {
   872  log "d2 has been successfully installed with homebrew."
   873  if [ -n "${TALA-}" ]; then
   874    log "tala has been successfully installed with homebrew."
   875  fi
   876  log "Rerun this install script with --uninstall to uninstall."
   877  log
   878  log "Run ${TALA:+D2_LAYOUT=tala }d2 --help for usage."
   879  log "Run man d2 for detailed docs."
   880  if [ -n "${TALA-}" ]; then
   881    log "Run man d2plugin-tala for detailed TALA docs."
   882  fi
   883
   884  VERSION=$(brew info d2 | head -n1 | cut -d' ' -f4)
   885  VERSION=${VERSION%,}
   886  if [ -n "${TALA-}" ]; then
   887    TALA_VERSION=$(brew info tala | head -n1 | cut -d' ' -f4)
   888    TALA_VERSION=${TALA_VERSION%,}
   889  fi
   890}
   891
   892install_post_warn() {
   893  if command -v d2 >/dev/null; then
   894    INSTALLED_VERSION=$(d2 --version)
   895    if [ "$INSTALLED_VERSION" != "$VERSION" ]; then
   896      warn "newly installed d2 $VERSION is shadowed by d2 $INSTALLED_VERSION in \$PATH"
   897    fi
   898  fi
   899  if [ -n "${TALA-}" ] && command -v d2plugin-tala >/dev/null; then
   900    INSTALLED_TALA_VERSION=$(d2plugin-tala --version)
   901    if [ "$INSTALLED_TALA_VERSION" != "$TALA_VERSION" ]; then
   902      warn "newly installed d2plugin-tala $TALA_VERSION is shadowed by d2plugin-tala $INSTALLED_TALA_VERSION in \$PATH"
   903    fi
   904  fi
   905}
   906
   907install_d2_standalone() {
   908  VERSION=${VERSION:-latest}
   909  header "installing d2-$VERSION"
   910
   911  if [ "$VERSION" = latest ]; then
   912    fetch_release_info
   913  fi
   914
   915  if command -v d2 >/dev/null; then
   916    INSTALLED_VERSION="$(d2 --version)"
   917    if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
   918      log "skipping installation as d2 $VERSION is already installed."
   919      return 0
   920    fi
   921    log "uninstalling d2 $INSTALLED_VERSION to install $VERSION"
   922    if ! uninstall_d2_standalone; then
   923      warn "failed to uninstall d2 $INSTALLED_VERSION"
   924    fi
   925  fi
   926
   927  ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz"
   928  log "installing standalone release $ARCHIVE from github"
   929
   930  fetch_release_info
   931  asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
   932  asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
   933  fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
   934
   935  ensure_prefix_sh_c
   936  "$sh_c" mkdir -p "'$INSTALL_DIR'"
   937  "$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
   938  "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
   939}
   940
   941install_d2_brew() {
   942  header "installing d2 with homebrew"
   943  sh_c brew update
   944  sh_c brew install d2
   945}
   946
   947install_tala_standalone() {
   948  REPO="${REPO_TALA:-terrastruct/tala}"
   949  VERSION=$TALA
   950
   951  header "installing tala-$VERSION"
   952
   953  if [ "$VERSION" = latest ]; then
   954    fetch_release_info
   955  fi
   956
   957  if command -v d2plugin-tala >/dev/null; then
   958    INSTALLED_VERSION="$(d2plugin-tala --version)"
   959    if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then
   960      log "skipping installation as tala $VERSION is already installed."
   961      return 0
   962    fi
   963    log "uninstalling tala $INSTALLED_VERSION to install $VERSION"
   964    if ! uninstall_tala_standalone; then
   965      warn "failed to uninstall tala $INSTALLED_VERSION"
   966    fi
   967  fi
   968
   969  ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz"
   970  log "installing standalone release $ARCHIVE from github"
   971
   972  fetch_release_info
   973  asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1')
   974  asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"')
   975
   976  fetch_gh "$asset_url" "$CACHE_DIR/$ARCHIVE" 'application/octet-stream'
   977
   978  ensure_prefix_sh_c
   979  "$sh_c" mkdir -p "'$INSTALL_DIR'"
   980  "$sh_c" tar -C "$INSTALL_DIR" -xzf "$CACHE_DIR/$ARCHIVE"
   981  "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'"
   982}
   983
   984install_tala_brew() {
   985  header "installing tala with homebrew"
   986  sh_c brew update
   987  sh_c brew install terrastruct/tap/tala
   988}
   989
   990uninstall() {
   991  # We uninstall tala first as package managers require that it be uninstalled before
   992  # uninstalling d2 as TALA depends on d2.
   993  if command -v d2plugin-tala >/dev/null; then
   994    INSTALLED_VERSION="$(d2plugin-tala --version)"
   995    header "uninstalling tala-$INSTALLED_VERSION"
   996    case $METHOD in
   997      standalone) uninstall_tala_standalone ;;
   998      homebrew) uninstall_tala_brew ;;
   999    esac
  1000  elif [ "${TALA-}" ]; then
  1001    warn "no version of tala installed"
  1002  fi
  1003
  1004  if ! command -v d2 >/dev/null; then
  1005    warn "no version of d2 installed"
  1006    return 0
  1007  fi
  1008
  1009  INSTALLED_VERSION="$(d2 --version)"
  1010  header "uninstalling d2-$INSTALLED_VERSION"
  1011  case $METHOD in
  1012    standalone) uninstall_d2_standalone ;;
  1013    homebrew) uninstall_d2_brew ;;
  1014  esac
  1015}
  1016
  1017uninstall_d2_standalone() {
  1018  log "uninstalling standalone release of d2-$INSTALLED_VERSION"
  1019
  1020  if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then
  1021    warn "missing standalone install release directory $INSTALL_DIR/d2-$INSTALLED_VERSION"
  1022    warn "d2 must have been installed via some other installation method."
  1023    return 1
  1024  fi
  1025
  1026  ensure_prefix_sh_c
  1027  "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
  1028  "$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION"
  1029}
  1030
  1031uninstall_d2_brew() {
  1032  sh_c brew remove d2
  1033}
  1034
  1035uninstall_tala_standalone() {
  1036  log "uninstalling standalone release tala-$INSTALLED_VERSION"
  1037
  1038  if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then
  1039    warn "missing standalone install release directory $INSTALL_DIR/tala-$INSTALLED_VERSION"
  1040    warn "tala must have been installed via some other installation method."
  1041    return 1
  1042  fi
  1043
  1044  ensure_prefix_sh_c
  1045  "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$INSTALLED_VERSION\" && make uninstall PREFIX=\"$PREFIX\"'"
  1046  "$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION"
  1047}
  1048
  1049uninstall_tala_brew() {
  1050  sh_c brew remove tala
  1051}
  1052
  1053cache_dir() {
  1054  if [ -n "${XDG_CACHE_HOME-}" ]; then
  1055    echo "$XDG_CACHE_HOME/d2/release"
  1056  elif [ -n "${HOME-}" ]; then
  1057    echo "$HOME/.cache/d2/release"
  1058  else
  1059    echo "/tmp/d2-cache/release"
  1060  fi
  1061}
  1062
  1063fetch_release_info() {
  1064  if [ -n "${RELEASE_INFO-}" ]; then
  1065    return 0
  1066  fi
  1067
  1068  log "fetching info on $VERSION version of $REPO"
  1069  RELEASE_INFO=$(mktempd)/release-info.json
  1070  if [ "$VERSION" = latest ]; then
  1071    release_info_url="https://api.github.com/repos/$REPO/releases/$VERSION"
  1072  else
  1073    release_info_url="https://api.github.com/repos/$REPO/releases/tags/$VERSION"
  1074  fi
  1075  DRY_RUN= fetch_gh "$release_info_url" "$RELEASE_INFO" \
  1076    'application/json'
  1077  VERSION=$(cat "$RELEASE_INFO" | grep -m1 tag_name | sed 's/^.*: "\(.*\)",$/\1/g')
  1078}
  1079
  1080curl_gh() {
  1081  sh_c curl -fL ${GITHUB_TOKEN:+"-H \"Authorization: Bearer \$GITHUB_TOKEN\""} "$@"
  1082}
  1083
  1084fetch_gh() {
  1085  url=$1
  1086  file=$2
  1087  accept=$3
  1088
  1089  if [ -e "$file" ]; then
  1090    log "reusing $file"
  1091    return
  1092  fi
  1093
  1094  curl_gh -#o "$file.inprogress" -C- -H "'Accept: $accept'" "$url"
  1095  sh_c mv "$file.inprogress" "$file"
  1096}
  1097
  1098# The main function does more than provide organization. It provides robustness in that if
  1099# the install script was to only partial download into sh, sh will not execute it because
  1100# main is not invoked until the very last byte.
  1101main "$@"

View as plain text