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