Article 76XPS Forking an open source project

Forking an open source project

by
jonskeet
from Jon Skeet's coding blog on (#76XPS)
Story ImageIntroduction and motivation

Some of the projects I work on - notably V-Drum Explorer and At Your Service - use MIDI to interact with hardware devices.

I use a couple of packages for this depending on the nature of the interaction, but the one I use most often is managed-midi, which currently has its source of truth" at https://github.com/atsushieno/managed-midi. This is a cross-platform library using the NuGet bait-and-switch" approach to allow a single package with multiple implementations within it. Note that unlike the use of the term in commerce etc, bait-and-switch" is not a derogatory term when it comes to NuGet packages. There's no deception involved. (Do let me know if you have a more recent source of documentation for bait-and-switch; the current link is from 2014, back in the dark days of PCL.)

A few months ago, managed-midi stopped working properly on Windows 11 installations that were fully updated, due to aspects of Windows MIDI Services either changing or being introduced for the first time. (I haven't dived into exactly what the change involved.) This
has broken other apps as well as the managed-midi library.

The author of managed-midi, Atsushi Eno, has accepted patches which I believe will fix the issue - but he doesn't have any plans to work on the .NET stack any more, so it's unlikely that there'll be a new release of managed-midi without someone else getting involved. (Note: I've assumed he/him pronouns for Atsushi, but I'm happy to edit this blog post if that turns out to be wrong.)

And that's where I'm possibly going to come in. This blog post is going to lay out my current plans and outline a few alternatives. This helps me just in terms of making my thoughts concrete, as well as providing an opportunity for feedback. (There's no great way of getting feedback from all existing managed-midi users, but I'm hoping my readership can provide useful thoughts without necessarily having used the package directly.)

Possible forking and next steps

I've already created a fork of the repo, but just forking the repo itself doesn't signify much. The big questions are whether I'm going to publish a fork of the code to NuGet, and what happens after that.

I should state up-front that I've been in touch with Atsushi on BlueSky, and he's been very open and constructive. He's happy for a wholesale fork to happen, and he'd then expect to effectively redirect traffic to the fork. This is in no way one of those community bust-ups we sometimes read about.

Now, I'm not going to claim I have huge amounts of time to take on another open source project. Right now, while I'm between jobs, I have more time than normal, but I'm hoping and expecting to be employed again soonish, at which point I naturally expect the new job to take most of my focus. I'd like to get the project into a state where it's easy enough to update dependencies, publish new versions etc - but without spending a lot of time on new features etc.

Things get slightly difficult due to the current state of the project. It hasn't been worked on very significantly for quite a while (just the first page of the commit history goes back to October 2019). To be really clear, I'm not in any way attacking Atsushi for this. I've got plenty of open source projects I haven't updated for ages - I'm sure many of us have. I'm incredibly grateful to Atsushi for the updates that managed-midi has received over the years; it's been a very valuable library to me. However, it does mean that the solution doesn't even build for me after a simple clone.

Fork location

For most public projects, I like having a core" repo and then my own personal fork. For example, Noda Time has https://github.com/nodatime/nodatime, and I create branches within https://github.com/jskeet/nodatime for PRs. Given that there isn't already a suitable GitHub organization, I plan to stick with just my fork in https://github.com/jskeet/managed-midi.

Dropping some platforms

Several of the platforms being targeted are either out of date or at least being deprecated. I don't like the idea of trying to maintain code that I can't test, or which targets platforms or toolchains which are no longer supported. It's really easy to get into that situation, trying to avoid breaking existing users.

But if I'm creating a new NuGet package anyway, I don't have any existing users - even if the codebase itself is forked. Where there are platforms which are out of date, I'll bump them to more recent versions. Where there are platforms which are no longer supported at all (or are already deprecated) I'll drop them entirely - at least to start with.

At first glance, I think this means the following changes from the current targets:

  • netstandard2.0: keep; this is the bait" target
  • net45: update to net48; used as the implementation for .NET Framework
  • netcoreapp2.1: update to net10; used as the implementation for .NET
  • net6.0-ios: drop for now
  • MonoAndroid: drop for now
  • XamariniOS: drop for now
  • XamarinMac: drop for now
  • uap10.0: drop for now

Yes, this means dropping all mobile targets for now, as well as UWP/UAP. I may be able to provide mobile targets via MAUI in the future, but I need to investigate that further. I don't know much about the UWP/UAP world, or the Windows App SDK and WinUI. Again, I may be able to support WinUI in the future; it depends on time more than anything else.

In terms of the implementations of IMidiAccess available, I'm expecting to keep ALSA, WinMM and CoreMidiApi (dropping the latter if I can't easily test it on my Mac Mini) and drop UWP, RtMidiSharp and PortMidiSharp. The implementations I'm dropping all come with significant caveats about the level of testing etc. I can add them back in later on if there's demand, along with a good way of testing them. (I'm okay with running manual tests, even limited ones that just run against my drum kit, but I don't want to ship anything that I've never run at all.)

Modernizing the build

The current repository uses:

  • Shared project files (and projitems files) which I believe are still supported, but which I don't have much experience of. It's possible that I can use normal csproj files instead, but this will need further investigation. (If there's good support for shared project files now, that might still be the best approach.)
  • Old-style csproj files which I'll definitely want to modernize to SDK-style.
  • An old-style sln file, which I'll definitely want to modernize to slnx.
  • A nuspec file while I suspect may still be required; unclear as yet.
  • Some slnf files which I'd never even seen before. I'm hoping to be able to delete these for now, as I think they're more for developer convenience than anything else.

This will be a voyage of discovery of techniques I've never used, first understanding them at least as far as I need to, then modernizing where possible.

Naming

Currently the package is called managed-midi, but all the code is in namespaces rooted in Commons.Music.Midi. Some of the directory names don't match namespaces (e.g. code for Commons.Music.Midi.Alsa is in a subdirectory called alsaseq).

Using shared projects quite possibly makes it hard/infeasible to make namespaces entirely match folder structure, but I'd like to do what I can here.

My current plan is to use the named ManagedMidi as both the namespace root and the NuGet package name.

Benefits:

  • It's a more conventional .NET name (for both a package and a namespace)
  • It doesn't imply any relation to Apache Commons or any other commons" library
  • It's not used by anything else at the moment, as far as I can tell
  • Making the NuGet package name match the namespace makes it easier to determine dependencies at a glance
  • It makes it clear to all consumers that this is a new package; forcing work on users is a good way to get them to read documentation around migration (which I will obviously need to provide).

Downsides:

  • Changing the package name (as opposed to asking Atsushi to let me publish new code to the existing package) means users will need to become aware of the new package in some out-of-band way; they won't get prompted by Visual Studio, Renovate etc.
  • Changing the namespace means users are likely to need to change multiple source files. If I just changed the package name, keeping the namespace of Commons.Music.Midi, then migrating would probably be simpler.
API deprecations

There are various obsolete parts of the public API, most notably IMidiAccess which is being replaced by IMidiAccess2, with (I believe) a plan to then effectively rename IMidiAccess2 to IMidiAccess. (IMidiAccess2 is just IMidiAccess with one extra member.)

My plan is to initially get everything working without changing code other than in terms of the namespace... but then I think it makes sense to remove all the obsolete members. At the moment we're in a bit of an in-between state in terms of the public API, and I'd rather not do a full GA release in that state.

Which brings me on to...

Versioning

The current latest version of managed-midi is 1.10.1. My plan is to effectively ignore the current versioning, given that it's for a different package than the one I intend to release. While I could start my versioning at 1.11.0 or 2.0.0 (modulo prerelease signifiers) I think it'll be less confusing to just treat it as a brand new package. I'm therefore intending to create releases of:

  • 1.0.0-alpha.1 (etc): initial hardly-change-any-code (but do drop platforms) releases
  • 1.0.0-beta.1 (etc): breaking change to fix the stable API (so there'll just be a non-obsolete IMidiAccess which has the extra member currently in IMidiAccess2, and all the obsolete members will be removed)
  • 1.0.0: GA release once the betas look successful without reported issues

I'll probably leave things in 1.0.0-beta.x for a while, as I expect it to take some time for current managed-midi users to migrate, and they're bound to find issues that I haven't found with my limited usage.

Non-goals

I'm not intending to revisit the whole public API or overall design as part of this process. If I find myself itching to do so, I'll consider that for a v2.0 release, but I doubt that I'll have enough time really. This is a perfectly good package which already works, so I'm trying to keep the changes to a minimum while taking the opportunity to create a v1.0 without obsolete members.

Alternatives

All of the above plans have alternatives, mostly fairly obvious ones. I could attempt to support the current set of target frameworks. I could avoid changing the namespace and/or package name. I could keep the obsolete members. I could continue the versioning scheme from where it was.

Most of these either create more work before I can first get to a release, or more technical debt (in the form of supporting deprecated platforms etc, or continuing with the obsolete members).

That said, there are arguments that could be made in favour of those alternatives, and I'd rather hear them now than after I've made an unfortunate choice.

Conclusion

I don't know how typical this is of an open source fork event. I hope I'm doing the right thing by both Atsushi and the current users. Whether it'll go smoothly or not is hard to tell ahead of time - there's a lot about this (including MIDI itself) that I only have minimal experience of.

Please leave comments with any thoughts, particularly if you're already a user of managed-midi.

External Content
Source RSS or Atom Feed
Feed Location http://codeblog.jonskeet.uk/feed/
Feed Title Jon Skeet's coding blog
Feed Link https://codeblog.jonskeet.uk/
Reply 0 comments