Dao De Code

curl based "package manager"

If you are an engineer who uses scala repl and sbt a lot like I do, chances are you probably use Ammonite and sbt-extras. If you don't, then you should really consider it. But this is not the point of the post. The point is that those tools (and many others of same kind) are usually installed on linux powered systems with curl. And here I want to show the way I automate installation and upgrades of such tools using sbt-extras and Ammonite repl as examples.

The easy part: sbt-extras

Here are instructions on installing sbt-extras:
sbt-extras installation instructions

Doing this once is easy:

  • copy
  • paste to your shell
  • ??? (hit enter)
  • PROFIT!!!

Now, let's say the script got updated (which happens all the time) and if sometimes you don't care about changes, there will be times when you do. So we just go and do it again, right?
Sure, But! I don't know about you, but I hate doing same thing twice and I hate even more doing it more than twice. So simple solution here is an alias:
In your .aliases (or .bashrc or .whatever-file-which-is sourced-on-terminal-opening) you add the alias:

alias update-sbt='sudo curl -S -o /usr/local/bin/sbt https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt && sudo chmod +x /usr/local/bin/sbt'  

If you wonder why it's different from the line on sbt-extras page, then you may guess i'm using debian based linux and prefer to use +x instead of 0755 with chmod.

Anyway, this is cool, as now all we need to do is to run update-sbt from the shell and the new version of sbt-extras is at our service.

Adding Ammonite

That was easy. Now let's install Ammonite, as regular scala repl is no longer what you want, trust me :) Sure, you don't have to trust me, but you better try it yourself and see.
Anyway, here are instructions for installing Ammonite:

Ammonite installation insturctions

Looks familiar, right? Are we going to repeat this again? Mmm, no! We are going to define a simple function in our .aliases file. Here it is:

# downloads a file specified by first parameter, puts it to /usr/local/bin (or second parameter) and gives it +x permissions
curl_install() {  
  if [[ $# == 0 ]]; then
     echo "At least one parameter (url) is required"
  elif [[ $# == 1 ]]; then
    local url=$1
    local file="/usr/local/bin/"${url##*/}
  elif [[ $# == 2 ]] ; then
    local url=$1
    local file=$2
  echo "Installing $url to $file ..."
  sudo curl -L -o $file $url && sudo chmod +x $file
  echo "Done!"

If only one parameter is provided, then file located at provided url is downloaded to /usr/local/bin with the name which is equal to the last path element of url. If two parameters are provided, then second serves as downloaded file path. In both cases, after downloading we assign execute permission to the file. Simple.
Now our aliases for updating sbt and Ammonite looks like that:

alias update-sbt='curl_install https://raw.githubusercontent.com/paulp/sbt-extras/master/sbt'

alias update-amm='curl_install https://git.io/vMF2M /usr/local/bin/amm'  

Clean and nice!

Making update work as update

But wait, update-sbt downloads latest version of the runner every time we invoke it, but it's not that straight for the Ammonite - new version will probably has different short url (this happened many times already).
Fortunately, Ammonite is also properly packaged and released on github! (Thanks @lihaoyi!) We can use it. https://github.com/lihaoyi/Ammonite/releases/latest points to the latest released version (0.8.2 at the moment) and knowing the latest version we can download it using our curl_install. Now we need a way to resolve latest as 0.8.2. Thus we write another shell function - resolve_url:

# expects single url parameter, returns url following redirects
resolve_url() {  
  curl -w "%{url_effective}\n" -I -L -s -S $1 -o /dev/null

Let's run it:

➜  ~ resolve_url https://github.com/lihaoyi/Ammonite/releases/latest


The final step is to turn our update-amm alias to a function, that will resolve proper url and download the file.

update-amm() {  
  local version="${$(resolve_url https://github.com/lihaoyi/Ammonite/releases/latest)##*/}"
  curl_install https://github.com/lihaoyi/Ammonite/releases/download/"$version"/2.12-"$version" /usr/local/bin/amm

And... our PROFIT!!!

update-amm test

Tested with curl 7.47.0 on linux mint 18

➜  ~ curl --version
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3  
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp  
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets  
➜  ~ 
comments powered by Disqus