2

I am using Jenkins on a Windows machine to build a Cygwin program. Recently that started to fail.

I turned out that was because Windows' git seems to represent symbolic links in the repo as plain text files.

There are two possible approaches to solve this:

  1. Use Cygwin's git to do the checkout.
  2. Convert the links (a.k.a. plain text files) to Cygwin links

The first turned out to be not as simple as I wished, pointing to Cygwin's git.exe was not enough as we need the cygwin1.dll in the path. This and some other issues made me abandon this apporach. I'm interested in suggestions on how to do that, in essence turning a Windows Jenkins into a Cygwin Jenkins.

However, turning Windows git "links" into Cygwin links seemed feasable. What would be a simple way to do that and could be run as an initial step in the Jenkins build?

thoni56
  • 3,145
  • 3
  • 31
  • 49

1 Answers1

2

I wrote a simple bash script to find all the "links", easy since they are marked with flags 120000 according to this answer. Then it was a simple matter to just pick up the target filename (the content of the text file) and then replace the file with a symbolic link.

Since it is a bash script it can easily be used from Cygwin and MSys.

#!/bin/bash
#
# https://stackoverflow.com/a/38140374/204658
#
# Script to convert symbolic links created by a windows git
# clone/checkout to cygwin links. When a typical Windows git checks
# out a symbolic link from the repo it (MsysGit, at least) tend to
# produce text files with flag 120000 and the link target file name as
# the content.  Strategy would simply be to find all those and replace
# by cygwin links.
#
# This will work if you are not planning on commiting anything, e.g.
# in a Jenkins, or other CI, build environment

for f in `git ls-files -s | awk '$1 == "120000" {print $4}'`
do
    # echo $f is a symlink pointing to $dir/$target
    dir=$(dirname "${f}")
    pushd "$dir" 2>&1 > /dev/null
    file=$(basename "$f")
    target=`cat "$file"`
    rm "$file"
    ln -s "$target" "$file"
    popd 2>&1 > /dev/null
done
Community
  • 1
  • 1
thoni56
  • 3,145
  • 3
  • 31
  • 49
  • You might want to make your awk use `^120000` or `$1 == "120000"` as the test, so that it will not fire on files whose hash happens to contain `120000` or whose name contains `120000`. (You may also run into issues with file names that contain whitespace, but this is a bit harder to work around with simple scripting.) – torek Jul 01 '16 at 13:47
  • Thanks, @torek, updated script with `$1 = "120000"` and more quotes to alleviate space problems. – thoni56 Jul 01 '16 at 14:15
  • Alas, the `$4` in awk will have already mangled a whitespace-containing name. (This is why `git ls-files` has `-z`, but to use that option you have to get pretty crazy with special Bash tricks, or write the whole thing in Python instead, or some such... Without `-z` Git encodes whitespace, perhaps using `read` in Bash will just work?) – torek Jul 01 '16 at 14:18
  • Yes, didn't consider that... What about `git ls-files -s | awk '$1="120000"{print $0}' | cut -d\ -f4-`? – thoni56 Jul 01 '16 at 14:34
  • I'd have to experiment: without `-z`, `git ls-files -s` does do some encoding, so maybe `$4` itself is fine and we just need to let bash `read` handle escapes. (What if a file name contains a literal backslash? Just need a torture-test Git repo... :-) ) – torek Jul 01 '16 at 19:57