Halcyon tutorial

Halcyon is a system for installing Haskell apps and development tools, including GHC and Cabal.

This tutorial shows how to develop a simple Haskell web app using Halcyon.

Set up

Halcyon can be installed by cloning the Halcyon source repository, or by running the setup script, which also installs the necessary OS packages and sets up the environment.

The tutorial assumes you’re using a Linux system with at least 4 GB RAM and GNU bash 4 or newer.

Run the setup script to install Halcyon:

$ eval "$( curl -sL https://github.com/mietek/halcyon/raw/master/setup.sh )"
-----> Welcome to Halcyon
[sudo] password for fnord:
-----> Creating base directory: /app
-----> Installing OS packages
       ...
-----> Installing Halcyon... done, fd5fc7d
-----> Installing bashmenot... done, 5165edd
-----> Extending .bash_profile

Halcyon is now ready to use:

$ which halcyon
/app/halcyon/halcyon

Options

If you don’t run the setup script, you’ll need to activate Halcyon manually before each use:

$ eval "$( /app/halcyon/halcyon paths )"

Before running the setup script, you can change the directory to which the Halcyon source repository will be cloned by setting the HALCYON_DIR environment variable.

Halcyon installs development tools and other dependencies in the base directory, which defaults to /app. Changing this path isn’t recommended, because it’ll require all dependencies to be built from scratch. If you still want to do it, set HALCYON_BASE before running the setup script.

Install GHC and Cabal

Execute the Halcyon install command to install GHC and Cabal:

Toggle

$ halcyon install
-----> Installing GHC and Cabal
       External storage:                         **public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Restoring GHC directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/halcyon-ghc-7.8.4.tar.gz... done
       Extracting halcyon-ghc-7.8.4.tar.gz... done, 701MB

-----> Locating Cabal directories
       Listing https://halcyon.global.ssl.fastly.net/?prefix=linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-... done
-----> Restoring Cabal directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... done
       Extracting halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... done, 180MB

-----> GHC and Cabal installed

-----> Examining cache changes
       + halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       + halcyon-ghc-7.8.4.tar.gz
Expected time: 20–30 seconds

In this step, Halcyon restores the GHC directory and the Cabal directory by extracting archives downloaded from public storage, which is an external cache for previously-built apps and dependencies.

All downloaded archives are cached in the Halcyon cache directory.

GHC and Cabal are now ready to use:

$ which ghc
/app/ghc/bin/ghc
$ which cabal
/app/cabal/bin/cabal

Options

All Halcyon options can be specified by setting an environment variable. You can also specify most options with a command-line argument.

By default, Halcyon installs GHC 7.8.4 and cabal-install 1.20.0.3. You can change this with the HALCYON_GHC_VERSION and HALCYON_CABAL_VERSION options.

The cache directory defaults to /var/tmp/halcyon-cache, and can be changed with the HALCYON_CACHE option.

Install the app

The tutorial app is a simple web service for storing notes, built with Servant.

The app includes a Cabal package description file, halcyon-tutorial.cabal file, used to declare dependencies, and a Halcyon constraints file, .halcyon/constraints file, used to declare version constraints.

Use the Halcyon install command to install the app directly from the halcyon-tutorial source repository:

Toggle

$ halcyon install https://github.com/mietek/halcyon-tutorial
-----> Examining cache contents
       halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       halcyon-ghc-7.8.4.tar.gz

-----> Cloning https://github.com/mietek/halcyon-tutorial... done, f1ccdd9
-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **b90bb47**
       External storage:                         **public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-b90bb47-halcyon-tutorial-1.0.tar.gz... done
       Extracting halcyon-install-b90bb47-halcyon-tutorial-1.0.tar.gz... done, 8.8MB
-----> Installing app to /app
-----> Installed halcyon-tutorial-1.0

-----> App installed:                            **halcyon-tutorial-1.0**

-----> Examining cache changes
       + halcyon-install-b90bb47-halcyon-tutorial-1.0.tar.gz
Expected time: 5–10 seconds

In this step, Halcyon restores the tutorial app’s install directory by using an archive from public storage.

The right archive to restore is determined by calculating a source hash of the app’s source directory.

Your app is now ready to run:

$ which halcyon-tutorial
/app/bin/halcyon-tutorial

Options

Some Halcyon options, such as HALCYON_CONSTRAINTS, can be specified by including a magic file in your app’s source directory. Command-line arguments take precedence over environment variables, which in turn take precedence over magic files.

Using magic files is the recommended way of specifing options, as it doesn’t require the user to perform any additional actions when installing the app.

Halcyon installs apps in the prefix directory, which defaults to /app. You can change this with the HALCYON_PREFIX option.

Run the app

The tutorial app exposes one HTTP endpoint, /notes, which accepts GET and POST requests.

Notes are JSON objects with a single text field, contents. The app responds to each request with a list of all existing notes.

Start your app in one shell:

$ halcyon-tutorial

In another shell, make a GET request to see an empty list of notes:

$ curl http://localhost:8080/notes
[]

Make a couple POST requests to add some notes:

$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello, world!" }'
[{"contents":"Hello, world!"}]
$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello?" }'
[{"contents":"Hello?"},{"contents":"Hello, world!"}]

Incoming notes are logged in the original shell:

$ halcyon-tutorial
Hello, world!
Hello?

Press control-C to stop your app.

Options

By default, the tutorial app listens on port 8080. You can change this by setting the PORT environment variable:

$ PORT=4040 halcyon-tutorial

Make a change

Let’s change the tutorial app so that each note can contain a timestamp.

The step2 version of the app includes a new dateTime field in each note.

Clone the app:

$ git clone -q https://github.com/mietek/halcyon-tutorial
$ cd halcyon-tutorial

Check out step2, and install it with the Halcyon install command:

Toggle

$ git checkout -q step2
$ halcyon install
-----> Examining cache contents
       halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       halcyon-ghc-7.8.4.tar.gz
       halcyon-install-0c985ba-halcyon-tutorial-1.0.tar.gz

-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **e682845**
       External storage:                         **public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz... 404 (not found)

-----> Determining constraints
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **e682845**
       Constraints hash:                         **f458aa8**
       Magic hash:                               **3bffeae**
       External storage:                         **public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Using existing GHC directory

-----> Using existing Cabal directory

-----> Restoring sandbox directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... done
       Extracting halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... done, 140MB

-----> Restoring build directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-build-halcyon-tutorial-1.0.tar.gz... done
       Extracting halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 9.4MB
-----> Examining source changes
       * Main.hs
-----> Building app
       Building halcyon-tutorial-1.0...
       Preprocessing executable 'halcyon-tutorial' for halcyon-tutorial-1.0...
       [1 of 1] Compiling Main             ( Main.hs, dist/build/halcyon-tutorial/halcyon-tutorial-tmp/Main.o )
       Linking dist/build/halcyon-tutorial/halcyon-tutorial ...
-----> App built, 12MB
       Stripping app... done, 9.8MB
-----> Archiving build directory
       Creating halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 2.1MB

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Preparing install directory
-----> Installing extra data files for dependencies
-----> Install directory prepared, 8.8MB
-----> Archiving install directory
       Creating halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz... done, 2.0MB
-----> Installing app to /app
-----> Installed halcyon-tutorial-1.0

-----> App installed:                            **halcyon-tutorial-1.0**

-----> Examining cache changes
       + halcyon-build-halcyon-tutorial-1.0.tar.gz
       - halcyon-install-b90bb47-halcyon-tutorial-1.0.tar.gz
       + halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz
       + halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz
Expected time: 30–40 seconds

In this step, Halcyon tries to restore the tutorial app’s install directory by using an archive from public storage. This fails, and so Halcyon falls back to building the app:

  1. First, the existing GHC and Cabal directories are reused, and the app’s sandbox directory is restored from public storage.

  2. Next, Halcyon restores the app’s build directory from public storage, and performs an incremental build.

  3. Finally, the app’s new install directory is prepared and archived, and the app is installed.

Halcyon determines which sandbox archive to restore by calculating a constraints hash of the version constraints declared by your app. Similarly, the right version of GHC to use is implied by the base package constraint:

$ grep -E '^base-' .halcyon/constraints
base-4.7.0.2

Your app is now ready to run again:

$ halcyon-tutorial
$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello, world!" }'
[{"contents":"Hello, world!","dateTime":""}]

Declare a dependency

Now, let’s change the tutorial app so that it remembers the time each note is added.

The step3 version of the app declares the standard Haskell old-locale and time libraries as dependencies:

Toggle

$ git diff step2 step3 halcyon-tutorial.cabal
...
**@@ -14,9 +14,11 @@** executable halcyon-tutorial
   ghc-options:        -O2 -Wall -threaded
   build-depends:      base,
                       aeson,
**+                      old-locale,**
                       servant,
                       servant-server,
                       stm,
                       text,
**+                      time,**
                       transformers,
                       warp

Check out and install step3:

Toggle

$ git checkout -q step3
$ halcyon install
-----> Examining cache contents
       halcyon-build-halcyon-tutorial-1.0.tar.gz
       halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       halcyon-ghc-7.8.4.tar.gz
       halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz
       halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz

-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **becc434**
       External storage:                         **public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz... 404 (not found)

-----> Determining constraints
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **becc434**
       Constraints hash:                         **f458aa8**
       Magic hash:                               **3bffeae**
       External storage:                         **public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Using existing GHC directory

-----> Using existing Cabal directory

-----> Using existing sandbox directory

-----> Restoring build directory
       Extracting halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 9.8MB
-----> Examining source changes
       * halcyon-tutorial.cabal
       * Main.hs
-----> Configuring app
-----> Building app
       Building halcyon-tutorial-1.0...
       Preprocessing executable 'halcyon-tutorial' for halcyon-tutorial-1.0...
       [1 of 1] Compiling Main             ( Main.hs, dist/build/halcyon-tutorial/halcyon-tutorial-tmp/Main.o )
       Linking dist/build/halcyon-tutorial/halcyon-tutorial ...
-----> App built, 12MB
       Stripping app... done, 9.8MB
-----> Archiving build directory
       Creating halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 2.1MB

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Preparing install directory
-----> Installing extra data files for dependencies
-----> Install directory prepared, 8.8MB
-----> Archiving install directory
       Creating halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz... done, 2.0MB
-----> Installing app to /app
-----> Installed halcyon-tutorial-1.0

-----> App installed:                            **halcyon-tutorial-1.0**

-----> Examining cache changes
       * halcyon-build-halcyon-tutorial-1.0.tar.gz
       + halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz
       - halcyon-install-e682845-halcyon-tutorial-1.0.tar.gz
Expected time: 20–25 seconds

In this step, Halcyon reuses the existing GHC, Cabal, and sandbox directories, performs an incremental build, and installs the app.

The previously-restored sandbox directory can be used again, because version constraints for our new dependencies are already declared:

$ grep -E '^(old-locale|time)' .halcyon/constraints
old-locale-1.0.0.6
time-1.4.2

Your app is now ready to run again:

$ halcyon-tutorial
$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello, world!" }'
[{"contents":"Hello, world!","dateTime":"2015-01-15T09:21:29Z"}]

Declare a constraint

Let’s try to simplify the code by using a third-party library.

The step4 version of the app replaces old-locale and time with the hourglass library:

Toggle

$ git diff step3 step4 halcyon-tutorial.cabal
...
**@@ -14,11 +14,10 @@** executable halcyon-tutorial
   ghc-options:        -O2 -Wall -threaded
   build-depends:      base,
                       aeson,
**-                      old-locale,**
**+                      hourglass,**
                       servant,
                       servant-server,
                       stm,
                       text,
**-                      time,**
                       transformers,
                       warp

In order for Halcyon to provide the right sandbox directory, we need to declare version constraints for hourglass and all of its dependencies. You can determine these constraints using Halcyon.

Check out step4, and try installing it:

Toggle

$ git checkout -q step4
$ halcyon install
-----> Examining cache contents
       halcyon-build-halcyon-tutorial-1.0.tar.gz
       halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       halcyon-ghc-7.8.4.tar.gz
       halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz
       halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz

-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **1300f53**
       External storage:                         **public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-bf9e916-halcyon-tutorial-1.0.tar.gz... 404 (not found)

-----> Determining constraints
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **1300f53**
       Constraints hash:                         **f458aa8**
       Magic hash:                               **3bffeae**
       External storage:                         **public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Using existing GHC directory

-----> Using existing Cabal directory

-----> Using existing sandbox directory
**   *** WARNING: Unexpected constraints difference**
       @@ -38,6 +38,7 @@
        free-4.10.0.1
        ghc-prim-0.3.1.0
        hashable-1.2.3.1
       +hourglass-0.2.8
        http-date-0.0.4
        http-types-0.8.5
        integer-gmp-0.5.1.0

-----> Restoring build directory
       Extracting halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 9.8MB
-----> Examining source changes
       * halcyon-tutorial.cabal
       * Main.hs
-----> Configuring app
       ...
       Could not resolve dependencies:
       trying: halcyon-tutorial-1.0 (user goal)
       next goal: hourglass (dependency of halcyon-tutorial-1.0)
       Dependency tree exhaustively searched.
       Configuring halcyon-tutorial-1.0...
       cabal: At least the following dependencies are missing:
       hourglass -any
**   *** ERROR: Failed to configure app**
Expected time: 10–15 seconds

In this step, Cabal fails to configure the app, because the hourglass library isn’t provided in the existing sandbox directory. Halcyon suggests adding a single version constraint, hourglass-0.2.8.

The step5 version of the app declares this constraint:

$ git diff -U1 step4 step5 .halcyon/constraints
...
**@@ -40,2 +40,3 @@** ghc-prim-0.3.1.0
 hashable-1.2.3.1
**+hourglass-0.2.8**
 http-date-0.0.4

Build the sandbox

Halcyon always provides a sandbox directory matching the declared version constraints. If needed, the sandbox directory is built on-the-fly — either from scratch, or based on a previously-built sandbox.

Check out and install step5:

Toggle

$ git checkout -q step5
$ halcyon install
-----> Examining cache contents
       halcyon-build-halcyon-tutorial-1.0.tar.gz
       halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       halcyon-ghc-7.8.4.tar.gz
       halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz
       halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz

-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **fba454f**
       External storage:                         **public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)

-----> Determining constraints
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **fba454f**
       Constraints hash:                         **0551a64**
       Magic hash:                               **4877c9d**
       External storage:                         **public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Using existing GHC directory

-----> Using existing Cabal directory

-----> Restoring sandbox directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Locating sandbox directories
       Listing https://halcyon.global.ssl.fastly.net/?prefix=linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-... done
-----> Examining partially-matching sandbox directories
       ...
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.constraints... done
-----> Scoring partially-matching sandbox directories
       Ignoring hello-yesod-1.0 (1a1d740) as asn1-encoding-0.9.0 is not needed
       Ignoring hello-happstack-1.0 (3b0b768) as extensible-exceptions-0.1.1.4 is not needed
       Ignoring hello-scotty-1.0 (110abd2) as data-default-0.5.3 is not needed
       Ignoring hello-snap-1.0 (335d31e) as HUnit-1.2.5.2 is not needed
           101 halcyon-tutorial-1.0 (f458aa8)
            41 hello-wai-1.0 (ffec23f)
-----> Using partially-matching sandbox directory: halcyon-tutorial-1.0 (f458aa8)
-----> Restoring sandbox directory
       Extracting halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... done, 140MB
-----> Building sandbox directory
-----> Building sandbox
       Resolving dependencies...
       Notice: installing into a sandbox located at /app/sandbox
       Downloading hourglass-0.2.8...
       Configuring hourglass-0.2.8...
       Building hourglass-0.2.8...
       Installed hourglass-0.2.8
-----> Sandbox built, 144MB
       Removing documentation from sandbox directory... done, 144MB
       Stripping sandbox directory... done, 143MB
-----> Archiving sandbox directory
       Creating halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... done, 24MB

-----> Restoring build directory
       Extracting halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 9.4MB
-----> Examining source changes
       * .halcyon/constraints
       * Main.hs
       * cabal.config
       * halcyon-tutorial.cabal
-----> Configuring app
-----> Building app
       Building halcyon-tutorial-1.0...
       Preprocessing executable 'halcyon-tutorial' for halcyon-tutorial-1.0...
       [1 of 1] Compiling Main             ( Main.hs, dist/build/halcyon-tutorial/halcyon-tutorial-tmp/Main.o )
       Linking dist/build/halcyon-tutorial/halcyon-tutorial ...
-----> App built, 13MB
       Stripping app... done, 9.8MB
-----> Archiving build directory
       Creating halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 2.2MB

-----> Restoring install directory
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Preparing install directory
-----> Installing extra data files for dependencies
-----> Install directory prepared, 9.1MB
-----> Archiving install directory
       Creating halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... done, 2.0MB
-----> Installing app to /app
-----> Installed halcyon-tutorial-1.0

-----> App installed:                            **halcyon-tutorial-1.0**

-----> Examining cache changes
       * halcyon-build-halcyon-tutorial-1.0.tar.gz
       - halcyon-install-becc434-halcyon-tutorial-1.0.tar.gz
       + halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz
       + halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz
Expected time: 60–90 seconds

In this step, Halcyon reuses the existing GHC and Cabal directories, and tries to locate the right sandbox directory for the current version of the app. This fails, and so Halcyon falls back to building the sandbox:

  1. First, each previously-built sandbox directory is located and assigned a score, which reflects the number of required dependencies contained within.

  2. Next, Halcyon builds and archives a new sandbox, based on the highest-scoring partially-matching sandbox directory.

  3. Finally, an incremental build is performed, and the app is installed.

Your app is now ready to run again:

$ halcyon-tutorial
$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello, world!" }'
[{"contents":"Hello, world!","dateTime":"2015-01-15T09:28:26+00:00"}]

Set up private storage

Halcyon can use private storage as well as public storage. Private storage is an external cache for the apps and dependencies you build, and a method for sharing build products between multiple machines.

By using private storage, you can perform builds on a fast machine, and share build products with your installation targets.

To use private storage, you’ll need to:

Once you’re done, configure private storage by setting HALCYON_AWS_ACCESS_KEY_ID, HALCYON_AWS_SECRET_ACCESS_KEY, and HALCYON_S3_BUCKET:

$ export HALCYON_AWS_ACCESS_KEY_ID=example-access-key-id
$ export HALCYON_AWS_SECRET_ACCESS_KEY=example-secret-access-key
$ export HALCYON_S3_BUCKET=example-bucket

If your S3 bucket isn’t located in the Amazon US Standard region, set HALCYON_S3_ENDPOINT to the address of the right region-specific S3 endpoint:

$ export HALCYON_S3_ENDPOINT=s3-example-region.amazonaws.com

Options

By default, all uploads are assigned the private Amazon S3 ACL. To make newly-uploaded files available to the public, set HALCYON_S3_ACL to public-read:

$ export HALCYON_S3_ACL=public-read

Use private storage

Let’s build a sandbox directory for your app again, in order to populate your private storage.

Remove the existing GHC, Cabal, and sandbox directories:

$ rm -rf /app/ghc /app/cabal /app/sandbox

Install the app again, using the HALCYON_PURGE_CACHE option to delete the existing sandbox directory archive from the cache:

Toggle

$ halcyon install --purge-cache
-----> Purging cache

-----> Installing halcyon-tutorial-1.0
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **fba454f**
       External storage:                         **private and public**
       GHC version:                              **7.8.4**

-----> Restoring install directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)

-----> Determining constraints
       Label:                                    **halcyon-tutorial-1.0**
       Prefix:                                   **/app**
       Source hash:                              **b28289b**
       Constraints hash:                         **3ad1ba3**
       Magic hash:                               **c23e21c**
       External storage:                         **private and public**
       GHC version:                              **7.8.4**
       Cabal version:                            **1.20.0.3**
       Cabal repository:                         **Hackage**

-----> Restoring GHC directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/halcyon-ghc-7.8.4.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/halcyon-ghc-7.8.4.tar.gz... done
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/halcyon-ghc-7.8.4.tar.gz... done
       Extracting halcyon-ghc-7.8.4.tar.gz... done, 701MB

-----> Locating Cabal directories
       Listing s3://example-bucket/?prefix=linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-... done
       Listing https://halcyon.global.ssl.fastly.net/?prefix=linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-... done
-----> Restoring Cabal directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... done
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... done
       Extracting halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz... done, 181MB

-----> Restoring sandbox directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Locating sandbox directories
       Listing s3://example-bucket/?prefix=linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-... done
       Listing https://halcyon.global.ssl.fastly.net/?prefix=linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-... done
-----> Examining partially-matching sandbox directories
       ...
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.constraints... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.constraints... done
-----> Scoring partially-matching sandbox directories
       Ignoring hello-yesod-1.0 (1a1d740) as asn1-encoding-0.9.0 is not needed
       Ignoring hello-happstack-1.0 (3b0b768) as extensible-exceptions-0.1.1.4 is not needed
       Ignoring hello-scotty-1.0 (110abd2) as data-default-0.5.3 is not needed
       Ignoring hello-snap-1.0 (335d31e) as HUnit-1.2.5.2 is not needed
           101 halcyon-tutorial-1.0 (f458aa8)
            41 hello-wai-1.0 (ffec23f)
-----> Using partially-matching sandbox directory: halcyon-tutorial-1.0 (f458aa8)
-----> Restoring sandbox directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... done
       Extracting halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz... done, 140MB
-----> Building sandbox directory
-----> Building sandbox
       Resolving dependencies...
       Notice: installing into a sandbox located at /app/sandbox
       Downloading hourglass-0.2.8...
       Configuring hourglass-0.2.8...
       Building hourglass-0.2.8...
       Installed hourglass-0.2.8
-----> Sandbox built, 144MB
       Removing documentation from sandbox directory... done, 144MB
       Stripping sandbox directory... done, 144MB
-----> Archiving sandbox directory
       Creating halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... done, 25MB
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz... done
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-0551a64-halcyon-tutorial-1.0.constraints... done
       Listing s3://example-bucket/?prefix=linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-sandbox-... done

-----> Restoring build directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-build-halcyon-tutorial-1.0.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-build-halcyon-tutorial-1.0.tar.gz... done
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-build-halcyon-tutorial-1.0.tar.gz... done
       Extracting halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 9.4MB
-----> Examining source changes
       * cabal.config
       * .halcyon/constraints
       * halcyon-tutorial.cabal
       * Main.hs
-----> Configuring app
-----> Building app
       Building halcyon-tutorial-1.0...
       Preprocessing executable 'halcyon-tutorial' for halcyon-tutorial-1.0...
       [1 of 1] Compiling Main             ( Main.hs, dist/build/halcyon-tutorial/halcyon-tutorial-tmp/Main.o )
       Linking dist/build/halcyon-tutorial/halcyon-tutorial ...
-----> App built, 13MB
       Stripping app... done, 11MB
-----> Archiving build directory
       Creating halcyon-build-halcyon-tutorial-1.0.tar.gz... done, 2.2MB
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-build-halcyon-tutorial-1.0.tar.gz... done

-----> Restoring install directory
       Downloading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)
       Downloading https://halcyon.global.ssl.fastly.net/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... 404 (not found)
-----> Preparing install directory
-----> Installing extra data files for dependencies
-----> Install directory prepared, 9.1MB
-----> Archiving install directory
       Creating halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... done, 2.0MB
       Uploading s3://example-bucket/linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz... done
       Listing s3://example-bucket/?prefix=linux-ubuntu-14.04-x86_64/ghc-7.8.4/halcyon-install-... done
-----> Installing app to /app
-----> Installed halcyon-tutorial-1.0

-----> App installed:                            **halcyon-tutorial-1.0**

-----> Examining cache changes
       + halcyon-build-halcyon-tutorial-1.0.tar.gz
       + halcyon-cabal-1.20.0.3-hackage-2015-01-15.tar.gz
       + halcyon-ghc-7.8.4.tar.gz
       + halcyon-install-fba454f-halcyon-tutorial-1.0.tar.gz
       + halcyon-sandbox-0551a64-halcyon-tutorial-1.0.tar.gz
       + halcyon-sandbox-f458aa8-halcyon-tutorial-1.0.tar.gz
Expected time: 90–120 seconds

In this step, Halcyon restores the GHC and Cabal directories from public storage, builds and archives a sandbox based on a partially-matching sandbox directory, performs an incremental build, and installs the app again.

All downloaded and newly-created archives are uploaded to your private storage.

Once again, your app is now ready to run:

$ halcyon-tutorial
$ curl -X POST http://localhost:8080/notes -d '{ "contents": "Hello, world!" }'
[{"contents":"Hello, world!","dateTime":"2015-01-15T09:32:14+00:00"}]

Options

If you want to avoid downloading any archives from public storage, set HALCYON_NO_PUBLIC_STORAGE to 1 before populating your private storage.

Next steps

You now know how to use Halcyon to develop Haskell apps. You have also developed a simple Haskell web service.