Staging Environment Builds

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

We now have two versions of our site deployed: our production deploy and a, sort of, "staging" deploy of a pretend feature we're working on. Blackfire is all set up on the production server, but not on the staging server. Let's fix that!

Back on the "Install" page, select "SymfonyCloud" as our host to get to its docs. To set up Blackfire on production, we did 3 things. One, added the extension. Two, ran this var:set command to configure our Blackfire Server id and token. And three, ran integration:add so that every deploy to master would trigger a Blackfire build in our environment.

Technically, on the staging server, the Blackfire extension is already enabled and it's set up to use the Server Id and token from our production Blackfire environment. But, as we talked about in the last chapter, I don't want to mix my production builds with builds from staging servers.

Creating a new Blackfire Environment

Instead, go back to our Blackfire organization and create a second environment. Let's call it "Sasquatch Sightings Non-master". For the endpoint, use the production environment URL. But don't worry, that URL won't actually be used. You'll see.

Hit "Create environment"... then remove the build notifications and save. View the new environment - I'll get the credentials in a minute. Now, stop the periodic builds. Why? Well in our setup, at any point, we may have zero or many different "staging" servers. There's not just one server to build... so if we did a periodic build... which "staging" server would it use? It just doesn't make sense in our case. What does make sense is to trigger a new build each time we deploy to a staging server.

Different Server Id and Token on Staging

Ok, let's think about this: we now have two Blackfire environments. We want the production server to use the Blackfire server id and token for the production environment... and we want every other deploy to use the Blackfire id and token from the new "Non-master" environment.

How you do that depends on how you deploy. For us, we can use a SymfonyCloud config trick. First, list which variables we have set with:

symfony vars

We have the two that were set by the var:set command we ran earlier. Delete both of them:

symfony var:delete BLACKFIRE_SERVER_ID BLACKFIRE_SERVER_TOKEN

We're going to re-add these in a minute... but with some different options. Now, go back to the installation page... and refresh... so this shows our new environment. For the var:set command, select the Non-master environment. Copy the command, move over and paste:

symfony var:set BLACKFIRE_SERVER_ID=XXXXXXX BLACKFIRE_SERVER_TOKEN=XXXXXX

If we stopped now, it would mean that every server would send its profiles to the new Non-Master environment... which is not exactly what we want. But here's the trick: on the "Install" page, change to the "Production" Blackfire environment, and copy its command. We're going to override these variables, but just on the SymfonyCloud master environment.

Paste the command, then add --env=master --env-level so that the variables are used as "overrides" for only that environment. Finish with --inheritable=false so that when we create new SymfonyCloud environments, they don't inherit these variables from master: we want them to use the original values:

symfony var:set BLACKFIRE_SERVER_ID=XXXXXXX BLACKFIRE_SERVER_TOKEN=XXXXXX \
--env=master --env-level --inheritable=false

This is a long way of saying that the master environment on SymfonyCloud will now use the server id and token for the "Sasquatch Sightings Production" Blackfire environment. And every other deploy will use the credentials for the "Non-Master" environment. To be sure, run:

symfony vars --env=master

Yep! 6900 is the server id for Production. Now try:

symfony vars --env=some_feature

Perfect: that uses the other Server id and token. We're good!

Staging: Builds on Deploy

The last thing I want to do is run this integration:add command again. We ran this earlier to tell SymfonyCloud that it should notify our "Production" Blackfire environment whenever we deploy to master. Now copy the "Non-Master" environment command... and run it:

symfony integration:add --type=webhook --url='https://USER:PASS@blackfire.io/api/v2/builds/env/aaaabbee-abcd-abcd-abcd-c49b32bb8f17/symfonycloud'

Say yes to all events, all states and all environments. Actually, what we really want to say is: create a build on the "Non-Master" environment every time any branch except for master is deployed... but I don't think that's possible.

Phew! Let's redeploy both SymfonyCloud environments to see all of this in action:

symfony redeploy --bypass-checks

Because we're currently checked out to the some_feature branch, this deploys that branch. When it finishes, run the same command but with --env=master to redeploy production:

symfony redeploy --bypass-checks --env=master

We also could have switched to that branch - git checkout master - and then ran symfony redeploy. That's the more traditional way.

Done! Let's go see what that did! First check out the Blackfire production environment. Yes! The redeploy to master created one new build. Perfect. Now check out the Non-master environment. Oh, this has two new builds: one for the some_feature deploy and another for the master deploy. We don't really want or care about that second one... but it's fine. What we do care about is that now, every time we deploy to a non-production server, we get a new build here.

If you use GitHub or Gitlab, you can take this one step further by doing 2 things. First, SymfonyCloud has a feature where it can automatically deploy the code you have on a pull request. And because that would trigger a new build, second, you can configure Blackfire to notify GitHub or Gitlab of your build results so that they show up on the pull request itself. Pretty awesome.

I love our setup. But there's one more environment feature that we haven't checked out yet: the ability to set variables that you use in your scenarios. Let's check that out next.

Leave a comment!

This tutorial can be used to learn how to profile any app - including Symfony 5.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "blackfire/php-sdk": "^1.20", // v1.20.0
        "sensio/framework-extra-bundle": "^5.4", // v5.5.1
        "symfony/console": "4.3.*", // v4.3.10
        "symfony/dotenv": "4.3.*", // v4.3.10
        "symfony/flex": "^1.3.1", // v1.6.0
        "symfony/form": "4.3.*", // v4.3.10
        "symfony/framework-bundle": "4.3.*", // v4.3.9
        "symfony/http-client": "4.3.*", // v4.3.10
        "symfony/orm-pack": "^1.0", // v1.0.7
        "symfony/security-bundle": "4.3.*", // v4.3.10
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "4.3.*", // v4.3.10
        "symfony/validator": "4.3.*", // v4.3.10
        "symfony/webpack-encore-bundle": "^1.6", // v1.7.2
        "symfony/yaml": "4.3.*", // v4.3.10
        "twig/extensions": "^1.5" // v1.5.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.2", // 3.2.2
        "fzaninotto/faker": "^1.8", // v1.8.0
        "symfony/debug-pack": "^1.0", // v1.0.7
        "symfony/maker-bundle": "^1.13", // v1.14.3
        "symfony/test-pack": "^1.0" // v1.0.6
    }
}