3

gitlab runner运行完相关的测试或打包后,我们总想让它把结果实时主动的告知我们,这时候便需要一个钉钉机器人了。报着试试看的想法在docker hub上搜索,虽然找到了几个关键字相同的,但很遗憾这些作者都很懒,没有任何的使用说明。索性就自己来一个吧。

前言

如果你并不想知道我是如何从0到1完成的构建过程,而仅仅是需要一个可以在itlab runner下发送钉钉消息话,请移步docker官方仓库来快速满足当前需求。

选择语言

钉钉开放文档上给出了两种代码的推送示例,分别是JAVA及PHP。由于PHP是个不需要的脚本语言,所以这里我们使用PHP。

<?php  
function request_by_curl($remote_server, $post_string) {  
    $ch = curl_init();  
    curl_setopt($ch, CURLOPT_URL, $remote_server);
    curl_setopt($ch, CURLOPT_POST, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 
    curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    // 线下环境不用开启curl证书验证, 未调通情况可尝试添加该代码
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); 
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    curl_close($ch);                
    return $data;  
}  

$webhook = "https://oapi.dingtalk.com/robot/send?access_token=xxxxxx";
$message="我就是我, 是不一样的烟火";
$data = array ('msgtype' => 'text','text' => array ('content' => $message));
$data_string = json_encode($data);

$result = request_by_curl($webhook, $data_string);  
echo $result;
?>

虽然在bash也可以使用最原始的curl命令来达到这一效果,但从开发的效率来讲,这并不是最佳的选择。

image

语言确定使用php后,我选择比较常用的php:5.6版本,同时我们在此仅需要使用php的脚本功能,所以使用php:5.6-cli版本便可以。但本地启动docker服务后,执行:docker run php:5.6-cli /bin/bash,稍微等待一些时候,一个php:5.6-cli环境就搭建好了。

panjie@panjies-iMac yunzhiclub % docker run -it php:5.6-cli /bin/bash
root@d46c5475b769:/# php -v
PHP 5.6.40 (cli) (built: Jan 23 2019 00:04:26) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
docker run -it php:5.6-cli /bin/bash 表示构造相应的容器后,进该容器.

最后我们使用exit命令退出容器,此时容器自动终止。

本地调试

接下来,我们在基于php:5.6-cli镜像上打造的contanier容器进行调试,步骤如下:

  1. 本地创建一个ding.php文件
  2. 复制官方的示例代码到ding.php文件中。
  3. 替换token为自己钉钉机器人token
  4. 将ding.php复制到contanier容器上
  5. 在容器上执行php ding.php查看效果

过程如下:

本地创建一个ding.php文件,然后我们复制官方的代码,并将access_token=xxx的部分进行替换:

<?php  
function request_by_curl($remote_server, $post_string) {  
    $ch = curl_init();  
    curl_setopt($ch, CURLOPT_URL, $remote_server);
    curl_setopt($ch, CURLOPT_POST, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 
    curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    // 线下环境不用开启curl证书验证, 未调通情况可尝试添加该代码
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); 
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    curl_close($ch);                
    return $data;  
}  

$webhook = "https://oapi.dingtalk.com/robot/send?access_token=这里替换为你自己的自定义钉钉机器人token";
$message="我就是我, 是不一样的烟火";
$data = array ('msgtype' => 'text','text' => array ('content' => $message));
$data_string = json_encode($data);

$result = request_by_curl($webhook, $data_string);  
echo $result;
?>

image.png

注意: 机器人类型一定是自定义机器

保存文件后使用docker run -t -d --name=php php:5.6-cli命令启动容器,然后继续执行docker cp ./ding.php php:/root/ding.php将本地的ding.php复制到容器中.

最后我们进行到容器中进行测试:

$ docker exec -it php /bin/bash
root@80adb7e221c3:/# cd /root
root@80adb7e221c3:~# php ding.php 
root@80adb7e221c3:~# exit

然后就可以在钉钉中发现测试的机器人推送的消息了:
image.png

mardown

但仅仅推送文本消息还是不够的,我们希望的是其推送带有PR地址的消息,这样当构造成功时,我们直接在钉钉中点击链接就可以直接打开PR的地址来完成合并操作了。

接下来,我们停止原容器,然后启动一个新容器,并使用挂载的方式将当前目录挂载到容器中,这样以来我们便可以在本地进行代码变更,然后在容器中来执行脚本进行测试了.

$ docker stop php
$ docker container rm php
$  docker run -it -v /Users/panjie/gitlab/yunzhiclub/process-evaluation/ding:/root --name=php php:5.6-cli /bin/bash 
root@488cfb9ae30a:/# cd /root
root@488cfb9ae30a:~# root@ddff97414a5c:~# ls
ding.php

注意: 上面的参数中少了-d参数,增加了-t参数及结尾的/bin/bash,作用是容器创建完成后便进入该容器。

其中: /Users/panjie/gitlab/yunzhiclub/process-evaluation/ding为ding.php所在的本地目录,必须是绝对路径, /root 为容器目录。

修改ding.php

钉钉官方文档,我们将推送机器人消息格式改成markdown。

<?php  
function request_by_curl($remote_server, $post_string) {  
    $ch = curl_init();  
    curl_setopt($ch, CURLOPT_URL, $remote_server);
    curl_setopt($ch, CURLOPT_POST, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 
    curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); 
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    curl_close($ch);                
    return $data;  
}  

$webhook = "https://oapi.dingtalk.com/robot/send?access_token=你自定义机器人的token";
$message = ["title" => "这是标题", "text" => "## 执行成功 \n 我就是我,不一样的烟火[这里是地址](https://www.baidu.com)"];

$data = array ('msgtype' => 'markdown','markdown' => $message);
$data_string = json_encode($data);

$result = request_by_curl($webhook, $data_string);  
echo $result;
echo PHP_EOL;
?>

在容器中执行php /root/ding.php 测试:

image.png

环境变量

在gitlab runner中的脚本中执行env便可以查看环境变量,然后我们找出需要的几项:

CI_MERGE_REQUEST_PROJECT_URL=https://xxx.xxx.com:xxx/yunzhiclub/process-evaluation
CI_MERGE_REQUEST_TITLE=用户管理 重置密码 和 编辑
CI_MERGE_REQUEST_IID=32
GITLAB_USER_LOGIN=weiweiyi189

用以拼接 [PR标题](PR地址)构建成功,运行者:weiweiyi189这条markdown信息。

PHP获取环境变量

为了使用PHP与gitlab runner中的环境变量相结合,我们对ding.php进行以下改造.

<?php  
$projectUrl = getenv("CI_MERGE_REQUEST_PROJECT_URL");
$prId = getenv("CI_MERGE_REQUEST_IID");
if (!$prId) {
    echo "当前脚本仅适用于PR" . PHP_EOL;
    exit(1);
}
$prUrl = $projectUrl . "/-/merge_requests/" . $prId;
$title = getenv("CI_MERGE_REQUEST_TITLE");
$author = getenv("GITLAB_USER_LOGIN");
$text = "## 😀 😃 😄 😁 😆 \n [$title]($prUrl)运行成功,提交者: $author";

function request_by_curl($remote_server, $post_string) {  
    $ch = curl_init();  
    curl_setopt($ch, CURLOPT_URL, $remote_server);
    curl_setopt($ch, CURLOPT_POST, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 
    curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); 
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    curl_close($ch);                
    return $data;  
}  

$webhook = "https://oapi.dingtalk.com/robot/send?access_token=你自定义机器的token";
$markdown = ["title" => "这是标题", "text" => $text];

$data = array ('msgtype' => 'markdown','markdown' => $markdown);
$data_string = json_encode($data);

$result = request_by_curl($webhook, $data_string);  
echo $result;
echo PHP_EOL;
?>

然后在容器中设计几个环境变量:

$ export CI_MERGE_REQUEST_PROJECT_URL=https://xxx.xxx.com:xxx/yunzhiclub/process-evaluation
$ export CI_MERGE_REQUEST_TITLE="title test"
$ export CI_MERGE_REQUEST_IID=32
$ export GITLAB_USER_LOGIN=weiweiyi189

最后再次执行php ding.php(注意:你可能需要更改下钉钉机器人关键字)

root@ddff97414a5c:~# php ding.php
{"errcode":0,"errmsg":"ok"}

image.png

剥离token

由于钉钉机器仅仅是token不一样,所以我们再把token也变成由环境变量中取值,最终完成版的ding.php

<?php  
$token = getenv("YZ_DING_TOKEN");
if (!$token) {
    echo "未检测到环境变量'YZ_DING_TOKEN'" . PHP_EOL;
    exit(1);
}
$projectUrl = getenv("CI_MERGE_REQUEST_PROJECT_URL");
$prId = getenv("CI_MERGE_REQUEST_IID");
if (!$prId) {
    echo "当前脚本仅适用于PR" . PHP_EOL;
    exit(1);
}
$prUrl = $projectUrl . "/-/merge_requests/" . $prId;
$title = getenv("CI_MERGE_REQUEST_TITLE");
$author = getenv("GITLAB_USER_LOGIN");
$text = "## 😀 😃 😄 😁 😆 \n [$title]($prUrl)运行成功,提交者: $author";

function request_by_curl($remote_server, $post_string) {  
    $ch = curl_init();  
    curl_setopt($ch, CURLOPT_URL, $remote_server);
    curl_setopt($ch, CURLOPT_POST, 1); 
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 
    curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0); 
    // curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    curl_close($ch);                
    return $data;  
}  

$webhook = "https://oapi.dingtalk.com/robot/send?access_token=$token";
$markdown = ["title" => "这是标题", "text" => $text];

$data = array ('msgtype' => 'markdown','markdown' => $markdown);
$data_string = json_encode($data);

$result = request_by_curl($webhook, $data_string);  
echo $result;
echo PHP_EOL;
?>

在使用前,我们先设置环境变量YZ_DING_TOKEN:

$ export YZ_DING_TOKEN=65adbb5f6d99955b86e0eff562e1d562exxxx
$ php ding.php

至此,我们更完成了本地测试的部分,接下来我们便可以把它打造为公用的image,然后上传到特定的仓库中了。关于如何借助阿里云的容器服务来自动构建,请参考如何创建一个运行angular单元测试的docker容器--DOCKER一节

使用阿里云构建image的过程略,构造会的image地址为:registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding:1.0.0

使用

至此,我们便可以在gitlab runner中使用这个用于钉钉消息推送的image了.

dingding-success:
  stage: notify
  tags:
    - docker
  image: registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding:1.0.0
  variables:
    YZ_DING_TOKEN: "65adbb5f6d99955b86e0eff562e1d562e85086cc18740e5c07b2xxxx"
  script:
    - php /root/ding.php

注意: 你的自定义机器的关键字,需要是运行成功,提交者的子字符串。

image.png

此时,当前image便可以在构建成功时向钉钉中发送运行成功的消息了。

image.png

当然了,你也可以自己改写ding.php中的内容,以达到更多的个性化定义机器人发送内容的需求。

构建官方DOCKER IMAGE

docker官方注册后,可以无限制的推公开的image。我们注册docker后,在本机安装docker desktop。

接下来使用阿里云构建好的image在本地进行构建:

$ docker run --name="dingding" registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding:1.0.0
$ docker image ls
REPOSITORY                                             TAG                     IMAGE ID       CREATED          SIZE
registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding   1.0.0                   3b817aa5b98b   19 minutes ago   344MB

由于docker官方仓库仅支持前缀为其用户名的image推送,所以我们在推送到官方仓库前,还需要将其重新命个名:

$ docker tag registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding:1.0.0 teacherpan/gitlab-dingtalk:1.0.0
$ docker image ls
registry.cn-beijing.aliyuncs.com/mengyunzhi/dingding   1.0.0                   3b817aa5b98b   19 minutes ago   344MB
teacherpan/gitlab-dingtalk                             1.0.0                   3b817aa5b98b   19 minutes ago   344MB

最后,打开docker desktop,找到以docker用户名为前缀的image,点击菜单中的push to hub

image.png

网速不同,上传的时间肯定也不相同:

image.png

最后,我们便可以在docker的官方库中找到它的身影并且成功的将其分享给其他的docker小伙伴了。

此时,我们便可以愉快的使用官方仓库了。

dingding-success:
  stage: notify
  tags:
    - docker
  image: teacherpan/gitlab-dingtalk
  variables:
    YZ_DING_TOKEN: "65adbb5f6d99955b86e0eff562e1d562e85086cc18740e5c07b2xxxx"
  script:
    - php /root/ding.php

最主要的,当别人对gitlab runner的钉钉推送有需求时,也可以在docker hub中快速的找到我们。


潘杰
3.1k 声望239 粉丝