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 }`