diff --git a/README.md b/README.md index b7d22c1..f27be9e 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,23 @@ well on the command line. For example, $ ./packcheck-safe.sh cabal-v2 PATH=/bin:/usr/bin:/opt/ghc/bin ``` +## packcheck-remote + +`packcheck-remote.sh` is a wrapper over `packcheck.sh`. It allows you to run +packcheck on a remote repository by cloning it locally and optionally merging a +branch into another branch (e.g. merging a PR branch into master). + +``` +$ ./packcheck-remote.sh --force \ + --remote=https://github.com/user/repo \ + --checkout=origin/master \ + --merge=origin/branch \ + --directory=./repo.packcheck \ + -- cabal-v2 GHCVER=8.8.3 +``` + +Use `./packcheck-remote.sh --help` for more information. + ## Full Reference Please use `cabal` version 2.4 or later. diff --git a/packcheck-remote.sh b/packcheck-remote.sh new file mode 100755 index 0000000..ea55a72 --- /dev/null +++ b/packcheck-remote.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash + +PACKCHECK_DIR=$(dirname "$0") +PACKCHECK_EXE="$PACKCHECK_DIR/packcheck.sh" +PWD="$(pwd)" +DEFAULT_DIRECTORY_NAME="packcheck-remote-work" +DEFAULT_DIRECTORY="$PWD/$DEFAULT_DIRECTORY_NAME" + +# $1: msg +show_step() { + echo + echo "--------------------------------------------------" + echo "$1" + echo "--------------------------------------------------" +} + +which_cmd() { + hash -r && type -P "$1" || true +} + +# $1: executable (eg. git) +require_cmd () { + local cmd_path + cmd_path=$(which_cmd "$1") + if test -z "$cmd_path" + then + echo "Required command [$1] not found in PATH [$PATH]." + exit 1 + else + echo "Using [$1] at [$cmd_path]" + fi +} + +# $1: command +function run_verbose() { + echo "$1" + bash -c "$1" +} + +# $1: Remote repository +# $2: Revision to checkout +# $3: Revision to merge into $2 +# $4: Directory to clone into +# $5: Force mode +# $6: Packcheck cli options +try_git_clone_and_merge() { + + # Check for prerequisites + require_cmd git + + local remote="$1" + local rev="$2" + local merge="$3" + local dir="$4" + local force_mode="$5" + local packcheck_cli_opts="$6" + + if test -z "$dir" + then + dir="$DEFAULT_DIRECTORY" + echo "No directory specified to clone to." + echo "Using $dir" + fi + + if test -z "$remote" + then + echo "No remote repository provided." + echo "Skipping cloning." + return + fi + + if test -z "$rev" + then + echo "No revision given." + echo "Defaulting to 'origin/master'." + rev="origin/master" + fi + + if test -d "$dir" + then + echo "$dir already exists" + + if [ "$force_mode" == "true" ]; + then + echo "Forcing the deletion of the directory." + echo "Removing $dir" + rm -rf "$dir" || exit 1 + else + echo "Set the script to force mode to force the deletion." + return + fi + fi + + mkdir -p "$dir" || exit 1 + + run_verbose "git clone $remote $dir" || exit 1 + + cd "$dir" || exit 1 + run_verbose "git branch $rev" || exit 1 + + if test -n "$merge" + then + # This will fail if there are any merge conflicts + run_verbose "git merge -X theirs $merge --no-edit --commit" || exit 1 + fi + + show_step "Running packcheck" + # Run packcheck here + run_verbose "$PACKCHECK_EXE $packcheck_cli_opts" +} + +# Arguments for the command line +FORCE="false" +REMOTE="" +CHECKOUT="" +MERGE="" +DIRECTORY="" +PACKCHECK_CLI_OPTS_ARR=() +PACKCHECK_CLI_OPTS="" + +function run_help() { + local script + script=$(basename "$0") + + echo + echo "USAGE: --option[=value]" + echo + echo "-f (or) --force: Puts the script in force mode. This is ideal for CIs." + echo "-h (or) --help: Print help." + echo "--remote: Repository to clone." + echo "--checkout: Revision to checkout. Defaults to 'origin/master'." + echo "--merge: Revision to merge in the checked out branch." + echo "--directory: Directory to clone the repository into." + echo " Defaults to '$DEFAULT_DIRECTORY_NAME' under the present working directory." + echo + echo "All the arguments after '--' are passed to packcheck" + echo + + echo + echo "EXAMPLE:" + echo + echo "$script --force \\" + echo " --remote=https://github.com/user/repo \\" + echo " --checkout=origin/master \\" + echo " --merge=origin/branch \\" + echo " --directory=./repo.packcheck \\" + echo " -- cabal-v2 GHCVER=8.8.3" + echo +} + +# Program entry point +for i in "$@" +do + case $i in + -f|--force) + FORCE="true" + shift + ;; + -h|--help) + run_help + exit 0 + ;; + --remote=*) + REMOTE="${i#*=}" + shift + ;; + --checkout=*) + CHECKOUT="${i#*=}" + shift + ;; + --merge=*) + MERGE="${i#*=}" + shift + ;; + --directory=*) + DIRECTORY="${i#*=}" + shift + ;; + --) + shift + PACKCHECK_CLI_OPTS_ARR=("$@") + break + ;; + *) + echo "Unknown argument to packcheck-remote" + run_help + exit 1 + ;; + esac +done + +for i in "${PACKCHECK_CLI_OPTS_ARR[@]}" +do + case $i in + *=*) + key=${1%%=*} + val=${1#*=} + PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $key=\"$val\"" + shift + ;; + *) + PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $i" + shift + ;; + esac +done + +try_git_clone_and_merge "$REMOTE" "$CHECKOUT" "$MERGE" "$DIRECTORY" "$FORCE" "$PACKCHECK_CLI_OPTS" diff --git a/packcheck-safe.sh b/packcheck-safe.sh index f7a92f6..c5d8de3 100755 --- a/packcheck-safe.sh +++ b/packcheck-safe.sh @@ -1,9 +1,28 @@ #!/usr/bin/env bash -PACKCHECK_DIR=$(dirname $0) +PACKCHECK_DIR=$(dirname "$0") echo "Running ${PACKCHECK_DIR}/packcheck.sh with clean environment and CHECK_ENV on..." echo "No environment variables are honored, you have to specifiy ALL the" echo "parameters explicitly on the command line, including PATH." echo -/usr/bin/env -i CHECK_ENV=y $(dirname $0)/packcheck.sh $* +PACKCHECK_CLI_OPTS_ARR=("$@") +PACKCHECK_CLI_OPTS="" + +for i in "${PACKCHECK_CLI_OPTS_ARR[@]}" +do + case $i in + *=*) + key=${1%%=*} + val=${1#*=} + PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $key=\"$val\"" + shift + ;; + *) + PACKCHECK_CLI_OPTS="$PACKCHECK_CLI_OPTS $i" + shift + ;; + esac +done + +eval "/usr/bin/env -i CHECK_ENV=y $PACKCHECK_DIR/packcheck.sh $PACKCHECK_CLI_OPTS" diff --git a/packcheck.cabal b/packcheck.cabal index d106ff5..236873a 100644 --- a/packcheck.cabal +++ b/packcheck.cabal @@ -53,6 +53,7 @@ extra-source-files: cabal.project.ci packcheck.sh packcheck-safe.sh + packcheck-remote.sh MAINTAINING.md source-repository head