A story in 8 tropes about time travel, space invaders, and lying to git.
The part where I have a shiny new idea
All of our programming stories begin with an idea, but one thing you need to understand about me is that I have a lot of ideas. They constantly dance out from behind the scenery like pagan woodland nymphs in a Shakespearian comedy; alluring and seductive. I am smitten by every one of them – besotted – to me, when they first appear, they are all fantastic. The problem is some of them are actually very bad ideas, like.. objectively bad, and honestly, sometimes for weeks at a time, I can’t tell the difference. I follow them willingly; a happy accomplice to the amoral whims of my own, often dim-witted ideas.
To give myself a fighting chance, I like to bounce my ideas off a certain kind of person. Bright, sensible, and brutally honest summs up the personality type. They are rare, like diamonds, but not nearly as well liked (which is why I think they put up with me). These people are to me, as spinach is to Popeye; together we can accomplish anything. I’m especially fond of the ones that can be counted on to forget my bad ideas and remember only the good ones (Pro tip: this is a common self-protection mechanism among sensible people and a good litmus test for detecting them).
Anyway I’m in a bit of a pinch just now, having recently begun a new work-from-home job, I guess you could say I find myself “between people” – in want of a bright, sensible, brutally honest person to bounce ideas off. Without a proper confidant I’m an easy mark for my own ideas, so what follows is the story of me putting into code, what is probably a bad idea.
It started when I noticed that my github contributions graph almost had a space invader in it, so I spent the next week or two trying to complete the pattern.
I can't be the only one trying to draw a space invader in their github graph can I? pic.twitter.com/4pPs50eUVl— Dave Josephsen (@DaveJosephsen) January 24, 2014
Sometime during that week the idea of hacking up something that would help me maintain a scrolling space invader github contributions graph hit me, and I don’t know, it seemed so overwhelmingly fantastic I had to do it.
If I’d had a bright, sensible, brutally honest person around, this idea probably wouldn’t have taken off. They probably would have pointed out that many potential employers would judge me by my github profile, and that github maybe wouldn’t appreciate me messing around with it, and maybe I should run it by somebody over there first. Also there could be negative functional side-effects associated with whatever actions I might take to affect my graph, unforeseen consequences. I can see the wisdom in this advice now that I’ve had a few weeks to reflect. Oh well.
The part where I lose interest after solving the “interesting part”
My normal strategy when I have a fantastic idea for a software project is to stay up all night implementing the most interesting part of it in the most interesting language to me at the moment, and then never look at any of it ever again. It’s as if, when the interesting part is done, the “problem” is solved, which is, of course nonsense. Solving problems in real life requires slogging drudgery and boring trivialities. All the little things that combine to form 90% of the project in earnest. I know this, but I get around it by telling myself that I can come back to that stuff “tomorrow”.
This space invader graph thing was just a little toy project for me, so it didn’t have to be elegant or feature-rich (I told myself), and there was going to be an awesome space invader graph (eventually) when I was done. So it was pretty easy to keep my eyes on the prize. I only slacked off for like two weeks after staying up all night and coding the interesting part first in the most interesting language to me at the moment
The part where I make an off-by-one error
I won’t say that I generate a zero-indexing bug every time I work with dates or multi-dimensional arrays, but I do it a sufficiently large percentage of the time that I work with dates or multi-dimensional arrays that it may as well be every time. I mean if you round up or whatever I basically do it every time. You’d have thought that after 15 years of programming I’d understand that computers count starting at 0, but nope. Not me. I’m evidently genetically predisposed to introducing off by one errors (or just stupid (probably the latter)).
This time I inserted a particularly insidious off-by-one bug such that everything I wrote would work perfectly for 216 days (the number of days it takes the invader pattern to loop), but on that 217th day, my Go code would explode with an invalid index runtime error. If there’s anything I have become familiar with as I’ve grown older however, it’s the inevitability of my own screw-ups, so I simulated the pattern for three hundred years or so in order to find the the off-by-one error I knew I must have introduced. I no longer strive for bug-free code, I strive only to be dead by the time it blows up.
The part where I experience the setback that leads to something cool
Starting out, I had a few goals that informed my design.
1 I wanted the data in the contributions graph to be representative of real commits, no cheating.
There have been other efforts of this sort, but they use fake commits to fake accounts. My goal here was not just to draw a cool graph somewhere on github, but to find an approach that rewarded me for my commits. I want to earn it, like every coder earns their graph, I just wanted mine to have space invaders.
2 I didn’t want whatever I came up with to dictate my schedule
I needed to arrange things so that I didn’t have to schedule my coding time around the graph, because realistically I have deadlines to keep etc.. I wasn’t going to be able to Bogart commits from someone waiting on a fix because ZOMG PRETTY GRAPH. On the flip-side, if I don’t commit enough there will be holes in the graph. Right now most of my commits go to private repos, so I’ll have to step up my OSS game, or live without space invaders, it’s not in the spirit of the thing to “save up” my commits and spread them out or whatever.
3 I didn’t want to change my tooling (much)
I wanted the process to be transparent to me; which is to say I wanted to continue using git the way I always did. This wasn’t going to work if I had to use git –prettygraph commit -am ‘foo’ on some days and not on others (because I’d never stick to it. See ‘stupid’ above). Also I do quite a bit of file-transfer via git, because I move between Linux workstations and Mac laptops, so whatever I did, couldn’t prevent me from pushing because it would mean never having the code where I wanted it.
So given those constraints, I figured I’d need three pieces of code to pull this off. The first piece would be a program that I could query to tell me whether I should push on a given day or not. Then I could write a shell wrapper (piece #2) for git, that would intercept my push commands, check to see if today was a “blank” day on the graph, and, if it was, push to a private repository instead. Then I’d have to come along later with the third piece, and gather up all of the delayed pushes, and merge them back to origin.
I had an idea for another approach which used two different github accounts, which would simplify the git part of this, but would (probably) require me to keep two sets of credentials on all of my machines. I went with the private repo because I thought it’d teach me more about git.
Anyway, while experimenting with different branching and cherry-picking schemes, I asked some git-adept friends how they thought I should handle it, and that’s when I hit what at first seemed like a catastrophic setback. Namely: github.com doesn’t populate the graph with your push dates, it populates the graph with your commit dates.
So delaying my pushes wasn’t going to work; github would use the date that I made the commit even if I eventually pushed it three days later, and I wasn’t going to be able to spoof the commit date after the fact because of the way git uses hashes to name things like logs internally. If I was going to spoof the commit date, it’d have to be before git got it’s hands on the commit.
But hey I’m the idea guy so like 18 ideas occurred to me immediately, the first was change the system clock on every commit. This was quickly disqualified because it’d trigger my screensaver every time I committed (and who knows what else). The second was to Vagrant or Docker up a VM with a broken clock for every commit. This was, of course, absurd, and for that reason alone I was sorely tempted to do it, but I didn’t because it would slow down my commit commands enormously, and therefore it was disqualified by the tooling-change constraint.
My third idea was to write a wrapper for the time() system call, and have the linker PRELOAD it. That way my Library could detect the name of the calling process and – if it’s name was ‘git’ – LIE to it about the current date. This seemed hacky and fun, and somehow kind of deliciously mischievous, so I went with it.
I should mention at this point that I’m aware that by doing this I may create git logs of impossible commit history down the road that might confound people. I plan to buy those people the beverage of their choice to atone for making their lives more difficult. It’s my sincere hope that the sight of my space invaders and a cold beer (or whatever) will make up for the negative energy I introduce into their lives. The date skews should never span more than three days given the invaders pattern, and I’m not a terribly prolific OSS contributor so I’m unlikely to bug you personally.
The part where I stumble across a gaping security vulnerability
Once my libc wrapper was ready, I had some fun experimenting with it and quickly realized that I could spoof commit-dates any length of time into the past or future and github will happily graph them (if you look at my github commit graph right now, it shows a commit a few years in the future, November 14th 2017).
This shouldn’t have really surprised me, but seeing it in action gave me pause. With what I had already coded, I could open an account today and give it a solid dark green contribution graph – the sort of thing Google Interns ignore the opposite sex to obtain. That’s an extreme example. It wouldn’t fool anyone who was paying attention, but I do think it would be hypothetically possible to take a random number generator and modify a mediocre looking account so that it looked orders of magnitude more impressive, in a way that would take some thorough analysis to detect.
Now, this isn’t really a security vulnerability on the github side – github can’t be expected to verify your commit dates or assess the quality of your contributions, and the graph is behaving exactly as it should. But I wonder how deeply the companies who use github as your resume ever dig beyond that graph. I’m sure it’s fine.
The part where the Internet provides
There’s another, closely related but mutually exclusive trope here, that goes “the part where I completely abandon my project to focus on a thing I made for the project that’s way cooler than the whole project”.
Once I started playing with my perfidious little time wrapper, I realized that I was really on to something cool. Every program I’d ever written that used date-based scheduling to do something could benefit from it. A few years ago I wrote a pretty great back-end processing library to do things like copy batch files tither and yon, and load them into oracle et al.. With my time wrapper I could see how my batch job processor would operate on leap-day in 2066 for example (wait.. is there a leap day in 2066? Well you get the point).
Of course, to turn my time wrapper into something that could be useful for testing arbitrary processes, I’d have to do all that slogging drudgery I alluded to above. Things like reading in options, parsing config files, and having a man page, you know, user stuff. Also, at the time I only had this working on Linux where I have some system chops. I still had to port it to Darwin for my Macbook, which meant learning about dyld, and delving into the Darwin headers and etc.. So before I abandoned my little project to hack up this time-warp testery thing, I took a moment to ask Google if someone had already done if for me. Hey google, has someone done this time thing already?
Of course they did, and way better than I ever would have. Thanks Internets! Not only do I get a WAY more functional and cross-platform means to lie to git for free, I don’t have to abandon my toy project to work on something real. How did we ever get anything done before there were millions of people falling over each other to solve every functional coding problem ever conceived for free?
The part where I wind up debugging someone else’s project
This trope also has a closely related (and not at all mutually exclusive) cousin called “The part where I shave a yak”. When running ‘make’ in the libfaketime tree failed for me, I wasted some time poking at the Makefile and then the headers (looking for the OSX symbols that match the Linux ones) before realizing there’s an OSX makefile. Then, seeing that macports contributed the OSX makefile I smacked my forehead and ran:
brew install libfaketime
Yup. Not only did the Internet provide an obscure cross-platform library that enables me to lie to arbitrary processes about what day it is, the Internet also packaged it up for me so I wouldn’t strain myself keeping track of two whole makefiles.
I proceeded to have some weird clock-skew problems using libfaketime’s ‘faketime’ wrapper. It seemed work ok for the ‘date’ binary, but it skewed years into the future for git. I poked at the code a little bit to see how they were forking the sub process before winding up back in the Darwin libc headers. But eventually gave up and bypassed the faketime binary by setting the Darwin environment variables manually in my shell wrapper, and that works. Test test test, by George, I think we’ve got it!
The part where I almost but not quite bother to blog about it
Whenever I do something cool, I like to write a draft of a blog post about it and then not share it with you. That way I feel like I could have shared, but you weren’t worthy. We both know you probably would have just said something mean about it and anyway it’s important that we both know who is wearing the pants in this relationship. Really, this post isn’t actually for your benefit, I only published as a pre-emptive apology to my future potential employers and the programmers on github who are confounded by how I could have contributed a fix to their feature three days before they contributed their feature. Also, I have a rule that I have to share every post that uses the word “perfidious”.
Take it easy -dave