Hacking & Bodging a Git Hook + Vagrant + WP-CLI + Bash Local to Dev Database Transfer

Ever since I started using Git to push local website changes to a development server, I’ve been vaguely irritated about dealing with the database in the same manner. For a long time, I used interconnect/it’s Search Replace DB for this side of things, but I was always wondering if I could somehow increase laziness time by automating the process. One hungover Sunday, plus a couple of hours on Monday, and one hacked and bodged success.

This isn’t a “How to do yer Git + Vagrant + local to dev” thing, nor is it a copy-paste, “Works for me!” party. Nonetheless, provided you’re using git-push, and are comfortable with WP-CLI or MySQL command-line, Bash, and generally thrashing small bits of code around, in principle it would work in any situation. And I do feel kinda dirty throwing all this into Git post-receive, but whatever seems to work.

So, here’s what I wanted to do:

  1. Do all my commits, and run git-push and throw all the file changes to the dev server.
  2. Combine that with a dump of the database, then get it to the dev server.
  3. Use something at the other end to import the database, and search-replace strings
  4. Clean up after at both ends.

When I first looked into this, it seemed using the pre-commit Git hook was the most common approach, dumping the database and adding it to the commit. I didn’t want to do this, for a couple of reasons: I do a lot of commits, and the majority have no database component; I wasn’t looking to version control the database; All I wanted to do was push local to dev database with the changed files. Looks like a job for pre-push hook.

Earlier this year, I started using Vagrant, so the first issue was how to dump the database from there. I do commits from the local folder, rather than SSH-ing into the VM, so mysqldump is not going to work without first getting into the VM. Which brought its own set of weirdnesses, and this was the point when I decided to flop over to WP-CLI, the WordPress command-line tool.

I often find solutions to this sort of thing are dependant on the combination of software and commands being used. I use mysqldump on its own all the time, but here, I needed to use Git to set the path for where the database would be dumped to — because git hooks are in a sub-directory of the git folder — and that, in combination with dumping the database inside the VM while within a Git command running from the local folder (yeah, probably should just do all my git via SSH), and hurling it at a remote server, means sometimes things that work in isolation get cranky. And this is a hack/bodge, so I went with:

  1. Set up paths for the database dump with Git, ’cos Git is running this show.
  2. SSH into the Vagrant box.
  3. WP-CLI dump the database to a gzipped file.
  4. SCP that up to the dev server.
  5. Delete all that on the local server, ’cos I’m tidy.

That’s half of it done. I’ve got my pushes working, the database file is up on the dev server, the local server is all cleaned up, so now it’s time for the other end.

In this case, I was doing it for a site on DreamHost, who conveniently give all kinds of fun command-line access, plus WP-CLI on their shared servers. Once Git has finished checking out the new file changes in post-receive, it’s time for frankly bodging it.

My current usual setup is a bare repository on the dev server, which checks out to the development website directory. This means neither the uploaded database, nor WP-CLI and the WordPress root are in the same place as the running hook. No big deal, just use –path=. The next thing though, is cleaning up post-import. Strings to be changed all over the place, like local URLs swapped to dev. And for that we have, wp search-replace, which is an awful lot like Search Replace DB. At the dev end then:

  1. Set up paths again, this time it’s WP-CLI running the show.
  2. Unzip the database then import it.
  3. Do database stuff like search-replace strings, and delete transients.
  4. Delete that uploaded database file on the dev server, ’cos I’m tidy.

I was looking at all this late last night, all those repeating lines of ‘wp search-replace’ and I thought, “That looks like a job for an array.” Which led me down the tunnel of Bash arrays, associative arrays, “How can I actually do ‘blah’, ’cos bash seems to be kinda unwilling here?” and finally settling on not quite what I wanted, but does the job. Also, bash syntax always looks like it’s cursing and swearing.

The pre-push hook:

#!/bin/sh

# a pre-push hook to dump the database to a folder in the repo's root directory, upload it to the dev server, then delete when finished

echo '***************************************************************'
echo 'preparing to back up database'
echo '***************************************************************'

# set up some variables, to keep things more readable later on
# backup_dir is relative to git hooks, i.e. 2 directories higher, so use git to set it

ROOT="$(git rev-parse --show-toplevel)"
BACKUP_DIR="$ROOT/.database"
DB_NAME="database"

# check there is a database backup directory, make it if it doesn't exist then cd to it

if [ ! -d "$BACKUP_DIR" ]; then
mkdir "$BACKUP_DIR"
cd "$BACKUP_DIR"
else
cd "$BACKUP_DIR"
fi

# cos this is vagrant, first ssh into it. there will be a password prompt
# using EOF to write the commands in bash, rather than in ssh quotation marks

ssh -t vagrant@172.17.0.10 << EOF

# cd to the new databases folder. this is absolute, cos is vm and not local folder
cd "/var/www/user/domain.tld/.database" 

# then export the database with wp-cli and gzip it
wp db export --add-drop-table - | gzip -9 > $DB_NAME.sql.gz

# exit ssh
exit

# bail out of eof
EOF

# scp the backup directory and database to dev server
scp -r $BACKUP_DIR user@domain.tld:~/

# remove that backup directory so it's not clogging up git changes
rm -r $BACKUP_DIR

echo '***************************************************************'
echo 'all done, finishing up git push stuff'
echo '***************************************************************'

The post-receive hook:

#!/bin/sh

echo '***************************************************************'
echo 'post-receive is working. checking out pushed changes.'
echo '***************************************************************'

# check out the received changes from local to the dev site
git --work-tree=/home/user/dev.domain.tld  --git-dir=/home/user/.repo.git checkout -f


# import the database with wp-cli
echo '***************************************************************'
echo 'starting database import'
echo '***************************************************************'

# setting up some paths
# on some webhosts, e.g. all-inkl, setting the alias to wp-cli.phar is required, uncomment and set if needed
# alias wp='/path/to/.wp-cli/wp-cli.phar'

# the path to wp-config, needed for wp-cli
WP_PATH="/home/user/dev.domain.tld/wordpress"
# database directory, created in git pre-push
DB_DIR="/home/user/.database"

# check there is a database directory
if [ -d "$DB_DIR" ]; then

	# then check it for sql.gz files
	DB_COUNT=`ls -1 $DB_DIR/*.sql.gz 2>/dev/null | wc -l` 

	# if there is exactly 1 database, proceed
	if [ $DB_COUNT == 1 ]; then

		#grab the db name, this way the db name isn't hardcoded
		DB_NAME=$(basename $DB_DIR/*)

		echo 'importing the database'
		echo '***************************************************************'

		# unzip the database, then import it with wp-cli
		gunzip < $DB_DIR/$DB_NAME | wp db import - --path=$WP_PATH

		# clear the transients
		wp transient delete --all --path=$WP_PATH

		# run search replace on the main strings needing to be updated
		# make an array of strings to be searched for and replaced
		search=(
			"local.domain.tld:8443"
			"local.domain.tld"
			"/var/www/user/"
		)
		replace=(
			"dev.domain.tld"
			"dev.domain.tld"
			"/home/user/"
		)

		#loop through the array and spit it into wp search-replace
		for (( i=0; i < ${#search[@]}; ++i )); do
			eval wp search-replace --all-tables --precise \"${search[i]}\" \"${replace[i]}\" --path=$WP_PATH
		done

		# any other wp-cli commands to run
		wp option update blogname "blog name" --path=$WP_PATH

		# delete the backup directory, so there's no junk lying around
		rm -rf $DB_DIR
	
	else
	
		echo 'database was not found'
		echo '***************************************************************'
	
	fi

else

	echo 'database folder was not found'
	echo '***************************************************************'

fi

echo '***************************************************************'
echo 'all done'
echo '***************************************************************'

What else? Dunno. It’s pretty rough, but basically proves something I didn’t find an example of all combined into one: that you can use git hooks to push the database and file changes at the same time, and automate the local-to-dev database transfer process. Is this the best way to do it? Nah, it’s majorly bodgy, and would have to be tailored for each server setup, and I’m not even sure doing such things in a git hook is advisable, even if it works. It does demonstrate that each step of the process can be automated — irrespective of how shonky your setup is — and provided you account for that and your own coding proclivities, there’s multiple ways of doing the same thing.

(edit, a day later.)
I decided to throw this into ‘production’, testing it on a development site I had to create on webhost I’m not so familiar with but who do provide the necessities (like SSH and Let’s Encrypt). Two things happened.

First, WP-CLI didn’t work at all in the post-receive script, even while it did if I ran commands directly in Terminal (or iTerm as I’m currently using). After much messing about, and trying a bunch of things it turned out that this was an issue of “has to be tailored for each server setup”, in this case adding an alias to wp-cli.phar.

Second, having a preference for over-compensation while automating, it occurred to me that I’d made some assumptions, like there’d only be one database file in the uploaded directory, and that hardcoding the filename — which was one of those “I’ll fix that later” things — had morphed into technical debt. So, feeling well competent in Bash today, I decided for the “make sure there’s actually a database folder, then check there’s actually a sql.gz file in it, and there’s only one of them, then get the name of that file, and use it as a variable”. I often wonder how much of this is too much, but trying to cover the more obvious possible bollocks seems reasonably sensible.

Both of these have been rolled into the code above. And as always, it occurs to me already there’s better — ‘better’ — ways to do this, like in pre-push, piping the database directly to the dev server with SSH, or simultaneously creating a separate, local database backup, or doing it all in SQL commands.

Git Hook + Vagrant + WP-CLI + Bash Local to Dev Database Transfer

& that’s me about ½ caught up on what I’ve be…

Status

& that’s me about ½ caught up on what I’ve been reading lately. Not even going to mention the size of the pile conveyor-belting into my maw.

Reading … A 10th Anniversary

Another year of reading. Ten years I’ve been at this, blogging every book I read (almost every, a few slipped by over the years). Going from just blogging the book covers, to a few lines on why I was reading, to my recent frankly absurd multi-thousand word essays on some of Iain (M. or not) Banks novels. Trying to rein in that latter particular excess.

Usually at this point, I look at what I wrote a year ago, so I can aim for some sort of consistency.

A lot of fiction this year, almost twice as much as non-fiction, for a total of 34 books read — or attempted, I gave up on a few, and there’s a couple that I’ve already started but won’t make this list, ’cos I haven’t blogged them yet. Blogging is reading, just like rubbing is racing.

The year got off to a brilliant start with three biographies by trans women: Janet Mock’s Redefining Realness: My Path to Womanhood, Identity, Love & So Much More, Laura Jane Grace’s Tranny: Confessions Of Punk Rock’s Most Infamous Anarchist Sellout, and more a collection of essays over decades that becomes biographical, Julia Serano’s Outspoken: A Decade of Transgender Activism and Trans Feminism. And Tranny is my Book of the Year. There’s a couple of others equally or maybe more deserving — thinking of recent reads Peter Fryer’s Black People in the British Empire: An Introduction and China Miéville’s October: The Story of the Russian Revolution — but Miéville’s had a couple of Books of the Year already, so that’s him out. Tranny just spoke to me on a very personal level (as did Redefining Realness, different but no less personal), and Laura Jane Grace has been making miles in my head all year, I’m listening to her now. I’d marry her, it’s that kind of thing.

Following that trio, I went straight into Gayatri Chakravorty Spivak’s An Aesthetic Education in the Era of Globalization. Still in it. Not an easy read, needs the kind of mental preparation and focus I’ve been lacking the last some years, though strangely not for Caroline Walker Bynum, who I’ve been reading for three years now, one of my absolute loves, and Christian Materiality: An Essay on Religion in Late Medieval Europe is also deserving of being a Book of the Year.

A couple of others on the non-fiction side: May Opitz, Katharina Oguntoye, Dagmar Schultz (eds.) Showing Our Colors: Afro-German Women Speak Out, I read after seeing it at Deutsches Historisches Museum’s Deutscher Kolonialismus: Fragmente Seiner Geschichte Und Gegenwart exhibition. I’m didactic and prescriptive, and just like Peter Fryer, this (or whatever more recent work) should be compulsory reading in Germany, along with Ruth Mandel’s Cosmopolitan Anxieties and Katherine Pratt Ewing’s Stolen Honor — and a bunch of other stuff. But the last year’s European, American, and Australian politics makes me think we haven’t got a chance, walking with their eyes open while we shout and plead with them against where they’re going, where they’re dragging us.

I haven’t been reading much on China lately (or Afghanistan for that matter, but remedying that at the mo), but did read Frank Dikötter’s The Cultural Revolution: A People’s History, 1962–1976, the final work in his China under Mao Zedong trilogy (preceded by The Tragedy of Liberation and Mao’s Great Famine). He’s one of the few historians writing on China I’ll always read, who’s also in the fortunate position to be able to publish semi-regularly (and for academic publications, not horrifically over-priced).

There were a few other non-fiction works, but let’s get onto the fiction, or science-fiction and fantasy, ’cos I still don’t read anything else. I went on a lengthy Iain M. (plus a couple of non-M.) Banks binge earlier this year. I needed to just read, eyes rush over the pages, know before I started I’d love the story, sink back into familiar worlds and lives. Obviously that mean starting with my favourite book ever, Feersum Endjinn, and this being my first Banks re-read in some years, I came to him with a tonne of new reading behind me, and wow did I ever write about all my new thoughts. I followed that up with Whit, which has never been one of my favourites, nor did I think of it as one of his best. Wrong again, Frances. Back to The Business after that, definitely one I adore, and have read at least 6 times, then back into his skiffy with the late / last trio: Surface Detail, The Hydrogen Sonata, and Matter. I feel a little unsure putting these in my year’s reading here, as though there’s nothing remarkable about reading him multiple times, or that this is supposed to be about new books I’ve read. On the other hand, fuck it, it’s my blog and my reading and I can fuck off if that’s the attitude I’m going to bring.

There was a sizeable dip early- to mid-year, disappointment in fiction, feeling apathetic about the heaviness of non-fiction (thanks, Twitter), and also perhaps just steamrolling through scores of books year after year is an unrealistic monotone that I’m not. I did have a thrill with one more of Steph Swainston’s Castle novels, Fair Rebel, followed almost immediately by Above the Snowline, and love that she decided to return to writing, ’cos she’s one of the best. Not easy, these are large, demanding works that don’t mainline narrative reward, but she’s got one of the most captivating and extensive fantasy worlds I’ve read.

At the same time as Swainston, I got my grubby mitts on Alastair Reynolds’ Revenger. Something of marketed as Young Adult (is not), and not especially long (longer though than his novella Slow Bullets), and it feels like a Girl’s Own bit of romp, then he massacres an entire ship’s crew and continues in his very, very dark and existentially terrifying way right up till the end. Book of the Year for me, right there. Then there was the aforementioned Banks tour, and not until I was in Brussels did I get mad thrilled about fiction again. Cheers, once again, Gala. Ben Aaronovitch’s PC Grant series, A young Idris Elba / Stormzy cop with Harry Potter powers. A more cheerful Liminal People series. I started with number 2, Moon Over Soho, which meant reading the first in the series, Rivers of London had both plenty of, “I know who these people are,” and “Oh shit, her face is gonna fall off, isn’t it?” I’ve got the other 5 in the series on order.

I get to this point of writing, and I’ve added the covers of all these books, so I’ve got a nice visual treat in front of my mug, and I scroll through them … smiles all the way. And a little shiver of goosebumps. I’m lucky as all shit to be able to buy new books almost every week even when I’m on the verge of poverty (cheers, Germany and your incomprehensible to Australia attitude to cheap books), and lucky as all shit to have the time and education and all the rest to be able to read them. It’s a human right and every day I give thanks to the people (shout out to Eleanor Roosevelt here!) who fought and continue to fight for our inalienable rights.

Maybe I’m going to make this a thing (which always feels contrived), but I’ll finish quoting myself again, first from 2013 and then from 2015:

Buy books! Buy books for your friends! Encourage people to read. If you know someone who Can’t Read Good (And Wanna Learn To Do Other Stuff Good Too), help them, reading is only difficult if you’ve been told it is. Support your local libraries!

And:

So here’s to the writers, and their publishers and proofreaders and editors and typesetters and designers and artists and agents and friends and families who make it possible for them to write so that I may read.

And speaking of designers and artists, I decided to do a Book Covers of the Year thing, dunno why I haven’t before now. Mainly because both Revenger and October have covers that smash it. Also the original Feersum Endjinn, class late-20th century sci-fi cover art there.

Thrilled and awed by all this reading? Here’s the last years’ anniversary lists:

Catching up on what I’ve been reading. Definitely …

Status

Catching up on what I’ve been reading. Definitely let this run away from me, so I’m trying to keep it ‘short’ for a change.

5-Character Dev Environment

Messing with my .bash_profile this afternoon, post-diving into Laravel and Git (which I’ve been doing much of the last week), I realised I could boot my entire dev environment with 5 letters. Fewer, if I wanted.

So instead of going to the Dock, clicking each of the icons, going to each and faffing around, I could at least boot them all, and set off some commands in Terminal (or ITerm2 as I’m now using).

Weirdly, until Justine gave me an evening of command-line Git learning, and wanted my .bash_profile, “Like so,” I hadn’t realised you could do stuff like that, despite amusing myself with all manner of shell scripts. Now I know what’s possible, I’m over-achieving in efficient laziness.

What’s missing is:

Some of these are probably uncomplicated, but this was a 30-minute experiment that turned out to be very useful.

5-character dev environment
5-character dev environment

And that’s me almost caught up with blogging my re…

Status

And that’s me almost caught up with blogging my recent reading. Now it’s just a threateningly large pile.

Bookmark Archaeology

Aside

I was cleaning out my browser bookmarks last night, first time in years, bookmarks going back to the early-’00s, thousands of them. I opened them in batches, every one, to see if I wanted to keep them. Hundreds, thousands of dead sites, no longer found, no longer existing. All that history and culture vanished as if it never was, only the link and title in my bookmarks proving they once existed, and once I deleted that …

Code Stupidity

Aside

I got sick of the tiny, Web1.0 images everywhere here, a hangover from the earliest days of supernaut, so I decided — ’cos I like visuality & pix — to make small, big. I thought it would be easy. Little did I know I also create and add to the pile of Technical Debt. So: most single images in the recent past are now huge-ified, 666px wide; recent image galleries which are not full of diverse image ratios are now evenly splitting the Number of the Beast. Older images and galleries should be retaining their previous diminutiveness, but who knows, 13 years of blog is difficult to homogenise. Mostly I got distracted with how to make portrait images not blow out of the available browser window space, which turns out to be a kinda traumatising process I didn’t achieve. Plus how to Lazy Load srcsets by preg_replacing the new WordPress caption shortcode. OMFG, Frances, WTF? All of which makes me think it might be time for yet another supernaut refresh. So much code. So many images. So much …

Website rsync Backups the Time Machine Way

Continuing my recent rash of stupid coding, after Spellcheck the Shell Way, I decided for Website rsync Backups the Time Machine Way.

For a few years now, I’ve been using a bash script I bodged together that does incremental-ish backups of my websites using the rather formidable rsync. This week I’ve been working for maschinentempel.de, helping get frohstoff.de‘s WooCommerce shop from Trabant to Hoonage. Which required repeated backing up of the entire site and database, and made me realise the shoddiness of my original backup script.

I thought, “Wouldn’t it be awesome, instead of having to make those stupid ‘backup.blah’ folders, to let the script create a time-stamped folder like Time Machine for each backup, and use the most recent backup for the rsync hard links link destination?” Fukken wouldn’t it, eh?

Creating time-stamped folders was easy. Using the most recent backup folder — which has the most recent date, and in standard list view on my Mac, the last folder in a list — was a little trickier. Especially because once a new folder was created to backup into, that previously most recent was now second to last. tail and head feels hilariously bodgy, but works? Of course it does.

Bare bones explaining: The script needs to be in a folder with another folder called ‘backups’, and a text file called ‘excludes.txt’.  Needs to be given chmod +x to make it executable, and generally can be re-bodged to work on any server you can ssh into. Much faster, more reliable, increased laziness, time-stamped server backups.

#!/bin/sh
# ---------------------------------------------------------------
# A script to manually back up your entire website
# Backup will include everything from the user directory up
# excludes.txt lists files and folders not backed up
# Subsequent backups only download changes, but each folder is a complete backup
# ---------------------------------------------------------------
# get the folder we're in
this_dir="`dirname \"$0\"`"
# set the folder in that to backup into
backup_dir="$this_dir/backups"
# cd to that folder
echo "******************"
echo "cd-ing to $backup_dir"
echo "******************"
cd "$backup_dir" || exit 1
# make a new folder with timestamp
time_stamp=$(date +%Y-%m-%d-%H%M%S)
mkdir "$backup_dir/${backuppath}supernaut-${time_stamp}"
echo "created backup folder: supernaut-${time_stamp}"
echo "******************"
# set link destination for hard links to previous backup
# this gets the last two folders (including the one just made)
# and then the first of those, which is the most recent backup
link_dest=`ls | tail -2 | head -n 1`
echo "hardlink destination: $link_dest"
echo "******************"
# set rsync backup destination to the folder we just made
backup_dest=`ls | tail -1`
echo "backup destination: $backup_dest"
echo "******************"
# run rsync to do the backup via ssh with passwordless login
rsync -avvzc --hard-links --delete --delete-excluded --progress --exclude-from="$this_dir/excludes.txt" --link-dest="$backup_dir/$link_dest" -e ssh username@supernaut.info:~/ "$backup_dir/$backup_dest"
echo "******************"
echo "Backup complete"
echo "******************"
#------------------------------------------------
# info on the backup commands:
# -a --archive archive mode; same as -rlptgoD (no -H)
# -r --recursive recurse into directories
# -l --links copy symlinks as symlinks
# -p --perms preserve permissions
# -t --times preserve times
# -g --group preserve group
# -o --owner preserve owner (super-user only)
# -D same as --devices --specials
# --devices preserve device files (super-user only)
# --specials preserve special files
# -v --verbose increase verbosity - can increment for more detail i.e. -vv -vvv
# -z --compress compress file data during the transfer
# -c --checksum skip based on checksum, not mod-time & size – SLOWER
# -H --hard-links preserve hard links
# --delete delete extraneous files from dest dirs
# --delete-excluded also delete excluded files from dest dirs
# --progress show progress during transfer
# --exclude-from=FILE read exclude patterns from FILE – one file or folder per line
# --link-dest=DIR hardlink to files in DIR when unchanged – set as previous backup
# -e --rsh=COMMAND specify the remote shell to use – SSH
# -n --dry-run show what would have been transferred

Reading … A 9th Anniversary

It’s that time of year again! Frances’ and supernaut’s Books of the Year for the 9th time. And some most excellent books were read indeed. This time last year, I realised I’d been struggling a bit with enjoying reading. I looked back over what I’d read in previous years, compared it with 2015’s crop, and noticed I’d dug myself into a bit of a hole with mediæval art and history.

What to do, Frances? I dunno, Other Frances, how about read about space travel and stuff? Good idea!

Unlike last year, my ninth iteration of looking back on a year’s reading — and it’s in October because that’s when I first started blogging about reading, almost a decade ago — has some absolute slammers on the fiction side. Last year I didn’t even name a fiction book of the year. This year, if it wasn’t for one in particular, there’s be 4 or 5 smashing at it for joint Book. And in non-fiction the situation’s pretty similar, or even better, cos there’s barely a single non-fiction work I’ve read in the past 12 months that was anything less than well awesome. It’s also one of my least-read years, only 29 that I read and blogged (possibly a couple of others I’ve forgotten); definitely plenty of internet — I mean Rainbow Autobahn distraction in the last year, exacerbating my inability to focus on pages. I blamed my poor reading last year on that distraction as well, probably time to harden the fuck up and put away the internet.

Of those 29, only 10 were non-fiction; the remaining 19 non-fiction skewed more to fantasy than sci-fi, with around 7 works explicitly skiffy, 9 explicitly fantasy, and a trio (maybe more depending on how dogmatically I apply those categories) deftly straddling both. I call those Speculative Fuckery, ’cos I love when the only two genres I read start boning each other.

On the non-fiction side, mediæval Northern European history continues filling my shelves, and there’s a bunch of “not easily categorised on their own” which nevertheless fit predictably into my decades-long interests.

Then there’s the new, or maybe to say newly clarified bunch that I kinda want to call Islamicate Studies, though that might miss something, so it encompasses that, human rights, identity, philosophy, feminism, and is primarily from women from and/or writing on Iran, Near/Middle East (I’m a bit iffy on this appellation right now, and have been trying out ‘West Asia’ also because it shifts the centre and subject of focus out of Europe, dunno though), and people from or descended from those regions in Europe, North America, Australia. I arrived at this field of interconnected subjects after increasing dissatisfaction with how feminist/queer/left-ist writing addressed brown and/or Muslim identities; regarded these people living in Europe, North America, Australia; and when I spent some time thinking about how the diverse subjects I was reading needed to come together. Also it’s a lot of living in Berlin/Germany/Europe and getting increasingly pissed at the racism against anyone not unequivocally ethnically correct, and the white feminist/queer/left-ist bullshit distractions, and my own personal, slow movement towards identifying if not myself as Turkish/brown/West Asian/Muslim, then definitely my family history (as you can see from all the slashes, I have no idea).

Books! I have read them!

Fiction first. This was a fine year. If I hadn’t read Sofia Samatar’s A Stranger in Olondria, she’d still be my Fiction Book of the Year with The Winged Histories, though sharing with a few others. I don’t actually know how I would pick a book of the year from a pile comprised of that plus Jo Walton’s Necessity and The Philosopher Kings;  Jaymee Goh and Joyce Chng’s The Sea Is Ours; and Ann Leckie’s masterful finish to her debut Imperial Radch trilogy, Ancillary Mercy. Impossible. I would probably give it to the latter, but then … Necessity, a brilliant conclusion to another trilogy, and The Winged Histories: sublime. So I could possibly get it down to a trio of exceptional literature, but no further. Lucky then A Stranger in Orlondria saved me from that anguish.

I don’t want to say it’s ‘better’ any of those other three — though perhaps that’s the case when comparing it to The Winged Histories, which would lose its spot in the trio just as The Philosopher Kings does to Necessity. I think of the two Samatar has written it’s a more major work. If this is my final trio then, I’m not claiming one is better than another, simply A Stranger in Olondria has had a significant effect on me. Would that effect stand up under re-reading? How would that re-reading compare to one of Leckie’s trilogy? If I read them both back-to-back, what then would be my judgement? The best questions always involve more reading.

This is all anyway just writing from memory, how I remember a book made me feel. I’ve been thinking recently that eventually my memory of a book dissolves until it’s just feelings, colours, a glimpse of an image or two. It’s like sediment, like geology, layers upon layers of this.

A quartet of other books I liked a lot: Ada Palmer’s Too Like the Lightning, Genevieve Cogman’s The Masked City, K. T. Davies’ Breed, and Alastair Reynolds’ Revelation Space.

Breed was a romp of Oglaf proportions and probably the most fun I had this year. I wish she’d write more of this. Reynolds’ Revelation Space I read because I needed some hard operatic space sci-fi, and his Slow Bullets novella was a favourite of mine last year. This one was good enough for me to slog through the whole, uneven trilogy. I like him, but there’s a hopelessness in his work, like the heat death of the universe.

As with Reynolds, Genevieve Cogman is another whose previous works got me to read her latest. The Invisible Library, which I also read last year was well tasty. I was super excited to find she had this sequel — and OMG! Just like last time when I discovered The Masked City, she has a sequel to that! Excellent! The immediate result of me writing about my favourite books is I’m ordering more.

Ada Palmer’s Too Like the Lightning. Yeah, loved a lot. Glorious cover art, almost almost one of my first choices, but a few flaws in it, and the cliffhanger “Will bad things happen? Stay tuned for Book 2!” guaranteed to piss me right off. Please, don’t do that to me. I’ve paid for a story, not half a story. If your story’s too big for one book, then at least divide it in a way that doesn’t leave me hanging.

All of these authors I’ll read again (along with a score of others on my Have You Written A New Book Yet? list). I might be a bit crabby here and there about the works, but I also possess a modicum of self-awareness that I’m a pretty fucking demanding reader. The authors and works above if you’re into sci-fi / fantasy (or if you’re not) are about as good as it gets. Not just for this year, but of everything I’ve read in the last 12 years or so. (And just wait for next year’s Books of the Decade! It’s gonna be hectic!)

Non-fiction!

I didn’t read much of this in the last year, but I lucked out here too, barely a dud among them (and that single one was an old book I realised I’d never finished), running out of superlatives here.

I tried to broaden my non-fiction reading a little again, move outside the clag of mediæval history. So I read Amy Shira Teitel’s Breaking the Chains of Gravity: The Story of Spaceflight before NASA, Julie Phillips’ James Tiptree, Jr.: The Double Life of Alice B. Sheldon, and Jo Walton’ What Makes This Book So Great. All excellent works in completely different ways, and which remind me I need to read more astronomy, astrophysics, and geology this year.

The Book of the Year though — and I’m forcing myself to pick only one — comes from another trio: Kathryn Babayan and  Afsaneh Najmabadi’s Islamicate Sexualities: Translations Across Temporal Geographies of Desire; Seyla Benhabib’s The Rights of Others: Aliens, Residents, and Citizens; and Svetlana Alexievich’s Zinky Boys: Soviet Voices from the Afghanistan War.

Svetlana Alexievich was awarded the Nobel Prize in Literature last year, in no small part for her writing on the Soviet occupation and war in Afghanistan. Her writing is chilling. Heart-rending. I even said Zinky Boys would be my Book of the Year. Pretty sure I said the same thing about Seyla Benhabib’s The Rights of Others. In truth I shouldn’t pick one over the other, except that Babayan and Najmabadi’s Islamicate Sexualities somehow is tying all this together, mediæval history, human rights, feminism, identity, migration, religion, and it’s so urgently pertinent to the slow stumbling back to the abyss Europe is currently taking. Read them all, or at least familiarise yourself with the writers.

Other books well worth reading: Kecia Ali’s Sexual Ethics in Islam: Feminist Reflections on Qur’an, Hadith, and Jurisprudence. Mediæval of course: Miri Rubin’s Emotion and Devotion: The Meaning of Mary in Medieval Religious Cultures, and Gude Suckale-Redlefsen’s Mauritius Der heilige Mohr / The Black Saint Maurice.

And that’s my reading for the last 12 months. As if I’m not sated and replete already, I’ve already got a pile of new stuff.

Reading is a great privilege. It’s not however, explicitly a human right. Article 26 i. and 27 i. of the UN Declaration of Human Rights either directly imply or by extrapolation intend reading as a human right, yet nowhere is it explicitly stated that reading comprehension or literacy, and the opportunity to gain this ability is a right. Perhaps I’m splitting hairs, yet I can interpret the UNDHR in a way that fulfils the letter of declaration while still populating my dictatorship with illiterate proles.

My ability to read, at the level I do, at the frequency, my ability to critically consider the works I read (with or without concomitant swearing), to write about them here, to discuss them with others, all this is a privilege. And I mean that in the sense of a special honour. And that necessitates obligation.

Rather than continuing blabbing, I’ll quote myself, first from 2013 and then from last year:

Buy books! Buy books for your friends! Encourage people to read. If you know someone who Can’t Read Good (And Wanna Learn To Do Other Stuff Good Too), help them, reading is only difficult if you’ve been told it is. Support your local libraries!

And:

So here’s to the writers, and their publishers and proofreaders and editors and typesetters and designers and artists and agents and friends and families who make it possible for them to write so that I may read.