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 want your .bash_profile to be extended, set HALCYON_NO_MODIFY_HOME to 1 before running the setup script. You’ll then need to activate Halcyon manually before each use:
$ eval "$( /app/halcyon/halcyon paths )"
You can change where Halcyon is installed by setting the HALCYON_DIR environment variable before running the setup script.
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:
Show command log
$ 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:
Show command log
$ 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:
Show command log
$ 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:
First, the existing GHC and Cabal directories are reused, and the app’s sandbox directory is restored from public storage.
Next, Halcyon restores the app’s build directory from public storage, and performs an incremental build.
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:
Show command log
$ 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,
warpCheck out and install step3:
Show command log
$ 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:
Show command log
$ 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,
warpIn 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:
Show command log
$ 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:
Show command log
$ 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:
First, each previously built sandbox directory is located and assigned a score, which reflects the number of required dependencies contained within.
Next, Halcyon builds and archives a new sandbox, based on the highest scoring partially matching sandbox directory.
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:
Sign up for an Amazon Web Services account
Create an Amazon IAM user and an Amazon S3 bucket
Give the IAM user permission to access the S3 bucket
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:
Show command log
$ 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.
See the Halcyon reference for a complete list of available commands and options.
Read the Haskell on Heroku tutorial to learn how to deploy Haskell web apps to Heroku.