Edit online

Using Git client-side hooks to run DITA publishing pipelines

1 May 2020
Read time: 9 minute(s)

This topic describes how to use Git client-side hooks to execute DITA commands and publishing workflows, with an emphasis on using the Oxygen's Git Client.

Prerequisites

  1. Oxygen's Git client is based on the JGit library. When running on Windows, this library relies on CYGWIN to run the Git hooks. CYGWIN must be installed and added to the path.
  2. If you are running on Windows, you either need an Oxygen Git client version 2.0.0 or one newer than 2.1.1 (at the time this post is written, 2.1.1 was the latest version, but a new one will soon be released). We are basically avoiding versions that come bundled with JGit 5.6.0, which has this known issue.
    If you need to install version 2.0.0 of the plugin, since this version is no longer available in the add-on, you have to install it manually:
    1. Uninstall the current Oxygen Git client version, if one is present.
    2. Download Oxygen Git client version 2.0.0.
    3. Unzip it inside {oxygenInstallDir}/plugins. Make sure you don't create any intermediate folders. The path should be like this: {oxygenInstallDir}/git.support-2.0.0/plugin.xml.
  3. The DITA-OT command utility needs to be added to the path so that the presented script will work.

What is a Git hook?

Git has a way to fire off custom scripts when certain important actions occur. There are two groups of these hooks: client-side and server-side. Client-side hooks are triggered by operations such as committing and merging, while server-side hooks run on network operations such as receiving pushed commits. You can read more about what types of hooks are available at Customizing Git - Git Hooks.

Using a client side hook with a DITA project

The main scenario we are focusing on in this topic is about using client-side hooks while working with DITA and storing it in a Git repository, and how to enable Oxygen’s built-in Git Client to execute these hooks.

The hooks are all stored in the hooks subdirectory of the Git directory, which is .git/hooks by default. A useful client-side hook is the pre-commit hook. It’s used to inspect the snapshot that’s about to be committed, to see if you’ve forgotten something, to make sure tests like Oxygen's Validate and Check for completeness run properly, or to examine whatever you need to inspect in the code.

To make it easier to follow, though, we will use a use case reported by one of Oxygen's users. That is to generate a Markdown version of the documentation whenever you commit something. When I tried to do the same thing myself, I encountered some challenges and I think it will help others if they read how I managed to solve it.

Installing a pre-commit hook

Git automatically populates the .git/hooks directory with example scripts when you initialize a repository. All we need to do is rename pre-commit.sample to simply pre-commit and put in it the script we are interested in.

Note:
The DITA-OT command utility needs to be added to the path in order for the following script to work.

The content of the pre-commit file is something like this: we assume that the map to publish is called README.ditamap and it is located inside the project root directory.

Note:
Oxygen's Git client is based on the JGit library. When running on Windows, this library relies on CYGWIN to run these Git scripts. CYGWIN must be installed and added to the path.
#!/bin/sh
set -x

echo "Start hook"

export GIT_HOOKS_DIR=`cd "\`dirname "\\\`readlink "$0" || echo $0\\\`"\`" && pwd`

# We assume the hooks directory is the default one: wc/.git/hooks
export ROOT_DIR="$GIT_HOOKS_DIR/../.."

 # OS specific support.  $var _must_ be set to either true or false.
cygwin=false;
case "`uname`" in
  CYGWIN*) cygwin=true ;;
esac

# For some reasons, if we let a cygwin path pass, the dita pipeline fails with:
# Error: Failed to run pipeline: [DOTA069F][FATAL] Input file 'file:/cygdrive/c/Users/.../git-hooks-sample/.git/hooks/../../README.ditamap' cannot be located or read. Ensure that file was specified properly and that you have permission to access it.
# It works if we pass it instead as: C:/Users/.../git-hooks-sample/README.ditamap
# Considering the fact that the dita script just passes the --input further on to ANT, it might have something to do with cygwin processing performed in ANT.
if $cygwin; then
  ROOT_DIR=`cygpath --mixed "$ROOT_DIR"`
fi


dita --input=$ROOT_DIR/README.ditamap --format=markdown --output=$ROOT_DIR/docs/

# Exit with status of last command
exit 
The first challenge in the script above is that if I don't pass the ditamap path through ROOT_DIR=`cygpath --mixed "$ROOT_DIR"`, the DITA publishing pipeline fails with:
Error: Failed to run pipeline: [DOTA069F][FATAL] Input file 'file:/cygdrive/c/Users/.../git-hooks-sample/.git/hooks/../../README.ditamap' cannot be located or read. Ensure that file was specified properly and that you have permission to access it.
Having done that, running this hook on Windows will still fail, inside the DITA pipeline, with:
Buildfile: \cygdrive\d\tools\dita-ot-3.4\build.xml does not exist!
To fix this, I had to go inside {dita.ot.dir}/bin/dita and search for this line:
# Add build script to arguments
ant_exec_args="$ant_exec_args \"-buildfile\" \"$DITA_HOME/build.xml\" \"-main\" \"org.dita.dost.invoker.Main\""
and replace it with:
##############################################
BUILD_FILE="$DITA_HOME/build.xml"
if $cygwin ; then
BUILD_FILE=`cygpath --mixed "$BUILD_FILE"`
fi
###############################################

# Add build script to arguments
ant_exec_args="$ant_exec_args \"-buildfile\" \"$BUILD_FILE\" \"-main\" \"org.dita.dost.invoker.Main\""

That's it. Each time you commit something in this repository, the Markdown version of your documentation gets generated.