Every now and again, I click around the tools I use to see what new features have snuck in while I was not paying attention. I know many of you are terrified of clicking into the unknown and I don't blame you, but since I am willing to throw caution to the wind in this area, why not learn from me and resolve issues with all the confidence and less of the anxiety. Here is how I found and resolved a security issue in my WordPress blog through the use of inquisitive clicking around.
Site Health
When I was writing a post on some of the great tools I use to support my software development, I spent a large amount of time procrastinating, clicking around the tools I use to look at features that I had forgotten or had never even seen before. One of the things I discovered was the Site Health feature of WordPress, which I just now discovered was added in their 5.2 release. You can find Site Health under the Tools menu of the admin dashboard side panel; I highly recommend checking it out once in a while to make sure your WordPress installation is secure and performing well.
Selecting the Site Health tool takes you to a page that runs a variety of performance and security checks on your WordPress installation. You can see what things it is checking once the report has been produced. If all is well, everything passes. However, when I ran it, I got a warning about the version of PHP being used.
For those that do not know, PHP is the language with which WordPress is implemented. It is usually installed and managed by your hosting service and it is your hosting service where you must go to fix this problem.
Changing the version of PHP
My host is BlueHost, one of those recommended by WordPress and therefore, quite likely to be your host too. As with many WordPress hosts, BlueHost provide cPanel to manage things and it is in cPanel where the PHP issue can be fixed. However, I did not really know that until I started investigating, and as I said before, I like to click around the tools I use. This is how I found the screen where I could change the PHP version.
Login to the site host account.
Open cPanel In BlueHost, this is accessed by clicking the Advanced option in the sidebar.
Find and click the MultiPHP Manager option
At this point, I saw the following screen.
The system default version of PHP is shown at the top as 7.2, below that the list of my domains show that they inherit that same version. However, my WordPress Site Health said I was running only 7.0 of PHP. One of these is wrong, and I suspect that it is the information displayed in cPanel1.
I checked the boxes on the left so that I could change all my domains and then I chose 7.3 from the dropdown and clicked Apply. Here is what my MultiPHP Manager screen showed; I've annotated it to make a few things clearer.
Verifying the change
At this point, returned to the WordPress Site Health page and refreshed it so that the health tests reran. After a few moments, the status was updated and everything was good2.
The Site Health feature is a great addition to the WordPress platform and though issues can be a little daunting, it often only takes a few moments to address them. Hopefully, my little journey into addressing this issue on my WordPress site is helpful; take a moment to check out your own site health.
Until next the time, I'll be me, you be you, and we will all have a lovely adventure. 👋🏻
if I were to guess, I'd say there is a bug in cPanel where the UX assumes the system version is inherited but in reality, it is the lowest available version, which in this case is 7.0 [↩]
NOTE: You may need to refresh more than once in order to give the system time to reload on the new version of PHP that you have selected [↩]
For my final post of 2019, I thought I would deal with some serious FOMO1 by adding one of my own to the legions of year-end listicles adorned by clickbait headlines. You will not believe what's at number four! Nevermore shall I lament having passed up the opportunity to proffer a subjective collection of arbitrary length for your attention. Of what engrossing subject shall this list be? Life achievements? 11 places to visit before you die? Things I have discovered when looking for other things that I have lost? Nay, it shall be tools! Tools, I say. To be specific, development tools for it is tools of software development that I use often. To be even more specific, this is a list of development tools for each of which I could write an additional list of killer features, extensions, and magical whosywotsits. I present to you a list of feature packed fancies for fruitful software fabrication. How many tools? Read on to find out2.
This open source, cross platform, integrated development environment (IDE) backed by Microsoft really is the best I have used so far. With its built-in terminal, text editor, and task engine, it really is an integrated environment where, if it weren't for Slack and my web browser, I would spend all of my software crafting days. In fact, if I liked the extensions that integrated Slack and web browsing into Visual Studio Code, I could use it or them too; but I don't, so I don't.
Unlike IDE's of old, Visual Studio Code – often referred to as just VS Code or Code (it's command line invocation is the delightful code the-file-I-want-to-edit.js), is implemented to avoid having opinions about the code you write. Instead, it is written to support your code dictating how you write code so that you can deftly move between projects without worrying that the settings for one team will somehow traipse all over the settings for another.
If you like vim-style editors, there's a vim emulator. If you don't like the menus, use Zen Mode or go fullscreen. Want to run tests integrated in the sidebar or the test file itself? Do that. Want to run them from a terminal or via a background task? Do that instead. I could (and probably will) write a whole new list on the best extensions to use with Visual Studio Code. It is versatile enough that I believe any developer could make it the editor they need.
Why it is great for software developers?
Either natively or via an extension, Visual Studio Code supports just about every aspect of the software development lifecycle you might encounter, on every platform that likely matters (Windows, macOS, and Linux), using any workflow that suits you. Not to mention it gets feature updates monthly and is supported by a huge community of users. It takes a little DNA from editors like Atom, Sublime, and your basic text editor, and elevates them to something, well, sublimer3.
When concentrating on software development tools, it is really easy for me to overlook this one – probably because it is not a software development tool, at least not from first impressions. I use WordPress for this blog; I always have. There are many alternatives out there; some more technically involved than others. I know I could use markdown in a GitHub repository; I have heard of Jekyll and Gatsby and so many other ways to generate a site; I know about Medium, but for me, WordPress wins because it has the features I need, including wide support for hosting, accessibility, themes, plugins, and autonomy from the whimsy and money-grabbing aggregation platforms like Medium.
The recent updates to the core editing experience, known as Gutenberg, have been amazing and the regular updates that are auto-applied without me raising a finger keep adding polish to an already awesome experience. I can schedule posts, manage comments, and use plugins to add syntax highlighting, footnotes, multi-factor authentication, backups, and spam filtering, to name just a few. Just as with Visual Studio Code extensions, I could write another list of plugins that I love for WordPress.
35% of the web uses WordPress, from hobby blogs to the biggest news sites online.
WordPress is used by over a third of the web. A third! That includes this blog, almost every site that Ann Arbor Give Camp has worked on in the past few years, and rollingstone.com! If you are considering putting together any kind of website, whether a blog, or something else, I highly recommend this freely available platform that has more versatility than Meryl Streep.
Why it is great for software developers?
As I mentioned earlier, WordPress seems entirely unrelated to software development (unless you are writing themes and plugins for WordPress4). However, I have learned that writing a blog about ones technical exploits is an absolutely amazing software development tool. I never realised how much I could learn just by trying to teach someone else. I used to think a blog was about its readers and being right; I have come to learn that a blog is merely its writer, being. The act of writing a blog is where its value lies. Writing this blog identifies gaps in my knowledge, personal biases, and more. It can shine a light on my laziness, focus the my mind on a gnarly problem, and provide a scaffold from which to hang my personal growth. There are numerous times where writing posts for this blog (including some I never published) has helped me become a better software engineer. The fact that sometimes, someone reads it and finds it useful, entertaining, or infuriating is really just a bonus.
For some readers, this may seem a pointless entry. Using GitHub for collaborative software development is so incredibly common that suggesting folks should use it seems a bit like suggesting folks should try breathing air5. Though there are alternatives such as GitLab, GitKracken6, or BitBucket, GitHub is almost ubiquitous. I do not recall an open source project that I have interacted with recently that was not hosted on GitHub7. With the recent changes allowing private and public repositories for personal accounts, the addition of GitHub Actions for automating all kinds of workflows – free to open source, and some great improvements to code review that have been released or are in beta, GitHub is an absolutely fantastic tool for those developing software. Add to that the integrations with other tools that I use like Visual Studio Code, Slack, and third-party issue trackers such as Jira, and GitHub shines. Many feared that its acquisition by Microsoft would doom it to failure, yet the Microsoft of today is a wonderful curator of open source goodies, and it seems that we all get to reap the benefits.
Why it is great for software developers?
Free backup of your source code, code reviews, automated workflows, and more, all on a tried and tested platform with a huge community. Not only that, but if you want, you get to collaborate, build, and present work with that community8.
🤷🏻♂️ That's it…
I don't know about you, but lists are exhausting. I've only written about three things and I'm already done with everything and ready for a lie down. I do stand by this list though. I really thought about what to put on it, considering the various tools I have used, not only because I have to, but because I want to. I would choose these tools from the very start of a new project unlike some others I use that, while I like them, are specific to a technology (such as React Developer Tools), are only what I use because the circumstances call for it, or are not really that standout against alternatives that I could be using.
Of course, this is all my personal opinion drawn from my personal experience; you have absolutely no obligation to agree with me. In fact, you have every right to use anything but the things I mentioned above, remaining in your state of willful ignorance, knowing you are wrong, unwilling to accept the truth as a way of life 😈. Just kidding, these are development tools, not religions – what works for you, works for you. These work for me. Perhaps you agree and want to pat me on the back from my excellent choices, maybe you care to tell me your preferred alternatives or shout at me about mine, or perhaps you read the footnotes and really have something to say about privilege, toxicity, and portfolios – feel free to engage in the comments; let's talk 🤗.
And with that, I bid you well until the 🎆New Year and all the productive software shenanigans that await us in 2020. 🙇🏻♂️
Fear Of Missing Out – a most annoying acronym, I find; why? no idea [↩]
before GitHub, it was SourceForge, before the DevShare debacle – https://en.wikipedia.org/wiki/SourceForge [↩]
Side Note: I think it is perfectly fine not to have a portfolio; some of the best developers I know do not have any public source, or fancy stuff to show off. This weird obsession some folks have with portfolios feels like another toxic manifestation of privilege in the software development world, and I don't care for it. Let's share what we want (and are able) to share, and accept that if we don't, that doesn't mean we're shit developers. 💙 [↩]
🥳The Holidays are upon us. In the US, what folks refer to as The Holidays is generally accepted to start around Thanksgiving – the third Thursday of November, though my observations suggest it really starts at the end of October, with Halloween. In the UK, in my anglo-Christian family, it was Christmas and the commercialised build up to it. Perhaps it is something else for you. Whatever The Holidays means to you, they are here and they can be a struggle for many. Suffocating societal expectations of Happy Holidays. Debt-fueling challenges to shower loved ones with gifts. Reminiscence of happier times. Remembrance of past pain. "Happy Holidays", we tell each other, persevering to manifest it as truth, wearing it like an inverted halloween mask, the scary facade on the inside. Upon us like a rabid gang armed with tinsel, Christmas music, and peppermint candy canes, intent on forcing us to ignore how we feel and join in with someone else's idea of fun, The Holidays lie waiting to sabotage some of us when we least expect it.
😣The Holidays are complicated. This post is not intended as some War on Christmas manifesto or a downer on the holiday season. I have incredibly happy memories of The Holidays: traditions, roaring fires, home-cooked family meals. The camaraderie of working shifts with friends at the local pub. My Grandma's birthday on Christmas Eve. Waking up early to open gifts. Going to bed late after too much food and far too many drinks. Friends, family, church, caroling, and more. I fondly remember my time in the church choir1, singing at midnight mass, or walking the village, caroling door to door. Or my Nana staying with us on Christmas Eve so that she could spend Christmas Day with us. And that time my Uncle Peter surprised my Nana and the rest of us, showing up at the local pub in England after calling us from his home in Canada just the night before (he was really in a hotel nearby). Or the time I surprised my mum with a visit when she thought I was spending Christmas here in the US (and yes, it was my Uncle's visit years before that inspired that surprise). My first Christmas meeting much of my girlfriend's family long before she became my wife, and the time I was welcomed into the home of Gary and Carol to eat together with many neighbours and friends. The Holidays have been filled with happiness for me and I am grateful to carry these memories with me. Yet The Holidays also represent an unhappy, twisted, ugly place. A place that ripped my cousin away in a tragic plane crash and stole the sparkle of my friend Mary, both far too soon. The annual reminder my Nana will visit nevermore, that my Grandma's birthday will be remembered without her.
The Holidays are happy moments and sad, and these moments do not cancel each other out, they weave themselves together into a complex tapestry of deep emotion that can in an instant swing from joyful remembrance to helpless sobbing and back again. This is The Holidays for me. A complicated dance of happiness and grief. Grief is good, grief is healthy if we embrace it rather than dodge it. I find my grief is a journey, walking hand in hand with my emotions as I learn a new way to live without the loves I never expected to lose.
😔The Holidays are lonely. This year is my first year alone for The Holidays since my partner and I concluded our marriage was over, since we separated, since we divorced. This is my first time publicly acknowledging that as a thing that happened. It felt crass to announce it as some thing worthy of everyone else's attention. I do not know if that is because it is or because I am just afraid or something else. There have been many days where I wished for my Nan or Mary to show up for a cup of tea and a chat to help me work that out. In some ways, they did. Grief is complicated.
After reading this, you may think I am misguided to be alone right now or to be sharing all this so candidly. Your gut reaction might be to reach out and invite me over to spend time with people for The Holidays. Please don't, though I very much appreciate the sentiment. I already have the right amount of plans (I will be spending Christmas Day with a few of my friends) and I need to do this. I need to do this and I need to do it now, this year, this moment, with these feelings. If this isn't The First Holidays Alone then I have to wait a whole year before I can get past that seemingly arbitrary yet looming milestone. Right now, I need to spend time with me and my grief, and continue our journey working out exactly how things work from this point on.
🤗The Holidays are hopeful. The Holidays are whatever you need them to be. For me, this year at least, they are a time to reflect and to grow as I come to terms with my grief, my loneliness, and the path I have lying ahead, somewhere in the unknown. I have been here before and I cleared a path to get where I am, I know I can learn to clear a different path than the one I planned. You can too. Whatever you are facing as the year end draws near, whatever The Holidays mean to you, you have the strength to endure. We all have our own paths to clear, and no one is better suited to clear yours than you. You can do it. You have done it before. Do not let society shame you into thinking you are not succeeding just because you don't fit the Hallmark picture of The Holidays. You are enough, you are loved. 💝
this may surprise those that know me to be an atheist – our roads are long and winding, with many turns and stops [↩]
I recently went to see Jojo Rabbit for the second time. It is a lovely movie filled with emotion. If you have not seen it, I recommend doing so. I would be surprised if it did not receive award nominations in several categories. That is the extent of my review; I am not one for crafting film critiques. I do not have the insight nor knowledge to be clever about cinema, nor the patience to make the effort. Instead, I share the following true story from my second viewing that you might smile and find security in knowing that you are not me.
🤫 Do not talk 📵 Do not text ⛔️ Do not arrive late.
There is an Alamo Drafthouse in my neighbourhood. It is really conveniently located and usually where I go to watch movies. If you are not familiar with the Alamo Drafthouse cinemas, they are the kind of place where you can order drinks and food during your movie. The kind of place with seats that can recline a bit. And the kind of place where, once the movie starts, you have to shut up and keep your phone off or risk being "ejected from the theater without a refund". I love it. No distracting chatter. No random bright lights from phones because some people apparently might get the most important notification of their lives while sat watching a film, but not so important that they couldn't be at the movies.
I was sat in the back row enjoying the film. I had a french press of decaf coffee to sip at, and a ridiculous amount of M&Ms to eat from the kind of box you would usually get from a take-out restaurant. It was quite lovely. A good film, some bitter coffee, and some sweet chocolate. What is not to like.
The film had my attention. I had already seen it and I was still fascinated, trying to spot things I had missed the first time, connect threads that I had not fully connected, seek out the intent of its director in every scene, and generally pretend that I got it more than I did1.
I reached for a sip of coffee. I reached for some M&Ms. I stared at the screen.
I was riveted.
And then my M&Ms felt hot and very wet.
I had apparently reached into my hot decaf coffee instead of the bucket of M&Ms, even though they were in entirely different places. Why would my brain sabotage me like this? I don't know. Why do I have an imaginary child? Some things just cannot be answered.
Now, in most circumstances, when my hand has been immersed in hot liquid, I might make some sort of sound like, "Ow!" or "Fuck me!" But remember, no talking. That and I did not want anyone around me to be alerted to the British man that just put his hand in his coffee for apparently no good reason.
I had never been in this situation before. A dark room. A movie. A few hundred people. A wet, slightly burning hand. I had not received training for this. Remaining calm, I removed my hand from the coffee. That seemed like a solid first step. I then moved it to my side and started carefully shaking it so as not to rouse suspicion. I then wiped it under my leg, then reached for an M&M and ate it. Crisis averted.
Relaxed and content that I had dealt with this new experience deftly, I lifted my hand for a quick sniff check to see if it was noticeable that it had been marinated in hot coffee. The screen flashed brightly, illuminating the audience (which included me, in case you forgot how cinemas work), and I made eye contact with the stranger in the seat next to me. Their eyes said, "Dude, why are you sniffing your hand?" and who knows what my eyes said. I feel like they said, "Shit, this is weird. Sorry. Just checking it doesn't smell of coffee," but I am pretty certain the other person saw, "I did something really perverted with my hand and I want to smell the perversion."
We both sat in silence for the rest of the movie, per the rules. We never made eye contact again.
This has happened to you, too, right?
I never can quite get things as deeply as they appear to be intended, I feel. [↩]
Have you ever written code in more than one place that needs to stay in sync? Perhaps there is a tool in your framework of choice that can generate multiple files from a single source of truth, like T4 templates in the .NET world; perhaps not. Even if there is such a tool, it adds a layer of complexity that is not necessarily easy to grok. If you look at the output files or the template itself, it may not be clear what files are affected or related.
At Khan Academy, we have a linter, written in Python, that is executed whenever we create a new diff for review. It runs across a subset of our files and looks for blocks of text that are marked up with a custom comment format that identifies those blocks as being synchronized with other target blocks. Included in that markup is a checksum of the target block content such that if the target changes, we will get an error from the linter. This is our signal to check if further changes are need and then update the checksums that are invalidated. The only bugbear folks seem to have is that instead of offering an option to auto-fix checksums in need of update, it outputs a perl script that has to be copied and run for that purpose.
Small bugbear aside, this tool is fantastic. It enables us to link code blocks that need to be synchronized and catches when we change them with reasonably low overhead. Though I believe it is hugely useful, it is sadly custom to our codebase. I have long wanted to address that and create an open source version for everyone to use. checksync is that open source version.
🤔 The Requirements
Before writing checksync, I started out with the following requirements:
It should work with existing marked up code in the Khan Academy codebase; specifically,
File paths are relative to the project root directory
Checksums are calculated using Adler-32
Both // and # style comments are used to comment the markup tags
Start tag format is: sync-start:<ID> <CHECKSUM> <TARGET_FILE_PATH>
End tag format is: sync-end:<ID>
Multiple start tags can exist for the same tag ID but with different target files
Sync tags are not included in the checksum'd content
An extra line of blank content is included in the checksum'd content (due to a holdover from an earlier implementation)
.gitignore files should be ignored
Additional files can be ignored
It should be comparably performant to the existing linter
The linter ran over the entire Khan Academy website codebase in less than 15 seconds
It should auto-update invalid checksums if asked to do so
It should output file paths such that editors like Visual Studio Code can open them on the correct line
It should support more comment styles
It should generally support any text file
It should run on Node 8 and above
Some of our projects are still using Node 8 and I wanted to support those uses
With these requirements in mind, I implemented checksync (and ancesdir, which I ended up needing to ensure project root-relative file paths). By making it compatible with the existing Khan Academy linter, I could leverage the existing Khan Academy codebase to help measure performance and verify that things worked correctly. After a few changes to address various bugs and performance issues, it is still mildly slower than the Python equivalent, but the added features it provides more than make up for that (especially the fact that it is available to folks outside of our organization).
🎉 Check It Out
checksync includes a --help option to get information on usage. I have included the output below to give an overview of usage and the options available to customize how checksync runs.
checksync --help
checksync ✅ 🔗
Checksync uses tags in your files to identify blocks that need to remain
synchronised. It works on any text file as long as it can find the tags.
Tag Format
Each tagged block is identified by one or more sync-start tags and a single
sync-end tag.
The sync-start tags take the form:
<comment> sync-start:<marker_id> <?checksum> <target_file>
The sync-end tags take the form:
<comment> sync-end:<marker_id>
Each marker_idcan have multiple sync-start tags, each with a different
target file, but there must be only one corresponding sync-endtag.
Where:
<comment> is one of the comment tokens provided by the --comment
argument
<marker_id> is the unique identifier for this marker
<checksum> is the expected checksum of the corresponding block in
the target file
<target_file> is the path from your package root to the target file
with a corresponding sync block with the same marker_id
Usage
checksync <arguments> <include_globs>
Where:
<arguments> are the arguments you provide (see below)
<include_globs> are glob patterns for identifying files to check
Arguments
--comments,-c A string containing comma-separated tokens that
indicate the start of lines where tags appear.
Defaults to "//,#".
--dry-run,-n Ignored unless supplied with --update-tags.
--help,-h Outputs this help text.
--ignore,-i A string containing comma-separated globs that identify
files that should not be checked.
--ignore-files A comma-separated list of .gitignore-like files that
provide path patterns to be ignored. These will be
combined with the --ignore globs.
Ignored if --no-ignore-file is present.
Defaults to .gitignore.
--no-ignore-file When true, does not use any ignore file. This is
useful when the default value for --ignore-file is not
wanted.
--root-marker,-m By default, the root directory (used to generate
interpret and generate target paths for sync-start
tags) for your project is determined by the nearest
ancestor directory to the processed files that
contains a package.json file. If you want to
use a different file or directory to identify your
root directory, specify that using this argument.
For example, --root-marker .gitignore would mean
the first ancestor directory containing a
.gitignore file.
--update-tags,-u Updates tags with incorrect target checksums. This
modifies files in place; run with --dry-run to see what
files will change without modifying them.
--verbose More details will be added to the output when this
option is provided. This is useful when determining if
provided glob patterns are applying as expected, for
example.
And here is a simple example (taken from the checksync code repository) of running checksync against a directory with two files, using the defaults. The two files are given below to show how they are marked up for use with checksync. In this example, the checksums do not match the tagged content (though you are not expected to know that just by looking at the files – that's what checksync is for).
// This is a a javascript (or similar language) file
// sync-start:update_me 45678 __examples__/checksums_need_updating/b.py
const someCode = "does a thing";
console.log(someCode);
// sync-end:update_me
# Test file in Python style
# sync-start:update_me 4567 __examples__/checksums_need_updating/a.js
code = 1
# sync-end:update_me
Additional examples that demonstrate various synchronization conditions and error cases can be found in the checksync code repository. To give checksync a try for yourself:
Install it from the npmjs.com repository: yarn install checksync