文档章节

前端发布脚本

开源中国董事会主席
 开源中国董事会主席
发布于 2014/06/03 11:53
字数 1692
阅读 2470
收藏 9
#!/bin/bash
#
# description: 该文件是基于 Git 为版本管理系统的前端自动化发布脚本,也实用与如 PHP、Python 等脚本语言系统。
#              该脚本主要是为了实现 javascript、css 文件的在发布时自动压缩。
#              该脚本是基于 YUI Compressor (http://yui.github.io/yuicompressor/) 来实现 javascript、css 文件的压缩。
#               所以,运行该脚本,需要 Java 环境支持。
#              由于,在 javascript 代码书写不规范的情况下,容易导致压缩后的 javascript 不可用;所以,在生产环境发布之前,一定要经过严格的测试.
#
#              执行流程:(1)如果是第一次发布时,会从 Git 仓库 clone 一份代码到 PROJECT_DIR;或非第一次发布时,会切换到 PROJECT_DIR 执行 git pull 命令;
#                        (2)将当次更新的文件,记录到 UPDATE_LIST_FILE 文件中;
#                        (3)将 javascript 和 css 文件,压缩输出到 WEB_ROOT,非 javascript 和 css 文件或在压缩文件时出错,则 copy 到 WEB_ROOT;
#                        (4)从 WEB_ROOT 下删除已经从 Git 仓库中删除了的文件和目录;
#                        (5)发布完成。
#
#              PROJECT_DIR:项目源目录,WEB_ROOT:网站根目录。之所以,不直接在 WEB_ROOT 下压缩,是为了,避免压缩后的文件与 Git 仓库中更新下来的文件产生冲突。
#

set -e

PROJECT_NAME=""
PROJECT_DIR=""
WEB_ROOT=""
LOG_DIR="/var/log/"
YUICOMPRESSOR_JAR="/home/admin/script/release/lib/yuicompressor.jar"

CHARSET="UTF-8"
GIT_CHAESET="UTF-8"

GIT_PROTOCOL=${GIT_PROTOCOL-"ssh"}
GIT_HOST=${GIT_HOST-""}
GIT_PORT=${GIT_PORT-0}
GIT_USER=${GIT_USER-""}
BRANCH=${BRANCH-"master"}
# WEB_ROOT 所属用户组
GROUP=${GROUP-"nobody"}
# WEB_ROOT 所属用户
USER=${USER-"nobody"}

OPTIONS=""

note() {
    printf "$*\n" 1>&2;
}

warning() {
    printf "warning: $*\n" 1>&2;
}

error() {
    printf "error: $*\n" 1>&2;
    exit 1
}

usage() {
    me=`basename "$0"`

    echo "Usage: $me {release|rollback|h|help} [options]"
    echo "       -p, --project-name=项目名称        Git 项目名称"
    echo "       --project-dir=项目路径                项目项目源码存放路径"
    echo "       --web-root=WEB ROOT                项目关联网站根目录"
    echo "       --charset=项目字符集                项目文件字符集"
    echo "       --git-charset=Git 字符集            Git 终端显示字符集"
    echo "       --git-protocol=Git 协议            Git 请求协议"
    echo "       --git-host=Git 主机名称            Git 主机名称"
    echo "       --git-port=Git 主机端口            Git 主机端口"
    echo "       --git-user=Git 用户名                Git 用户名"
    echo "       -b, --branch=分支名称                当前使用分支"
    echo "       -g, --group=文件所属组                WEB ROOT 文件所属用户组"
    echo "       -u, --user=文件所说用户            WEB ROOT 文件所属用户"
    echo "       --log-dir=日志目录                    log direetory"

    exit 1
}

check() {
    if [ -z "$PROJECT_NAME" ]; then
        error "project name could not be empty"
    fi

    if [ -z "$PROJECT_DIR" ]; then
        error "project direetory could not be empty"
    fi

    if [ -z "$WEB_ROOT" ]; then
        error "project web root could not be empty"
    fi
}

update_files_init() {
    local arg=$1
    local path="$PROJECT_DIR/$arg"

    if [ -d "$path" ]; then
        local files=`ls "$path"`

        for file in $files
        do
            local _path="$path/$file"
            local temp="$file"

            if [ ! "$arg" == "" ]; then
                temp="$arg/$temp"
            fi

            if [ -d "$_path" ]; then
                update_files_init "$temp"
            else
                echo "$temp" >> $UPDATE_LIST_FILE
            fi
        done
    elif  [ -f "$path" ]; then
        echo "$arg" >> $UPDATE_LIST_FILE
    else
        warning "$path is not exists";
    fi

    return 0
}

condense() {
    local type=$1
    local source_file=$2
    local target_file=$3
    local MSG="Compression "

    if [ "$type" == "js" ]; then
        MSG=$MSG"javascript"
    else
        MSG=$MSG"css"
    fi
    MSG=$MSG" file $source_file to $target_file"

    note $MSG" success"
    java -jar ${YUICOMPRESSOR_JAR} --type ${type} --charset ${CHARSET} "$source_file" -o "$target_file" || { warning "$MSG failure"; note "so copy $source_file to $target_file"; \cp $source_file $target_file; }

    chown ${USER}:${GROUP} "$target_file"
}

operate() {
    local file=$1

    if [ ! -z "$file" ]; then
        local source_file="$PROJECT_DIR/$file"
        local target_file="$WEB_ROOT/$file"
        local target_dir=`dirname "$target_file"`

        if [ -f "$source_file" ]; then
            mkdir -p "$target_dir" || { warning "create target directory $target_dir failure"; }

            if [[ "$source_file" =~ .js$ ]]; then
                condense "js" "$source_file" "$target_file"
            elif [[ "$source_file" =~ .css$ ]]; then
                condense "css" "$source_file" "$target_file"
            else
                note $"Copy file $source_file to $target_file"
                cp "$source_file" "$target_file"
            fi

            chown ${USER}:${GROUP} "$target_file"
        fi
    fi
}

delete_file() {
    local file=$1

    if [ ! -z "$file" ]; then
        local source_dir=`dirname "$PROJECT_DIR/$file"`
        local target_file="$WEB_ROOT/$file"
        local target_dir=`dirname "$target_file"`

        if [ ! -d "$source_dir" ]; then
            note "clear directory $target_dir"
            rm -fR "$target_dir"
        else
            note "Delete file $target_file"
            rm -f "$target_file"
        fi
    fi
}

release() {
    note "Release project $PROJECT_NAME";

    check

    local git_dir=$PROJECT_DIR"/.git"
    if [ ! -d $git_dir ]; then
        if [ -z "$GIT_PROTOCOL" ]; then
            error "project git protocol could not be empty"
        fi

        if [ -z "$GIT_HOST" ]; then
            error "project git host could not be empty"
        fi

        if [ -z "$GIT_PORT" ]; then
            error "project git port could not be empty"
        fi

        if [ -z "$GIT_USER" ]; then
            error "project git username could not be empty"
        fi

        note "initialize project $PROJECT_NAME with source direetory $PROJECT_DIR and WEB ROOT $WEB_ROOT";
    fi

    mkdir -p $LOG_DIR || { error "create log directory $LOG_DIR failure"; }

    if [ -f $UPDATE_LIST_FILE ]; then
        rm $UPDATE_LIST_FILE || { error "Remove $UPDATE_LIST_FILE failure"; }
    fi

    if [ -d $PROJECT_DIR ]
    then
        cd $PROJECT_DIR
        git pull origin ${BRANCH} > $RELEASE_LOG || { error "git pull code failure"; }

        local temp=`grep 'Already up-to-date' $RELEASE_LOG`
        if [ "$temp" == "" ]; then
            note "List update files";
        else
            warning "Already up-to-date";
            exit 1;
        fi

        note "checkout $BRANCH branch"
        git checkout ${BRANCH}
        sleep 1
        git diff-tree -r --name-status --no-commit-id ORIG_HEAD HEAD > $UPDATE_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^A' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            operate "$temp";
        done < $UPDATE_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^M' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            operate "$temp";
        done < $UPDATE_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^D' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            delete_file "$temp";
        done < $UPDATE_LIST_FILE
    else
        cd `dirname "$PROJECT_DIR"`

        local project_git_url="$GIT_PROTOCOL://$GIT_USER@$GIT_HOST:$GIT_PORT/$PROJECT_NAME"
        git clone ${project_git_url} > $RELEASE_LOG || { error "git clone $PROJECT_NAME form $GIT_URL error"; }

        cd $PROJECT_DIR

        note "Checkout $BRANCH branch"
        git checkout ${BRANCH}
        sleep 1

        note "List update files"

        update_files_init "" || { error "update files init failure"; }

        local files=`cat $UPDATE_LIST_FILE`

        for file in $files
        do
            operate "$file"
        done

        chown ${USER}:${GROUP} "$WEB_ROOT"
    fi
}

rollback() {
    note "Rollback project $PROJECT_NAME";

    check

    if [ "$OPTIONS" == "" ]; then
        error "rollback options could not be empty";
    else
        cd $PROJECT_DIR

        note "Checkout $BRANCH branch"
        git checkout ${BRANCH}

        sleep 1

        git reset --hard $OPTIONS
        git diff-tree HEAD HEAD^ -r --name-status > $ROLLBACK_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^A' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            delete_file "$temp";
        done < $ROLLBACK_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^M' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            operate "$temp";
        done < $ROLLBACK_LIST_FILE

        while read i;
        do
            temp=`echo $i|awk -F '^D' '{gsub("\"", "", $2); gsub(/^ *| *$/, "", $2); print $2;}'`;
            operate "$temp";
        done < $ROLLBACK_LIST_FILE
    fi
}

ACTION=$1
shift

args_count=$#
args_count=`expr $args_count - 1`
i=0

while test $i -le $args_count ;
do
    case "$1" in
        -p=*)
            PROJECT_NAME=${1##-p=}
            shift
            i=`expr $i + 1`
            ;;
        --project-name=*)
            PROJECT_NAME=${1##--project-name=}
            shift
            i=`expr $i + 1`
            ;;
        --project-dir=*)
            PROJECT_DIR=${1##--project-dir=}
            shift
            i=`expr $i + 1`
            ;;
        --web-root=*)
            WEB_ROOT=${1##--web-root=}
            shift
            i=`expr $i + 1`
            ;;
        --charset=*)
            CHARSET=${1##--charset=}
            shift
            i=`expr $i + 1`
            ;;
        --git-charset=*)
            GIT_CHAESET=${1##--git-charset=}
            shift
            i=`expr $i + 1`
            ;;
        --git-protocol=*)
            GIT_PROTOCOL=${1##--git-protocol=}
            shift
            i=`expr $i + 1`
            ;;
        --git-host=*)
            GIT_HOST=${1##--git-host=}
            shift
            i=`expr $i + 1`
            ;;
        --git-port=*)
            GIT_PORT=${1##--git-port=}
            shift
            i=`expr $i + 1`
            ;;
        --git-user=*)
            GIT_USER=${1##--git-user=}
            shift
            i=`expr $i + 1`
            ;;
        -b=*)
            BRANCH=${1##-b=}
            shift
            i=`expr $i + 1`
            ;;
        --branch=*)
            BRANCH=${1##--branch=}
            shift
            i=`expr $i + 1`
            ;;
        -g=*)
            GROUP=${1##-g=}
            shift
            i=`expr $i + 1`
            ;;
        --group=*)
            GROUP=${1##--group=}
            shift
            i=`expr $i + 1`
            ;;
        -u=*)
            USER=${1##-u=}
            shift
            i=`expr $i + 1`
            ;;
        --user=*)
            USER=${1##--user=}
            shift
            i=`expr $i + 1`
            ;;
        --log-dir=*)
            LOG_DIR=${1##--log-dir=}
            shift
            i=`expr $i + 1`
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            case "$ACTION" in
                rollback)
                    OPTIONS=$OPTIONS" "$1
                    shift
                    i=`expr $i + 1`
                    ;;
                *)
                    usage
                    exit 0
                    ;;
            esac
    esac
done

# 设置编码和文件名允许中文等字符  
git config --global core.quotepath false         # 设置文件名允许中文等字符
git config --global i18n.logoutputencoding ${GIT_CHAESET} # 设置 git log 输出时编码
export LESSCHARSET=${GIT_CHAESET}

RELEASE_LOG=$LOG_DIR"/release.log"
UPDATE_LIST_FILE=$LOG_DIR"/update_list.txt"
ROLLBACK_LIST_FILE=$LOG_DIR"/rollback_list.txt"

case "$ACTION" in
    release|rollback)
        $ACTION
        ;;
    *)
        echo "Usage: $0 {release|rollback}"
        ;;
esac

exit 0

#!/bin/bash

PROJECT_NAME="project"
DOMAIN="www.example.com"
HOME_ROOT="/data/projects"
SOURCE_DIR=$HOME_ROOT"/source"
PROJECT_DIR=$SOURCE_DIR"/"$PROJECT_NAME
WEB_ROOT="/data/htdocs/"$DOMAIN
LOG_DIR=$HOME_ROOT"/logs/"$PROJECT_NAME

GIT_HOST=${GIT_HOST-"git host"}
GIT_PORT=${GIT_PORT-git port}
GIT_USER=${GIT_USER-"git user"}
BRANCH="master"

CHARSET="UTF-8"
GIT_CHARSET="UTF-8"

GROUP="www"
USER="www"

RELEASE_EXCE=$(cd "$(dirname "$0")"; pwd)
RELEASE_EXCE=$RELEASE_EXCE"/release_static.sh"

ACTION=$1
shift

args_count=$#
args_count=`expr $args_count - 1`
OPTION=""
i=0

while test $i -le $args_count ;
do
    OPTION=$OPTION" "$1
    shift
    i=`expr $i + 1`
done

case "$ACTION" in
    release)
        bash ${RELEASE_EXCE} release -p="$PROJECT_NAME" --project-dir="$PROJECT_DIR" --branch="$BRANCH" --web-root="$WEB_ROOT" --git-protocol="ssh" --git-host="$GIT_HOST" --git-port=$GIT_PORT --git-user="$GIT_USER" --group="$GROUP" --user="$USER" --charset="$CHARSET" --git-charset="$GIT_CHARSET" --log-dir="$LOG_DIR"
        ;;
    rollback)
        bash ${RELEASE_EXCE} rollback -p="$PROJECT_NAME" --project-dir="$PROJECT_DIR" --branch="$BRANCH" --web-root="$WEB_ROOT" --group="$GROUP" --user="$USER" --charset="$CHARSET" --git-charset="$GIT_CHARSET" --log-dir="$LOG_DIR" $OPTION
        ;;
    *)
        bash ${RELEASE_EXCE} --help
        ;;
esac

exit 0

© 著作权归作者所有

共有 人打赏支持
开源中国董事会主席

开源中国董事会主席

粉丝 111
博文 20
码字总数 14304
作品 2
成都
高级程序员
加载中

评论(9)

开源中国董事会主席
开源中国董事会主席
修改了两处错误
blu10ph
blu10ph
h~
开源中国首席发言人
开源中国首席发言人
好!
许雷神
许雷神
好标题
开源中国董事会主席
开源中国董事会主席

引用来自“Bery”的评论

好文章,我只看了一下标题
不是小白
不是小白
好文章,我连标题都没看
花花兽
花花兽
好文章,我连标题都没看
后山悟道人
后山悟道人

引用来自“Bery”的评论

好文章,我只看了一下标题
嗯,是个好标题
不是胖子
不是胖子
好文章,我只看了一下标题
真▪一行代码完成从前端代码build到部署线上

在经历了6,7个项目同时开工,频繁发布测试 ,不得不学会一点偷懒的小技巧来提高效率了,所以这篇文章要讲的就是如何更加优化发布流程。 工作以来,经历了build后,然后用上传服务器完成部署。...

TimeFly
08/10
0
0
使用python构建一个简单的发布系统

一、流程 通过前端上传要发布的压缩包,逻辑端获取后将压缩包分发到远程服务器上,并执行远程服务器上使用shell编写的发布脚本,实现代码发布。 二、前端文件上传代码

yaoliang833
2016/11/22
0
0
阿里·云效平台成2016北京QCon大会亮点

4月21~23日,2016年北京QCon大会在北京国际会议中心举行,大会邀请了互联网领域的大咖分享国内外技术交流的最新趋势资讯,涉及自动化运维、移动开发挑战、移动测试技术、大数据平台架构等精彩...

云效平台
2016/04/21
0
0
我的架构经验系列文章 - 前端架构

框架层面:近几年前端发展很快,前端之所以叫前端因为前端是已经可以独立成为一种职业了,js也不再是十年前的玩具了,以前富客户端RIA的应用可能会用flash/flex或是silverlight,现在可以使用...

fadevd
2014/07/29
21
0
前端模块化解决方案--fis-pure

fis-pure 是基于FIS的纯前端模块化解决方案pure pure是基于FIS二次封装能力封装而成的解决方案,可以直接使用,也可以参考fis-pure自行封装或组合使用插件来构建属于自己的一体化解决方案。 ...

叶秀兰
2014/07/28
3.7K
0

没有更多内容

加载失败,请刷新页面

加载更多

linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
49分钟前
1
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
57分钟前
2
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
10
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
0
0
Coding and Paper Letter(四十)

资源整理。 1 Coding: 1.Tomislav Hengl撰写的非官方作者指南:Michael Gould•Wouter Gerritsma。 UnofficialGuide4Authors 2.R语言包rwrfhydro,社区贡献的工具箱,用于管理,分析和可视化...

胖胖雕
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部