如何使用 gitlab api 触发 CI

Gitlab 是一个类似于 Github 用于托管和审查代码,管理项目和构建软件;相较于 Github,其源代码本身也开源,允许用户自行搭建部署,是很多公司管理代码首选。可以通过 Webhook 或者 api 来触发 ci,配合 gulp 等工具,以此来构建更加完整的自动化工作流。本文将带你初步学会使用 Gitlab api,更多深入使用请参考官方文档

准备

首先,对于我们需要通过 api 触发的项目 IDE,来到项目页面,顺序点击图中 1.2.3. 来到设置界面。

然后就能打开添加 trigger 的页面,描述完 trigger 的用途后点击 add trigger 按钮就能生成我们的 token,我们需要使用此 token 来触发 ci。

然后我们就能通过以下方式来触发 ci。

curl -X POST -F token=TOKEN -F ref=REF_NAME https://gitlab.example.com/api/v4/projects/project_id/trigger/pipeline

如果你打开的设置页面与上图不同,可能是你没有该项目的权限,那你需要找同事帮你申请 token ,及上面这条链接。

上述命令中我们需要将 TOKEN 替换为 我们自己的 token,将 REF_NAME 替换为目标分支名,则将在该目标分支上触发 ci。

其中 https://gitlab.example.com/api/v4/projects/project_id/trigger/pipeline 是打开上述设置页面自动生成的,不需要我们修改,不同项目的连接不同。

如果想给 ci 传入环境变量,可通过以下方式。

curl -X POST -F token=TOKEN -F ref=REF_NAME "variables[v_toolkit]=${version}" https://gitlab.example.com/api/v4/projects/project_id/trigger/pipeline

然后在我们的代码中通过以下方式获取环境变量:

console.log(`process.env.v_toolkit: ${process.env.v_toolkit}`);

通过环境变量,我们还能控制只触发 ci 中特定的 job,而绕过其他的 job。需要注意的是,通过 api 的方式是不能触发需要手动触发的 job 的,所以我们不要将 job 设为手动触发,而是通过传入环境变量,来控制 ci 中特定的 job 的实际运行。

举例说明

我们可以通过 gulp 脚本来触发 ci 。

// gulpfile.js
const path = require("path");
const cp = require("child_process");
const gulp = require("gulp");

function runci(cb) {
  const versionIndex = process.argv.indexOf("--ve");
  // 读取我们命令行输入的版本号
  const version = process.argv[versionIndex + 1];
  const branchIndex = process.argv.indexOf("--br");
  const branch = process.argv[branchIndex + 1];
  console.log(`version: ${version}`);
  console.log(`branch: ${branch}`);
  cp.execSync(
    `curl -X POST -F token=你的TOKEN -F "ref=${branch}" -F "variables[v_transformer]=${version}" https://gitlab.example.cn/api/v4/projects/1234/trigger/pipeline`
  );
  cb();
}

module.exports = {
  runci
};

命令行输入:

npx gulp runci --ve 1.0.0 --br master

如此,即完成了通过 Gitlab api 触发 CI 相关工作。

深入一点

我们接着上面的讲,初步实现一个完整的工作流。以下是一个 CI 配置示例:

image: node:lts

cache:
  paths:
    - node_modules/
    - your-toolkit/node_modules/

before_script:
  - npm install --registry=https://registry.npm.taobao.org
  - npx envinfo

stages:
  - build

build-toolkit:
  stage: build
  # when: manual
  script:
    - cd your-toolkit && rm -rf ./*.vsix
    - node build.js
  artifacts:
    paths:
      - your-toolkit/*.vsix
    when: on_success
    expire_in: 3 weeks
  tags:
    - docker

安装了 gulp 后,命令行输入:

npx gulp runci --ve 1.0.0 --br master

会在 master 分支触发 build-toolkit 任务,然后会运行我们的 build.js 脚本来执行构建任务。

// build.js
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
// @xyz/xchat-node 外网不可用
const XChat = require("@xyz/xchat-node/dist/index.js").default;

const xchat = new XChat('yourAccount', 'yourSecretKeyForUserApi', '');
const fileName = "./package.json";
var file = require(fileName);

console.log(`process.env.v_toolkit: ${process.env.v_toolkit}`)
// process.env.v_toolkit = process.env.v_toolkit || "1.0.0"
if (!process.env.v_toolkit) {
  console.log("请提供一个版本号文件。");
  console.log("pass");
} else {
  let version = process.env.v_toolkit;
  console.log(`version: ${version}`)
  // 可以在此执行咱们的构建任务
  console.log(`很棒,已构建成功!`)
  // 通知用户构建成功
  xchat.sendMsgToUser({
    account: "your account",
    fromUserCode: "your account",
    toUserCode: "target account",
    chatType: "TEXT",
    msgText: "build-toolkit success!",
    showFlag: 0,
    ts: new Date().toString()
  }, 'build-toolkit success!').then(r => console.log(r));
}

构建成功后就会立即通过特定通讯工具通知给用户。而 Gitlab 也可以通过浏览器通知我们构建成功,不过相比较而言 gitlab 通知的就要慢很多了,而且通知的消息也没那么灵活。

如果贵公司有内部通信工具,将 @xyz/xchat-node 换成对应通信工具 node 封装库即可。或者直接将 xchat 换成 nodemailer,构建完成后发 Email 通知用户,更加通用

  • 安装 nodemailer
npm install nodemailer --save
  • 使用 nodemailer
"use strict";

const nodemailer = require("nodemailer");

let transporter = nodemailer.createTransport({
  // host: 'smtp.ethereal.email',
  service: "qq", // 使用了内置传输发送邮件 查看支持列表:https://nodemailer.com/smtp/well-known/
  port: 465, // SMTP 端口
  secureConnection: true, // 使用了 SSL
  auth: {
    user: "xxxxxx@qq.com",
    // 这里密码不是qq密码,是你设置的smtp授权码
    pass: "xxxxxx"
  }
});

let mailOptions = {
  from: '"我是小可爱" <xxxxx@qq.com>', // sender address
  to: "xxxxxxxx@163.com", // list of receivers
  subject: "Hello", // Subject line
  // 发送text或者html格式
  // text: '我是小可爱?', // plain text body
  html: "<b>我是小可爱?</b>" // html body
};

// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => {
  if (error) {
    return console.log(error);
  }
  console.log("Message sent: %s", info.messageId);
});

备注:关于 nodemailer 的更多使用,请参考其官网说明