Накарайте Vagrant+Chef да стартира спасителен режим, ако файловете на шаблона са били извлечени с CRLF окончания на редове

Продължавам да получавам досадна грешка, която се случва, когато разработчиците на Windows проверяват готварски книги от моето хранилище на Git с autocrlf на Git, зададено на true . Когато стартират vagrant up, за да изведат виртуална машина на Linux, файловете на готварската книга се нанасят във виртуалната машина с CRLF окончания на редове, което причинява безкрайни неясни грешки, когато обвивката и другите помощни програми на POSIX се опитват да работят с (вече невалидни) шаблонни файлове, които са били копирани във VM.

Поправката за това е достатъчно проста: клонирайте отново хранилището след промяна на настройката autocrlf на input или false.

Моят проблем е, че когато имате грешни краища на редове, единствените симптоми са грешки на странни места, които по никакъв начин не сочат, че има проблем с краищата на редове.

Как мога да накарам Chef† да проверява за грешни окончания на редове в, да речем, шаблонните файлове на готварската книга и да изведе грешка, ако намери такава? Мисля, че прост Ruby фрагмент, който прави твърдение за окончанията на редовете в даден файл, който мога да поставя в горната част на рецепта, ще работи.

Забележка: в конкретния случай на моето репо, последователността от стъпки е:

  1. Разработчикът проверява репото
  2. Разработчикът изпълнява vagrant up
  3. Vagrant стартира готвач във виртуалната машина
  4. И накрая, скриптът за изграждане на репото се изпълнява във VM

† Или наистина нещо друго, включено в репото


person Michael Kropat    schedule 23.07.2015    source източник


Отговори (2)


Rubocop може да се използва за налагане на завършвания на редове в стил на unix (сред много други неща).

Например (от командния ред, в рамките на гост):

gem install rubocop
rubocop --only Style/EndOfLine  # only check line endings

Или може да се направи от контекста на самия готвач с нещо като следната рецепта:

chef_gem 'rubocop'

ruby_block 'check line endings' do
  block do
    # It's probably better to call rubo cop code directly, rather than
    # shelling out, but that can be an exercise for the reader ;-)
    rubocop_cmd = Mixlib::ShellOut.new(
      'rubocop --only Style/EndOfLine',
      :cwd => 'dir_to_check'
    )
    rubocop_cmd.run_command

    # Raise an exception if it didn't exit with 0.
    rubocop_cmd.error!
  end
end

Неуловеното изключение ще накара главния готвач да се спаси.

person Jon Burgess    schedule 24.07.2015
comment
+1 Добър отговор. В моя случай не съм сигурен, че мога да приложа това, тъй като локалните скриптове за изграждане се изпълняват във виртуалната машина само след като се е случило изпълнението на Chef. Съжалявам, че първоначално не включих всички подробности във въпроса. - person Michael Kropat; 24.07.2015
comment
Независимо дали работи в контекста на готвача или не, напр. след, всъщност няма значение. Докато имате наличен ruby/bundler, горното ще работи. - person Jon Burgess; 26.07.2015
comment
Като казах това, бих препоръчал да стартирате нещо подобно преди краищата на DOS редове да стигнат до главния клон (или каквото и да е) в scm. В противен случай там ще има само шум. - person Jon Burgess; 26.07.2015
comment
Всички добри точки, но за да обясня ситуацията си малко по-добре: част от причината, поради която използвам Vagrant, е, че програмистът не се нуждае от ruby/bundler на хост машината -- всичко, което трябва да направи, е vagrant up и необходимите зависимости и процесите на изграждане се случват вътре във VM. Напълно съм съгласен за окончанията на редовете на DOS, но ето кое е лудото: msysgit в Windows по подразбиране е autocrlf = true, което означава, че дори ако файловете в репото имат окончания на редове на unix, те ще бъдат изтеглени на хост машината с DOS ред окончания. - person Michael Kropat; 26.07.2015
comment
Все още не съм много сигурен, че разбирам правилно вашия случай на употреба. Актуализирах отговора си, за да е ясно, че примерът на командния ред може да се изпълнява от виртуалната машина за гости, но също така да включва как същото нещо може да се изпълнява от chef. - person Jon Burgess; 27.07.2015
comment
Има смисъл. Стъпката chef_gem беше това, което концептуално ми липсваше. Най-накрая се опитах да внедря това, но изглежда не работи, защото rubocop не иска да проверява .erb файлове (моите шаблонни файлове). Не е част от първоначалния въпрос, но сега бих искал също да проверя файловете на готварските книги и не мисля, че rubocop също би работил с тях. Благодаря все пак за идеята. - person Michael Kropat; 11.09.2015

Ето Ruby фрагмент, който можете да поставите във всяка рецепта на готвач:

def cookbook_supporting_files(*cookbooks)
  cookbooks = cookbooks.map {|name| run_context.cookbook_collection[name]}

  cookbooks.flat_map do |cb|
    (cb.manifest[:files] + cb.manifest[:templates]) \
      .map {|f| ::File.join(cb.root_dir, f['path']) }
  end
end

def dos_eol?(f)
  ::File.open(f, 'rb').read(4096).include? "\r\n"
end

cookbook_supporting_files(cookbook_name).each do |f|
  if dos_eol? f
    raise "Cookbook template '#{f}' contains CRLF line endings"
  end
end

Това ще провери окончанията на редовете на файловете и шаблоните на готварската книга, които съществуват във всяка готварска книга, в която е поставен горният фрагмент.

Ако искате същият фрагмент да проверява други готварски книги, просто заменете:

cookbook_supporting_files(cookbook_name)

Със списъка с готварските книги, които искате да проверите:

cookbook_supporting_files('some_cookbook', 'another_cookbook')
person Michael Kropat    schedule 24.07.2015