Wednesday, 4 December 2013

Missing HOMEDRIVE and HOMEPATH Variables Under Jenkins

TL;DR - If you’re looking for the reason why the variables are AWOL then I can’t help, but if you want to know what I tried and how I worked around my specific problem, then read on…

Back when we first set up Jenkins I soon noticed that all the files in the workspace had LF style line endings instead of CR/LF as you would expect on Windows; this was despite following the advice in the often-cited GitHub page to set things up correctly. I’d managed to live with it to date but now we were suddenly involved in a tactical project and were deploying to staging & production where trying to edit application .config files on servers that only had Notepad was like pulling teeth. I really needed to sort it out once and for all.

1st Attempt

The last time I tried to fix this I logged into the Jenkins build server, fired up a command prompt (as the Jenkins user account using RUNAS) and then executed “git config --global core.autocrlf true”. For good measure I also wiped the Jenkins workspace to ensure that I’d get a freshly cloned repo when the build job next ran. It didn’t work and I’d already put a few hours into the problem so had to put my spade down and go do some work instead.

2nd Attempt

Coming to the problem once again all fresh and reinvigorated some months later I surmised that the problem must be down to the Jenkins build account not picking up the --global scoped setting. After a quick recap on the differences between --system, --global and --local and where the files were stored, my next guess was that the account might be being used without a profile. So I checked again, this time using the /noprofile switch with RUNAS:-

C:\> runas /noprofile /user:DOMAIN\BuildAccount cmd.exe
<new command prompt>
E:\Jenkins> git config --config core.autocrlf

The setting was exactly as I had left it last time. The next stop was to inject an extra build step into the Jenkins job and see what it thought was going on. For good measure I dumped all the different scoped values to see what exactly was set and where:-

E:\Jenkins> C:\Program Files (x86)\git\bin\git.exe config --local core.autocrlf
E:\Jenkins> C:\Program Files (x86)\git\bin\git.exe config --global core.autocrlf
E:\Jenkins> C:\Program Files (x86)\git\bin\git.exe config --system core.autocrlf

Suspicion confirmed - the Jenkins job doesn’t see the setting. But why?

I started reading a little more closely about where the .gitconfig file would be picked up from and used “git config --global --edit” to see what path the editor [1] had loaded the file from. Sure enough, from my command prompt it loaded the correct file, although the HOMEDRIVE was set to C: and HOMEPATH was set to \Windows\system32 which seemed a little odd. The USERPROFILE variable on the other hand pointed correctly to the profile folder, not that it’s used by Git, but it was a useful check.

So I decided to just go ahead and set the autocrlf setting via the Jenkins job, hoping that at least it would stick even if I didn’t know at that moment where the setting would end up. To my surprise I got the following weird error:-

E:\Jenkins> "c:\Program Files (x86)\Git\bin\git.exe" config --global core.autocrlf true
error: could not lock config file (null)/(null)/.gitconfig: No such file or directory

I didn’t twig at first what the error in the path was telling me so naturally I Googled it. I got a hit that was related to Jenkins (JENKINS-19249) and was quite excited. When I read the issue I found that it was a similar problem, superficially, but there were no comments; not even so much as a “me too!” to at least show me I wasn’t the only one. I did a bit more Googling about how the path to the --global .gitconfig file is derived and it dawned on me what might be happening, so I dump out all the environment variables the Jenkins job sees with a call to SET.

Guess what - the HOMEDRIVE and HOMEPATH variables are not set. The way Git forms the path is with %HOMEDRIVE%/%HOMEPATH%. In C/C++ if you printf(“%s”, NULL); by accident you’ll often see the value “(null)” instead of it crashing [2] - hence there is one “(null)” for the HOMEDRIVE and another “(null)” for the HOMEPATH. For what it’s worth the USERPROFILE variable was still set correctly.

Solving My Problem

Ultimately I just wanted the line endings to be correct and so I took the rather heavy handed approach and used “git config --system core.autocrlf true” from an elevated command prompt as it meant altering the gitconfig file in the C:\Program Files (x86)\git\etc folder. Actually I forgot to use an elevated command prompt first time around and the file-system redirection added back in Vista kicked in and it wrote to a per-user virtualised version of the gitconfig file instead, doh!

I don’t particularly like this solution but I’m modifying the build server which is a carefully controlled environment anyway and it’s highly unlikely to be used for mixing-and-matching Git repos with different line endings.

But Why?

As I mentioned right back at the very start - I don’t know why the variables aren’t set. At first I assumed it might have been down to the way the user profile might have been loaded as Jenkins runs as a service, but I couldn’t find anything in the MSDN Knowledge Base that suggested why this might happen.

Failing that it might be a Jenkins issue. It’s a CI server and so perhaps this is done on purpose to ensure builds are user agnostic or something. If that was the case though I’d expect there to be a very popular StackOverflow post asking why their variables have been silently nobbled, but I didn’t stumble across one.

As always, once you know the answer, Googling for it is so much easier. As a Jenkins newbie too I’ll probably find out what’s really going on as I get into it more.


[1] Watch out for this as it’ll use VI as the editor by default. Luckily the one key sequence I can still remember after 25 years is to how exit it!

[2] I’m guessing this behaviour isn’t defined by the C/C++ standard but a common implementation choice?