Related to process of writing from a few days ago, I've been working on another tool that I find myself using fairly often: Yeoman. This is a scaffolding tool, which basically means it asks a few questions and then sets up a project. In my case, it does a lot of the drudge work of creating a writing project and getting it ready for Gitlab's CI which I also use heavily (and why I wrote
$ sudo npm install -g yo generator-mfgames-writing
The first thing it does is ask a number of questions about the project. I'll use Nor Curse Be Found, my Beauty and the Beast sequel, as an example.
$ mkdir nor-curse-be-found $ cd nor-curse-be-found $ yo mfgames-writing ? The title for your novel or story: (Nor Curse Be Found)
All of my writing projects have a unique “slug” to describe them. This is used for the website's URL, such as https://fedran.com/nor-curse-be-found, and also the name of the output (
dmoonfire-nor-curse-be-found-0.0.1.epub). I use a library to pull out the current directory's name and try to make a reasonable title out of it. I can change the title to something more accurate, maybe with apostrophes or possessives, but generally I keep the slug pretty even. In this case, the parenthetical item is the suggested output, so I can just hit return.
? The title for your novel or story: Nor Curse Be Found ? The slug based on the title: (nor-curse-be-found)
Just in case the directory doesn't match the directory, I use a library to take that given title and create a slug out of it. One of the biggest things, my Fast Trip rule, is that I don't want to tell someone how to work. That is why I called this semi-opinionated in that there are a lot of options but the default is how I work.
There is also a question about bylines, but then we start to get into the technical bits.
? The title for your novel or story: Nor Curse Be Found ? The slug based on the title: nor-curse-be-found ? Author's name or byline: D. Moonfire ? Do you want to create the Git repository? (Y/n)
This came from some years ago, but I do one story per repository, more so if I think the story has a possibility of being a bigger piece (which many of mine are), I want to track the versions independently, or I might be sending to an editor or submitting it somewhere (not likely these days, I self-censor a lot there). Since this is so common, I put it in as part of the questions.
I have a slowly evolving set of practices when it comes to writing processes. One of the first ones is setting up a Git repository. This always starts with configuring husky, semantic-release, conventional-commits, and commitlint. (I'm working on updating my Yeoman generator to make this a lot easier, but that will be later this week.)
? Do you want to create the Git repository? Yes ? Any command to run after `git init`? set-moonfire-git-user
I also write for a number of different projects (and bylines) which means I don't always use the same Gitlab login or even email. To faciliate this, I do not have a global
user.email set up in Git so it blows up if I try to make a commit without setting a local one. Since that is tedious, I write a bunch of scripts that set the user information and SSH keys (
set-moonfire-git-user for writing,
set-mfgames-git-user for programming for example) and I want to call them before I do that first commit. If I just hit return to have a blank, then nothing extra will happen.
? Any command to run after `git init`? set-moonfire-git-user ? Do you want to create the initial commit? Yes ? Do you want to create the initial Git tag? Yes ? What version do you want to start? 0.0.1
Now, the initial commit is a messy one and a philosophical difference between me and the Semantic Release folks. They have their reasons to hard-code the value to “1.0.0” but I'm specifically start this process before a release, so I feel it should be “0.0.1” because I want that tracking as soon as possible.
Since I don't have the ability to set it via a configuration file, I get around it by creating the initial commit and then allowing it to be tagged so it can be pushed up and get the starting version I want.
This is also because I tag versions I give to editors, beta readers, and even my writing group (when I went to it). The tagged version also shows up on the various Fedran pages and mainly it shows progression as I write.
So, Fast Trip so I jump through a few hoops. It should also be clear why I needed that command to run after
git init to set my user, otherwise this part would blow up with a “user not set” message.
? What version do you want to start? 0.0.1 ? Which package manager do you use? (Use arrow keys) ❯ NPM Yarn
Moving away from Git and into the build process, the first question is package management. I have a love/hate relationships with
npm. The command works great, but I've been struggling with it (more on than off lately) so I've been using yarn fairly heavily in the last year or so. But, with Yeoman I can give both so the user can use up or down keys to select and hit return to choose. Once they do, the menu goes away and is replaced with the choice.
? Which package manager do you use? Yarn ? Do you want to use semantic releases? Yes ? Do you want to use conventional commits? Yes ? Do you want to use Husky? Yes
The next three are to set up the common functionality I use with releases. They set up semantic-release, conventional commits, commitlint, and Husky which I use to make sure all the commit messages are clean. These four program have been a stable in most of my development lately, for coding and writing, for Typescript and even C#.
? Do you want to use Husky? Yes ? Do you want to use CI/CD? Yes ? Do you use Gitlab for your CI/CD? Yes
I can't say enough for Gitlab's CI. Whenever I push up code to the server, it runs the entire publication process. Assuming it works, it then produces a zip archive with EPUB, MOBI, PDF, and other formats without me doing anything else. Every single time. With sematic releases and the rest, each one has a unique version so I don't have “final”, “final-2”, “final-2a”, etc. Just a clean version number that shows up everywhere, starting at 0.0.1.
? Do you use Gitlab for your CI/CD? Yes ? Do you use asdf? No
This is another tool I use, asdf. This was also something that pretty much started last year, but one of the goals of mfgames-writing was to be able to produce consistent output of novels even years later. Well, what I didn't realize is how much the Node ecosystem would change and Node 15 doesn't exactly run Node 8 packages very well due to deprecations and changing libraries.
This is where
asdf comes in. It puts a small file (
.tool-versions) which says which version of Node, Yarn, C#, Python, whatever and will automatically change to those versions when I
cd into the directory. It basically rolls in all the functionality of
virtualenv, and a bunch of other localized version libraries into one. Since I write and work across many languages, having one tool to manage that is easier for me. So, saying “yes” to the question copies that
.tool-versions into the resulting directory.
? Do you use asdf? Yes ? Which formats do you want to use? (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◉ PDF ◉ EPUB ◯ DOCX ◯ HTML
With this next question, we are out of the build section and into the formats. Basically, this just queries which types of files will be written out. The directions are pretty clear, but basically this will determine which of the format libraries are included.
? Which formats do you want to use? PDF, EPUB, DOCX, HTML ? The name of the theme package to use: (@mfgames-writing/clean-theme)
I only have three themes right now, two are “generic” and one I prefer just for Fedran stories (but obviously don't prevent anyone from using it). Later, I hope to have more themes, but basically the choices are
@mfgames-writing/greekil-theme (a Gentium-based theme).
? The name of the theme package to use: @mfgames-writing/clean-theme Creating initial Git repository Initialized empty Git repository in fedran/nor-curse-be-found/.git/ Running custom Git command I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself. create package.json force ../../../.yo-rc-global.json force .yo-rc.json create release.config.js create commitlint.config.js create .husky/commit-msg create .gitlab-ci.yml create .tool-versions create publication.yaml create yarn.lock create README.md create .gitignore create chapters/chapter-01.md Changes to package.json were detected. Running yarn install for you to install the required dependencies.
With that, it just producing a wall of tell as it creates all the files, hooks up everything, and basically gets my entire project ready, including a sample first chapter, a really basic
README.md, and a Git repository ready to push.
$ ls -a . .git node_modules README.md .. .gitignore package.json release.config.js chapters .gitlab-ci.yml package-lock.json .yo-rc.json commitlint.config.js .husky publication.yaml
Right off the bat, I can build the files and see the output.
$ yarn build:pdf ... lots of output $ ls *.pdf nor-curse-be-found-0.0.1.pdf $ yarn build ... huge amounts of output $ ls nor-curse-be-found* nor-curse-be-found-0.0.1.docx nor-curse-be-found-0.0.1.epub nor-curse-be-found-0.0.1.html nor-curse-be-found-0.0.1.pdf
I'm also ready to push up to Gitlab.
$ git remote add origin email@example.com:fedran/nor-curse-be-found.git $ git push --tags $ git push -u origin main
Hopping over to Gitlab, I can see it building immediately.
As soon as it is done, it uploads the output as an artifact.
I can then click on the browse (or download) button on the right.
Browsing lets me see a list of all the outputs that I've added.
And I can even click on the PDF to preview it.