Skip to main content

Preparing the pre-commit and formatter

This section explains how to work with Arria's pre-commit hook for Git.

  1. Create a file named pre-commit (no extension!) inside the hidden directory .githooks of your repository, and save the contents of Script 1 (below) in it.

  2. Create a file named format.py in the root of your repository, and save the contents of Script 2 (below) in it.

Script 1

#!/bin/sh

## CONFIG

FORMAT_PATH="format.py"
PYTHON_PATH=python

## ENABLE_NAME_CLEANUP - optional param
## when set it will do Studio file name cleanup: remove version and replace spaces with underscores
## comment out to disable
ENABLE_NAME_CLEANUP=yes

## FUNCTIONS

checkJQ () {
  echo "- checking if JQ is installed"
  if ! $JQ --version > /dev/null 2> /dev/null
  then
    return 1
  fi
  return 0
}

checkLocalJQ () {
  echo "- checking if local JQ is installed"
  JQ=./jq
  if ! $JQ --version > /dev/null 2> /dev/null
  then
    return 1
  fi
  return 0
}

downloadJQ () {
  echo "- downloading JQ into project dir"
  if [[ "$OSTYPE" == "linux-gnu" ]]; then
    curl -o "jq" -L "https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64"
  elif [[ "$OSTYPE" == "darwin"* ]]; then
    curl -o "jq" -L "https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64"  
  elif [[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" || "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
    curl -o "jq.exe" -L "https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win64.exe"
  else
    echo "FAILED: JQ could not be downloaded. Unknown OS: $OSTYPE"
    return 1
  fi

  export PATH=.:$PATH
  echo "jq" >> .git/info/exclude
  echo "jq.exe" >> .git/info/exclude
  chmod +x jq 2> /dev/null
  chmod +x jq.exe 2> /dev/null
}

isStudioFile () {
  if [ ! -s "$1" ]
  then 
    # empty file
    return 1
  fi

  if ! cat "$1" | $JQ -ce ".templates[].value" > /dev/null 2> /dev/null
  then 
    echo "- non-Studio file: $staged"
    return 1
  fi
  return 0
}

isDeletedFile () {
  if [ -r "$1" ]
  then
    return 1
  fi
  return 0
}

formatStudio () {
  path=$1
  temppath=$1.tmp

  echo "- remove .sampleOutput, .version, .collaborators, .activeUser fields and format Studio file"
  cat "$path" | $JQ "del(.sampleOutput, .version, .collaborators, .activeUser, .activeApiKey, .allowPublication, .published, .settingsNotNull)" > "$temppath" || ( echo "OPTIONAL: remove step failed for $path . Most probably the step was not applicable."; cp "$path" "$temppath" )

  echo "- apply common formatting"
  cat "$temppath" | $JQ -c . > "$path" || return 1

  rm "$temppath"

  return 0
}

setupJQ() {
  # exit if no JQ
  if ! checkJQ && ! checkLocalJQ
  then
    downloadJQ
    if ! checkLocalJQ
    then
      echo "FAILED: no JQ installed. Pre-commit could not install JQ in the local repo. Please, install it manually, set PATH and verify 'jq --version'"
      return 1
    fi
  fi
}

extractScripts () {
  chmod +x "$FORMAT_PATH"
  "$PYTHON_PATH" "$FORMAT_PATH" "$1" || return 1
  return 0
}

## MAIN

echo "-------------------------------"
echo "- pre-commit hook started..."

setupJQ || exit $?

OLDIFS=$IFS
IFS=$'\n'
for staged in `git diff --name-only --cached`
do
  # skip deleted file
  if isDeletedFile "$staged"
  then
    continue
  fi

  # skip non-Studio files
  if ! isStudioFile "$staged"
  then
    continue
  fi

  echo "- staged studio file: $staged"

  # optional: normalize name - remove version and replace spaces with underscores
  if [ -n $ENABLE_NAME_CLEANUP ]
  then
    filename=`basename "$staged"`
    dir=`dirname "$staged"`
    normalized_name=`echo "$filename" | tr '[A-Z ]' '[a-z_]' | sed -E 's/_\([0-9]+\)//g' | sed -E 's/_v[0-9]+\.json$/.json/g' | sed -E 's/^\[.+\]//g'`
    if [ "$filename" != "$normalized_name" ]
    then
      echo "- name normalization: $filename -> $normalized_name"
      git mv -f "$staged" "$dir/$normalized_name"
      staged="$dir/$normalized_name"
    fi
  fi

  # format Studio file
  echo "- formatting staged Studio file with JQ"
  if ! formatStudio "$staged"
  then
    echo "FAILED: formatting failed. Check if the staged file is a valid Studio project. Try to re-import from Studio."
    exit 1
  fi

  # create peer review files
  echo "- creating peer review files"
  if ! extractScripts "$staged"
  then
    echo "FAILED: could not create peer review files from Studio project. Check if the staged file is a valid Studio project. Try to re-import from Studio."
    exit 1
  fi

  # re-stage edited file
  echo "- re-staging processed files"

  if ! git add "$staged"
  then
    echo "FAILED: re-stage processed files: $staged"
    exit 1
  fi

  if ! git add "${staged%\.json}_peer_review"
  then
    echo "FAILED: re-stage processed files: ${staged%\.json}_peer_review"
    exit 1
  fi

done
IFS=$OLDIFS

echo ""
echo "- pre-commit hook finished."
echo "-------------------------------"

Script 2

'''
This script generates .atl files from Studio projects, in order to make peer review easier. It creates 1 file for each script (template) in the project, pretty-printing some HTML tags in the process.
'''

import os
import sys
import json

file = sys.argv[1]

# set constants
SCRIPTS_KEY = 'templates'
OUTPUT_DIR = file[:-5] + '_peer_review'

# create output directory
if not os.path.exists(OUTPUT_DIR):
    os.makedirs(OUTPUT_DIR)

# create script files
with open(file) as inFile:
	data = json.load(inFile)
newFiles = []
for script in data[SCRIPTS_KEY]:
	name = script['id']
	code = script['value']
	code = code.replace("<p>", "\n")
	code = code.replace("</p>", "\n")
	code = code.replace("<br>", "\n")
	code = code.replace("<br/>", "\n")
	code = code.replace("&gt;", ">")
	code = code.replace("&lt;", "<")
	code = code.replace("&amp;", "&")
	atlFileName = name + '.atl'
	newFiles.append(atlFileName) 
	atlFile = open(OUTPUT_DIR + '/' + atlFileName, 'w')
	code = code.encode('ascii', 'ignore').decode('ascii')
	atlFile.write(code)

# deleted obsolete script files
for (dirpath, dirnames, filenames) in os.walk(OUTPUT_DIR):
	for filename in filenames:
		if filename.endswith('.atl') and filename not in newFiles:
			os.remove(OUTPUT_DIR + '/' + filename)