Tracking Envoyer Releases in Sentry

Note: this article I wrote originally appeared on the Humaan blog.

Dan looking out from a Sentry tower

Tracking down bugs can be hard. Damn hard. And figuring out how or when they cropped up in the first place can be even harder. Recently, we launched our social media aggregator platform, Waaffle. It’s written in Laravel and deployed with Envoyer, but we also took the opportunity to try out Sentry for tracking bugs.

Spoiler alert: it was awesome.

Releases in a Sentry issue

One of the features I really like is the ability to track releases, which means you can tell at a glance which release was responsible for your bug. We’ve got a deployment hook in Envoyer that pings Sentry when we deploy a new release. Our hook runs in the “After” section of the “Activate New Release” action and contains the following command:

curl \ -X POST -H 'Content-Type: application/json' -d '{"version": "{{ sha }}"}'

(FYI, it’s the very last post-deployment hook we run, just in case another hook fails.)

The Sentry PHP package includes a section in the config for specifying your app version. However, things can get tricky if, like us, you’re using, say, git commit hashes instead of version numbers.

Thankfully, Sentry’s Laravel package provides code for a nice release hash you can use for getting your git commit hash:

'release' => trim(exec('git log --pretty="%h" -n1 HEAD'))

Which if you run in Artisan’s tinker mode, gives you something like this:


Now the problem: we use Envoyer for our deployments, which downloads the tarball of a specified branch/release/tag instead the entire git repository. That tarball doesn’t contain any of the git history in it, so it’s not technically a repository, thus running the Sentry Laravel command won’t give you a commit hash. If you try, you’ll get the following lovely error:

fatal: Not a git repository (or any of the parent directories): .git

Which means if Sentry tracks an error and saves it, you’ll see there’s no release attached to the issue. Drats!

Somehow, we need a way to get the current commit hash from our Envoyer deployments so we can tell Sentry how to get the current commit. Thankfully, Envoyer comes to the rescue!

In the Envoyer docs, under Deployment Hooks, you’ll see a section on how you can get the current git commit hash (or sha1 hash). Now, this hash is actually the first 12 characters of the current git commit hash – aka. something you can use in the deployment lifecycle.

From here, we want to target the current release directory, so let’s create a deployment hook in the “After” section of “Activate New Release” and call it “Write Git Hash to File” with the user “forge”:

echo "{{ sha }}" > {{release}}/.commit_hash
Our Envoyer hook

Pretty simple, huh? All it does is echoes the current git hash to stdout (standard output), then we redirect that output to a file called .commit_hash in the current release directory. The single arrow (>) means “set the file .commit_hash to 0 length, then append to it”. If we used two arrows (>>) we’d only append to the file, which we wouldn’t want to do – though technically it doesn’t really matter given the release directory has just been created, but it’s the principle of the thing!

Once we’ve got that file written on every deployment, we need to tell Sentry how to get that commit hash! If you wanted to, you could change the config so Sentry fetches the release, like so:

'release' => trim(exec('echo "$(< .commit_hash)"'))

(Note that I’m using echo and redirection here, I don’t want to get into the UUOC debate!)

Except this only works if you have that .commit_hash file in every environment, which we don’t. So, we’re stuck between a rock and a hard place. What we need is a solution that uses the .commit_hash file if it exists, or falls back to a git repository, or finally, returns null if neither of those exist.

In our project, we’ve got a small helpers file with all functions that exist in the global namespace. That’s where we put our solution:

Pretty basic really – just an if/elseif/else block with three possible outcomes. Note that I’m using the base_path Laravel helper, so we can be sure we’ve got the full path to the file/folder in question. Then, in our Sentry configuration file we have the following for the release:

'release' => get_commit_hash(),

Now whenever Sentry needs the commit hash, it’ll call that function and get its return value. Nice! And for those of you concerned about the overheads of getting the commit hash, you can do a php artisan config:cache so the hash is only calculated once. I highly recommend caching the app configuration as part of your deployment process to help to make things that little bit faster. That file is located in bootstrap/cache/config.php and you can verify the existence of this cached value by doing the following (in the root of the project, with the configuration cached):

grep -n --context=3 \'release\' bootstrap/cache/config.php

You should see something like this:

484-   'sentry' =>
485-   array (
486-     'dsn' => '',
487:     'release' => '1a2b3c4d5e6f',
488-   ),
489-   'auth' =>
490-   array (

(The line numbers to the left indicate where in the file it was found.)

Now you’re set! The next time you do a deployment, you’ll have Sentry correctly logging issues with the current release throughout the life of the project.

Happy tracking, folks!

The Age of the Good Samaritan is Over

So I got punched in the face this morning. “Why?” you ask? Because I stepped in to prevent a fight.

Northbridge, the lively hub of Perth, has a bit of an assault problem (funnily enough, the brawl in that article was the exact same place as my incident). The combination of alcohol, drugs, and a culture that promotes fist fighting, in my opinion leads to this anti-social and violent behaviour.

I was on my way home from a work dinner (followed by some 90s pop hits) when two men decided to take it outside, as they say. Normally I wouldn’t be one to get involved in someone else’s fight, but when it occurred literally right next to me, I was unwittingly brought into the situation.

Because the elbow of the windup for the punch meant for the victim almost hit me, I grabbed the arm and started trying to talk him down when he decided to shut me up by punching me in the mouth. I guess it worked because I ended up with my butt on the ground (and with a pair of now broken headphones). I’m very lucky to have not hit my head on the pavement, or on the corner of a table just next to where the incident took place.

Thankfully there were other people around, and some police officers were only about a hundred metres up the road so they were able to quickly intervene and restrain the attacker. After giving my side of the story to the police and knowing that he was heading off to the local police station, I made my way home and called it a night.

Today I went to the doctor to get a checkup to ensure there was no bad damage done to me. Again, thankfully, nothing bad, just a swollen lip and a bit of a sore jaw. Nothing serious at all. But it could’ve been much worse.

Whenever an assault hits the news I often see that people ask for more police presence. In this case, there were uniformed police officers around a hundred metres up the road so how much more police presence does there need to be for it to become a deterrence? Have officers stationed every fifty metres? Doesn’t really sound that practical.

I’m far from an expert in preventing violence so I don’t have any good ideas or thoughts on how to combat anti-social behaviour. Gone are the days where the Good Samaritan can be safe when stepping in to help another person. It’s a shame, really.

Why am I posting this? Mainly because I’m annoyed and frustrated that these kind of incidents keep happening, week after week, day after day. Come on people, let’s be nice to each other. As George Costanza would say:

George Costanza from Seinfeld shouting 'you know, we're living in a society!

LocalStorage is for Fun

Note: I originally wrote this post for the Humaan blog.

Cookie Monster eating cookies
Cookie Monster eating… cookies!

In case you’ve been living under a rock, LocalStorage is a JavaScript API that allows you to store content in the browser’s cache and access it later on when you need it. Similar to how cookies work, but you’ve got much more than ~4KB (the maximum size for a cookie). That said, while there’s no size limit for each key/value pair in LocalStorage, you’re restricted to around 5-10 MB for each domain. I say 5-10 MB because as per usual, different browsers have different maximum limits. Classic!

Recently we’ve had the opportunity to use LocalStorage for our content. I quite liked it at first because it was very simple to use, but it wasn’t long before we ran into issues. Get this: LocalStorage stores content indefinitely, while SessionStorage stores content for the lifetime of the browser session… and there’s no middle ground. What if I want to store something for 30 minutes? Tough luck!

To make things more interesting, even though some browsers report that they have LocalStorage (I’m looking at you, Safari in Private Browsing mode!) you can’t actually use it (trying to write to/read from it will throw an exception).

Bearing these in mind, we decided to write a small custom library that gives you the ability to use expiries, fail-safe LocalStorage detection, and callbacks for when fetching data doesn’t run as expected.

Just want to see the final result? Here it is.

Let’s run through it!

Our most important method is supportsLocalStorage().

This method makes sure we can actually use LocalStorage, bypassing the likes of our aforementioned friend, Safari in Private Browsing mode. Here we wrap the LocalStorage set/get in a try/catch statement to catch any read/write exceptions.

Then, we call supportsLocalStorage() at the start of all the other methods to ensure we can actually use LocalStorage. If not, just abort any call early.

Now let’s look at the setter, setItem():

Not a particularly complex method when you think about it. The first two parameters are the key and value for the content you want to store. If you’re experienced with JavaScript (or any other language that uses the concept of keys and values with arrays/objects/hashes/etc) you’ll understand what they mean with no problems. The value can be anything that the core JSON object can stringify.

The last parameter, expiry, is an optional numeric parameter that if supplied, is used as the lifespan of your key/value combination. When combined with our LocalStorage getter, we can use this expiry value to know whether we should bust the key/value next time we go to fetch it!

Be warned: there’s not a tremendous amount of validation ensuring legitimate numbers are used as an expiry – just provide an integer for the number of seconds you want your key/value combination remaining valid in the LocalStorage cache. It’s easy, you’ll be fine.

You’ll notice this method returns true without checking whether your item was actually set in LocalStorage. Unfortunately the API itself doesn’t return a “save” response, so we have to assume the best and say that it did. But if this isn’t good enough, you can write a fetch method to confirm your suspicions. To me, that seems like overkill because the ideal time to check your item is when you’re actually fetching the data to use it. LocalStorage, like cookies, can be easily modified by the end user, so don’t assume that just because you saved it, it’ll be there the next time you go to pull it.

Our next very important method is our getter, getItem()!

This method is a little more complex. Obviously, the first parameter is the key name for the value you want to retrieve. The second value, however, is a callback you can provide to handle when a value doesn’t exist in LocalStorage. While a callback isn’t technically necessary, because LocalStorage calls aren’t asynchronous, I prefer using them as I feel it makes my code neater.

In your closure, your value in LocalStorage will be returned if it exists. Otherwise, if it doesn’t exist or if it has expired, you’ll get a null return value. This was the cleanest way I could come up with for handling both scenarios, which unfortunately does mean having to do an if/else statement in the closure to check whether the response is null or not.

Finally, our last method is a simple remover method.

The native LocalStorage method doesn’t return a status on whether the operation was successful or not, so I mimic the core method by returning void. Again, you could use a getter to ensure data has been removed, but I don’t really see a point in ensuring the core method has worked correctly.

Now that we’ve run through the class and its methods, here it is in its entirety:

Usage Examples

Both of these examples use the expiry feature in the LocalStorage helper. If the devices key/value doesn’t exist in LocalStorage, we’ll fetch it via AJAX and save it to LocalStorage for 1 hour (60 minutes), then either pass it off to another function or return it.

And there you have it! Check the Bitbucket snippet link for the latest updates as we continue to use and refine our LocalStorage helper.

Bigger (square) thumbnails from the Instagram API

Note: I wrote this article originally for the Humaan blog.

When Instagram added the ability to upload non-square media images to their service, the only way to get a cropped version of the image from the API was via the thumbnail attribute from the media endpoint. Those familiar with the API would know that Instagram’s thumbnails are served at a rather small size of 150×150 which is alright in some cases, but if you need a larger thumbnail size a 150×150 image scales up very poorly.

Here’s the images attribute in the API response for a recent Instagram post we did:

And here’s the thumbnail for that image:

Example 1 Image: Square

Scaling that thumbnail up even 1.5 times makes it look very grainy and shows very little detail. For normal square images you can just use a different image size (like low_resolution or standard_resolution), but for images that aren’t square, you won’t get a nice square image. Not great if you’re trying to use a square grid to display Instagram pictures.

Now, here’s the images attribute in an API response for another recent Instagram post we did, except this time the image in question is landscaped:

Instagram are nice enough to provide a cropped, square version of the image for the thumbnail, but the low_resolution and standard_resolution URLs are both uncropped and are not square. Gross!

Say we want a 320×320 thumbnail of the Instagram post, but as you can see, we want a square image. If we use the low_resolution URI, we’ll get this:

Example 2 Image: Rectangle, but we want a square

If you take a look at the URIs for the images, you’ll notice that the thumbnail URI has an extra parameter in the filename path… the magical crop dimensions! Unfortunately, the crop dimensions aren’t accessible anywhere else in the API (as far as I know), except for in the thumbnail URI. With a bit of spelunking and messing around with the URI, you can change the s150x150 part of the URI to s200x200 for example to get a 200×200 sized thumbnail.

With that in mind, I’ve set the thumbnail URL to use 320×320 instead of 150×150 and voila! We have a larger, square version of the Instagram image:

Example 3 Image: Square, cropped image

Note that you can’t provide arbitrary dimensions and expect the API to automagically generate custom images sizes to your specification (Instagram isn’t an image processing CDN!). Change the dimensions to s220x220 and you’ll see what I mean.

curl -v

If you run the above command you’ll see that the server returns a 404 “Unsupported Size” error in the headers but the body response is 5xx server error. Assuming the thumbnail size you wanted was a thumbnail size Instagram generated, you can semi-reliably use your custom dimensions, however I couldn’t guarantee that those thumbnail sizes will remain accessible forever. We’ve decided to bite the bullet and use 320×320 image dimensions where we can, so fingers crossed they don’t remove those dimensions!

If you’re crawling the API with a script, you can do something like this in PHP to alter the thumbnail (where $post is your Instagram post object):

$thumbnail = str_replace('s150x150/', 's320x320/', $post->images->thumbnail->url);

Alternatively if you’re using JavaScript, you can do something like this:

var thumbnail = post.images.thumbnail.url.replace('s150x150/', 's320x320/');

I’m not going to post how to do it in each language, but the two examples above should hopefully suit most people.

On Mental Health

This is a post I’ve been ruminating on and wanting to write for 6 years now. I will be talking about mental health, depression, suicide and the like. You’ve been warned! If you need help, do a search for your local mental health hotline and speak to someone that can help.

In the beginning…

Mental health has always played a part in my family history. Particularly on my fathers side. That and my predisposition for consuming large quantities of alcohol can make for the perfect storm and exacerbate ones mental health issues. I was diagnosed with severe depression sometime in September or October 2007 when my parents walked in on me setting up to hang myself (great look, I know).

Tears aside, first order of business was to see a psychiatrist. For the first several years of my treatment I wasn’t on any sort of medication. Each week, I’d see the psychiatrist and we’d work on getting through school and dealing with normal teenage kid problems. As an aside, the diagnosis of depression wasn’t linked to alcohol, in fact I didn’t start drinking until around 18–19 years old. There were also no drugs involved, I’ve heard the risks and links with marijuana and that’s why I’m still staunchly opposed to legalising it (legitimate medicinal usage aside). Anyway, back to the issue at hand. Through the sessions we figured out that I had been having feelings of depression as early as 2005 or so, but being a rather angsty teenager, neither my family or I would’ve guessed it was depression – it’s normal teenager stuff, right?

Up until early 2009 I was doing okay. I finished high school, got into the dream course that I’d wanted to study (Bachelor of Science – Professional Software Development) and I had a great group of friends and we still caught up regularly. I’d found school rather hard to deal with, I didn’t particularly apply myself and I had to deal with my fair share of bullying. I really only enjoyed it because my friends were there too. When school wrapped up forever and there was the three month-ish break between school and university I squandered my time and just faffed about doing bugger all (my exact memory is hazy, I don’t remember a lot of that time).

Finally, it was time to start university. Week one went down well. The core group of students were great, I got to know a few people, and we all hung out and socialised a fair bit. I think after the first week I realised what I’d gotten myself back into – it was the fucking pressure of education! I was thinking to myself “what a fucking wuss, everyone knows the first year of university is easy.” I was one week into uni and feeling just as stressed as I was in the last year of school.

At the start of the second week of university, it hit me like a slippery fish (that’s a GTA Vice City: VCPR reference). I was feeling like utter shit because I felt like I’d gotten in too deep. I felt like a failure because I was stressed about university and I couldn’t run away. My standard response for a shitty situation is flight (not literally flight, but to remove myself from the situation). My feeling was that literally running away from the university campus wasn’t going to help, so I should remove myself from the earth entirely. Again, that’s not literal, I didn’t have plans for a space death or space suicide.

That night, I attempted to hang myself again. This time, I’d kicked the chair away, thrashed around for a few seconds due to the immense pain, and somehow managed to get the chair back with my foot and saved myself from death. At the time I remember thinking that it hurt a lot more than I expected (I don’t know why I didn’t think it’d hurt that much, my pain tolerance is quite low; I have no idea how I’ve managed to get seven tattoos). After spending a few minutes sobbing and composing myself to essentially hand myself in to my parents as a “convicted suicide attempter” I went off and spoke to them about what had happened.

The next morning, we spoke to the psychiatrist and by that afternoon I was in the intensive care unit at a psychiatric hospital. To clear up any confusion, this ICU was essentially 24 hour suicide watch, not a standard hospital ICU where you’d be rushed if you were having a heart attack or something. This ICU was the closest I’ve ever been to feeling like I was in jail. Our whereabouts were monitored and checked off. We had twice daily inspections to make sure we didn’t have any contraband that could be used for self harm (or the harm of others I guess, but we were all more of a risk to ourselves than others). Every part of the unit (bathrooms included) had no way of hanging things (towels, ropes etc) and there were no sharp or hard edges anywhere. I realise writing this 7 years after being there that it sounds a lot worse that it actually was. The staff were very nice and very helpful, the facility was nice, and I got really good care.

I started taking medication for the first time while I was in ICU. After a week or two they started to kick in and I gradually started feeling better, along with the work that the carers and psychologists did in classes and sessions.

I spent exactly four weeks in the intensive care unit. Having your every move (or lack of movement) watched every minute of every day can really make you feel uncomfortable, but I came to like it. It means they care, right? (that’s not a dig at the staff or hospital, I am forever thankful that they dedicate their lives to help others). Once graduating from ICU, I got to move up (literally, to the second floor) in the hospital, to a less invasive ward known as Unit One. This section was more a “help yourself” section where there were a number of classes every day which you would be encouraged to attend. From memory, it was about you making the effort to help yourself, but if you didn’t go to classes for a few days, I’m pretty sure they’d start making you attend. I don’t know exactly, I never found that out, I really liked the classes.

In Unit One you would share a room with another roommate. My roommate was a man in his forties, also called Daniel, and he was extremely overweight. I found out that night he snored like a chainsaw. The next morning, partially from the lack of sleep and partially from the stress of Unit One life, I had a rather bad panic attack, had valium which didn’t help (and has given my hands a slight permanent shake to them, I’ve never been able to build card towers since) and got my ass shipped back to ICU because I wanted to kill myself again. After a few hours of being back in ICU, I had calmed down, my feelings had subsided and I was back with my friends and carers in ICU. From then on, I knew I had to work on myself more, because if I freaked out just moving wards in a hospital, I was never going to be able to live a normal life out in the real world.

After spending the next one to two weeks in ICU, I graduated from ICU and this time moved to Unit Two. While Unit Two was almost the same as Unit One, Unit Two just seemed to have a better vibe about it, and the people seemed nicer. I think the people in Unit Two were longer term people, were as Unit One people were short term, but I was never sure about that. This time, I got my own room, met some really nice people, and went to more classes to get better. Later on that week I had a session with my hospital psychiatrist’s assistant (I forget the exact word for it, the assistant was already trained in other mental health care, but they were becoming a fully fledged psychiatrist) and they did a technique on me which was essentially “negging” (but not in the sleazy seduction way), apparently it was to provoke a different way of thinking, and to make myself standup for what I am (or some such bullshit). Thankfully, as this was a privately run hospital, I was allowed to check myself out, and I went to live with my parents back home.

Over the coming weeks I attended a few outreach programs and continued working on building up my mental health with my psychiatrist.

Seven Years Later…

It’s been seven years since I went to hospital for my mental illness. I still take anti-depressants every day, just to stop my mood from fluctuating too much. I will probably be on them for the rest of my life. Over the past seven years my psychiatrist and I did two attempts at very slowly weening off them, but both times I started slipping back into the hole of depression. Despite taking medication to help stabilise my mood, I still have days where I wake up just feeling “off”. A trough in the sine wave that is life. Most days are the middle though, I never have the “peak” of a sine wave.

Despite having a number of very dark moments in my life, I’m very careful not to blame anyone, especially my parents or myself. Trying to change things in the past doesn’t work (for obvious reasons…) and it’s just a waste of effort. No one in my family, not even myself, could’ve seen the depression coming in so strong during a critical time during my life. Teenagers can be moody bastards at the best of times, so having down days just seemed normal. One of my biggest issues I had was ruminating on the past, and thinking of all the possibilities or things I could’ve done to change the outcome of an event. To a certain extent that can be helpful, but when it becomes something you obsessively do over and over for even the most minor of events (like seeing someone turn without using their indicator) then it becomes a problem, because there’s nothing you can do to change the past! I know, it sounds so obvious, but when your head isn’t in the right space, you don’t think logically.

Even during the darkest days of my current life I never think of suicide as an option. When I first heard suicide being described as selfish I got real mad. “How dare they! They have no idea what it’s like!” ran through my head. While it’s true that each person suffers in a different way it doesn’t detract from the action itself. I never once considered the ripple effect that would’ve gone through my family and friends. People questioning themselves, second guessing themselves if there was something they could’ve said or done, or if they’d said or done anything to push me over the edge. It’s selfish and cowardly. The easy way out. It’s a short term “solution” which existentially doesn’t solve your problem, and hurts everyone around you.

Mental Health in the Media

Now a days whenever there’s a mass shooting in the US, the media and politicians are quick to pounce on the mental health bandwagon and side skirt the real issue (a certain “right”). Just because I have a mental illness, doesn’t mean I want to pick up a gun and go around shooting people. That’s not to say that some form of mental illness isn’t a contributing factor, it is, but there’s not the only piece in the puzzle. There’s a lot more I could say about the main issue with mass shootings, but that’s not the point of this piece.

Mental Health in Society

Mental health is still made a mockery of in this day and age. While there’s more awareness of it, many people still don’t either care or lack the tact to talk about it in social situations.

If we’re told that we should accept who we are, why should we have to feel like we need to hide a part of us away from society for fear that we’ll be mocked and treated as an unstable looney who should be locked up. That might be stretching a bit, but I’ve met people who have had some extremely negative views of people with mental illness. It boggles the mind as to why some people think that just because someone has a mental illness it means they should essentially be incarcerated because they could be a menace to society. Needless to say, I don’t usually stick around those people long as they always seem to be like the people that are against gay marriage, evolution, or global warming – nothing you can say will ever change their mind.

Mental Health in the Workplace

Here in Perth, Western Australia, mining is a large part of our society, and yet, the mining companies are some of the worst offenders at supporting their employees with mental health issues. Being diagnosed with mental health issues and/or having to take medication for it is considered a liability so you’re deemed unsafe to operate pretty much anything aside from a computer at a desk. All because your brain may not be able to produce serotonin properly you’re now considered unsafe to use machinery because you could cause an accident. Which is kind of an odd way to think, because with the condition going untreated, they could potentially be much more dangerous. Point being, the way mining companies handle mental health issues is utterly disgraceful and everyone involved in making those decisions to cut someones career short because of mental illness should hang their heads in shame.

I, thankfully, have had very accepting and supportive workplaces who have all been fine with me ducking off for an hour here and there to visit my psychiatrist to help maintain my mental health. Wouldn’t you rather a happier employee that knew their employer had their back?

The Future

I continue to get through each day as normal. As I’ve mentioned, some days are rough, but for the most part I chug along fine. I’ve got a great family, I work with an amazing team, and I have fantastic friends. I’ve learnt that alcohol and I just don’t mix (it also doesn’t deal well with medication) so I’m jumping off the booze bandwagon and hoping into the sober sedan. Sure, I’m really going to really miss that feeling of cracking open an ice cold beer after a hard days work, but I know I’ll be better off in the long run without it.

If there was anything I’ve learnt through my years of dealing with a mental illness, it’s to build up your support network. Don’t be afraid to ask someone for help, or even if you can just open up and vent to them for five minutes. Don’t be afraid. We’re all loved.