Today, I've been trying to configure Dokku to deploy a statically-generated website of mine (built with middleman): push the middleman source to the host, generate the website on the host, and tell a nginx to serve those static files.
Following these resources 1 and 2, I setup my project with:
a .buildpacks
file, containing one buildpack to build the site, and the nginx buildpack to serve the generated static HTML files:
https://github.com/heroku/heroku-buildpack-ruby.git#v222
https://github.com/dokku/buildpack-nginx.git#v14
a .static
file so that the nginx buildpack knows it must serve a static website
a predeploy action in charge of building the site, using app.json
:
{
"scripts": {
"dokku": {
"predeploy": "bundle exec middleman build",
}
}
}
Apparently, at the moment I'm trying to build my middleman site with the predeploy hook, the bundle command is not available anymore. Here's an example output I'm getting:
➜ git push dokku master
Enumerating objects: 397, done.
Counting objects: 100% (397/397), done.
Delta compression using up to 8 threads
Compressing objects: 100% (258/258), done.
Writing objects: 100% (397/397), 819.50 KiB | 14.90 MiB/s, done.
Total 397 (delta 195), reused 244 (delta 111), pack-reused 0
remote: Resolving deltas: 100% (195/195), done.
-----> Cleaning up...
-----> Building site from herokuish...
-----> Adding BUILD_ENV to build environment...
-----> Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used.
Detected buildpacks: multi ruby static
-----> Multipack app detected
=====> Downloading Buildpack: https://github.com/heroku/heroku-buildpack-ruby.git
=====> Detected Framework: Ruby
-----> Installing bundler 2.1.4
-----> Removing BUNDLED WITH version in the Gemfile.lock
-----> Compiling Ruby/Rack
-----> Using Ruby version: ruby-2.6.3
-----> Installing dependencies using bundler 2.1.4
Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
Using concurrent-ruby 1.1.7
Using i18n 0.9.5
Using minitest 5.14.2
Using thread_safe 0.3.6
Using tzinfo 1.2.7
Using activesupport 5.2.4.4
Using public_suffix 4.0.6
Using addressable 2.7.0
Using execjs 2.7.0
Using autoprefixer-rails 9.8.6.5
Using backports 3.18.2
Using bundler 2.1.4
Using coffee-script-source 1.12.2
Using coffee-script 2.4.1
Using contracts 0.13.0
Using dotenv 2.7.6
Using erubis 2.7.0
Using fast_blank 1.0.0
Using fastimage 2.2.0
Using ffi 1.13.1
Using temple 0.8.2
Using tilt 2.0.10
Using haml 5.2.0
Using hamster 3.0.0
Using hashie 3.6.0
Using rexml 3.2.4
Using kramdown 2.3.0
Using rb-fsevent 0.10.4
Using rb-inotify 0.10.1
Using listen 3.0.8
Using memoist 0.16.2
Using thor 1.0.1
Using middleman-cli 4.3.11
Using padrino-support 0.13.3.4
Using padrino-helpers 0.13.3.4
Using parallel 1.19.2
Using rack 2.2.3
Using sassc 2.4.0
Using servolux 0.13.0
Using uglifier 3.2.0
Using middleman-core 4.3.11
Using middleman 4.3.11
Using middleman-autoprefixer 2.10.1
Bundle complete! 3 Gemfile dependencies, 43 gems now installed.
Gems in the groups development and test were not installed.
Bundled gems are installed into `./vendor/bundle`
Bundle completed (0.52s)
Cleaning up the bundler cache.
-----> Writing config/database.yml to read from DATABASE_URL
-----> Installing node-v12.16.2-linux-x64
-----> Detecting rake tasks
###### WARNING:
No Procfile detected, using the default web server.
We recommend explicitly declaring how to boot your server process via a Procfile.
https://devcenter.heroku.com/articles/ruby-default-web-server
=====> Downloading Buildpack: https://github.com/dokku/buildpack-nginx.git
=====> Detected Framework: .static
-----> Copy static files to www
-----> Reusing nginx binary from cache
-----> Using default app-nginx.conf.sigil
-----> Using default mime.types
Using release configuration from last framework (.static).
-----> Discovering process types
Default types for -> web
-----> Releasing site...
-----> Deploying site...
-----> Checking for predeploy task
-----> Executing predeploy task from app.json: bundle exec middleman build
=====> Start of site predeploy task (22fcf68dd) output
remote: ! Execution of predeploy task failed: bundle exec middleman build
/bin/bash: bundle: command not found
remote: 2021/01/02 15:06:19 exit status 1
remote: 2021/01/02 15:06:19 exit status 1
remote: 2021/01/02 15:06:19 exit status 1
=====> End of site predeploy task (22fcf68dd) output
To host:site
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'host:site'
I noticed the first buildpack is executed, then the second one, then the predeploy hook kicks in.
I tried removing the nginx "static serve" buildpack and keep only the ruby one, and keep a bundle
command in my predeploy hook. It worked and I didn't get the "command not found" error. So switching to the second buildpack seems to be messing things up for me.
I compared the path with and without the nginx "static serve" buildpack, and nothing weird. With the nginx buildpack, a new path is added but the previously set ruby paths are still there:
# with only ruby buildpack
/app/bin:/app/vendor/bundle/bin:/app/vendor/bundle/ruby/2.6.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# with both ruby & nginx buildpacks
/app/bin:/app/vendor/bundle/bin:/app/vendor/bundle/ruby/2.6.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/app/nginx
Eventually, I'm a bit lost here. Is it expected? Am I not following the dokku way? Am I getting this because of permissions issues, or something in Dokku that I'm not aware of?
For now, as a fallback, I'm serving the site by running a middleman server that receives all requests, but it makes no sense to dynamically serve a statically-generated website.
Thanks to jonrsharpe comment, I reoriented my searches and found this blog post on heroku engineering blog. Eventually, as stated by jonrsharpe:
The Nginx buildpack won't have Ruby in at all - you need to do any building in the Ruby buildpack context, so all the static buildpack needs to do is serve the results.
Therefore, to launch my middleman build
command, I needed to hook somewhere in the ruby buildpack thing. And in the "jekyll on heroku" link, everything is explained: one should override the assets:precompile
rake task.
gem "rake"
to my Gemfile (and bundle, of course)Rakefile
with the assets:precompile
task :task "assets:precompile" do
exec("middleman build")
end