Seed 2019-05-29
One thing I noticed while working on Dennis was that I was dividing the code into different components, roughly per-module. Some code is responsible for displaying graphics, some is a generic implementation of a high-level design pattern, some is for generating fake events for testing purposes, some code is for generating fake events for testing purposes. It's hard for me to come up with a precise count, but nearly half of the source files would have general utility for game programming, in that I could imagine using them to make a game in a different "genre" than roguelike. This makes me think of trying to use the same code in multiple distinct projects, and thus think "How would I like to do that?"
The standard dichotomy for this question of coordinating this kind of reusable code is "monorepo" vs "multirepo". I don't want to rehash the pros and cons as applied to either an abstract repository that exists for rhetorical purposes, or a specific closed-source repository; there's plenty of both of those rundowns on the internet, as well as some open-source examples.
So, in Dennis, right now, there are several modules that have no in-project dependencies. These are potential candidates for factoring out into their own projects. Some of them are project-specific and just happen to not import stuff, so they shouldn't be broken out. (And maybe this is an indicator that some of their interface should be broken out within the project? I'm not sure.)
But suppose I go "You know what, this vector code seems pretty generic, and I'd rather not copy it around"... I'd want to create a new project containing the vector code, and if it somehow has other code, remove that code. Make sure the new project has a different name, and a fresh semver, get it working. Then, add a dependency on the new project, and remove the old code, get it working, and cut a new release of the original project.
In an internet-approved monorepo, each project is a directory under the repository root, and near as I can tell, semver isn't really a thing in the relevant workflows. I'm not sure anyone's laid out any "here's how you multirepo" guides in contrast to the "here's how you monorepo" ones, but here's something that occurred to me... The biggest friction I've had, that projects like cookiecutter are meant to deal with, is in bringing a fresh repository from "nothing" to "valid configuration for building". Doing that is a pain, it has a lot of busywork, so I would say that creating fresh repositories is hard. But, in a distributed world, in a world of Mercurial and Pijul, Darcs, Fossil, Bazaar, and, yes, Git, if you have one repo in a valid configuration for building, you have as many repos in a valid configuration for building as you want. Suppose the workflow for making a new project isn't based on init, cp, or mkdir, but clone?
What I'm visualizing is a collection of projects that all derive from a common "seed" repo that represents a minimal project. The "seed" repo should be in the paths config for all other repositories. In the event of collaboration, the "default" url should be the shared remote repo; I haven't thought too hard yet about how collaboration should actually work. So, tasks involved in this layout:
- Fix bugs/add functionality: work within a single repo, cut a new version
- Factor out functionality into new project: clone source, rename/update, publish (probably to local index), add dependency to source and remove old code
- Add common tooling: update seed repo, and then, um... not sure when and how best to require repos to pull and merge. Do normal development on topic branches, only allow merge to master when tests are green, disallow merge to master when there's a diff with the seed repo, pull seed repo changes to topic branch and follow normal procedure for merging? Will need to take another look at this, that was all just spitballing.
(Maybe I want to have a "seed" branch that is kept in synch between all repos, and merged into master?) (I'm moderately tempted to see how much effort it would take to formalize this on top of Pijul. Maybe avoid the topic-branch stuff. Do this with Pijul, and there's a seed repo that holds the common set of patches. Maybe use branches kind of like, okay, there's master, and release. Pushes and development hit master, builds are from release, and hm, I haven't thought this all the way through.)
Since I'm thinking about this for personal projects that I might or might not open-source all or any of, I'm going to focus on stuff I can set up up on my laptop.
Okay, I looked into Pijul a little more, and here's what I think I want whatever script I write, working title "seed", to do:
- seed init: Create a Pijul repo with a default seed.toml file in it.
- seed fork <name>: In a seed repo, creates a clone of the current repo, configured to use the current repo as the seed. In a non-seed repo, creates a clone of the current repo, configured to use the same seed as the current repo. Question: how to distringuish between seed and non-seed? The TOML file? Note: Seed repo should have just "master" branch. Non-seed should have "master" and "release". Clones should clone "master" from source, not sure about "release". Note: New repo should be next to source repo, fail if name exists.
- seed pull: Pulls changes from the seed repo to "master".
- seed pr <patches>: Creates a pr branch with a unique name from "release", applies patches, runs tests. Need to figure out the rest of the flow.
- Some kind of command for actually cutting a release, which should create a tag in the release branch.
The high-level idea I have behind the configuration file is that, for a given task, such as "run tests", there should be a simple command to accomplish that.
Okay, this idea isn't all-the-way baked, but I think there's some potential in prototyping it. I'll give it a shot later.