More ranting on npm and the state of node libraries

2015-03-23

Yeesh!

All I friggen want to do is find out of a git managed project is clean, as in are their any modified files that need to be checked in or not. You'd think this would be easy. I can of course shell to git and parse stdout but here's hoping there's a library with more features in case I need them.

So I go looking by searching npmjs.org for "git".

First hit is the git module. Let's see, 990 downloads in the last week. Everyone must be using it so it must be good. Check the repo, No updates in 5 years. Maybe that means it's super stable but it more likely means it's abandoned. Well, 990 people downloading it it much be ok? All I need to do is call `git status` so lets try to do that. Hmmm, no docs!

Let's guess

var git = require('git');
var repo = new git.Repo('path/to/repo');
repo.status(function(err, data) {
  if (err) throw(err);
  console.log(data);
});

Run it. Something about missing ls_files. Check the tests. Apparently you need 40 lines of complex callbacks just to get a status. WTF!!!!!!!! DELETED

Okay next up git-wrapper.

It's gotten 1010 downloads in the last month. It must be good.

Check it out, it's just a 40 line wrapper around calling git. It's up to me to parse the output. Why would I use a library for that? It's really adding nothing AFAICT. DELETED

Next up git-control.

It claims to be an api that returns Promises/A for all functions. It's had 160 downloads this month. Try it. Again no docs

var git = require('git-control');
var repo = git.repo({ path: "/path/to/repo" });
repo.status().then(function(data) {
  console.log(data);
}.catch(function(err) {
  console.error(err);
});

Result? Missing "promised−io/promises". Check the package.json, "promised−io/promises" is only including that in dev. In other words this project NEVER WORKED FOR ANYONE! WTF!!!

Ok, submit a patch, manually install "promised−io" package. Try again.

Result? "catch is undefined". Apparently this is based on an outdated Promises/A? Or maybe I don't understand promises. According to MDN I sholud be able to do this

promiseReturningFunc().then(fn).then(fn).then(fn).catch(fn);

Which I've used in other implementations. Okay fine

var git = require('git-control');
var repo = git.repo({ path: "/path/to/repo" });
repo.status().then(function(data) {
  console.log(data);
}, function(err) {
  console.error(err);
});

result: "undefined"

No data is passed to the fulfilled function. Okay, dig through the code. It puts the data on the repo.info object. Wow, really? That's seems like a really poor design for an async library. I suppose it's valid for the moment you're in the callback?

So a next day the guy running git-control except my patch but points out his project abandoned and I should check out nodegit so...

Next up nodegit.

Check the repo, updated within 2 days. Apparently supported by github themselves so that's good news? Docs? Claims to have some. Try to use it. Ugh!!! Yea, docs need lots of work.

See the main sample which shows calling NodeGit.Repository.open(path). Ok, then there's a function `Repository#getStatusExt`. Fuck if I can tell what it's supposed to do. The docs just say it takes options. No descriptions about that the options do. It's a synchronous function, ok. Call it and see what it returns. Check the source, looks like returns an array of StatusEntry so I guess maybe if I check if there are zero of them it's a clean repo? Nope.

Maybe StatusList is itself a promise? But the docs say it's synchronous. Not clear at all.

Okay fine, let's dig through the unit tests. The status example doesn't call any of the Repository api calls it calls Node.Status.forEach. Really? How would you possibly guess calling Status.forEach is the correct function. I guess these people have never learned that part of designing a good API is making it easy to guess how to use it.

Okay so fine, let's copy their code. It declares an empty array and a callback that is apparently called for each entry. The sample just pushes each entry into the array. So, I guess if the array is empty the repo must be clean. Try it.

Nope: It's showing entries for all my repos, even ones I know are clean.

About 2 hours later I've finally cobbled together something that appears to work. The API returns info even about ignored files, they just have a flag, NodeGit.Status.STATUS.IGNORE. Of course the docs for status don't link the the enum for status, there's no example and search yields nothing. Yea, that was clear...NOT!

Sigh...

And you wonder why people re−write things themselves...


So this is probably obvious in hindsight? I've been programming lots of JavaScript. I'd really prefer to stick to one language. I'm using node.js and there's arguably no reason I can't make my build system in JavaScript as well ... except, pretty much all node code and libraries is written for making a web server. In the web server world, under JavaScript, everything needs to be asynchronous. You get an event, you do a little bit of work then setup for other events. So for example you can't just get the status of a bunch of files in git. You instead create requests and asynchronously ask for the status, you need to collect the results async, then work on the results async. Various ways to make async code easier to deal with have come up. The latest is Promises. I've been using Promises and they're pretty neat but also have various issues as well.

Anyway, TL;DR After 2 days struggling to use JavaScript make a build script I gave up and switched to python. In just a few hours I'm further long than 2 days of JavaScript. This is not really an ding against JavaScript but rather the fact that everything all the libraries are async. I don't need async anything in my case and so not having to deal with that is a huge win. If there were sync libraries for all that stuff it'd be easy.

To give an example of what I wanted to do

  1. Optionally inc the version number of a .NET lib
  2. Look up the version number of a .NET lib
  3. Build the .NET lib
  4. Copy the build results somewhere
  5. For each of 10 projects
    1. Check if the project is git clean (no outstanding uncommited changes)
  6. If any of the projects are not git clean abort
  7. For each of 10 projects
    1. Copy the build results into the project
    2. git add each copied file
    3. If the project is dirty
      1. commit the new files
      2. bump the project version
      3. commit the bump
      4. generate a package
      5. upload to github as a release

Trying to do that synchronously is enough of a headache. I'd really like to see it done in JavaScript by someone who thinks of themselves as an expert if just to see an example style to take inspiration from.

Comments
The VR Workspace
TWGL.js