Shell腳本結合Git實現增量項目部署

應用部署是開發、測試、上線必須面對的一個過程,尤爲是微服務架構的出現,運維部署從單體的部署逐漸脫離出,而且越顯複雜。java

然而,拋開多語言,多環境,集羣,分佈式的部署以外。就單單討論增量部署和全量部署git

1. 增量和全量部署

部署,除卻項目初始化部署,最理想的狀況即爲:新版本更改哪些內容則更新哪些內容github

1.1 增量部署

1.1.1 增量部署簡介

​ 增量部署通常指在每次部署過程當中首先提取當前版本和即將部署版本之間的增量(包括代碼、可執行文件或者配置等),並在部署過程當中僅更新增量部分。shell

1.1.2 常見部署流程

  1. 利用代碼管理工具(SVN、GIT等)提取兩個版本之間的增量,並結合其餘方面的增量變化。
  2. 按照增量部分制定具體的部署方式,編寫部署腳本,並準備增量部署包(包括混淆代碼等)。
  3. 分發和部署增量部署包到已經運行上一版本的目標環境,完成系統的版本升級。

1.1.3 增量部署優勢

  1. 部署速度快。每次只對增量部分進行更新,縮短部署時間
  2. 減小變化量。減小對整個系統的變化幅度,有些配置內容是不須要每次都更新迭代的
  3. 提升安全性。因爲每次支隊增量進行更新,避免所有代碼的泄露

1.1.4 增量部署缺點

  1. 增量部署 若存在其餘外在部署環境依賴,則下降部署效率數據庫

    增量部署不像安全

  2. 部署環境多的狀況下,對可重複性要求高架構

  3. 增量部署對回滾操做變得不友好負載均衡

1.2 如何選擇增量仍是全量

​ 現有的自動化部署,大多數都 全量部署,但全量部署也有一些弊端。但能夠經過一些策略進行篩選:運維

  • 提早準全量部署的全部配置和材料(部署包,外在配置文件等)在進行部署,能夠提升效率和速度
  • 使用灰度發佈或負載均衡等方法下降全量部署對應用可用性的影響

​ 對於現代系統中絕大部分狀態無關的部署單元(應用、模塊,微服務等),全量部署通常應是最優的選擇。而狀態相關的部署單元(數據庫等)則依然適合增量部署邏輯。maven

2. 進入主題

​ 前面講述了一些關於增量和全量部署的狀況。接下來說述如何經過shell腳本結合Git Log進行增量部署

2.1 前提環境

  • Java項目

  • Maven進行管理

  • Git做爲代碼倉庫

2.2 shell 腳本

shell新手,寫得不夠完美,輕噴。

2.2.1 整個shell腳本的模塊

  • Git環境準備
  • Maven對欲構建項目進行編譯
  • 建立增量部署文件夾
  • 檢索項目 target目錄
  • 經過 git diff 檢索兩次commit之間的差別,再經過檢索將對應文件拷貝到「增量文件夾」中

2.2.2 Git環境準備

# git環境

if [[ ! -d ".git" ]]; then
    ECHO error: please init  Git Repository
    exit 1;
fi

if [[ ! -z ${branch} ]]; then
    git checkout ${branch}
fi

# 獲取默認commit-hash
if [[ -z "$begin_hash" ]] && [[ -z "$end_hash" ]] ; then
    for p in $(git log --pretty=oneline -2) ; do
        if [[ ${#p} -eq 40 ]]; then
            if [[ -z ${begin_hash} ]]; then
                begin_hash=${p}
            else
                end_hash=${p}
                break
            fi
        fi
    done
fi

is_begin_has=false

# 是否當前最新commit
if [[ $(git log --pretty=oneline -1) == *${begin_hash}* ]]; then
    is_begin_has=true
fi

# 非當前最新分支commit,回滾到原始版本,可能當時maven原始配置不支持compile或會出現構建失敗(如:使用本地倉/私有倉庫等)
if [[ ${is_begin_has} = false ]]; then
    project_path=$(pwd)
    project_name=${project_path##*/}
    cd ..
    build_project_name=${project_name}_build_temp_project
    if [[ ! -d ${build_project_name} ]]; then
        mkdir ${build_project_name}
    fi
    \cp -rf  ${project_name}/.  ${build_project_name}
    cd ${build_project_name}
    git reset --hard ${begin_hash}
fi
複製代碼
2.2.2.1 校驗是否git倉庫代碼
if [[ ! -d ".git" ]]; then
    ECHO error: please init  Git Repository
    exit 1;
fi
複製代碼
2.2.2.2 檢查是否須要切換分支
if [[ ! -z ${branch} ]]; then
    git checkout ${branch}
fi
複製代碼
2.2.2.3 是否須要設置默認構建的commit值

若執行構建時,沒給添加 --begin_hash= 和 --end_hash= 進行賦值,則默認使用最新的兩次commit來進行增量部署。

經過 git log --pretty=oneline -2 獲取最近兩次commit的hash

# 獲取默認commit-hash
if [[ -z "$begin_hash" ]] && [[ -z "$end_hash" ]] ; then
    for p in $(git log --pretty=oneline -2) ; do
        if [[ ${#p} -eq 40 ]]; then
            if [[ -z ${begin_hash} ]]; then
                begin_hash=${p}
            else
                end_hash=${p}
                break
            fi
        fi
    done
fi
複製代碼
2.2.2.4 校驗傳參的begin_hash值是否爲當前分支最新commit hash

若非當前分支最新commit hash,則須要回滾到對應commit,進行項目構建編譯

if [[ $(git log --pretty=oneline -1) == *${begin_hash}* ]]; then
    is_begin_has=true
fi
複製代碼
2.2.2.5 若begin_hash非當前最新commit hash

若傳參begin_hash的值非當前最新commit hash。則須要回滾到對應commit進行構建編譯。

  1. 將現有項目進行拷貝到新的目錄環境
  2. 到新目錄環境對項目進行reset,用於構建項目
if [[ ${is_begin_has} = false ]]; then
    project_path=$(pwd)
    project_name=${project_path##*/}
    cd ..
    build_project_name=${project_name}_build_temp_project
    if [[ ! -d ${build_project_name} ]]; then
        mkdir ${build_project_name}
    fi
    \cp -rf  ${project_name}/.  ${build_project_name}
    cd ${build_project_name}
    git reset --hard ${begin_hash}
fi
複製代碼

2.2.3 Maven對欲構建項目進行編譯

對項目進行編譯,生成對應class文件以及相關配置文件

mvn clean compile -q -DskipTest
複製代碼

若歷史版本中存在使用本地倉庫,而maven中沒有配置好的狀況能夠從新配置,經過scope以及systemPath進行引入,如:

<dependency>
    <groupId>cn.catalpaflat</groupId>
    <artifactId>core</artifactId>
    <version>1.0.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/core-1.0.jar</systemPath>
</dependency>
複製代碼

2.2.4 建立增量部署文件夾

爲了防止增量文件夾被刪除或者被commit到git倉庫,能夠統一化到一個目錄中,並經過 .gitignore 對其進行忽略。能夠比對每次增量部署的差別

build_path=build-path/
current_date=`date +%Y%m%d%H%m%s`

if [[ ! -d "$build_path$current_date" ]]; then
    mkdir -p ${build_path}${current_date}
else
    rm -rf ${build_path}${current_date}
    mkdir -p ${build_path}${current_date}
fi
複製代碼

2.2.5 檢索項目 target目錄

若項目爲Maven項目,而且是Java項目,因爲存在Maven多模塊狀況,須要檢索每一個模塊下的編譯後的代碼路徑,用於後續進行class等文件的拷貝。

default_target_paths=()
default_java_file=java

module_index=0
# 檢索當前項目是否maven多模塊開發,遞歸檢索,並設置其編譯後的代碼位置(暫只提供了java類型)
obtain_module(){
    for module in ` cat ./pom.xml | grep '<module>' | awk -F '>' '{print $2}' | awk -F '<' '{print $1}' `
    do
        cd ${module}

        if [[ ! -d "/pom.xml" ]]; then
           module_exist=`cat ./pom.xml | grep '<module>' | awk -F '>' '{print $2}' | awk -F '<' '{print $1}'`
           if [[ -z ${module_exist} ]]; then
                if [[ ! -d "/target" ]]; then
                    if [[ -z $1 ]]; then
                        default_target_paths[module_index]=${module}/target/classes
                    else
                        default_target_paths[module_index]=$1/${module}/target/classes
                    fi
                    ((module_index++))
                fi
           else
                 if [[ -z $1 ]]; then
                      obtain_module ${module}
                 else
                      obtain_module $1/${module}
                 fi
           fi
        fi
        cd ..
    done
}

obtain_module
複製代碼

2.2.6 檢索並拷貝變動文件到增量文件夾

  1. git diff --name-only 排查兩次commit之間文件差別
  2. 並將begin_hash的commit 編譯後的代碼拷貝到增量文件夾中,以備後續打包進行部署
# 經過git diff --name-only實現兩次commit之間文件差別,而且將begin_hash的代碼進行編譯後,將差別的文件拷貝到「增量文件夾」中,以備後續進行增量部署

for file_path in $(git diff --name-only ${begin_hash} ${end_hash}) ; do
    package_path=${file_path%/*}
    file_name=${file_path##*/}
    file_type=${file_name##*.}
	# 文件所在校驗文件夾是否建立
    if [[ ${package_path} != *.* ]]; then
          if [[ ! -d "./${build_path}${current_date}/$package_path" ]] ; then
                mkdir -p ./${build_path}${current_date}/${package_path}
           fi
    fi
	# 是否java
    if [[ ${file_type} = ${default_java_file} ]]; then
        module_path=${package_path##*java}
        file_class_name=${file_name%.*}
        module_type=${package_path%%/*}
		# 排查在哪一個maven模塊路徑下
        for default_target_path in ${default_target_paths[@]}; do
            target_module_path=$(echo ${default_target_path} | awk -F '/target/' '{print $1}')
            file_target_module_path=$(echo ${package_path} | awk -F '/src/' '{print $1}')
            file_target_package_path=$(echo ${package_path} | awk -F '/src/main/java/' '{print $2}')
            default_module_type=${default_target_path%%/*}
            if [[ ${target_module_path} = ${file_target_module_path} ]]; then
            	# 排查到對應maven模塊的target目錄,進行cp操做
                cp -afx ${default_target_path}/${file_target_package_path}/${file_class_name}* ./${build_path}${current_date}/${package_path}
            fi
        done

    else
    # 非java文件,直接拷貝文件到對應目錄下
        if [[ ${package_path} != *.* ]]; then
            if [[ ! -d "./${build_path}${current_date}/$package_path" ]] ; then
                mkdir -p ./${build_path}${current_date}/${package_path}
            fi
        else
             package_path=${package_path%/*}
        fi

        cp -afx ${file_path} ./${build_path}${current_date}/${package_path}

    fi
done
複製代碼

源碼直通車!!!

到此爲止,1.0版本的簡陋版本初步完成,目測可使用,哈哈哈哈

相關文章
相關標籤/搜索