# Using Bedrock + Sage to deploy with Capistrano (theme Gulp dist files?)

**URL:** https://discourse.roots.io/t/using-bedrock-sage-to-deploy-with-capistrano-theme-gulp-dist-files/3325
**Category:** uncategorized
**Created:** 2015-03-24T06:48:09Z
**Posts:** 22

## Post 1 by @brett — 2015-03-24T06:48:10Z

I’m able to deploy with Capistrano and all is mostly well. However, my theme (built on Sage) doesn’t have the dist folder and all of the assets etc on the server when I deploy. I could add dist to the git repo and then it would be deployed t o the server that way, but it seems like I should probably be using Capistrano to generate these during deployment or something?

How do I configure Capistrano to run gulp --production and deploy the dist directory and all of its assets to my server during a Capistrano deploy?

Or maybe I’m looking at this the wrong way and there’s something obvious I am overlooking. What I want is to be able to run `bundle exec cap staging(/production) deploy` and have my Sage theme dist files included and on the server. What’s the best way to go about doing this?

There are some great resources for Bedrock, and some great resources for Sage. Are there any resources for using these two together?

---

## Post 2 by @treb0r — 2015-03-24T11:25:37Z

This needs updating for Gulp but you should be able to figure it out.

> [@Capistrano: run Grunt locally and upload files](https://discourse.roots.io/t/capistrano-run-grunt-locally-and-upload-files/2062):
>
> So is there a way to run grunt build on the remote server in some way? I notice Scott has this gem: [GitHub - roots/capistrano-grunt: Capistrano extension for Grunt tasks](https://github.com/roots/capistrano-grunt) Or do we run grunt build locally and then scp these compiled assets to the remote server using a Capistrano task? I’m a bit confused as to what’s the best way. Any advice would be appreciated.

---

## Post 3 by @brett — 2015-03-24T22:43:17Z

thanks. this looks like what i’m going to want.

The main thing I’m going for here is to have the production server contain only the necessary files for the production environment. I don’t want it to include anything it doesn’t need to. Ideally, I’d rather it didn’t include composer.json in the site root (I’m using bedrock), though I suppose at least this isn’t in the web root.

When I read your comment last night (on my phone, sorry for the late reply), it got me thinking about what would be the ideal way to deploy.

I feel like I should have a “build” command on my development server that will compile everything, optimize it and remove the unnecessary “scaffolding” files like composer.json, package.json etc, and will zip everything up and upload that to github/bitbucket as a “release” archive. The server would then pull that and unpack it on the server, so I would end up with the server containing only the necessary files to run.

For example, looking at just the Sage theme, you’d rather not publish all of the .scss/.less files to your production server, and would rather include only the compiled/concatenated/minified css/js files.

However, as it stands with a Bedrock and Capistrano deploy, it appears as though it pulls the code from the repo itself on bitbucket (I’m using bitbucket, not github), and then actually runs composer update on the server.

Is there a better way? I feel like there must be. Do I want to look into CI servers or something? Sorry if this is all a bit off topic and/or rambling.

_edit_

Something like this: [http://stackoverflow.com/a/21722205/3972095](http://stackoverflow.com/a/21722205/3972095)

The guy basically says, you don’t want to install dependencies on the server, only production files and stuff. How can I do this?

---

## Post 4 by @treb0r — 2015-03-24T23:28:08Z

Don’t worry about composer, it’s central to the power of Bedrock.

There is a great article I read recently that sets out the various options for this stuff, but I can’t find it just now.

The roots guys are planning to switch to ansible based deploys and I think this could involve ci and will give you the solution that I think you are looking for.

Just have Capistrano compile the assets and copy them to production for now.

I find that if you just follow what roots are doing you should be in a good place that keeps on getting better all the time.

---

## Post 5 by @swalkinshaw — 2015-03-25T03:08:57Z

The solution to this problem is just to compile your assets locally.

Here’s a Gist of a `deploy.rb`: [https://gist.github.com/swalkinshaw/9e4556d4b0c402eba29a](https://gist.github.com/swalkinshaw/9e4556d4b0c402eba29a)

Note: haven’t tested this but it’s pretty straightforward.

It’s just the default Bedrock `deploy.rb` with an extra task to compile and copy assets.

---

## Post 6 by @brett — 2015-03-25T06:50:05Z

Thanks for the reply. I tried integrating the stuff from your Gist, but it’s giving me a pretty strange error:

```
INFO[7c53ef33] Running /usr/bin/env composer install --no-dev --prefer-dist --no-interaction --quiet --optimize-autoloader on theproject.com

INFO[7c53ef33] Finished in 4.270 seconds with exit status 0 (successful).

cap aborted!

SSHKit::Command::Failed: if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi exit status: 256

if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi stdout: Nothing written

if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi stderr: ! was unexpected at this time.

config/deploy.rb:71:in `block (3 levels) in <top (required)>'

config/deploy.rb:70:in `block (2 levels) in <top (required)>'

Tasks: TOP => deploy:updated => assets:deploy => assets:compile

(See full trace by running task with --trace)

The deploy has failed with an error: #<SSHKit::Command::Failed: if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi exit status: 256

if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi stdout: Nothing written

if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme; then echo "Directory does not exist 'D:/Projects/theproject.com/web/app/themes/thetheme'" 1>&2; false; fi stderr: ! was unexpected at this time.

>

PS D:\Projects\theproject.com>
```

I added an extra space between each line to (hopefully) make it easier to read. It’s a pretty odd error, because I don’t see anything about an “if test” in the deploy.rb file, so I can only assume it’s including something that is making that test? Hell, I really don’t even know lol…

It looks like it’s echoing the source code or something.

---

## Post 7 by @swalkinshaw — 2015-03-25T12:16:52Z

When Capistrano sees a `within fetch(:local_theme_path)` line, it first tests if the directory exists.

So it’s running `if test ! -d D:/Projects/theproject.com/web/app/themes/thetheme;` (reads as “if NOT a directory”) and then it’s failing with "Directory does not exist ".

Obviously if that directory doesn’t exist it can’t go on so it just fails. So make sure those paths are correct but it could be a Windows problem.

Only thing I can suggest to try is this:

```
set :theme_path, Pathname.new(File.join('web', 'app', 'themes', 'thetheme'))
set :local_app_path, Pathname.new(File.join('D:', 'Projects', 'theproject.com'))
set :local_theme_path, fetch(:local_app_path).join(fetch(:theme_path))
```

edit:

Or try backslashes:

```
set :theme_path, Pathname.new('web\app\themes\thetheme')
set :local_app_path, Pathname.new('D:\Projects\theproject.com')
set :local_theme_path, fetch(:local_app_path).join(fetch(:theme_path))
```

---

## Post 8 by @rodrigo — 2015-04-02T05:18:09Z

For anyone falling on this thread, @swalkinshaw has provided an example he hasn’t yet tested.

Needs a correction `invoke 'deploy:compile_assets` is not a native task.

Theres this thread ([Bedrock & Roots: Copying Production Assets with Capistrano - #2 by bigsweater](https://discourse.roots.io/t/bedrock-roots-copying-production-assets-with-capistrano/2187/2)) where the discussion is around using a manifest within the deploy.rb / see [https://gist.github.com/nateroling/22b51c0cfbe210b00698](https://gist.github.com/nateroling/22b51c0cfbe210b00698)

Just remove `invoke 'deploy:compile_assets` on this line [https://gist.github.com/swalkinshaw/9e4556d4b0c402eba29a#file-deploy-rb-L63](https://gist.github.com/swalkinshaw/9e4556d4b0c402eba29a#file-deploy-rb-L63) and it will run.

To be safe and since I don’t have roles setup, I changed [https://gist.github.com/nateroling/22b51c0cfbe210b00698#file-deploy-addendum-rb-L50](https://gist.github.com/nateroling/22b51c0cfbe210b00698#file-deploy-addendum-rb-L50) to `on roles(:app) do`

Also please note that bash shortcuts don’t work as per @bigsweater suggested on that thread, so the paths to local files need to be absolute.

> “Figured it out. Using the ~ homedir shortcut doesn’t work in Capistrano, at least for the upload! method, because SCP don’t play that. ~ is a Bash shortcut, not SCP, so SCP wasn’t finding the directory, naturally.”

---

## Post 9 by @swalkinshaw — 2015-04-02T16:16:54Z

Thanks! That was left behind from another old Gist for Grunt that I based it off of. I updated my gist.

---

## Post 10 by @rodrigo — 2015-04-03T03:57:36Z

To close this out @swalkinshaw and @brett , adding some modularity to the deployment method so that anyone that is interested won’t have to hardcode local paths to their deploy script.

Find the gist below. It’s been tested and verified with Cap 3.2.0

Done.

You can verify that the command will run during deploy by running `bundle exec cap staging assets:copy --trace`

This task will run automatically when you run `cap staging deploy`

Here is the Gist

> <https://gist.github.com/el-rotny/909e8f220933635bad7a?1>

---

## Post 11 by @timichango — 2015-04-24T14:05:49Z

@rodrigo this gist seems to be _so_ close to working on my first attempt to get sage deployed on a bedrock wp install via capistrano to digitalocean, but… for some reason, even though I can **see** the compile and upload taking place successfully while the deploy runs, the _dist folder simply never appears on the server within the theme_.

Any idea why that might be happening?? It’s really perplexing me.

Here’s the output gleaned from the commit (server/site names changed): [http://tny.cz/4d137413](http://tny.cz/4d137413)

All of the paths look bang-on, but the dist folder never makes it into the theme… and can’t be found anywhere else either.

---

## Post 12 by @swalkinshaw — 2015-04-24T14:51:44Z

You can see what’s happening right in the log:

```
INFOYour local distribution path: web/app/themes/faketheme2015/dist
INFOBoom!!! Your remote distribution path: /var/www/mysitename/current/web/app/themes/faketheme2015/dist
```

And later on:

```
INFO[fc7b431c] Running /usr/bin/env rm -rf /var/www/mysitename/current on fakeserver.com
DEBUG[fc7b431c] Command: ( WP_ENV=staging /usr/bin/env rm -rf /var/www/mysitename/current )
INFO[fc7b431c] Finished in 0.084 seconds with exit status 0 (successful).
INFO[e59bb167] Running /usr/bin/env ln -s /var/www/mysitename/releases/20150424133950 /var/www/mysitename/current on fakeserver.com
DEBUG[e59bb167] Command: ( WP_ENV=staging /usr/bin/env ln -s /var/www/mysitename/releases/20150424133950 /var/www/mysitename/current )
```

1. Assets uploaded to `/var/www/mysitename/current/web/app/themes/faketheme2015/dist`
2. `/var/www/mysitename/current` deleted
3. `/var/www/mysitename/current` re-linked to newest release

Step #1 is what’s wrong because it should be uploading them to the **release path** and not hte **current** symlink.

Try changing:

```
set :remote_theme_path, release_path.join(fetch(:theme_path))
```

to

```
set :remote_theme_path, -> { release_path.join(fetch(:theme_path)) }
```

---

## Post 13 by @timichango — 2015-04-24T15:52:59Z

> [@swalkinshaw](#):
>
> You can see what’s happening right in the log:

Ah, but seeing and interpreting… big difference!

Anyway — I tried implementing that modification, and no joy — problem persisted.

Also, I have a new problem: starting from a perfectly clean server state with no deploys (ie. all deploys removed from server deploy destination), I’m getting the following error as soon as gulp is invoked during the deploy:

```
INFO[53571b0f] Finished in 13.256 seconds with exit status 0 (successful).
INFOYour local distribution path: web/app/themes/faketheme2015/dist
INFOBoom!!! Your remote distribution path: /var/www/fakesite/current/web/app/themes/faketheme2015/dist
INFOUploading files to remote
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing on host fakeserver.com: scp: /var/www/fakesite/current/web/app/themes/faketheme2015/dist: No such file or directory
```

It’s not happy about there not being a preexisting `/dist/` folder in the theme path

This would be easier if I knew anything at all about ruby syntax :frowning:

---

## Post 14 by @timichango — 2015-04-24T22:38:33Z

Welp — after many, many hours of bashing my face into the keyboard, I figured out the problem: the asset build/deploy seems to have been happening on the wrong deployment hook.

- I could find no way to modify the gist from @rodrigo to get the upload happening to the specific release folder and _not_ the current symlink. I supposed it’s probably possible and I’m simply too much of a ruby/capistrano newb to suss out the correct way to do that, but the deploy script kept trying to upload to `/dist/` within the `/current/` path, and either failling if `dist` didn’t already exist, or dumping the files into the previous release if it did.
- the hook running the gulp + upload routine was running _before_ the new deploy was created (ie. before the new release was symlinked properly), so hooking into the `deploy:published` hook instead allowed the `current` path to work properly, and resolved the problem…

```
# Remote Paths
# set :remote_theme_path, release_path.join(fetch(:theme_path))
set :remote_theme_path, -> { release_path.join(fetch(:theme_path)) }
set :remote_dist_path, fetch(:remote_theme_path).join('dist')

namespace :assets do
  task :compile do
    run_locally do
      within fetch(:local_theme_path) do
        # execute :gulp, '--production'
        execute :gulp, ''
      end
    end
  end

  task :copy do
    on roles(:web) do
      ld = fetch(:local_dist_path)
      rd = fetch(:remote_dist_path)
      info " Your local distribution path: #{ld} "
      info " Boom!!! Your remote distribution path: #{rd} "
      info " Uploading files to remote "
      # execute "rm -r #{fetch(:remote_theme_path).to_s}/dist"
      # execute "mkdir #{rd} "

      upload! fetch(:local_dist_path).to_s, fetch(:remote_dist_path), recursive: true

    end
  end

  task deploy: %w(compile copy)
end

after 'deploy:published', 'assets:deploy'
```

Please advise if that’s not a technically sound way of running the task, but… it is working. Hopefully that’s useful to someone.

---

## Post 15 by @rodrigo — 2015-04-24T23:45:49Z

@timichango

I totally dropped the ball on updating you guys on this. Its still WIP

The simple fix for making it “work” is on the last line. I did resolve it locally on my implementation but forgot to make the changes to the Gist, exactly in the same way.

Basically, it waits till the remote is published (symlink activated) then uploads the file.

```
after 'deploy:published', 'assets:deploy'
```

NOTE: There is one problem with this though (for production). Your assets will not be present from the time the symlink is published to the moment your assets are uploaded so I am only running it for staging and won’t be coming up with a solution until we move to production.

We need to use the `latest_release` variable = release path or the current release depending on if the current symlink is valid

And hook to happen before publishing (after deploy:updated),

```
deploy:updated
    [before]
      deploy:bundle
    [after]
      deploy:migrate
      deploy:compile_assets
      deploy:normalize_assets
```

See  
[http://capistranorb.com/documentation/getting-started/flow/](http://capistranorb.com/documentation/getting-started/flow/)

Anyways, I updated my Gist to reflect the non production fix/change even though its WIP

---

## Post 16 by @jaywilliams — 2015-05-01T22:00:00Z

Thanks @rodrigo for sharing your deployment configuration. I’ve spent some time working with it to get the uploading to take place _before the new site goes live_ with the symlink update.

The key was to lazy-load the `release_path` variable only after the deploy task has initiated.

Doing it this way ensures that all of the files are uploaded and ready to serve when the new deploy goes live.

> <https://gist.github.com/jaywilliams/c0abbec89ef6bc81cb49>

---

## Post 17 by @Simon_Peters — 2015-05-02T15:55:08Z

@jaywilliams I just tested your latest gist and it worked for me! The only weird thing is that it also added a dist folder in my dist folder and also copied the files in there. So now I have all my files in /dist but also in /dist/dist.

Has this issue occured with you as well?

---

## Post 18 by @jaywilliams — 2015-05-04T21:32:04Z

No, I’m afraid not. I just ran a deploy to test it, and everything was created and uploaded properly.

I’m curious though, were you seeing the additional `/dist/` folder on the server or your local computer? And if so, are you sure your paths are configured correctly?

As a side note, on my personal Capistrano config, I added a callback to run `grunt` again without the `--production` flag, to recompile the dev assets on my local machine.

---

## Post 19 by @brett — 2015-05-15T08:08:12Z

Hey all, thanks for the assistance on this. I’m using the `deploy.rb` that @jaywilliams uploaded ([https://gist.github.com/jaywilliams/c0abbec89ef6bc81cb49](https://gist.github.com/jaywilliams/c0abbec89ef6bc81cb49)) and, while the assets:copy task seems to be working, the assets:compile task doesn’t want to. It looks like it’s having an issue with the local theme directory? Here are the errors I get:  
[https://gist.github.com/BrettLefty/5823f6707509ebcefbb7](https://gist.github.com/BrettLefty/5823f6707509ebcefbb7)

You’ll notice my local directory is absolute and not relative. I first assumed this was the issue, so I made a small change to the `set :local_app_path` line, and changed it from:  
`set :local_app_path, Pathname.new(File.dirname( __FILE__ )).join('../')`  
…to…  
`set :local_app_path, Pathname.new(Dir.pwd)`

Here’s my full `deploy.rb` file:  
[https://gist.github.com/BrettLefty/a0025d7d2bfcec102595](https://gist.github.com/BrettLefty/a0025d7d2bfcec102595)

I’m running these commands in PowerShell (I know right?), but I have tried them in Git Bash.

---

## Post 20 by @Daniel_Marquez — 2015-06-15T04:04:53Z

Hello,  
Just a suggestion,  
Make sure the theme folder exists before using:

```
upload! fetch(:local_dist_path).to_s, fetch(:remote_dist_path), recursive: true
```

Before that line, add something like:

```
remote_theme_path = fetch(:remote_theme_path)
execute 'mkdir -p #{remote_theme_path}'
```

And add :remote\_theme\_path as Global (after :theme\_path):

```
set :remote_theme_path, Pathname.new(fetch(:deploy_to) + '/current/web/app/themes').join(fetch(:theme_name))
```

Or something like that…

:wink:

---

## Post 21 by @follano — 2017-06-19T17:17:52Z

I am still having the same error timichango is having even with using the updated gist. I know this thread is over a year old, but i would really appreciate some help.

> cap aborted!  
> SSHKit::runner::ExecuteError: Exception while executing as serveruser@serverip: scp: ~/apps/myapp/current/web/app/themes/themename/dist: No such file or directory

> scp: ~/apps/myapp/current/web/app/themes/themename/dist: No such file or directory

> Tasks: TOP =\> assets:deploy =\> assets:copy  
> (See full trace by running task with --trace)  
> The deploy has failed with an error: Exception while executing as serveruser@serverip: scp: ~/apps/myapp/current/web/app/themes/themename/dist: No such file or directory

It seems to me it is failing right at:

```
upload! fetch(:local_dist_path).to_s, fetch(:remote_dist_path), recursive: true
```

---

## Post 22 by @follano — 2017-06-19T17:24:51Z

Could it be a permissions error? Base on what the error is throwing, It seems to me that it is not allowing capistrano to create a new directories (being that the directory /dist doesn’t exist on the remote server) when the build is pushed to production. I could be way off base here…
