Deploying With Ansistrano

I have a major problem. I keep waaay too many open tabs on my phone. Every time I see something interesting – via Twitter, Hacker News, or Reddit – I open the link in a tab and promise myself I will check it out more thoroughly soon.

I currently have 47 open tabs 🙁

This is only amplified by the fact I do the same on desktop. Right now, across three desktops I have over a hundred open tabs.

It’s becoming an epidemic.

Anyway, once in a while, one of these tabs becomes useful.

Recently I hit on a problem whereby I needed to deploy a bunch of individual JavaScript files – node.js scripts – to a server to be used as Rabbit MQ workers.

I had a bunch of requirements for these workers:

  • start with specific flags (–harmony-async-await )
  • restart automatically if the server reboots
  • restart if the script crashes
  • can run multiple instances

And so on.

These turned out to be the superficial problems – and I say this because there’s a tool out there that already nails this problem – PM2.

Initially I thought these would be the hard problems.

What I hadn’t banked on was how much of a royal pain in the backside it might be to deploy my node.js scripts to dev / prod / wherever.

 

My requirements are fairly straightforward – they could be solved by using rsync. However, rsync becomes unmanageable as a project grows.

There’s the issue of remembering the right command, and then duplicating the command – altering slightly – for the prod deploy.

And what if it goes wrong? Well, you have to handle that yourself.

Call me spoiled, but having become accustomed to Deployer (Matt did a fantastic job on this course btw, you should check it out), I now use that as my baseline for deployments.

I have a similar tool I use on JavaScript projects called Flightplan. It offers a decent level of functionality, but with one major issue (from my p.o.v):

It is a pain to deploy more than one directory.

Flightplan works on the assumption – as best I can tell – that you will be running your project through some webpack-style setup first, producing a dist directory which contains everything you need to boot your single page app, or whatever.

This is cool, but I needed to run many different worker scripts – all ideally from one directory.

As best I understand it, webpack allows this via it’s multiple entry options, but I’m not using webpack. Actually, I tried to use webpack but it threw out a bunch of errors right away and I gave up.

I also tried Deployer. But that didn’t work much good either. JS mixed with PHP leads to mess.

Enter Ansistrano

Ok, so all that was a very long-winded precursor to the eventual solution.

However, I felt I needed to do justice to how much I have struggled to get this thing working. It’s taken 5 hours… ouch.

Needless to say I tried to give up on getting Ansistrano at numerous times (see how I ended up at Deployer, Flightplan, webpack etc).

In the end though, I cracked it. So here goes:

Firstly, my playbook:

---
- hosts: all

  vars:
    ansistrano_deploy_to: "/var/www/your/remote/path" # server side path you want to deploy too
    ansistrano_keep_releases: 3

    ansistrano_deploy_via: "git"

    ansistrano_git_repo: "ssh://git@your.gitlab.server/your-gitlab-user/your-project.git"
    ansistrano_git_branch: "master"


  roles:
    - { role: ansistrano.deploy }

Pay special attention to the ansistrano_git_repo entry, whereby I needed to add the prefix of ssh:// to make this work. If you don’t, you will find Ansible doesn’t understand the path you are providing, and blanks it out instead :/

I guess I wasn’t the only person to notice this.

Also, note that the typical git path given by gitlab will contains colons, which need to be replaced with spaces:

git@your.gitlab.com:your-user/your-project.git

// becomes

git@your.gitlab.com/your-user/your-project.git

 

Now note, this is an Ansible issue, not an Ansistrano issue.

This should be enough to get most of the way there.

However, I hit upon another issue.

No matter what I did, all the Ansistrano managed folders were being created as root .

Since the days of yore, I have been using the same set of flags on my runs of ansible-playbook, and today I was well and truly bitten on the backside:

ansible-playbook playbook/deploy.yml -i hosts -l my-server.dev -k -K  -vvvv

Ultimately this command sees me through. I’ve started using -vvvv on every playbook run as it saves me having to re-run when things inevitably go wrong. Also, for the love of God, use snapshots before running.

But yeah, my issue was I was running with the additional flag of -s which forced the playbook to run as root. Silly me.

Anyway, early signs are promising. It all works. I just wish it hadn’t taken me so much time to figure out these problems. Hopefully though, by sharing I can save someone some hassle in future.

What’s new this week at CodeReviewVideos? 9th December 2016

I hope you’ve had a fun week full of coding. Have you upgraded to PHP 7.x yet? With the recent release of PHP 7.1, a number of the new languages features are really usable.

This week there have been three new videos, two of which continue the dive into PHP 7.1:
https://codereviewvideos.com/course/php-7-1/video/class-constant-visibility

We start off with a look at class constant visibility. As with all these computer science-y sounding things, it initially doesn’t seem that appealing, I’m sure.

However, this is a fairly handy tool to have in your belt. It allows setting private, and protected constants. Not so useful if you only ever add constants to your interfaces, but if you’re a little more liberal with their usage then this ones going to be helpful, I’m sure.

https://codereviewvideos.com/course/php-7-1/video/void-functions

Last week we looked at nullable types – the idea being that functions may either return something, or null. We learned how this also applies to type hinting variables inside function arguments.

In this video on void types, we cover the edge case of what happens when your function doesn’t return anything – and that includes null.

https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/login-part-4

Finally we continue on with the login journey for our React / Redux / Redux Saga front end to our Symfony 3 API back end.

If you’ve ever wanted to get started with React / Redux then I cannot recommend this course to you enough.

One thing that really bugged me when learning React was in how the examples covered the API connectivity. Frequently the way this would be done would be to provide some JSON mock. That’s all good for a small tutorial, but back in the real world, the API part is super important to know, and can be quite tricky to build.

Thankfully we’ve already built the API in Symfony 3, and now we get to see how a modern front end JavaScript application might interact with it. Redux, and Redux Saga have fundamentally changed my approach to front end development, and learning about them will likely make you a more rounded developer. Sharing this with you is an absolute privilege.

See you all next week 🙂

Chris

What’s New This Week At CodeReviewVideos (2nd December 2016)

This week there have been three new videos.

https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/login-part-2

Firstly, we continue with the Login Journey throughout the React / Redux / Redux Saga app we are building. In this video we cover a fundamental part of the stack – the Action. As we finish up this video, you will have dispatched your first Action.

Ok, but you may be wondering: why should that thrill me?

Because it’s at the heart of how our application will change its state. And that ultimately leads to applications that are many, many times more fun to work with than spaghetti slop.

https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/login-part-3

Secondly, we go from dispatching this Action to handling that Action inside the world of Redux Saga.

This is the sort of stuff that’s absolutely MASSIVE in the jobs market right now. It’s popular for a reason. This will contribute greatly not only to your CV, but to making applications that you are highly more likely to enjoy working on.

https://codereviewvideos.com/course/php-7-1/video/nullable-types

And then the final video for the week covers probably the best thing about PHP 7.1 (released today btw):

Nullable types.

This is a major usability fix to a really nice, but largely unusable PHP 7.0 zero : Return types.

It also fixes scalar type declarations (int, string, bool, etc).

In other words, these will now become much more common as more and more migrate their codebases to PHP7.x, and with all these reported speed boosts, why wouldn’t you?

See you all next week 🙂

Chris

An issue with React Widgets DateTimePicker

When you have the fix to a problem, but don’t know the root cause of why that problem is happening, you have two choices:

  1. Accept you have a solution, and move on, or;
  2. Accept you have a solution, and figure out the cause of the problem.

The problem is, #2 takes time. And that might not be time you have currently.

Dwelling on some problems is not the best use of my time in my current position.

So here’s my problem, and a solution, but I cannot offer you the root cause to this problem. Nor is it the defacto solution, I believe.

Using React Widgets DateTimePicker

I guess you’re either the kind of developer who creates everything themselves.

Or you first try using other people’s.

I wanted a Date Picker, ideally with time, and I would like it to be hooked up to Redux Form.

The good news, Redux Form already works nicely with React Widgets. See the previous link for a decent tutorial. And this code sample.

As a result, I can quite easily add in a Date Time Picker that’s hooked up to my Redux Store. This is good. It pleases me.

It also allows me to start laser focusing my A/B tests.

date-of-birth-as-a-timestamp
You’ve never A/B tested unless you know for sure customers don’t prefer timestamps.

But joviality aside (wait, that was humour?), I did hit a problem along the way:

react-widgets-calendar-size-issue
That’s not so good, Al.

There is a quick fix to this.

Add in a local (S)CSS style:

.rw-popup-container.rw-calendar-popup {
  width: 24em !important;
}
redux-form-datetime-picker-react-widgets
Better

It’s good, but it’s not quite right. Good enough for me to continue, though has been firmly lodged into the ticket queue under ‘improvement’.

Here’s what it should look like:

react-widgets-datetime-picker-proper

So there you go. Hope it helps someone. It’s unofficial. It’s not the real fix. Any pointers appreciated.

How I Fixed: uncaught at check call: argument [object Promise] is not a function

Earlier this month I started dabbling with Redux Sagas as an alternative to Redux Thunks.

At the time I was highly skeptical – largely around whether re-writing a significant portion of my JavaScript was good use of my time, given that I had no prior experience with generators, and that conceptually sagas sounded pretty hard.

But I kept hitting issues with Thunks. They just seemed so cumbersome, and error prone. I found writing tests for them to be painful, and adding in the Redux API Middleware made that even more complicated.

Ultimately, switching to Redux Saga has been one of the highlights of my current project. I love it. Every problem I have had so far has been made easier through the use of sagas.

But today I hit on a problem I haven’t seen before:

uncaught at check call: argument [object Promise] is not a function

This is a generic version of the code I am using:

export function *doRequestProfile(action) {

  try {

    yield put({
      type: types.REQUEST__STARTED,
      payload: {
        requestFrom: 'my-saga'
      }
    });

    const profile = yield call(api.fetchProfile(action.payload.accountId));

    yield put({
       type: types.PROFILE__SUCCESSFULLY_RECEIVED,
       payload: {
         profile
       }
    });

  } catch (e) {

    yield put({
      type: types.PROFILE__FAILED_RECEIVING,
      payload: {
        message: e.message,
        statusCode: e.statusCode
      }
    });

  } finally {

    yield put({
      type: types.REQUEST__FINISHED,
      payload: {
        requestFrom: 'my-saga'
      }
    });

  }
}

export function *watchRequestProfile() {
  yield* takeLatest(types.PROFILE__REQUESTED, doRequestProfile);
}

I have highlighted the problematic line.

The issue here is that the error in the browser is fairly cryptic. It took me digging into the source code to figure this one out.

But actually the issue is really simple to fix.

The highlighted line above should be :

const profile = yield call(api.fetchProfile, action.payload.accountId);

I was calling my function myself, and then passing the promise on to the Saga… whoopsie daisy.