The Clockwork Mind
...armed with the knowledge you get by simply paying attention...

Case matters in weird ways

Writing this down for my own edification, even though everyone else probably knows it:

When you checkout a branch in a Windows git repository that matches a branch name on the GitHub remote, but the case in the branch name is different, Windows doesn’t care and will happily give you the correct branch.

However, the git client doesn’t automatically match that branch with the one on the remote, so you’ll likely get messages about needing to set up the remote.

When you do, check the case between your local and GitHub. There doesn’t appear to be any issues with doing another checkout with the correct case in Windows, and magically you will be linked to the GitHub branch again.

Every change breaks someone's workflow

That’s the first line of The Windows Update, a story in The Daily WTF. Punchline: Sometimes you can fix a user’s problem by doing something that seems useless.

As a developer, the article is funny. As a developer on legacy systems, the article is also profound. I don’t know how often we’ve fixed something that was obviously wrong for years and ended up with upset users because their workaround had become part of their workflow.

You can’t do much about the icons changing during a Windows upgrade, but you can familiarize yourself with how people use your applications and try to anticipate problem areas. Consider that no one wants to work with a bad interface or an inefficient workflow, but if they’ve been dealing with it for years you need to treat any improvements as seriously as you would any new feature. Possibly more.

At the end of the day, you can judge yourself by how elegant or efficient your systems are, but everyone else will judge you based on whether your work brings about positive emotions.

Fixing skipped updates in JIRA Agile

Just a quick post to get a solution out there for a weird problem that apparently only I’ve had.

Over the weekend, I worked on the long-overdue production JIRA upgrade at my work. (Seriously, we were three major versions behind. By the time I became the admin, we were all afraid to touch it for fear of losing our precious workflows.) I had tested the upgrade process on another server, taking that one from 5.2.7 to 7.12 fairly smoothly, except for when it became apparent our test VM needed more than 2GB of RAM. So, forewarned about add-on incompatibilities and upgrade paths, I was reasonably confident I could handle any upgrade weirdness.

What I wasn’t expecting was for JIRA to refuse to recognize a step in the upgrade process, meaning the carefully planned 5.2.7 to 6.4 to 7.0 to 7.12 path left some pieces behind when the 6.4 upgrade didn’t register or run some of the upgrade steps, even though it appeared to be working when tested. Specifically, the Greenhopper/JIRA Agile/JIRA Software transition only about half worked.

The Symptom: We found our Agile boards had disappeared in 7.12.

The Cause: The JIRA Software application wasn’t starting up due to failed upgrades.

The Log Entry:

2019-08-11 10:55:54,059 JIRA-Bootstrap ERROR      [c.a.s.core.upgrade.PluginUpgrader] Upgrade failed: This version of JIRA Agile requires a minimum build number of #45 in order to proceed. The latest upgrade task to have been successfully completed is #39. You must uninstall this version of JIRA Agile and attempt an upgrade via an intermediary version.

The Reason: Atlassian stopped providing cumulative updates from the beginning of time in Greenhopper plugin 7.1. The 7.12 version of Greenhopper could only start the upgrade process at step 45. We were at step 39.

Suggested Solution: Replace the Greenhopper plugin with the last version that had all cumulative updates, 7.0.11. Restart the server and watch the updates fly. Very reasonable. Easy to find on the Atlassian site.

Result: Failure.

The Symptom: No upgrades. No boards.

The Cause: JIRA Agile never starts up to try to upgrade anything.

The Log Entry:

'com.pyxis.greenhopper.jira' - 'JIRA Agile'  failed to load.
    		Cannot start plugin: com.pyxis.greenhopper.jira
    			Unresolved constraint in bundle com.pyxis.greenhopper.jira [185]: Unable to resolve 185.0: missing requirement [185.0] osgi.wiring.package; (&(osgi.wiring.package=net.java.ao)(version>=0.9.0)(!(version>=2.0.0)))

Suggested Solution: None. That’s weird.

Desire: Not to roll back to the beginning and disrupt all of the users that don’t need the Agile Boards.

Next Step: Digging through documentation and muttering.

The Find: JIRA’s OSGI Browser

The Clue: The OSGI browser made one thing obvious. JIRA 7.12 uses net.java.ao 2.0.0, making that part of the log entry very meaningful.

The Question: What happens if I grab the last version of net.java.ao from the JIRA upgrade backups and temporarily replace the bundled plugin, while running Greenhopper 7.0.11? (Always say yes to backups.)

Result: Me watching the build number tick up one by one in the database. Yes!

Cleanup: Replace the replaced files with their correct versions, restart, and watch the build numbers hit 51. Reindex, and we’re golden.

Lessons Learned: Update JIRA often, and always pay attention to log files.

RIP, Joe Armstrong

Sad news today. Joe Armstrong, co-creator of the Erlang programming language, passed away.

I never met him, but I read his writings in books, Twitter, and his blog, and he always struck me as a person who knew how to be an older programmer. He was constantly teaching people and learning new things, behaving with grace, dignity, and humor, always curious…this is the model I want to follow.

It’s tempting to say the world got a little darker today, but instead we’ll remember how Joe Armstrong’s life made it a little brighter.

For the curious, a collection of links:

Calling Erlang from Elixir

Lately I’ve been working with Elixir, a new-ish language based on the Erlang VM (BEAM). Elixir is designed to take advantage of Erlang’s parallel processing power while presenting the developer with less intimidating syntax.

I’m not going to spend any time here trying to sell Elixir. Elixir-lang.org does that well, and much like with Ruby on Rails, Elixir has the Phoenix Framework leading the way to greater adoption. I will say I got it right off the bat better than any other functional language I’ve seen.

I’ve been doing programming exercises from Exercism.io, which is a great resource for a lot of different languages. While doing the Gigasecond exercise, I had to drop down into the Erlang weeds for some time/date functionality. (Erlang and Elixir relate much as Java and Jython do.)

This exercise calculates the date one billion seconds after a given date. While there might be some Elixir-native libraries that can do this sort of calculation by now, I decided to use Erlang’s Calendar module to transform the data.

def from({year, month, day}) do
  :calendar.datetime_to_gregorian_seconds({ {year, month, day}, {0, 0, 0} })
  |> + 1000000000
  |> :calendar.gregorian_seconds_to_datetime
  |> elem(0)
end

Going through it step-by-step:

The function is called from/1 – defined by the test module Exercism supplied – and accepts a tuple as an argument with the year, month, and day.

(You’ll see a lot of function/n notation in Elixir. It means the function takes n arguments. You can have functions with the same name that take different numbers of arguments…but it’s more complex than that. Elixir does pattern matching to determine which function to call. We’ll talk about that in the future.)

In order to call an Erlang function, you have to call its module using the syntax for an atom, an Elixir constant with a value of itself. In this case, calling the Calendar module is done with :calendar, and invoking the function is through the oft-used dot notation.

We’ll get a new value from the passed-in date – Elixir variables are generally intended to be immutable – by passing the date into the :calendar.datetime_to_gregorian_seconds/1 function, which should return the number of seconds since year 0.

In this case, Erlang’s :calendar.datetime_to_gregorian_seconds/1 function takes one argument, a datetime, which is represented as a tuple consisting of a date tuple {year, month, day} and time tuple {hour, minute, second}. We only care about the date, and not the time, so we pass in {0, 0, 0} as the time.

Next, we pipe the return value of the Erlang function, using the pipe operator |>, to the next step, which returns a new value with 1000000000 added to the previous value. So, the number of seconds since year 0 plus 1 billion. And yes, the pipe operator is very similar to the Unix version.

Passing that new value to another function in the Erlang calendar module: :calendar.gregorian_seconds_to_datetime/1, which takes in an integer value of seconds and converts it back to a datetime calculated from year 0.

We get back a datetime – which, remember, is a tuple of a date tuple and time tuple – but we don’t care about the time. So, we use elem/2 to get back the 0th element of the datetime tuple.

But wait? elem/2? Elem takes a tuple as the first argument, and the index position as the second. Since we are piping the datetime tuple into the elem/2 function, the tuple ends up as the first argument even when not explicitly written out, and the supplied 0 becomes the second argument.

What we get back is the date tuple, and since that is the last value before the end, from/1 returns that date tuple to the calling function.

I mostly wrote this out to remind myself that Erlang modules are available as atoms in Elixir, but I like it as an example of piping through a series of data transformations as well.

I found this very helpful: Calling Erlang code from Elixir: a tale of hubris, strings, and how to read the documentation.

The nice thing about Elixir is that there are a few other ways to do this same thing, and as near as I can tell, they all work about as efficiently. So, you can write code in the way that you find easiest to read.

Stay tuned for more adventures in Elixir.