It looks like this will meet my constraints above - I get to keep using GitHub
Pages, I don’t have to manage a cert (CloudFlare takes care of this), and I can
keep using my custom domain.
The steps I followed to do this were relatively simple:
Exported a zone file from current nameservers
Completed the CloudFlare onboarding, during which I imported the above zone file
Updated the authoritative DNS servers for my domain to the
*.ns.cloudflare.com name servers:
Tested the site out,
a CSS link that was loaded over HTTP
Forced HTTPS in CloudFlare:
… and that was it. I finished this in part of an afternoon.
There is one major shortcoming with this setup: there is no certificate
validation between CloudFlare and GitHub (CloudFlare supports fetching from
an origin without validating certificates, which is the option I’ve chosen -
‘strict’ HTTPS can be enabled for most use cases).
As we mentioned before, the GitHub cert is valid for *.github.io, and we’re
using my custom domain, which is mdjnewman.me.
If we switched off the custom domain on GitHub, and did some smarts in
CloudFront to rewrite requests so that the request to GitHub was using
mdjnewman.github.io, then we’d get HTTPS all the way to GitHub servers.
I could switch to using CloudFront with
an AWS Certificate Manager cert,
which would meet all the above constraints except for ‘no cost’ (admittedly, my tiny
blog doesn’t get much traffic, so the cost would be minimal).
Given that most of the shenanigans with injecting content into web sites
happens at the last leg of a connection (I’m looking at you, dodgy internet
cafe), I’m happy that the new setup for this blog mitigates that problem and am
willing to accept the cost/security trade-off. While it’s possible for someone
to perform a man in the middle attack and impersonate GitHub, given my site has
no sensitive information I’m not too worried about this threat model (Troy Hunt
In the above, the commits adding features 3 and 4 on each branch are logically
the same, but on the feature-5 branch they’ve somehow ended up after the
commit adding feature 5.
The team was using feature branches, and the author of feature 5 above was
trying to rebase their changes onto master, but somehow ended up inserting
their commit between commits on master. At this stage, you’re in a pretty bad
place, as you’ve diverged from master.
How did this happen? I’m guessing it was the following series of events:
Developer branches from master to create feature-5
Features 3 & 4 are pushed to master
Feature 5 is committed to the feature-5 branch
Developer runs git pull --rebase origin master or similar
… some time passes …
Developer runs git pull --rebase without really thinking about it
After the first few steps above, we have something like the following:
So far, so good. We want to rebase our changes onto master, so that we can test
and push our code. After git pull --rebase origin master:
Still looking good. At this stage, we could git push --force origin feature-5
and all would be well in the world.
But what happens if we go for a tea and forget what we were doing (or we use an
overzealous git GUI tool), and we try to rebase onto origin/feature-5?
What we see above is the result of Git rebasing commits 94989d0, 232e985
and 44e1c44 on top of origin/feature-5. As the commit ID is computed by
hashing of the contents of a commit and its parent, the same logical commits
from master now exist on our branch with different IDs.
This could have been avoided if we followed this rule of thumb:
If you’re working on your own branch, always push immediately after rebasing.
Some people on the team were seeing a message like the following:
Your branch and 'origin/feature-5' have diverged,
and have 3 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
and assuming that they should git pull --rebase, which in this case is exactly what you don’t want.
One of the exercises to implement a simplified command line version of
Morra, which involves keeping
track of scores and reading user input.
My initial method to play a round looked something like this:
playRound::Config->Scores->IO(Bool,Scores)playRoundconfigscores=doputStr"P1: "p1HandMaybe<-getHumanHandcasep1HandMaybeofNothing->return(True,scores)-- error case 1Justx->doputStr"P2: "p2HandMaybe<-getHumanHandcasep2HandMaybeofNothing->return(True,scores)-- error case 2Justy->doletevenWins=(x+y)`mod`2==0return(False,updateScoresconfigevenWinsscores)
playRound takes the configuration for the game and the current score, and
returns a side effecting computation that will return a tuple with the new
scores and a boolean indicating if the game is finished.
The method getHumanHand used above returns a IO (Maybe Int), which can be
interpreted as a side effecting action that might return an integer (in this
case, the side effect is reading from the console and we can’t trust the user
to enter an integer, hence the Maybe).
The problem then is that we’re then manually unpacking these Maybe Int
values, which leads to the ugly nesting and case statements. However, we can
see on the lines marked ‘error case’ above that the handling for both cases is
the same - we assume that if the user has entered something other than an Int
that they want to end the game.
I recently learned about Monad transformers, which allow you to compose monads.
In this case, we want to compose the Maybe monad with the IO monad, so we will
Rewriting getHumanHand to return a MaybeT IO Int and rewriting playRound
results in the following:
The nice thing about this implementation is that we’ve avoided the need for
pattern matching, as the do block above where we’re dealing with the
potentially failing computations will immediately short circuit and return a
Nothing if either user fails to provide a legitimate value.
This is the first time I’ve actually used a Monad transformer, and it was good
to see how it cleans up the implementation of playRound.
I was recently working in a codebase where there were chunks of Java code that
used generic interfaces from the Apache Commons Collections project as well as
java.util Collections in a type unsafe way, even though the
interfaces/classes that were being used supported generics.
Our gradle build was emitting the following warning:
:compileJavaNote: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
This warning should be an error in my books. It’s possible to get this
behaviour by adding the following to your build.gradle file: