Coraline Ada Ehmke – Antisocial Coding: My Year At GitHub

A powerful recount of Coraline Ada Ehmke’s terrible treatment at GitHub. Please take some time to read it.

I think back on the lack of options I was given in response to my mental health situation and I see a complete lack of empathy. I reflect on the weekly one-on-ones that I had with my manager prior to my review and the positive feedback I was getting, in contrast to the surprise annual review. I consider the journal entries that I made and all the effort I put in on following the PIP and demonstrating my commitment to improving, only to be judged negatively for the most petty reasons. I think about how I opened up to my manager about my trauma and was accused of trying to manipulate her feelings. I remember coming back from burying my grandmother and being told that I was fired.

GitHub has made some very public commitments to turning its culture around, but I feel now that these statements are just PR. I am increasingly of the opinion that in hiring me and other prominent activists, they were attempting to use our names and reputations to convince the world that they took diversity, inclusivity, and social justice issues seriously. And I feel naive for having fallen for it.

This isn’t the first time GitHub have run afoul of having a toxic internal culture. Perhaps ironically, it appears from my view that it was the “corporate” controls implemented after the previous fallout, combined with cultural issues and inexperience of how those controls are meant to be applied that led to the horrible experience.

I’ve done Staff Management in companies hundreds of times the size of GitHub; I recognise every tool and process mentioned in Coraline’s recount, and have been part of them numerous times. Each instance in this retelling seems to be a perversion of what is meant to be applied. Tools designed to help and protect everyone in involved in the process were turned against the party with the least power.



A Follow-Up on Adding JSON Feed to Jekyll

After my last post, I came across some discussion about implementing JSON Feed, and whether using a template is a good way to implement JSON Feed in a site. The concensus seems to be “No,” with one of the authors of the spec weighing in. For the most part, I do agree – a template is more likely to break under some edge case than a proper serializer – but until there is more support for the spec I see it as a pragmattic short-term solution. So proceed at your own risk for now!



Add JSON Feed to a Jekyll Site

I can’t take credit for this – I found the code below (from vallieres) after a quick search for adding feed.json to Jekyll without plugins. The only thing I’ve added is the sitemap front-matter which will exclude the output file from our sitemap.xml

---
layout: null
sitemap:
  exclude: 'yes'
---
{
    "version": "https://jsonfeed.org/version/1",
    "title": "{{ site.title | xml_escape }}",
    "home_page_url": "{{ "/" | absolute_url }}",
    "feed_url": "{{ "/feed.json" | absolute_url }}",
    "description": {{ site.description | jsonify }},
    "icon": "{{ "/apple-touch-icon.png" | absolute_url }}",
    "favicon": "{{ "/favicon.ico" | absolute_url }}",
    "expired": false,
    {% if site.author %}
    "author": {% if site.author.name %} {
        "name": "{{ site.author.name }}",
        "url": {% if site.author.url %}"{{ site.author.url }}"{% else %}null{% endif %},
        "avatar": {% if site.author.avatar %}"{{ site.author.avatar }}"{% else %}null{% endif %}
    },{% else %}"{{ site.author }}",{% endif %}
    {% endif %}
"items": [
    {% for post in site.posts limit:36 %}
        {
            "id": "{{ post.url | absolute_url | sha1 }}",
            "title": {{ post.title | jsonify }},
            "summary": {{ post.seo_description | jsonify }},
            "content_text": {{ post.content | strip_html | strip_newlines | jsonify }},
            "content_html": {{ post.content | strip_newlines | jsonify }},
            "url": "{{ post.url | absolute_url }}",
            {% if post.image.size > 1 %}"image": {{ post.image | jsonify }},{% endif %}
            {% if post.link.size > 1 %}"external_url": "{{ post.link }}",{% endif %}
            {% if post.banner.size > 1 %}"banner_image": "{{ post.banner }}",{% endif %}
            {% if post.tags.size > 1 %}"tags": {{ post.tags | jsonify }},{% endif %}
            {% if post.enclosure.size > 1 %}"attachments": [ {
              "url": "{{ post.enclosure }}",
              "mime_type": "{{ post.enclosure_type }}",
              "size_in_bytes": "{{ post.enclosure_length }}"
            },{% endif %}
            "date_published": "{{ post.date | date_to_xmlschema }}",
            "date_modified": "{{ post.date | date_to_xmlschema }}",
            {% if post.author %}
                "author": {% if post.author.name %} {
                "name": "{{ post.author.name }}",
                "url": {% if post.author.url %}"{{ post.author.url }}"{% else %}null{% endif %},
                "avatar": {% if post.author.avatar %}"{{ post.author.avatar }}"{% else %}null{% endif %}
                }
                {% else %}"{{ post.author }}"{% endif %}
            {% else %}
                "author": {% if site.author.name %} {
                "name": "{{ site.author.name }}",
                "url": {% if site.author.url %}"{{ site.author.url }}"{% else %}null{% endif %},
                "avatar": {% if site.author.avatar %}"{{ site.author.avatar }}"{% else %}null{% endif %}
                }
                {% else %}
                "{{ site.author }}"
                {% endif %}
            {% endif %}
        }{% if forloop.last == false %},{% endif %}
    {% endfor %}
    ]
}


Hitman (2016) Initial Impressions

Lately I’ve found myself enjoying the YouTube channel Outside XBox (and their sister channel, Outside Xtra). Normally what will happen is I’ll put a YouTube video on the TV as “background noise” and it ends up playing videos from the same channel for a few hours; in this case I ended up going down a rabbit hole of their Hitman videos. Whether it was the normal play throughs, the challenge modes, or the “3 ways to play”, I was hooked and wanted to play the game for myself. But I refuse to pay full price for a game that’s been out for a while, so it sat on my Steam Wishlist until last week, when it was discounted by 60% on the evening before the Steam Summer Sale.

I finally got round to playing Hitman today, and so far I’d say it’s got the potential to be the first game to really hold my attention in since Metal Gear Solid 5. I loved Hitman 2, and kept tabs on the ups and downs of the series after that, so having another game in the series feel as good as that one did is certainly welcome.

So far I’ve only completed the prologue missions (though I did replay the very first mission several times), so these are very early impressions. Fingers crossed the game holds up as I get further through it!



Thoughts on the trailer for Marvel’s Inhumans

I caught the first trailer for the new Marvel’s Inhumans TV series. The whole thing looked so stiff, awkward, and sterile. I’m not sure what I was expecting, but based purely on the trailer I have zero desire to watch the show. Marvel’s movies and Netflix series (mostly) manage to feel somewhat anchored to the “real world” despite how fantastical the plot or setup might be… but Inhumans had none of that quality on show.



Hands on with Starcraft: Remastered

“We had no code and no art assets,” Blizzard 3D Art Director Brian Sousa confirmed to Ars Technica. The 2017 project’s entire art pipeline was “eyeballed,” Sousa said, with recovered concept artwork, sketches, and original boxes and manuals used as reference materials. Not all code was missing, as Blizzard has been issuing patches to the original game’s code base for nearly 20 years. Also, a member of the sound team thankfully had backups of the original sound and voice recordings, which are now reprocessed in higher-fidelity 44,100Hz format.

I’d heard the majority of the original Starcraft code had been lost, years ago, but I figured it was just a rumour. Sounds like the team of Starcraft: Remastered had a big task to recreate the game in a way some of its biggest fans would appreciate.

External link: StarCraft Remastered devs unveil price, explain how much is being rebuilt



Favourite Warhammer 40K Podcasts

I’ve listened to a lot of 40K podcasts over the last couple of years. Over that time I’ve slowly winnowed my subscriptions down to just a handful.

  1. Forge The Narrative – my favourite 40K podcast of the last few years.
  2. Chapter Tactics – from Frontline Gaming, but distinct enough from their other shows to merit its own subscription
  3. Frontline Gaming – this is the main Frontline Gaming Podcast – the feed also includes Chapter Tactics and some other smaller shows
  4. Ashes of the Imperium – this one is new, but it’s by the team behind the very good Bad Dice AoS Podcast

My biggest gripe with most 40K podcasts tends to be length. Sorry, but unless you’re very, very compelling to listen to, I am not going to listen to a Podcast episode which is 2-3 hours long (or more!). The Podcasts above tend to clock-in at around an hour to an hour and a half, which I find to be perfect to my listening habits.

Bonus: Podcasts I’m Evaluating:

8th Edition has brought about a few new Podcasts, some of which I’m still deciding if they’re going to stay in my subscriptions list.

Bonus 2: Some Age of Sigmar podcasts

For a while I found the quality of the AOS podcasts to be in general higher than most 40K podcasts, with only a couple of exceptions. Sadly, my favourite AOS podcast — Heelanhammer — has recently gone on hiatus so I’m not including it here.

  • Bad Dice
  • Facehammer – can be a bit sweary, so proceed under advisory if that’s not your thing.


Configure Caddy to Remove WWW from your Domain

For various reasons I prefer to remove the www part from my personal-use domains. Setting up Caddy to serve the site from just domain.com is as simple as:

domain.com {
    root /path/to/site/files
    # other directives
}

But this set-up doesn’t provide any way to redirect from www to non-www, meaning anyone who types www.domain.com into the address bar is out of luck. So what to do? Well, Caddy provides a redir directive. Combine with a new site directive and a placeholder like this:

# Original non-WWW site:
domain.com {
    root /path/to/site/files
    # other directives
}
# New, additional "site", for doing the redir
www.domain.com {
    redir domain.com{uri}
}


Note to Self – Posting Liquid Code Blocks

Having just spent faaaar too long to get a sample Liquid code block to not be parsed by Jekyll, I thought I better make note of this, for my own benefit:

When posting Liquid code, make use of the raw tag. Which I can’t seem to post an example of using, because it creates some sort of Inception effect or something…



Add a Sitemap to your Jekyll Site

An XML Sitemap can be useful for optimising your site with Google, particularly if you make use of their Webmaster Tools. Jekyll doesn’t come with one out-of-the-box, but it is easy to add one. There’s probably a plugin out there which will automate things, but I just used a normal Jekyll-generated file for mine, based on code found on Robert Birnie’s site.

The only modification I made was to exclude feed.xml from the sitemap. Because this is auto-generated by a plugin I couldn’t add any front-matter to a file to exclude it in the same way as other files.

Create a file called sitemap.xml in the root of your site, and paste the following code into it:

---
layout: null
sitemap:
  exclude: 'yes'
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  {% for post in site.posts %}
    {% unless post.published == false %}
    <url>
      <loc>{{ site.url }}{{ post.url }}</loc>
      {% if post.sitemap.lastmod %}
        <lastmod>{{ post.sitemap.lastmod | date: "%Y-%m-%d" }}</lastmod>
      {% elsif post.date %}
        <lastmod>{{ post.date | date_to_xmlschema }}</lastmod>
      {% else %}
        <lastmod>{{ site.time | date_to_xmlschema }}</lastmod>
      {% endif %}
      {% if post.sitemap.changefreq %}
        <changefreq>{{ post.sitemap.changefreq }}</changefreq>
      {% else %}
        <changefreq>monthly</changefreq>
      {% endif %}
      {% if post.sitemap.priority %}
        <priority>{{ post.sitemap.priority }}</priority>
      {% else %}
        <priority>0.5</priority>
      {% endif %}
    </url>
    {% endunless %}
  {% endfor %}
  {% for page in site.pages %}
    {% unless page.sitemap.exclude == "yes" or page.url=="/feed.xml" %}
    <url>
      <loc>{{ site.url }}{{ page.url | remove: "index.html" }}</loc>
      {% if page.sitemap.lastmod %}
        <lastmod>{{ page.sitemap.lastmod | date: "%Y-%m-%d" }}</lastmod>
      {% elsif page.date %}
        <lastmod>{{ page.date | date_to_xmlschema }}</lastmod>
      {% else %}
        <lastmod>{{ site.time | date_to_xmlschema }}</lastmod>
      {% endif %}
      {% if page.sitemap.changefreq %}
        <changefreq>{{ page.sitemap.changefreq }}</changefreq>
      {% else %}
        <changefreq>monthly</changefreq>
      {% endif %}
      {% if page.sitemap.priority %}
        <priority>{{ page.sitemap.priority }}</priority>
      {% else %}
        <priority>0.3</priority>
      {% endif %}
    </url>
    {% endunless %}
  {% endfor %}
</urlset>

If you want fine-control over what appears in the sitemap, you can use any of the following front-matter variables.

sitemap:
  lastmod: 2014-01-23
  priority: 0.7
  changefreq: 'monthly'
  exclude: 'yes'

As an example, I use this in my feed.json template to exclude the generated file from the sitemap:

sitemap:
  exclude: 'yes'

And this in my index/archive pages for a daily change frequency:

sitemap:
  changefreq: 'daily'


Enable HTTP/2 Push in Caddy

It’s super simple. Just include a push directive in your site definition. You can leave it as just that, and Caddy will use any Link HTTP headers to figure it out.

If you want more control, you can expand the directive and specify both the path and associated resources, like so:

example.com {
    root /var/www/example
    push / {
        /assets/css/site.min.css
        /assets/img/logo.png
        /assets/js/site.min.js
    }
}

What this block does is say “for every request with a base of / (i.e. every request), Push the following 3 files.” You can customise the base path if you want to, and add more files if you need, but a block like the one above is what I’m using for this site.

You can find out full details in the Caddy Push documentation.



Time to Revisit An Old Hobby Project

Lately I’ve been feeling a pull to return to my Warhammer 40,000 Flesh Tearers army, which I started around 4 years ago (and promptly only completed one unit of). I had an idea of a small strike-force that was basically just a load of Jump Pack Assault Squads, supported by Land Speeders (with some Death Company elements thrown in). It wouldn’t have been very “competetive” but it would have been thematic and fun. I didn’t progress the idea very far, as the Blood Angels codex in 7th Edition was… very not good; it also took away the ability to field Assault Squads as a troops choice — rendering the entire idea invalid.

Now we’re in 8th Edition, I can build the army as I imagined it, using the new detachments in the rule book. By getting back to a small “passion project” of mine, I’m hoping I’ll be able to revive my motivation for hobby projects which has been worryingly low recently. Who knows — I might even add some Primaris Inceptors to the mix for some mobile firepower.