Deploy Spree 0.70.x with Heroku Cedar stack

UPDATE: This tutorial is updated with details related to S3 superclass mismatch bug.

Heroku’s just released the public beta of their major new Celadon Cedar stack, introducing many new technologies like Process Model, new HTTP stack, default Ruby 1.9.2. One feature that I like is allowing you to choose different webserver than the Thin webserver, for example Unicorn which is recommended for Rails 3.1.x. And another feature is new Process Model that is similar to Foreman where you can control process worker via Procfile.

In this post, I am going to test Spree 0.70.x deployment on this new stack.

Setup

I opted for the upstream of Spree because I want to test how ready version 0.70.x on Heroku. I generate new Rails 3.1.3 and append Spree gem into Gemfile, and because Heroku only supports PostgreSQL, I also specify the database adapter:

$ rails new sandbox -d postgresql

The content of Gemfile:

gem 'rails', '3.1.3'
gem 'unicorn'
gem 'spree', '0.70.4'

group :production do
  gem 'pg'
end

Then I set up Unicorn to use 4 workers processes, according to Michael’s blog, this is the optimal configuration. You can scale up to more Dynos should the app needs more processing power.

# config/unicorn.rb

worker_processes 4 # amount of unicorn workers to spin up
timeout 30         # restarts workers that hang for 30 seconds

and also remember to add in Cedar’s new Rails.root/Procfile

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

Heroku now also needs the Gemfile.lock committed so please bundle up first

bundle install

Next is to create initial commit and push to heroku

git init
git add -A
git commit -m "Initial commit"
heroku create --stack cedar
git push heroku master

If you ever bump into issues where Bundler fails to locate gems, the best workaround is to cache the bundle:

bundle cache
git add -A
git commit -m 'Bundle cache'

and try to push again:

git push heroku master

The successful reports:

...
       Injecting rails_log_stdout
       Injecting rails3_serve_static_assets
-----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       rake aborted!
       could not connect to server: Connection refused
       Is the server running on host "127.0.0.1" and accepting
       TCP/IP connections on port 5432?

       Tasks: TOP => environment
       (See full trace by running task with --trace)
       Precompiling assets failed, enabling runtime asset compilation
       Injecting rails31_enable_runtime_asset_compilation
       Please see this article for troubleshooting help:
       http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting
-----> Discovering process types
       Procfile declares types      -> web
       Default types for Ruby/Rails -> console, rake, worker
-----> Compiled slug size is 44.0MB
-----> Launching... done, v41
       http://falling-mist-496.herokuapp.com deployed to Heroku

Asset Pipeline

We could see from the output that there is a problem with Heroku and asset pipeline. It seems to me that Heroku fail to precompile assets during slug compilation. It make some sense though because Spree requires access to DB to complete this task and yet before you push to Heroku the environment config are not present for boot. To workaround this issue, the only way I could think of is to locally precompile assets:

Precompiling assets locally
First, we need to set up our DEVELOPMENT DB in config/database.yml. Then we precompile our assets locally:

bundle exec rake assets:precompile RAILS_ENV=development

What will happen next is Sprocket will compile our assets and place them in public/assets folder. What Heroku really care is the public/assets/manifest.yml. This file contains all MD5 checksum of our assets and Heroku will check the existence of the file to tell if we compile our assets locally or not. Okay, next is to commit and push to Heroku:

git add -A public/assets
git commit -m 'Added precompiled assets'
git push heroku master

and we should see output:

....
-----> Preparing app for Rails asset pipeline
       Detected manifest.yml, assuming assets were compiled locally
...

You could read more on Rails 3.1 on Heroku

Bootstrapping

First, we install the generator to do basic setup:

heroku run rails g spree:site

However, there is an issue with PostgreSQL:

/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.1.1/lib/active_record/connection_adapters/postgresql_adapter.rb:958:in `async_exec': PGError: ERROR:  relation "activators" does not exist (ActiveRecord::StatementInvalid)
:             SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
             WHERE a.attrelid = '"activators"'::regclass
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum
	from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.1.1/lib/active_record/connection_adapters/postgresql_adapter.rb:958:in `exec_no_cache'

Again, it is very bad to see how broken things are in bootstrap step, above issue could be easily resolved by 2 ways:

1. Reset the remote Database

One command to do the fix job:

heroku run rake db:reset

We don’t have to worry about db:migrate and db:seed steps because db:reset has actually invoked those two for us already.

2. Push local DB to remote DB

We can do DB migration on local development environment first then push to production environment. To do so, please make sure you set up a database in your local PostgresSQL server and configure the DB settings for development in config/database.yml. Then we carry out:

bundle exec rake db:migrate
bundle exec rake db:seed

After the above step, we have a working development DB. Now we need to push this DB to the remote heroku. To do so, we need to have taps gem pre-installed:

gem install taps

After the gem is installed, we can push with command:

heroku db:push

Next we create admin user by:

heroku run rake db:admin:create

Once you’ve create your own admin user, we can test our spree. Yet first we need to restart heroku processees:

heroku restart

Then we test the site by opening the app URL with command:

heroku open

You should see a empty products page w/o any errors.

Load Sample Data

Because Heroku is disk-less therefore rake spree_sample:load CLI will NOT work. A little tweak for paperclip to use Cloud Storage service like Amazon S3 is required.

I have consolidated all forks of RSpace’s spree-heroku extension to work with upstream spree, ie. as an mountable engine. I am in talk with Spree core team regarding of moving this extension to official spree github tree so everyone don’t have to look around and confused with bunch of forks.

Alright, let’s set up Amazon S3 for Spree, please make sure you have S3 Account and create a US Standard region bucket for this app. Again, please make sure your bucket is US Standard region or else you will bump into this error:

The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.

Firstly, append this line into your Gemfile:

gem 'spree_heroku', :git => 'git://github.com/joneslee85/spree-heroku.git', :branch => '0-70-stable'

then

bundle install

Next, we create a new file under config/s3.yml and modify the key in accordance to your S3 account

development:
  bucket: your_app_dev
  access_key_id: your_access_key
  secret_access_key: secret_access_key

test:
  bucket: your_app_test
  access_key_id: your_access_key
  secret_access_key: secret_access_key

production:
  bucket: your_app_prod
  access_key_id: your_access_key
  secret_access_key: secret_access_key

Now you’re ready to push again

git add -A
git commit -m 'Added spree_heroku'
git push heroku master

and load up sample data

heroku run rake spree_sample:load

if nothing goes wrong, you would get to this:

...
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
loading ruby    /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/sample/lib/tasks/../../db/sample/taxons.rb
loading ruby    /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/sample/lib/tasks/../../db/sample/update_memberships.rb

should any problems occurs, you can reset the db:

heroku run rake db:reset

Okay, yet you are not done yet, though w/o getting errors, you won’t get the app running. Checking the logs with

heroku logs

shows that there is some problem with Calculator::PriceBucket calculator:

2011-08-18T03:55:33+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: E, [2011-08-18T03:55:34.699856 #2054] ERROR -- : superclass mismatch for class PriceBucket (TypeError)
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/core/app/models/calculator/price_bucket.rb:1:in `'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `block in require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:225:in `load_dependency'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:344:in `require_or_load'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:298:in `depend_on'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:214:in `require_dependency'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:416:in `block (2 levels) in eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:415:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:415:in `block in eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:413:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:413:in `eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/railties.rb:8:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/railties.rb:8:in `all'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:411:in `eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/finisher.rb:51:in `block in '
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:25:in `instance_exec'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:25:in `run'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:50:in `block in run_initializers'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:49:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:49:in `run_initializers'
2011-08-18T03:55:34+00:00 app[web.1]: E, [2011-08-18T03:55:34.732186 #1] ERROR -- : reaped # worker=2

Shocking, I did not have any idea what could possibly go wrong. Thanks to our hero Ryan (@ryanbigg), he figured out that the keyword Bucket is reserved in S3, thus our PriceBucket is so in the warzone. The 0-70-stable branch has addressed this issue which can be found here. You need to use the 0-70-stable branch or backport that commit locally to your app.

For your Gemfile:

gem 'spree', :git => 'git://github.com/spree/spree.git', :branch => '0-70-stable'

and bravo, then checkout your site with

heroku apps:open

you would see this when browsing:

Disable SSL in Production (optional)

You can disable SSL in Production by creating file RAILS_ROOT/app/models/app_configuration_decorator.rb with content:

AppConfiguration.class_eval do
  preference :allow_ssl_in_production, :boolean, :default => false
end

and please make sure you commit and push to heroku

Add spree-blue-theme

To add spree-blue-theme, simply append this line into the end of your Gemfile:

gem 'spree_blue_theme', :git => 'git://github.com/spree/spree_blue_theme.git'

Then bundle update and precompile assets:

bundle install
bundle exec rake assets:precompile
git add -A
git commit -m 'Added spree-blue-theme'
git push heroku master

Issues

1. First time push to heroku returns with errors with PG regarding ‘activators’ table – Solution: run rake db:reset
2. Non-US Standard S3 bucket won’t work
3. Get superclass mismatch error with Calculator::PriceBucket if loading additional extensions

Conclusion

I am relieved that in the end I could get Spree and its sample data up and running on Heroku Cedar stack though the journey is very painstaking.

Heroku Cedar stack seems potential hosting solution for Spree for its newly introduced technologies. At the time of the writing, there are issues outstanding. However I believe those issues will be polished by the time Spree 0.70 is released.

Advertisements

About Jones Lee

Nothing much about me..

27 responses to “Deploy Spree 0.70.x with Heroku Cedar stack

  1. Great post (Thank you!) but it seems not to be displaying fully, or it is incomplete: You begin the Bootstrapping section, show the database migration, and then just say “Then”. Then there’s an advert for warehouse-13, and no more spree goodness.

    I would find the rest of the post very useful. What happened next?

  2. slavix

    Hi,
    thanks for this post!
    I am getting the same error you’ve got

    /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/core/app/models/calculator/price_bucket.rb:1:in `’: superclass mismatch for class PriceBucket (TypeError)

    Do you know how to deal with it?

  3. slavix

    Also, for some reason text in some places appears twice..
    For example take a look at
    http://bitcoin-derivatives.herokuapp.com/products/ruby-on-rails-ringer-t-shirt
    Take a look at text inside Shpping Cart button
    Add to Cart is repeated twice, or http://bitcoin-derivatives.herokuapp.com/cart
    Update is repeated twice, so is Checkout.

  4. I don’t follow what your problems were here spree_heroku is just adding aws-s3 configs to paperclip in a couple of models, unless you’ve introduced some bugs in your fork.

    I’ve deployed to cedar several times with zero problems only I’ve used thin so far not unicorn but I haven’t had any problems what-so-ever. I think you’re giving Spree a bit of a bad rap where it might not deserve it.

    • I speculate there are issues with my fork, however I could not figure out what’s gone wrong because copying those 2 models file and the s3 config file locally from the extension fixed the issue. So I think it is Spree related or Heroku related issue.

      The being said, I welcome any feedbacks.

      • The issue has been resolved on the Spree Google group. It turns out that the Price Bucket class in Spree was being interpreted somehow by the AWS-S3 gem which looks for “buckets” in S3.
        Changing the 4 lines + file name of Price Bucket in Spree fixes the issue about superclass price_bucket problems.

        I’m not sure if Price Category is the right alternative and I don’t know how it will get resolved in the code. I repository is under the user AlanMcCann

  5. slavix

    tried
    heroku run rake spree:assets:sync_images
    restart..
    no change
    and
    heroku run rake spree:assets:relocate_images
    restarted.. no change

  6. slavix

    Hi there!
    I still can’t get any of the spree core images images ands styles to show up on heroku. Tried everything I could think up.. Do you have any ideas on what can be tried?

  7. Did you have any luck fixing this I have the same problem. I am now wondering if it has to do with the use of RVM? I’ve tried everything here but can’t get themes to work.

  8. Thanks for your awesome post! I followed it and everything worked just fine.
    I used spree v0.70.3 if anyone is having problems (gem ‘spree’, git: ‘https://github.com/spree/spree.git’, tag: ‘v0.70.3’)

  9. Awesome post. everything here worked for me on Spree v1.0.0 and Rails 3.1.3 except for disabling SSL in production. I put the above code in RAILS_ROOT/app/models/app_configuration_decorator.rb. I also changed the preference in RAILS_ROOT/config/initializers/spree.rb file but the ssl requirement switches back to “true” periodically. any idea how i can solve this?

  10. OMG thank you! I was trying to seed the heroku db with the sample data to save time with customizations but couldn’t figure it out for the longest! ‘heroku run rake spree_sample:load’ was all I needed. Thank you!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: