10
\$\begingroup\$

In my company I follow the following workflow. (I think it can be defined a kind of 'Continuous Integration'.)

Workflow:

  • We have 3 fixed branches (staging/master/production)
  • Every push to staging or production automatically deploy to staging/production server.

We use git in the following way:
(suppose I'm working on the new functionality 'A')

  1. I create a new branch from Master (ex. branch 'A')
  2. If I want to show my changes to the client I merge A to staging and push it.
  3. once finish my work on A I merge back A on Master and push master
  4. to send changes live I merge Master in Production branch and push production

I find quite boring/time consuming to git checkout ... git pull .. bla bla every time I have to deploy live/staging my work.

So I have produced the following bash script, the aim is to simplify the process of deploying branch A into a single command deploy.sh live A

#!/bin/bash
function merge() {  
    SOURCE=$1
    DEST=$2

    echo ''
    echo "--- Merging $SOURCE with $DEST ---" 
    echo "--> Checkout $DEST ..." 
    git checkout $DEST

    echo "--> Pull $DEST ..."
    git pull --ff-only origin $DEST
    if [ $? -ne 0 ]
    then
      echo "ERROR: Could not PULL" 
     return 1
    fi

    echo "--> Merging $SOURCE with $DEST ..." 
    git merge --ff-only $SOURCE --no-edit  # --ff-only trigger errors if merge/pull is not possible
    if [ $? -ne 0 ]
    then
      echo "ERROR: Could not MERGE"
      return 1
    fi

    echo "--> Push $DEST ..."
    git push origin $DEST
    return 0
}

function deploy() {
    MODE=$1
    SOURCE_BRANCH=$2

    echo ''
    echo "### START ###"
    echo ''
    echo "--- Pull changes from Master ---"
    git checkout $SOURCE_BRANCH
    git pull --progress --no-edit --no-stat -v --progress origin master


    merge $SOURCE_BRANCH 'staging'
    status=$?
    if [ $status -ne 0 ]
    then
      echo "ERROR: STEP 1" >&2
      exit 1
    fi

    if [ $MODE = "live" ]
    then

        merge $SOURCE_BRANCH 'master'
        status=$?
        if [ $status -ne 0 ]
        then
          echo "ERROR: STEP 2"
          return 1
        fi

        merge 'master' 'production'
        status=$?
        if [ $status -ne 0 ]
        then
          echo "ERROR: STEP 3"
          return 1
        fi
    fi

    echo ''
    echo "### END ###"
    echo ''
}

MODE=$1;
SOURCE_BRANCH=$2;

if [ -z "$MODE"  -o -z "$SOURCE_BRANCH" ]
    then
        echo "Usage:"
        echo ""
        echo  "MODE BRANCH_NAME (MODE: live|staging)"
    else
        if git show-ref --verify --quiet "refs/heads/$SOURCE_BRANCH";
        # if [ `git branch --list $SOURCE_BRANCH ` ]
            then
                deploy $MODE $SOURCE_BRANCH
        else
            echo ''
            echo "Error: Branch $SOURCE_BRANCH not found"
        fi
fi

The question:

I'm quite newby both on GIT and BASH scripting. So I would like to know if the above workflow/script is ok ? Any recommendation is welcome. (I'm happy to share this as resource.)

\$\endgroup\$
13
\$\begingroup\$

Use exit codes directly

Instead of this:

merge $SOURCE_BRANCH 'staging'
status=$?
if [ $status -ne 0 ]
then
  echo "ERROR: STEP 1" >&2
  exit 1
fi

You can use exit codes directly in conditions, like this:

if ! merge $SOURCE_BRANCH 'staging'
then
  echo "ERROR: STEP 1" >&2
  exit 1
fi

Use it this way everywhere.

Handle errors consistently

The error handling is inconsistent throughout the script:

  • Sometimes you exit 1, sometimes you return 1 in functions. It would be better to do consistently
  • Sometimes you print the error message on stdout, sometimes on stderr. It would be better to print it consistently on stderr

Style

To print a blank line you can simply echo without a parameter.

You can drop return 0 as the last statement of a function. The exit code of the last statement will be used as the exit code of the function.

A semicolon is unnecessary at the end of the line.

Long comments at the end of a line are generally hard to read. Move these to the previous line.

The indentation is not consistent. The code inside the functions looks nice, do the same way for the code outside.

Ex. ( edited by @WonderLand)

#!/bin/bash

function merge() {  
    SOURCE=$1
    DEST=$2

    echo
    echo "---> Merging $SOURCE with $DEST" 

    echo
    echo "---> ---> Checkout $DEST ..."     
    git checkout $DEST

    echo
    echo "---> ---> Pull $DEST ..."
    if ! git pull --ff-only origin $DEST
    then
     exit 1
    fi

    echo
    echo "---> ---> Merging $SOURCE with $DEST ..." 
    # --ff-only trigger errors if merge/pull is not possible
    if ! git merge --ff-only $SOURCE --no-edit
    then
      exit 1
    fi

    echo
    echo "---> ---> Push $DEST ..."
    git push origin $DEST
}

function deploy() {
    MODE=$1
    SOURCE_BRANCH=$2

    echo     
    echo "---> Pull changes from Master ..."

    if ! git checkout $SOURCE_BRANCH
    then    
        exit 1
    fi

    git pull --progress --no-edit --no-stat -v --progress origin master

    if ! merge $SOURCE_BRANCH 'staging'
    then      
      exit 1
    fi

    if [ $MODE = "live" ]
    then
        if ! merge $SOURCE_BRANCH 'master'
        then          
          exit 1
        fi

        if ! merge 'master' 'production'
        then          
          exit 1
        fi
    fi
}

MODE=$1
SOURCE_BRANCH=$2

if [ -z "$MODE"  -o -z "$SOURCE_BRANCH" ]
then
    echo "Usage:"
    echo ""
    echo  "MODE BRANCH_NAME (MODE: live|staging)"
else
    if git show-ref --verify --quiet "refs/heads/$SOURCE_BRANCH"
    then
        echo
        echo "### START ###"
        echo
        deploy $MODE $SOURCE_BRANCH
        echo
        echo "### END ###"
        echo
    else
        echo
        echo "Error: Branch $SOURCE_BRANCH not found"
    fi
fi
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.