如何在运行gem / bundle安装后轻松修补和构建gem?

时间:2016-08-20 10:47:02

标签: automation rubygems patch

如何在运行gem / bundle update后自动修补和重建宝石?

(我想修补gem-ctags以使用ripper-tags代替ctags

1 个答案:

答案 0 :(得分:0)

以下脚本实现了这一点,并且有一些聪明的方法可以在patch处理部分或全部补丁时返回失败代码:

#!/bin/bash

# Usage: gem-patch
# Code updates: https://gist.github.com/HaleTom/275f28403828b9b9b93d313990fc94f4

# Features: 
# Work around `patch` returning non-zero if some patch hunks are already applied
# Apply all patches in $patch_dir (in order) to their corresponding gem(s)
# Build a gem only after all patches have been applied
# Only build the gem if it was patched
# Robust error handling

patch_dir="$HOME/lib/gem-patch"
# Patches are assumed to be made with git patch
# Files are to be named gem-name or gem-name.patch-explanation
# Multiple patches are applied in filename order

set -e -u
shopt -s nullglob # Globs are '' when no files match a pattern
gems_dir="$(gem environment gemdir)/gems"

if ! compgen -G "$patch_dir/*" > /dev/null; then
  echo "Couldn't find any patches located in $patch_dir. Quitting." 2>&1
  exit 1
fi

# Save the current "gemname-1.2.3" so that when it changes to a new one
# (ie, all patches have been applied) it can be built only once
function build_prev_if_needed {
  if [[ ${prev_gem_ver:="${gem_ver:=''}"} != "$gem_ver" ]]; then
    # We've moved on to another gem, build the last one
    ( cd "$gems_dir/$prev_gem_ver" &&
      gem build "${prev_gem_ver%%-[-0-9.]*}.gemspec"
    )
  fi
  prev_gem_ver="$gem_ver"
}

for patch in "$patch_dir"/*; do
  gem_name=$(basename "${patch%%[.]*}") found_one=false
  # $gem_dir becomes "rails-5.0.0.1" from find at end of loop
  while read -d '' -r gem_dir; do
    found_one=true
    # Build the previously seen gem if we've moved on to a new one
    gem_ver=${gem_dir##$gems_dir/}
    echo -n "$gem_ver (patch $(basename "$patch")): "
    # If we could reverse the patch, then it has already been applied; skip it
    if patch --dry-run --reverse -d "$gem_dir" -fp1 --ignore-whitespace -i "$patch" >/dev/null 2>&1; then
      echo "skipping (already applied)"
      continue
    else # patch not yet applied
      echo "patching..."
      # Patch returns non-zero if some hunks have already been applied
      if ! patch -d "$gem_dir" -fsp1 --ignore-whitespace -i "$patch"; then
        # Check that the patch was fully applied by pretending to reverse it
        if patch --dry-run --reverse -d "$gem_dir" -fp1 --ignore-whitespace -i "$patch" >/dev/null 2>&1; then
          echo "Ignoring failure: hunk(s) were already applied"
        else
          echo "Patch failed for $gem_dir" >&2; exit 1;
        fi
      fi
      build_prev_if_needed
    fi
  done < <(find "$gems_dir" -maxdepth 1 -type d -regex ".*/$gem_name-[-0-9.]+" -print0)
  if [[ $found_one != true ]]; then
    echo "Fatal: Patch file '$(basename "$patch")': Couldn't find any gem sources named $gems_dir/$(basename "$patch")*" >&2; exit 1
  fi
done # $gem_dir is now blank

gem_ver=''
build_prev_if_needed