# Pipeline 入门

# 什么是CORNERSTONE Pipeline?

CORNERSTONE Pipeline (或简称为 "Pipeline" ),它是一个自动化服务模块,可用于自动化各种任务, 如构建,测试和部署软件。跟Jenkins的流水线类似,CORNERSTONE通过Pipeline实现了持续集成(CI)和持续交付(CD)的功能。

需要注意的是,CORNERSTONE Pipeline虽然兼容了大量Jenkins流水线的概念,但依然与Jenkins流水线有明显的不同,Jenkins流水线的设计思想表达了这样一种流程:将基于版本控制管理的软件持续的交付到用户手中。

而与Jenkins不同,CORNERSTONE Pipeline希望通过提供一些基础的概念设计以及命令函数,来允许用户自定义的实现自己想要的功能(代码的持续集成,APP的编译发布,Sonar代码检查等等)。 换言之,CORNERSTONE Pipeline更像是一种粘合剂,我们并不直接提供特定功能,Pipeline最终能实现何种功能,取决于您如何使用它。

下面我们先从一个简单的例子开始讲起。

# Hello Pipeline

var pipeline={
    stages:{
        download:{
            node:'compile',
            workspace:'/opt/soft/',
            steps:{
                download(){
                    ret = $.sh('wget http://download.redis.io/releases/redis-4.0.14.tar.gz');
                    if(ret!=0){
                        $.print('下载Redis文件失败');
                    }
                }
            }
        }
    }
}

上面是一个最简单的Pipeline,它只实现了一个功能,在一台名叫compile的主机上,执行wget这个shell命令,以达成将redis安装包下载到/opt/soft/目录的目的。如果下载失败, 那么打印输出下载Redis文件失败。

让我们先忽略掉stages,node,workspace,steps这些概念,我们会在接下来的章节一一讲到他们。您可以看出实际上一个pipeline就是一段JavaScript的脚本。在满足我们对pipeline基础格式的前提下, 您可以基于JavaScript语法任意书写pipeline来实现您的功能。

下面我们会先开始讲解pipeline中的一些基本的概念,等概念讲解完毕后,我们会继续讲解pipeline的基础格式要求。

# 入门指南

# Pipeline 基本概念

  1. stages

    描述了 Pipeline 的一个阶段集合,Pipeline 实际上就是由多个 stage 组成,每个 stage 完成对应的一组事情。 在语法上,stages是pipeline对象里面的一个属性,stages属性本身也是一个对象。 我们规定,stages对象里面定义的任意属性都是一个stage阶段,Pipeline会按照stages对象中属性出现的顺序执行各个stage阶段。

    让我们回看下上一节的例子:

    var pipeline={
        stages:{
            download:{
                node:'compile',
                workspace:'/opt/soft/',
                steps:{
                    download(){
                        ret = $.sh('wget http://download.redis.io/releases/redis-4.0.14.tar.gz');
                        if(ret!=0){
                            $.print('下载Redis文件失败');
                        }
                    }
                }
            }
        }
    }
    

    在这个简单的例子中,我们在Pipeline中只定义了一个download的stage。Pipeline并不强制约束您拆分stage的方式,这取决于您流水线的复杂程度。 例如,您可以根据您流水线的实际情况,将stage拆分为"compile"、"test"和"deploy"等等。

    每一个Stage中,有三个属性有特殊含义,分别是node,workspace,steps。

  2. node

    node是指主机,您可以回顾我们前面章节的讲解。node的有效取值是在主机页面定义的某一个主机的名称。 node是必填字段,pipeline中需要至少有一个node属性字段。

    node属性可以存在于pipeline对象中,和stages属性平级,如果定义在此处,意味着pipeline中所有的操作都是执行在该主机下。

    node属性也可以存在于某个stage对象中(就像我们上面的例子一样),这意味着这个stage阶段里面的所有操作都会执行在该主机下。

  3. workspace

    workspace是指pipeline运行在主机时所在的文件目录,需要跟node配合使用。还是以我们上面的例子为例,在这个例子里面,node为compile, workspace为/opt/soft/。也就是最终redis的包会下载到名字为compile的主机的/opt/soft/目录下,如果该主机上/opt/soft/目录不存在,pipeline会执行报错。

  4. steps

    steps是一个对象,我们规定,所有定义在steps中的函数都是一个step(执行步骤)。pipeline会按照step出现的先后顺序来逐一执行。

# Pipeline 方法摘要

在上面的例子中我们看到最终在download step函数中,shell命令通过$.sh()来执行,其中sh就是pipeline的函数,pipeline约定所有的函数都是需要以$.函数名()的方式来调用。 下面是全部的pipeline函数。

返回值 方法名和摘要
int sh(String cmd)
执行命令

cmd: Shell命令
void copyFrom(String machineName,String remoteFilePath,String localFilePath)
拉取指定节点[主机]指定远程目录下的文件至本地目录

machineName:主机节点名称
remoteFilePath:远程文件路径
localFilePath:本地文件路径
void copyTo(String localFilePath,String machineName,String remoteFilePath)
拷贝本地路径下的文件至指定节点[主机]目录。

remoteFilePath:本地文件路径
machineName:主机节点名称
remoteFilePath:远程文件路径
String sendNotification(String userName,String content)
发送通知给指定用户

userName:系统用户名
content:通知内容
boolean portTest(String host,int port)
测试host对应服务器port端口是否在监听

host: 主机IP或域名
port:端口
void sleep(long millis)
休眠。

millis: 毫秒
void print(String message)
打印消息,并记录消息内容。

message: 打印消息内容
void printQRCode(String message)
打印二维码消息,并记录消息内容。

message: 打印消息内容
Object parameter(String parameterName)
接收参数

parameterName: 参数名称

==参数接收超时时间为:60s==
void abort(String message)
终止

message: 终止说明,并记录日志内容

sh的额外说明:

一次性执行多条shell的方法:

  1. 每个命令之间用;隔开

    各命令的执行给果,不会影响其它命令的执行。换句话说,各个命令都会执行,但不保证每个命令都执行成功。

  2. 每个命令之间用&&隔开

    若前面的命令执行成功,才会去执行后面的命令。这样可以保证所有的命令执行完毕后,执行过程都是成功的。

  3. 每个命令之间用||隔开

    ||是或的意思,只有前面的命令执行失败后才去执行下一条命令,直到执行成功一条命令为止。

# Pipeline 运行状态

用户可以通过Pipeline执行窗口,查阅当前执行的状态

# Pipeline 入门总结

在了解完Pipeline所有的基本属性后,我们可以回过头来总结下Pipeline的基础写法。

var pipeline={
    stages:{         
        stage1:{
            node:"主机名称",
            workspace:'工作位置',
            steps:{
                step1(){
                    $.sh('echo helloworld');
                },
                //...
            }
        },
        stage2:{
            //...
        }
    }
};
  • 首先Pipeline中必须定义一个pipeline对象,固定写法var pipeline={};
  • stages是执行阶段的集合,stages对象里面定义的所有变量都是一个stage,我们要求stages中至少要有一个stage阶段。
  • stage中可以定义node和workspace的值。
  • steps是stage中执行步骤的汇总,所有定义在steps中的函数都是一个step(执行步骤)。pipeline会按照step出现的先后顺序来逐一执行
  • 我们可以通过$.xxx()来执行pipeline函数。

# Pipeline 构建触发器

在持续集成的过程中,我们经常会遇到一些需要定期执行的Pipeline任务。比如我们需要在明天早上9点前,发布最新的应用版本。此时就需要通过定时任务来辅助我们完成这些定时的触发任务,已达到周期进行构建的目的。 我们通过Cron表达式来完成定时任务的工作。Cron表达式的格式如下:

Cron 结构说明 从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份

Cron表示式示例

表示式 说明
0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
0 0 12 * * ? 每天12点运行
0 15 10 ? * * 每天上午10:15触发
0 15 10 * * ? * 每天上午10:15触发
0 15 10 * * ? 2020 在2020年的每天10:15运行
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 12 ? * WED 表示每个星期三中午12点
0 15 10 15 * ? 每月15日上午10:15触发
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
0 15 10 ? * 6L 2019-2020 2019年至2020年的每月的最后一个星期五上午10:15触发