semantic-release2024-03-27T17:39:22Zhttps://d.moonfire.us/tags/semantic-release/D. MoonfireCreative Commons Attribution-NonCommercial-ShareAlike 4.0 Internationalgenerator-mfgames-nix-project v1.4.1 and generator-mfgames-writing 1.0.02021-12-19T06:00:00Zhttps://d.moonfire.us/blog/2021/12/19/yeoman-generators/A new Yeoman generator and a refresh on a second one.
<p>I create a lot of projects. I think between Github and Gitlab, I have a few hundred now. Much if it is because I started pushing toward strictly being one story per repository (OSPR). This means I have a lot of common patterns that not only do I have to set up but also maintain as my skills and knowledge evolve. The problem comes when I figure out a new pattern, I have to update a couple hundred repositories if I want to keep them consistent. Needless to say, this is one drawback of a monorepo but there are a lot of advantages of OSPR.</p>
<p>A lot of my inspiration comes from Larry Wall's Three Virtues of a Great Programmer:</p>
<ol>
<li>Laziness: The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote so you don't have to answer so many questions about it.</li>
<li>Impatience: The anger you feel when the computer is being lazy. This makes you write programs that don't just react to your needs, but actually anticipate them. Or at least pretend to.</li>
<li>Hubris: The quality that makes you write (and maintain) programs that other people won't want to say bad things about.</li>
</ol>
<p>This means, instead of manually doing it, I want to find a nice automated way (virtue one) that I can script and have it run across everything at once (virtue two).</p>
<p>Previously, I had used <a href="https://yeoman.io/">Yeoman</a> but I was wondering if I needed to break out of that to work on a “me-specific” CLI tool. That line of thought came down because so many of the tools I'm using all package their own custom CLIs. While I do enjoy writing a good CLI, I don't want to write one if I can't.</p>
<p>In the process of finding an alternative, I finally understood how to compose Yeoman generators together. That means I could create a generic project generator and then layer the <a href="/tags/mfgames-writing/">writing</a> one on top of it.</p>
<p>At the same time, I could migrate away from <a href="/tags/asdf/">asdf</a> and onto my preferred infrastructure du jour, <a href="/tags/nix/">Nix</a>.</p>
<p><em>Side Note: Let me be honest, I'm constantly evolving my structures and they change as I learn new tools and my processes change with me. I won't be on Nix forever, but right now, Nix works out very nicely for what I want it to do.</em></p>
<p>This weekend I pulled something together and actually got a working version by tonight. This involves three projects:</p>
<ul>
<li><a href="https://gitlab.com/mfgames-writing/mfgames-writing-js/">mfgames-writing</a>: My publishing framework for Markdown and YAML files.</li>
<li><a href="https://gitlab.com/mfgames-js/generator-mfgames-nix-project">generator-mfgames-nix-project</a>: A Yeoman generator for creating a basic project layout.</li>
<li><a href="https://gitlab.com/mfgames-writing/generator-mfgames-writing-js">generator-mfgames-writing</a>: A Yeoman generator for setting up a writing project.</li>
</ul>
<h1>generator-mfgames-nix-project v1.4.1</h1>
<p>I pulled out the basic project stuff from <code>generator-mfgames-writing</code> and made it a dedicated generator while improving it. It still asks a bunch of questions, but mainly it does the following:</p>
<ul>
<li>Set up <a href="/tags/semantic-release/">semantic-release</a> to handle non-romantic releases. “Non-romantic” meaning automated and every time I push up to <code>main</code>.</li>
<li>Set up <a href="/tags/conventional-commits/">Conventional Commits</a> to drive the semantic release process. This is why my check-ins start with <code>feat:</code>, <code>fix:</code>, or something other tag. That way, I can just focus on a given commit being a new feature, fixing something, or breaking everything.</li>
<li>Set up <a href="/tags/commitlint/">commitlint</a> to make sure the commits are properly handled for the semantic releasing.</li>
<li>Set up <a href="/tags/husky/">Husky</a> to make sure commitlint is automatically handled.</li>
<li>Configure <a href="/tags/prettier/">Prettier</a> to make everything pretty and normalized. This mostly helps if someone ever contributes, but I like things being consistent and neat. While I disagree with Prettier on many things, it does a fantastic job.</li>
<li>Set up a <a href="/tags/nix/">Nix flake</a> to represent all the build tools needed to build the project.</li>
<li>Set up <a href="/tags/direnv/">direnv</a> to automatically ‘enter’ the environment when you go into the directory.</li>
<li>Write out the files needed to run <a href="/tags/gitlab/">Gitlab's CI/CD</a> on push and generate all the files and automatically release.</li>
<li>Set up <a href="/tags/editorconfig/">EditorConfig</a> for normalized formatting.</li>
</ul>
<p>That's a lot of work and it was getting tedious as I migrated/created new projects over the last month or so as I figured out the pattern. I like it, though it is opinionated. Previously, I hated that word, but this is honestly how <em>I</em> work and I think it's a reasonable pattern for any writing or coding project.</p>
<p>Running it is pretty simple:</p>
<pre><code>$ npm install -g generator-mfgames-nix-project yo
$ mkdir name-of-project
$ cd name-of-project
$ yo mfgames-nix-project
... lots of prompting
</code></pre>
<p>Overall, I like the resulting project and I'm going to be testing it fairly heavily in the new few weeks.</p>
<h1>generator-mfgames-writing v1.0.0</h1>
<p>Most of the intial work for <code>generator-mfgames-nix-project</code> came from <code>generator-mfgames-writing</code>, but I ended up also expanding that over the weekend to ask better questions about theme, author information, and the release process. It works the same as the project generator.</p>
<pre><code>$ npm install -g generator-mfgames-writing yo
$ mkdir name-of-project
$ cd name-of-project
$ yo mfgames-writing
... a different set of questions
$ npm run build
... lots of noise
$ ls *.epub *.pdf
name-of-project-0.0.1.epub
name-of-project-0.0.1.pdf
</code></pre>
<p>This one bypasses some questions that the project one does (build, test, and release questions) and adds some other ones (theme, configuration file format, title of the work), but otherwise works the same as the other. The end result is something you should be able to build and get the output immediately.</p>
<p>I honestly think that is really cool.</p>
<h1>mfgames-writing</h1>
<p>There were a bunch of versions with this, but I needed to tweak some processes because I found some bugs in these tools while writing the generators. Sadly, a “simple” fix of moving <code>rimraf</code> from a developer dependency to a normal dependency required me to upgrade <a href="/tags/typescript/">TypeScript</a> to the last version and accept a wall of warnings because some of the linting tools don't like the latest version.</p>
semantic-release-nuget v1.1.02021-09-04T05:00:00Zhttps://d.moonfire.us/blog/2021/09/04/semantic-release-nuget/To pair with last's week utility, I finished up the first version of semantic-release-nuget and used it.
<p>Last week, I wrote <a href="/blog/2021/08/29/semantic-release-dotnet/">semantic-release-dotnet</a> which was a <a href="https://semantic-release.gitbook.io/semantic-release/">semantic-release</a> plugin to automatically set the version to the appropriate one before building.</p>
<p>One of the key parts missing from the normal .NET development cycle was also publishing the packages. I decided to break that into a separate plugin because I have a number of places where I don't want to publish but I do want something versioned (internal projects and customized deployments). So, in the essence of the <a href="https://en.wikipedia.org/wiki/Single-responsibility_principle">Single Responsibility Principle</a>, I created a second utility which does one thing: build and publish NuGet packages.</p>
<p>Introducing <a href="https://www.npmjs.com/package/semantic-release-nuget">semantic-release-nuget</a>. It doesn't have a lot of configurations, but the documentation covers all of them. Basically, it does one thing.</p>
<p>I mostly tested with <a href="/tags/mfgames-locking/">MfGames.Locking</a>, my CIL library for some thread-locking patterns, because I'm the process of carving out <a href="/tags/gallium/">Gallium</a> and <a href="/tags/nitride/">Nitride</a> into their own packages. I'm just not sure where to put them, so they are probably going in my <a href="https://gitlab.com/mfgames-cil/">Gitlab</a> organization until I find a “better” organization/home.</p>
semantic-release-dotnet v1.0.02021-08-29T05:00:00Zhttps://d.moonfire.us/blog/2021/08/29/semantic-release-dotnet/I wrote a little utility for `semantic-release` to handle versioning of .NET projects.
<p>I've pretty much <a href="/tags/semantic-release/">embraced</a> <a href="https://semantic-release.gitbook.io/semantic-release/">semantic-release</a> for most of my release processes, both for books and software. It has gone pretty smoothly, but occasionally I find myself hacking it to get something working. One of the more recent ones (and something that is going to affect me in the near future) is the interaction between semantic-release and .NET projects.</p>
<p>These are two very different systems, including packaging, and there are plenty of NuGet packages to do semantic-release as a .NET project, but I use Node for my package management, thanks to <a href="https://typicode.github.io/husky/#/">Husky</a>. So, after a year or so of writing up little ad-hoc programs, I decided to write a formal package.</p>
<p>Introducing <a href="https://gitlab.com/dmoonfire/semantic-release-dotnet/">semantic-release-dotnet</a>. It is basically a “prepare” plugin for <code>semantic-release</code> and writes out the <code><Version/></code> element into <code>Directory.Build.props</code> or a <code>.csproj</code> file. It uses minimatch (via glob) to update the files, so you can edit the project files directly and it will write out certain files if they are missing.</p>
generator-mfgames-writing v0.3.22021-07-31T05:00:00Zhttps://d.moonfire.us/blog/2021/07/31/generator-mfgames-writing/Today, I finished putting the final touches on additional features for `generator-mfgames-writing`, my scaffolding generator for Yeoman that lets me set up writing projects quickly.
<p>Related to process of writing from <a href="/blog/2021/07/27/commitlint-gitlab-ci/">a few days ago</a>, I've been working on another tool that I find myself using fairly often: <a href="https://yeoman.io/">Yeoman</a>. 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 <a href="https://gitlab.com/">Gitlab's CI</a> which I also use heavily (and why I wrote <code>commitlint-gitlab-ci</code>).</p>
<p>Like many of the writing tools, this is based on Javascript (but not Typescript this time).</p>
<pre><code class="language-shell">$ sudo npm install -g yo generator-mfgames-writing
</code></pre>
<p>The first thing it does is ask a number of questions about the project. I'll use <a href="https://fedran.com/nor-curse-be-found/">Nor Curse Be Found</a>, my Beauty and the Beast sequel, as an example.</p>
<pre><code class="language-shell">$ mkdir nor-curse-be-found
$ cd nor-curse-be-found
$ yo mfgames-writing
? The title for your novel or story: (Nor Curse Be Found)
</code></pre>
<p>All of my writing projects have a unique “slug” to describe them. This is used for the website's URL, such as <a href="https://fedran.com/nor-curse-be-found">https://fedran.com/nor-curse-be-found</a>, and also the name of the output (<code>dmoonfire-nor-curse-be-found-0.0.1.epub</code>). 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.</p>
<pre><code>? The title for your novel or story: Nor Curse Be Found
? The slug based on the title: (nor-curse-be-found)
</code></pre>
<p>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 <a href="http://www.sectorgeneral.com/shortstories/fasttrip.html">Fast Trip</a> 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 <em>I</em> work.</p>
<p>There is also a question about bylines, but then we start to get into the technical bits.</p>
<pre><code>? 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)
</code></pre>
<p>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.</p>
<p>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 <a href="https://typicode.github.io/husky/#/">husky</a>, <a href="https://www.conventionalcommits.org/en/">semantic-release</a>, <a href="https://www.conventionalcommits.org/en/">conventional-commits</a>, and <a href="https://commitlint.js.org/#/">commitlint</a>. (I'm working on updating my <a href="https://www.npmjs.com/package/generator-mfgames-writing">Yeoman generator</a> to make this a lot easier, but that will be later this week.)</p>
<pre><code>? Do you want to create the Git repository? Yes
? Any command to run after `git init`? set-moonfire-git-user
</code></pre>
<p>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 <em>not</em> have a global <code>user.name</code> and <code>user.email</code> 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 (<code>set-moonfire-git-user</code> for writing, <code>set-mfgames-git-user</code> 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.</p>
<pre><code>? 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
</code></pre>
<p>Now, the initial commit is a messy one and a philosophical difference between me and the Semantic Release folks. They have <a href="https://github.com/semantic-release/semantic-release/blob/caribou/docs/support/FAQ.md#can-i-set-the-initial-release-version-of-my-package-to-001">their reasons</a> to hard-code the value to “1.0.0” but I'm specifically start this process <em>before</em> a release, so I feel it should be “0.0.1” because I want that tracking as soon as possible.</p>
<p>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.</p>
<p>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.</p>
<p>So, <em>Fast Trip</em> so I jump through a few hoops. It should also be clear why I needed that command to run after <code>git init</code> to set my user, otherwise this part would blow up with a “user not set” message.</p>
<pre><code>? What version do you want to start? 0.0.1
? Which package manager do you use? (Use arrow keys)
❯ NPM
Yarn
</code></pre>
<p>Moving away from Git and into the build process, the first question is package management. I have a love/hate relationships with <code>npm</code>. The command works great, but I've been struggling with it (more on than off lately) so I've been using <a href="https://yarnpkg.com/">yarn</a> 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.</p>
<pre><code>? 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
</code></pre>
<p>The next three are to set up the common functionality I use with releases. They set up <a href="https://semantic-release.gitbook.io/semantic-release/">semantic-release</a>, <a href="https://www.conventionalcommits.org/en/">conventional commits</a>, <a href="https://commitlint.js.org/#/">commitlint</a>, and <a href="https://typicode.github.io/husky/#/">Husky</a> 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#.</p>
<pre><code>? Do you want to use Husky? Yes
? Do you want to use CI/CD? Yes
? Do you use Gitlab for your CI/CD? Yes
</code></pre>
<p>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.</p>
<pre><code>? Do you use Gitlab for your CI/CD? Yes
? Do you use asdf? No
</code></pre>
<p>This is another tool I use, <a href="https://github.com/asdf-vm/asdf">asdf</a>. This was also something that pretty much started last year, but one of the goals of <a href="https://gitlab.com/mfgames-writing/mfgames-writing-js/">mfgames-writing</a> 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.</p>
<p>This is where <code>asdf</code> comes in. It puts a small file (<code>.tool-versions</code>) which says which version of Node, Yarn, C#, Python, whatever and will automatically change to those versions when I <code>cd</code> into the directory. It basically rolls in all the functionality of <code>nvm</code>, <code>virtualenv</code>, 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 <code>.tool-versions</code> into the resulting directory.</p>
<pre><code>? 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
</code></pre>
<p>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.</p>
<pre><code>? Which formats do you want to use? PDF, EPUB, DOCX, HTML
? The name of the theme package to use: (@mfgames-writing/clean-theme)
</code></pre>
<p>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 <code>@mfgames-writing/clean-theme</code> and <code>@mfgames-writing/greekil-theme</code> (a Gentium-based theme).</p>
<pre><code>? 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.
</code></pre>
<p>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 <code>README.md</code>, and a Git repository ready to push.</p>
<pre><code class="language-shell">$ 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
</code></pre>
<p>Right off the bat, I can build the files and see the output.</p>
<pre><code class="language-shell">$ 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
</code></pre>
<p>I'm also ready to push up to Gitlab.</p>
<pre><code class="language-shell">$ git remote add origin git@gitlab.com:fedran/nor-curse-be-found.git
$ git push --tags
$ git push -u origin main
</code></pre>
<p>Hopping over to Gitlab, I can see it building immediately.</p>
<p><img src="./initial-push.png" height="256" style="margin-top:1em;" alt="Initial CI Run" /></p>
<p>As soon as it is done, it uploads the output as an artifact.</p>
<p><img src="./gitlab-log.png" height="256" style="margin-top:1em;" alt="End of Log" /></p>
<p>I can then click on the browse (or download) button on the right.</p>
<p><img src="./gitlab-download.png" height="256" style="margin-top:1em;" alt="Browse and Download Buttons" /></p>
<p>Browsing lets me see a list of all the outputs that I've added.</p>
<p><img src="./gitlab-browse.png" height="256" style="margin-top:1em;" alt="Browse Artifacts" /></p>
<p>And I can even click on the PDF to preview it.</p>
<p><img src="./gitlab-pdf.png" height="256" style="margin-top:1em;" alt="View PDF" /></p>
commitlint-gitlab-ci v0.0.42021-07-27T05:00:00Zhttps://d.moonfire.us/blog/2021/07/27/commitlint-gitlab-ci/Since I'm usually creating a new Git repo (about 3/month) and commitlint is one of the first things I set up, I ended up writing a little NPM utility to solve a bug that was causing me problems on Gitlab.
<p>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 <a href="https://typicode.github.io/husky/#/">husky</a>, <a href="https://semantic-release.gitbook.io/semantic-release/">semantic-release</a>, <a href="https://www.conventionalcommits.org/en/">conventional-commits</a>, and <a href="https://commitlint.js.org/#/">commitlint</a>. (I'm working on updating my <a href="https://www.npmjs.com/package/generator-mfgames-writing">Yeoman generator</a> to make this a lot easier, but that will be later this week.)</p>
<p>Sadly, my Git host of choice, <a href="https://gitlab.com/">Gitlab</a>, has a little problem with <code>commitlint</code>. It blows up on the first few commits which lead to me reporting <a href="https://github.com/conventional-changelog/commitlint/issues/885">this</a> issue.</p>
<p>Since I'm usually creating a number of new Git repositories in a month and <code>commitlint</code> is one of the first things I set up, I ended up writing <a href="https://www.npmjs.com/package/commitlint-gitlab-ci">a little NPM utility</a> to handle the bug for me since the original package (rightfully, I feel) don't want to add Gitlab-specific code:</p>
<pre><code>npx commitlint-gitlab-ci -x @commitlint/config-conventional
</code></pre>
<p>It seems to work fairly well for me, I've been messing with it for a little while and haven't had too many complaints.</p>