I had searched for a solution to mvn clean install
on my changed projects after a git pull
. I even asked a question over at SO about the same thing. The answer I got pointed me into creating my own script to achieve what I wanted to accomplish. So I went, I saw, and I conquered the script that I wanted. However, it is probably not efficient, and could use refactoring. I am new to bash scripting and learned a bit while doing this.
The structure I was targeting is a Maven POM Project. That project contains several sub-POM Projects, which in turn contain Maven Java Projects.
Example Structure:
-Parent
|-Sub_Parent_A
|-Child_1
|-Child_2
|-Sub_Parent_B
|-Child_3
|-Child_4
I wanted to only call mvn clean install
if a project had files that changed in the child projects. This would reduce time on going to each project by hand and calling the command or by just calling the command on the parent project.
The code, along with revisions, can also be found on GitHub
################################################################################
#
# License:.....GNU General Public License v3.0
# Author:......CodeMonkey
# Date:........14 November 2018
# Title:.......GitMavenCleanInstall.sh
# Description: This script is designed to cd to a set Maven POM Project,
# perform a git remote update and pull, and clean install the changed
# files projects.
# Notice:......The project structure this script was originally set to target
# is structured as a Maven POM Project that contains several sub-POM Projects.
# The sub-POM Projects contain Maven Java Application projects. The targets
# should be easy to change, and allow for others to target other structures.
#
################################################################################
#
# Change History: N/A
#
################################################################################
#!/bin/bash
#Function to check if array has element
containsElement () {
local e match="1ドル"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
#Navigate to the POM Project
cd PATH/TO/POM/PROJECT
#Remote update
git remote update -p
#Pull
git pull
#Get the current working branch
CURRENT_BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/1円/p')"
#Get the output of the command git diff
GIT_DIFF_OUTPUT="$(git diff --name-status HEAD@{1} ${CURRENT_BRANCH})"
#Split the diff output into an array
read -a GIT_DIFF_OUTPUT_ARY <<< $GIT_DIF_OUTPUT
#Declare empty array for root path
declare -a GIT_DIFF_OUTPUT_ARY_ROOT_PATH=()
FORWARD='/'
#Loop diff output array
for i in "${GIT_DIFF_OUTPUT_ARY[@]}"
do
#Check that the string is not 1 Character
if [[ "$(echo -n 1ドル | wc -m)" != 1 ]]
then
#Split the file path by /
IFS='/' read -ra SPLIT <<< $i
#Concatenate first path + / + second path
path=${SPLIT[0]}$FORWARD${SPLIT[1]}
#Call function to see if it already exists in the root path array
containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}"
if [[ $? != 0 ]]
then
#Add the path since it was not found
GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
fi
fi
done
#Loop root path array
for val in ${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}
do
#CD into root path
cd $val
#Maven call to clean install
mvn -DskipTests=true --errors -T 8 -e clean install
#CD back up before next project
cd ../../
done
1 Answer 1
Take a cleaner approach if you can
The current approach is basically a dirty hack to work around Maven's inability to do incremental builds. I recommend to start looking into another more modern build system that is able to re-build just what needs to be rebuilt. Such as Gradle.
Always double-quote variables in arguments
As a rule of thumb, always put double-quotes around variables used as arguments. For example in all these places, there should be double-quotes around the variables:
read -a GIT_DIFF_OUTPUT_ARY <<< $GIT_DIFF_OUTPUT ... GIT_DIFF_OUTPUT="$(git diff --name-status HEAD@{1} ${CURRENT_BRANCH})" ... IFS='/' read -ra SPLIT <<< $i ... GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path) ... for val in ${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]} ... cd $val ...
Avoid performance pitfalls
For each path in the diff,
the script builds the array GIT_DIFF_OUTPUT_ARY_ROOT_PATH
with the first two path segments,
and keeps the elements unique by not adding a value if it's already in the array.
As the size of the array grows, this becomes increasingly inefficient,
as this is quadratic time complexity.
I believe the paths printed by git diff
is sorted by default.
You can take advantage of that:
track the previous element,
and only add a new value when it's different from the previous.
You will achieve better performance and without using the containsElement
function.
Use the exit code directly in conditional expressions
Instead of this:
containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}" if [[ $? != 0 ]] then #Add the path since it was not found GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path) fi
You could use the exit code of the function directly in a conditional:
if ! containsElement "$path" "${GIT_DIFF_OUTPUT_ARY_ROOT_PATH[@]}"
then
#Add the path since it was not found
GIT_DIFF_OUTPUT_ARY_ROOT_PATH+=($path)
fi
Get current Git branch name using native Git commands
Instead of this:
CURRENT_BRANCH="$(git branch | sed -n -e 's/^\* \(.*\)/1円/p')"
This is a better, native way:
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
Use native Bash to get string length
This line tries to check the length of a variable:
if [[ "$(echo -n $i | wc -m)" != 1 ]]
Although -m
is used, the possible values will not be multibyte,
so the solution is equivalent to using -c
.
In this case, a better solution exists with native Bash:
if [[ ${#i} != 1 ]]
Use better variable names
i
is good for simple counters. It's not a good name for anything else.
What is FORWARD
? path_separator
or pathsep
would have been better.
-
\$\begingroup\$ Thanks for the tips. Script has been updated. Seems to run faster in the initial loop, which will help greatly with bigger code changes across multiple projects. \$\endgroup\$CodeMonkey– CodeMonkey2018年11月19日 23:22:05 +00:00Commented Nov 19, 2018 at 23:22
git diff --name-status
does get that output, which is why the output is split into an array and looped over to insert the root file paths into a different array. Though I did not know of--name-only
. That will let me eliminate theif
check. \$\endgroup\$GIT_DIF_OUTPUT
. It would be really best if you tested your script, and then copy-paste the fully working version without typos. \$\endgroup\$