Breaking Build Postmortem: Biting the line that feeds you
It was early one weekend morning and I was trying to integrate AppVeyor with my GitHub project. But there was one problem: my build was failing miserably on AppVeyor. Strangely, it built just fine on my machine; but on AppVeyor the test ToStringShouldReturnResourceKey
was failing a string comparison.
Investigating the failure
Fortunately I was using Shouldly, which produces very readable test output when your tests fail. See what you notice in the snippet below.
resource.ToString()
should be
"<data name="My_resource" xml:space="preserve">
<value>Banana's are quite good</value>
</data>"
but was
"<data name="My_resource" xml:space="preserve">
<value>Banana's are quite good</value>
</data>"
difference
Case Sensitive Comparison
Difference | | | | | | | | | | | | | | | | | | | | |
| \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/
Index | ... 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 ...
Expected Value | ... \n \s \s < v a l u e > B a n a n a ' s \s a r ...
Actual Value | ... \r \n \s \s < v a l u e > B a n a n a ' s \s a ...
Expected Code | ... 10 32 32 60 118 97 108 117 101 62 66 97 110 97 110 97 39 115 32 97 114 ...
Actual Code | ... 13 10 32 32 60 118 97 108 117 101 62 66 97 110 97 110 97 39 115 32 97
Yes, you spotted it. Shouldly is expecting a string that starts with ‘\n’, a line feed (LF, 10), whereas the actual value string starts with ‘\r\n’, a carriage return (CR, 13) followed by a line feed (CRLF). Typically, CRLF is the character sequence used for line endings in Windows, and LF is the end of line character used in Linux. So why would this test pass on my system, and fail on Appveyor?
The expected string is a multi-line C# verbatim string, while the actual string is created by the ToString()
method of XElement
. Buried as it is deep in the Windows and .NET ecosystem, XElement.ToString()
uses CRLF for its line breaks. Verbatim strings, on the other hand, use the line endings setting of the file they live in.
Making the test more environment agnostic
My first thought was that this was a really lousy reason for a test to fail. The purpose of this unit test was not to test Windows-vs-Linux line endings. To make this test less brittle (more pliable?), I introduced a string extension method to strip all of the line endings from the string:
public static string StripNewLines(this string str)
{
return Regex.Replace(str, @"\r\n?|\n", string.Empty);
}
Case closed
While that solved the immediate issue, I still wanted to solve the mystery at hand.: Why would the line endings be different on my machine and the build machine? Being on GitHub, the project is stored in the Git source code management system. Git originated on Linux, so it favors LF
line endings. Git does play nice with Windows however, and offers the autocrlf
configuration option. When this option is set to true
, source files are checked out with CRLF line endings, but committed to Git with LF line endings. But what happens when someone clones your repository and they haven’t set the autocrlf
option? You guessed it - the files are checked out with LF line endings. And that’s exactly what broke baby bird’s balloon on the build box.
git config core.autocrlf true
Configure Git on AppVeyor
If you’re using AppVeyor and you run into this issue, how do you fix it? In the “Environment” section of your project’s settings, find the “Init script” text box and enter the following:
git config --global core.autocrlf true
That’s all folks! Case closed. :)