github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/bundled/epm.elv.go (about)

     1  package bundled
     2  
     3  const epmElv = `use re
     4  
     5  # Verbosity configuration
     6  debug-mode = $false
     7  
     8  # Configuration for common domains
     9  -default-domain-config = [
    10    &"github.com"= [
    11      &method= git
    12      &protocol= https
    13      &levels= 2
    14    ]
    15    &"bitbucket.org"= [
    16      &method= git
    17      &protocol= https
    18      &levels= 2
    19    ]
    20    &"gitlab.com"= [
    21      &method= git
    22      &protocol= https
    23      &levels= 2
    24    ]
    25  ]
    26  
    27  # Internal configuration
    28  -data-dir = ~/.elvish
    29  -lib-dir = $-data-dir/lib
    30  
    31  # General utility functions
    32  
    33  fn -debug [text]{
    34    if $debug-mode {
    35      print (edit:styled '=> ' blue)
    36      echo $text
    37    }
    38  }
    39  
    40  fn -info [text]{
    41    print (edit:styled '=> ' green)
    42    echo $text
    43  }
    44  
    45  fn -warn [text]{
    46    print (edit:styled '=> ' yellow)
    47    echo $text
    48  }
    49  
    50  fn -error [text]{
    51    print (edit:styled '=> ' red)
    52    echo $text
    53  }
    54  
    55  fn dest [pkg]{
    56    put $-lib-dir/$pkg
    57  }
    58  
    59  fn is-installed [pkg]{
    60    bool ?(test -e (dest $pkg))
    61  }
    62  
    63  fn -package-domain [pkg]{
    64    splits &max=2 / $pkg | take 1
    65  }
    66  
    67  fn -package-without-domain [pkg]{
    68    splits &max=2 / $pkg | drop 1 | joins ''
    69  }
    70  
    71  # Merge two maps
    72  fn -merge [a b]{
    73    keys $b | each [k]{ a[$k] = $b[$k] }
    74    put $a
    75  }
    76  
    77  # Uppercase first letter of a string
    78  fn -first-upper [s]{
    79    put (echo $s[0] | tr '[:lower:]' '[:upper:]')$s[(count $s[0]):]
    80  }
    81  
    82  # Expand tilde at the beginning of a string to the home dir
    83  fn -tilde-expand [p]{
    84    re:replace "^~" $E:HOME $p
    85  }
    86  
    87  # Known method handlers. Each entry is indexed by method name (the
    88  # value of the "method" key in the domain configs), and must contain
    89  # two keys: install and upgrade, each one must be a closure that
    90  # received two arguments: package name and the domain config entry
    91  #
    92  # - Method 'git' requires the key 'protocol' in the domain config,
    93  #   which has to be 'http' or 'https'
    94  # - Method 'rsync' requires the key 'location' in the domain config,
    95  #   which has to contain the directory where the domain files are
    96  #   stored. It can be any source location understood by the rsync
    97  #   command.
    98  -method-handler = [
    99    &git= [
   100      &src= [pkg dom-cfg]{
   101        put $dom-cfg[protocol]"://"$pkg
   102      }
   103  
   104      &install= [pkg dom-cfg]{
   105        dest = (dest $pkg)
   106        -info "Installing "$pkg
   107        mkdir -p $dest
   108        git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
   109      }
   110  
   111      &upgrade= [pkg dom-cfg]{
   112        dest = (dest $pkg)
   113        -info "Updating "$pkg
   114        try {
   115          git -C $dest pull
   116        } except _ {
   117            -error "Something failed, please check error above and retry."
   118        }
   119      }
   120    ]
   121  
   122    &rsync= [
   123      &src= [pkg dom-cfg]{
   124        put (-tilde-expand $dom-cfg[location])/(-package-without-domain $pkg)/
   125      }
   126  
   127      &install= [pkg dom-cfg]{
   128        dest = (dest $pkg)
   129        pkgd = (-package-without-domain $pkg)
   130        -info "Installing "$pkg
   131        rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
   132      }
   133  
   134      &upgrade= [pkg dom-cfg]{
   135        dest = (dest $pkg)
   136        pkgd = (-package-without-domain $pkg)
   137        if (not (is-installed $pkg)) {
   138          -error "Package "$pkg" is not installed."
   139          return
   140        }
   141        -info "Updating "$pkg
   142        rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
   143      }
   144    ]
   145  ]
   146  
   147  # Return the filename of the domain config file for the given domain
   148  # (regardless of whether it exists)
   149  fn -domain-config-file [dom]{
   150    put $-lib-dir/$dom/epm-domain.cfg
   151  }
   152  
   153  # Return the filename of the metadata file for the given package
   154  # (regardless of whether it exists)
   155  fn -package-metadata-file [pkg]{
   156    put (dest $pkg)/metadata.json
   157  }
   158  
   159  fn -write-domain-config [dom]{
   160    cfgfile = (-domain-config-file $dom)
   161    mkdir -p (dirname $cfgfile)
   162    if (has-key $-default-domain-config $dom) {
   163      put $-default-domain-config[$dom] | to-json > $cfgfile
   164    } else {
   165      -error "No default config exists for domain "$dom"."
   166    }
   167  }
   168  
   169  # Returns the domain config for a given domain, as a JSON data
   170  # structure. If the file does not exist but we have a built-in
   171  # definition, then we return the default. Otherwise we return $false,
   172  # so the result can always be checked with 'if'.
   173  fn -domain-config [dom]{
   174    cfgfile = (-domain-config-file $dom)
   175    cfg = $false
   176    if ?(test -f $cfgfile) {
   177      # If the config file exists, read it...
   178      cfg = (cat $cfgfile | from-json)
   179      -debug "Read domain config for "$dom": "(to-string $cfg)
   180    } else {
   181      # ...otherwise check if we have a default config for the domain, and save it
   182      if (has-key $-default-domain-config $dom) {
   183        cfg = $-default-domain-config[$dom]
   184        -debug "No existing config for "$dom", using the default: "(to-string $cfg)
   185      } else {
   186        -debug "No existing config for "$dom" and no default available."
   187      }
   188    }
   189    put $cfg
   190  }
   191  
   192  
   193  # Return the method by which a package is installed
   194  fn -package-method [pkg]{
   195    dom = (-package-domain $pkg)
   196    cfg = (-domain-config $dom)
   197    if $cfg {
   198      put $cfg[method]
   199    } else {
   200      put $false
   201    }
   202  }
   203  
   204  # Invoke package operations defined in $-method-handler above
   205  fn -package-op [pkg what]{
   206    dom = (-package-domain $pkg)
   207    cfg = (-domain-config $dom)
   208    if $cfg {
   209      method = $cfg[method]
   210      if (has-key $-method-handler $method) {
   211        if (has-key $-method-handler[$method] $what) {
   212          $-method-handler[$method][$what] $pkg $cfg
   213        } else {
   214          fail "Unknown operation '"$what"' for package "$pkg
   215        }
   216      } else {
   217        fail "Unknown method '"$method"', specified in in config file "(-domain-config-file $dom)
   218      }
   219    } else {
   220      -error "No config for domain '"$dom"'."
   221    }
   222  }
   223  
   224  # Uninstall a single package by removing its directory
   225  fn -uninstall-package [pkg]{
   226    if (not (is-installed $pkg)) {
   227      -error "Package "$pkg" is not installed."
   228      return
   229    }
   230    dest = (dest $pkg)
   231    -info "Removing package "$pkg
   232    rm -rf $dest
   233  }
   234  
   235  ######################################################################
   236  # Main user-facing functions
   237  
   238  # Read and parse the package metadata, if it exists
   239  fn metadata [pkg]{
   240    # Base metadata attributes
   241    res = [
   242      &name= $pkg
   243      &method= (-package-method $pkg)
   244      &src= (-package-op $pkg src)
   245      &dst= (dest $pkg)
   246      &installed= (is-installed $pkg)
   247    ]
   248    # Merge with package-specified attributes, if any
   249    file = (-package-metadata-file $pkg)
   250    if (and (is-installed $pkg) ?(test -f $file)) {
   251      res = (-merge (cat $file | from-json) $res)
   252    }
   253    put $res
   254  }
   255  
   256  # Print out information about a package
   257  fn query [pkg]{
   258    data = (metadata $pkg)
   259    special-keys = [name method installed src dst]
   260    echo (edit:styled "Package "$data[name] cyan)
   261    if $data[installed] {
   262      echo (edit:styled "Installed at "$data[dst] green)
   263    } else {
   264      echo (edit:styled "Not installed" red)
   265    }
   266    echo (edit:styled "Source:" blue) $data[method] $data[src]
   267    keys $data | each [key]{
   268      if (not (has-value $special-keys $key)) {
   269        val = $data[$key]
   270        if (eq (kind-of $val) list) {
   271          val = (joins ", " $val)
   272        }
   273        echo (edit:styled (-first-upper $key)":" blue) $val
   274      }
   275    }
   276  }
   277  
   278  # List installed packages
   279  fn installed {
   280    put $-lib-dir/*[nomatch-ok] | each [dir]{
   281      dom = (re:replace &literal $-lib-dir/ '' $dir)
   282      cfg = (-domain-config $dom)
   283      # Only list domains for which we know the config, so that the user
   284      # can have his own non-package directories under ~/.elvish/lib
   285      # without conflicts.
   286      if $cfg {
   287        lvl = $cfg[levels]
   288        pat = '^\Q'$-lib-dir'/\E('(repeat (+ $lvl 1) '[^/]+' | joins '/')')/$'
   289        put (each [d]{ re:find $pat $d } [ $-lib-dir/$dom/**[nomatch-ok]/ ] )[groups][1][text]
   290      }
   291    }
   292  }
   293  
   294  # epm:list is an alias for epm:installed
   295  fn list { installed }
   296  
   297  # Install and upgrade are method-specific, so we call the
   298  # corresponding functions using -package-op
   299  fn install [&silent-if-installed=$false @pkgs]{
   300    if (eq $pkgs []) {
   301      -error "You must specify at least one package."
   302      return
   303    }
   304    for pkg $pkgs {
   305      if (is-installed $pkg) {
   306        if (not $silent-if-installed) {
   307          -info "Package "$pkg" is already installed."
   308        }
   309      } else {
   310        -package-op $pkg install
   311        # Check if there are any dependencies to install
   312        metadata = (metadata $pkg)
   313        if (has-key $metadata dependencies) {
   314          deps = $metadata[dependencies]
   315          -info "Installing dependencies: "(joins " " $deps)
   316          # If the installation of dependencies fails, uninstall the
   317          # target package (leave any already-installed dependencies in
   318          # place)
   319          try {
   320            install $@deps
   321          } except e {
   322            -error "Dependency installation failed. Uninstalling "$pkg", please check the errors above and try again."
   323            -uninstall-package $pkg
   324          }
   325        }
   326      }
   327    }
   328  }
   329  
   330  fn upgrade [@pkgs]{
   331    if (eq $pkgs []) {
   332      pkgs = [(installed)]
   333      -info 'Upgrading all installed packages'
   334    }
   335    for pkg $pkgs {
   336      if (not (is-installed $pkg)) {
   337        -error "Package "$pkg" is not installed."
   338      } else {
   339        -package-op $pkg upgrade
   340      }
   341    }
   342  }
   343  
   344  # Uninstall is the same for everyone, just remove the directory
   345  fn uninstall [@pkgs]{
   346    if (eq $pkgs []) {
   347      -error 'You must specify at least one package.'
   348      return
   349    }
   350    for pkg $pkgs {
   351      -uninstall-package $pkg
   352    }
   353  }`