skills/tencentblueking/bk-ci/pipeline-plugin-development

pipeline-plugin-development

SKILL.md

流水线插件开发完整指南

模块定位: 本指南详细介绍蓝盾(BK-CI)流水线插件的开发规范、配置方法、多语言实现示例、发布流程和调试技巧,帮助开发者快速上手插件开发。

一、插件开发概述

1.1 什么是流水线插件

流水线插件(Atom)是蓝盾流水线中的最小执行单元,用于完成特定的构建任务,如代码拉取、编译、测试、部署等。

1.2 支持的开发语言

语言 推荐度 SDK
Java ⭐⭐⭐⭐⭐ java-atom-sdk
Python ⭐⭐⭐⭐ python-atom-sdk
NodeJS ⭐⭐⭐ @tencent/nodejs_atom_sdk
Golang ⭐⭐⭐ golang-atom-sdk

1.3 插件开发流程

┌─────────────────────────────────────────────────────────────────────────┐
│                        插件开发完整流程                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  1. 初始化插件          2. 开发插件           3. 发布插件                │
│  ┌──────────────┐      ┌──────────────┐      ┌──────────────┐           │
│  │ 登录工作台    │  ──► │ 编写 task.json│ ──► │ 提交构建     │           │
│  │ 新增插件      │      │ 开发业务逻辑  │      │ 测试验证     │           │
│  │ 克隆代码库    │      │ 本地调试      │      │ 审核发布     │           │
│  └──────────────┘      └──────────────┘      └──────────────┘           │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

二、task.json 配置规范

2.1 整体结构

{
    "atomCode": "myAtom",           // 插件唯一标识(必填)
    "defaultLocaleLanguage": "zh_CN", // 默认语言(非必填)
    "execution": {                   // 执行配置(必填)
        "language": "python",
        "demands": [],
        "target": "demo"
    },
    "inputGroups": [],               // 输入字段分组(非必填)
    "input": {},                     // 输入字段定义(必填)
    "output": {},                    // 输出字段定义(非必填)
    "config": {}                     // 插件控制特性(非必填)
}

注意: task.json 内容长度最大为 64KB

2.2 execution 执行配置

属性名 说明 格式 必填
language 开发语言 字符串
target 执行入口命令 字符串
demands 执行前置依赖命令 数组
runtimeVersion 运行时版本 字符串
finishKillFlag 执行完成后是否杀进程 布尔
os 操作系统相关配置 数组

2.2.1 runtimeVersion 配置值

语言 配置值 备注
python python2 默认
python python3 推荐
nodejs 10.* ~ 18.* 默认 10.*
java 8 默认
java 17 新版本

2.2.2 os 跨平台配置示例

{
    "execution": {
        "language": "golang",
        "os": [
            {
                "osName": "linux",
                "osArch": "amd64",
                "target": "./app",
                "demands": [],
                "defaultFlag": true
            },
            {
                "osName": "windows",
                "osArch": "386",
                "target": "./app.exe",
                "demands": [],
                "defaultFlag": false
            },
            {
                "osName": "darwin",
                "osArch": "arm64",
                "target": "./app",
                "demands": [],
                "defaultFlag": false
            }
        ]
    }
}

2.3 input 输入字段配置

2.3.1 支持的 UI 组件类型

组件 type 组件名称 获取到的值格式
vuex-input 单行文本框 字符串
vuex-textarea 多行文本框 字符串
atom-ace-editor 代码编辑框 字符串
selector 下拉框(只选不输入) 单选: 字符串; 多选: ["str1", "str2"]
select-input 可输入下拉框 同上
devops-select 可输入下拉框(仅变量) 同上
atom-checkbox-list 复选框列表 ["id1", "id2"]
atom-checkbox 复选框(布尔) "true""false"
enum-input 单选 Radio 字符串
time-picker 日期选择器 字符串
staff-input 人名选择器(项目成员) 字符串
company-staff-input 人名选择器(公司成员) 字符串
tips 提示信息
parameter 不定参数列表 JSON 字符串
dynamic-parameter 动态参数列表 JSON 字符串
dynamic-parameter-simple 动态参数(简易版) JSON 字符串

2.3.2 输入字段公共属性

属性名 说明 格式 必填
label 中文名 字符串
type 组件类型 字符串
default 默认值 根据组件类型
placeholder 占位提示 字符串
groupName 所属分组 字符串
desc 字段说明 字符串
required 是否必填 布尔
disabled 是否禁用 布尔
hidden 是否隐藏 布尔
isSensitive 是否敏感信息 布尔
rely 条件显示/隐藏 对象
rule 值校验规则 对象

2.3.3 完整输入字段示例

{
    "input": {
        "repositoryUrl": {
            "label": "代码库地址",
            "type": "vuex-input",
            "placeholder": "请输入 Git 仓库地址",
            "desc": "支持 HTTP/HTTPS/SSH 协议",
            "required": true,
            "rule": {
                "regex": "^(https?|git)://"
            }
        },
        "branch": {
            "label": "分支",
            "type": "select-input",
            "default": "master",
            "optionsConf": {
                "searchable": true,
                "multiple": false,
                "url": "/repository/api/user/repositories/{projectId}/branches",
                "paramId": "name",
                "paramName": "name"
            }
        },
        "enableCache": {
            "label": "",
            "type": "atom-checkbox",
            "text": "启用缓存",
            "default": true
        },
        "buildType": {
            "label": "构建类型",
            "type": "enum-input",
            "default": "release",
            "list": [
                {"label": "Debug", "value": "debug"},
                {"label": "Release", "value": "release"}
            ]
        },
        "advancedOptions": {
            "label": "高级选项",
            "type": "vuex-input",
            "rely": {
                "operation": "AND",
                "expression": [
                    {"key": "enableCache", "value": true}
                ]
            }
        }
    }
}

2.4 output 输出字段配置

{
    "output": {
        "buildResult": {
            "type": "string",
            "description": "构建结果",
            "isSensitive": false
        },
        "artifactPath": {
            "type": "artifact",
            "description": "构建产物路径"
        },
        "reportHtml": {
            "type": "report",
            "description": "测试报告"
        }
    }
}

输出类型说明:

类型 说明 限制
string 字符串变量 长度不超过 4KB
artifact 构件文件 自动归档到仓库
report 报告文件 支持 HTML 渲染

2.5 config 插件控制特性

{
    "config": {
        "canPauseBeforeRun": false,
        "defaultTimeout": 60,
        "defaultFailPolicy": "MANUALLY_CONTINUE",
        "defaultRetryPolicy": ["AUTO_RETRY"],
        "retryTimes": 3
    }
}

2.6 内置变量

变量名 说明
{projectId} 项目英文 ID
{pipelineId} 流水线 ID
{buildId} 构建 ID

三、多语言开发示例

3.1 Python 插件开发

3.1.1 目录结构

my-python-atom/
├── task.json
├── setup.py
├── requirements.txt
└── demo/
    ├── __init__.py
    └── command_line.py

3.1.2 task.json

{
    "atomCode": "myPythonAtom",
    "execution": {
        "language": "python",
        "demands": [],
        "target": "demo"
    },
    "input": {
        "inputDemo": {
            "label": "输入示例",
            "type": "vuex-input",
            "required": true
        }
    },
    "output": {
        "outputDemo": {
            "type": "string",
            "description": "输出示例"
        }
    }
}

3.1.3 setup.py

from setuptools import setup, find_packages

setup(
    name="myPythonAtom",
    packages=find_packages(),
    install_requires=["python-atom-sdk"],
    entry_points={
        "console_scripts": [
            "demo = demo.command_line:main"
        ]
    }
)

3.1.4 command_line.py

# -*- coding: utf-8 -*-
import python_atom_sdk as sdk

def main():
    sdk.log.info("插件开始执行")
    
    # 获取输入参数
    input_params = sdk.get_input()
    input_demo = input_params.get("inputDemo", "")
    sdk.log.info(f"输入参数: {input_demo}")
    
    # 业务逻辑处理
    result = f"处理结果: {input_demo}"
    
    # 设置输出
    output_data = {
        "status": sdk.status.SUCCESS,
        "message": "执行成功",
        "errorCode": 0,
        "type": sdk.output_template_type.DEFAULT,
        "data": {
            "outputDemo": {
                "type": sdk.output_field_type.STRING,
                "value": result
            }
        }
    }
    sdk.set_output(output_data)
    
    sdk.log.info("插件执行完成")
    exit(0)

if __name__ == "__main__":
    main()

3.2 Java 插件开发

3.2.1 目录结构

my-java-atom/
├── task.json
├── pom.xml
├── settings.xml
└── src/main/
    ├── java/com/example/atom/
    │   ├── AtomParam.java
    │   └── DemoAtom.java
    └── resources/META-INF/services/
        └── com.tencent.bk.devops.atom.spi.TaskAtom

3.2.2 task.json

{
    "atomCode": "myJavaAtom",
    "defaultLocaleLanguage": "zh_CN",
    "execution": {
        "language": "java",
        "minimumVersion": "1.8",
        "demands": [],
        "target": "java -jar myJavaAtom-jar-with-dependencies.jar"
    },
    "input": {
        "desc": {
            "label": "描述",
            "type": "vuex-input",
            "placeholder": "请输入描述信息",
            "required": true
        }
    },
    "output": {
        "testResult": {
            "type": "string",
            "description": "执行结果"
        }
    }
}

3.2.3 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.tencent.bk.devops.atom</groupId>
        <artifactId>sdk-dependencies</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>myJavaAtom</artifactId>
    <version>1.0.0</version>

    <properties>
        <sdk.version>1.1.58</sdk.version>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.tencent.bk.devops.atom</groupId>
            <artifactId>java-atom-sdk</artifactId>
            <version>${sdk.version}</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.name}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2.4 AtomParam.java

package com.example.atom;

import com.tencent.bk.devops.atom.pojo.AtomBaseParam;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class AtomParam extends AtomBaseParam {
    private String desc;
}

3.2.5 DemoAtom.java

package com.example.atom;

import com.tencent.bk.devops.atom.AtomContext;
import com.tencent.bk.devops.atom.common.Status;
import com.tencent.bk.devops.atom.pojo.AtomResult;
import com.tencent.bk.devops.atom.pojo.StringData;
import com.tencent.bk.devops.atom.spi.AtomService;
import com.tencent.bk.devops.atom.spi.TaskAtom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@AtomService(paramClass = AtomParam.class)
public class DemoAtom implements TaskAtom<AtomParam> {

    private static final Logger logger = LoggerFactory.getLogger(DemoAtom.class);

    @Override
    public void execute(AtomContext<AtomParam> atomContext) {
        // 获取参数
        AtomParam param = atomContext.getParam();
        AtomResult result = atomContext.getResult();
        
        logger.info("输入参数: {}", param.getDesc());
        
        // 参数校验
        if (param.getDesc() == null || param.getDesc().isEmpty()) {
            result.setStatus(Status.failure);
            result.setMessage("描述不能为空");
            return;
        }
        
        // 业务逻辑
        logger.groupStart("执行任务");
        String output = "处理结果: " + param.getDesc();
        logger.info(output);
        logger.groupEnd("执行任务");
        
        // 设置输出
        result.setStatus(Status.success);
        result.getData().put("testResult", new StringData(output));
    }
}

3.2.6 SPI 配置文件

src/main/resources/META-INF/services/com.tencent.bk.devops.atom.spi.TaskAtom:

com.example.atom.DemoAtom

3.3 NodeJS 插件开发

3.3.1 目录结构

my-nodejs-atom/
├── task.json
├── package.json
├── rollup.config.js
├── index.js
└── .gitignore

3.3.2 task.json

{
    "atomCode": "myNodejsAtom",
    "execution": {
        "language": "nodejs",
        "demands": [],
        "target": "node dist/bundle.js"
    },
    "input": {
        "inputDemo": {
            "label": "输入示例",
            "type": "vuex-input",
            "required": true
        }
    },
    "output": {
        "outputDemo": {
            "type": "string",
            "description": "输出示例"
        }
    }
}

3.3.3 package.json

{
    "name": "myNodejsAtom",
    "version": "1.0.0",
    "main": "./dist/bundle.js",
    "dependencies": {
        "@tencent/nodejs_atom_sdk": "^1.1.12"
    },
    "devDependencies": {
        "@babel/core": "^7.4.5",
        "@babel/preset-env": "^7.4.5",
        "rollup": "^1.16.2",
        "rollup-plugin-babel": "^4.3.3"
    }
}

3.3.4 rollup.config.js

import babel from 'rollup-plugin-babel'

export default {
    input: 'index.js',
    output: {
        file: 'dist/bundle.js',
        format: 'cjs'
    },
    plugins: [babel()]
}

3.3.5 index.js

import { 
    getInputParams,
    setOutput,
    BK_ATOM_STATUS,
    BK_OUTPUT_TEMPLATE_TYPE
} from '@tencent/nodejs_atom_sdk'

const params = getInputParams()
console.log('输入参数:', params)

const inputDemo = params.inputDemo || ''
const result = `处理结果: ${inputDemo}`

setOutput({
    "type": BK_OUTPUT_TEMPLATE_TYPE.DEFAULT,
    "status": BK_ATOM_STATUS.SUCCESS,
    "data": {
        "outputDemo": {
            "type": "string",
            "value": result
        }
    }
})

3.4 Golang 插件开发

3.4.1 目录结构

my-golang-atom/
├── task.json
├── go.mod
├── go.sum
└── main.go

3.4.2 task.json

{
    "atomCode": "myGolangAtom",
    "execution": {
        "language": "golang",
        "demands": [],
        "target": "./app"
    },
    "input": {
        "greeting": {
            "label": "欢迎词",
            "type": "vuex-input",
            "default": "Hello",
            "required": true
        },
        "userName": {
            "label": "用户名",
            "type": "vuex-input",
            "required": true
        }
    },
    "output": {
        "result": {
            "type": "string",
            "description": "输出结果"
        }
    }
}

3.4.3 go.mod

module xxx/bkdevops/myGolangAtom

go 1.14

require xxx/bkdevops/golang-atom-sdk v1.1.9

3.4.4 main.go

package main

import (
    "fmt"
    sdk "xxx/bkdevops/golang-atom-sdk"
)

func main() {
    // 获取输入参数
    input := sdk.GetInput()
    greeting := input["greeting"]
    userName := input["userName"]
    
    fmt.Printf("输入参数: greeting=%s, userName=%s\n", greeting, userName)
    
    // 业务逻辑
    result := fmt.Sprintf("%s, %s!", greeting, userName)
    
    // 设置输出
    output := sdk.Output{
        Status:  sdk.StatusSuccess,
        Message: "执行成功",
        Type:    sdk.OutputTypeDefault,
        Data: map[string]sdk.DataField{
            "result": {
                Type:  "string",
                Value: result,
            },
        },
    }
    sdk.SetOutput(output)
}

四、插件输出规范

4.1 输出数据结构

{
    "status": "success",
    "message": "执行成功",
    "errorType": 1,
    "errorCode": 0,
    "platformCode": "",
    "platformErrorCode": 0,
    "type": "default",
    "data": {
        "outputVar1": {
            "type": "string",
            "value": "输出值"
        },
        "outputVar2": {
            "type": "artifact",
            "value": ["/path/to/file1", "/path/to/file2"],
            "artifactoryType": "PIPELINE",
            "path": "/test/"
        },
        "outputVar3": {
            "type": "report",
            "reportType": "INTERNAL",
            "label": "测试报告",
            "path": "/workspace/report",
            "target": "index.html"
        }
    }
}

4.2 status 状态值

说明
success 执行成功
failure 执行失败

4.3 输出类型详解

4.3.1 string 类型

{
    "outputVar": {
        "type": "string",
        "value": "输出字符串,长度不超过4KB"
    }
}

4.3.2 artifact 类型(构件归档)

{
    "buildArtifact": {
        "type": "artifact",
        "value": ["/workspace/output/app.apk", "/workspace/output/app.ipa"],
        "artifactoryType": "PIPELINE",
        "path": "/release/"
    }
}
属性 说明
value 文件绝对路径数组
artifactoryType PIPELINE(流水线仓库)或 CUSTOM_DIR(自定义仓库)
path 自定义仓库时的相对路径

4.3.3 report 类型(报告归档)

{
    "testReport": {
        "type": "report",
        "reportType": "INTERNAL",
        "label": "单元测试报告",
        "path": "/workspace/test-report",
        "target": "index.html"
    }
}
属性 说明
reportType INTERNAL(内置报告)或 THIRDPARTY(第三方链接)
label 报告别名
path 报告目录绝对路径
target 入口文件(相对于 path)
url 第三方报告链接(reportType=THIRDPARTY 时)

五、错误码规范

5.1 错误类型(errorType)

类型 说明
1 USER 用户配置错误(参数不合法等)
2 THIRD_PARTY 第三方平台错误
3 PLUGIN 插件逻辑错误(默认)

5.2 通用错误码(100 开头)

错误码 说明 message 规范
100001 输入参数非法 输入参数[xxx]非法:<具体要求>
100002 网络连接超时 接口[xxx],连接超时
100003 插件异常 未知的插件异常:<报错信息>
100004 缺少必要参数 缺少必要参数xxx
100400 HTTP 400 接口[xxx],返回码[400]
100401 HTTP 401 接口[xxx],返回码[401]
100403 HTTP 403 接口[xxx],返回码[403]
100404 HTTP 404 接口[xxx],返回码[404]
100500 HTTP 500 接口[xxx],返回码[500]
100502 HTTP 502 接口[xxx],返回码[502]

5.3 插件自定义错误码

  • 统一以 8 开头
  • 在插件代码库下增加 error.json 文件声明:
[
    {
        "errorCode": 800001,
        "errorMsgZhCn": "配置文件不存在",
        "errorMsgEn": "Config file not found"
    },
    {
        "errorCode": 800002,
        "errorMsgZhCn": "编译失败",
        "errorMsgEn": "Build failed"
    }
]

5.4 第三方平台错误上报

errorType=2 时,需要填写:

{
    "errorType": 2,
    "errorCode": 106001,
    "platformCode": "tgit",
    "platformErrorCode": 500,
    "message": "调用工蜂接口失败:/api/v4/projects,返回码[500]"
}

已注册的第三方平台标识:

平台 标识 errorCode 前缀
蓝盾 bkci 101
CodeCC bkci_codecc 102
制品库 bkci_repo 103
编译加速 bkci_turbo 104
作业平台 bk_job 105
工蜂 tgit 106
TAPD tapd 107
智研 zhiyan 108
七彩石 rainbow 109
腾讯云 COS cloud_cos 110

六、插件发布流程

6.1 发布流程图

┌─────────────────────────────────────────────────────────────────────────┐
│                         插件发布流程                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐          │
│  │ 提交发布  │ ─► │ 系统构建  │ ─► │ 测试验证  │ ─► │ 审核发布  │          │
│  │          │    │          │    │          │    │          │          │
│  │ 填写信息  │    │ 自动打包  │    │ 创建流水线 │    │ 首次需审核 │          │
│  │ 选择版本  │    │ 上传制品  │    │ 验证功能  │    │ 升级免审核 │          │
│  └──────────┘    └──────────┘    └──────────┘    └──────────┘          │
│       │               │               │               │                 │
│       ▼               ▼               ▼               ▼                 │
│    COMMITTING     BUILDING        TESTING         AUDITING              │
│                       │                               │                 │
│                       ▼                               ▼                 │
│                  BUILD_FAIL                     AUDIT_REJECT            │
│                                                       │                 │
│                                                       ▼                 │
│                                                   RELEASED              │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

6.2 发布类型

类型 说明 版本号变化
新上架 首次发布 1.0.0
非兼容式升级 输入输出不兼容 主版本号 +1
兼容式功能更新 新增功能,兼容旧版 次版本号 +1
兼容式问题修正 Bug 修复 修正号 +1
历史大版本 Bug fix 修复历史版本 修正号 +1

6.3 版本号规范

遵循 SemVer 规范:主版本号.次版本号.修正号

  • 主版本号:不兼容的 API 修改
  • 次版本号:向下兼容的功能性新增
  • 修正号:向下兼容的问题修正

七、本地调试指南

7.1 调试准备

在代码库根目录创建以下文件(不要提交到代码库):

7.1.1 input.json

{
    "inputParam1": "测试值1",
    "inputParam2": "测试值2"
}

7.1.2 .sdk.json

{
    "buildType": "DOCKER",
    "projectId": "test-project",
    "agentId": "1",
    "secretKey": "test-secret",
    "gateway": "xxx.xxx.xxx.xxx",
    "buildId": "test-build-id",
    "vmSeqId": "1"
}

注意: 本地调试时不能调用蓝盾后台服务(需要在流水线中执行才有权限)

7.2 Python 插件调试

# 安装 SDK
pip install python-atom-sdk

# 打包插件
python ./setup.py sdist

# 安装插件
pip install dist/XXX.tar.gz

# 在 input.json 所在目录执行入口命令
demo

7.3 Java 插件调试

# 打包
mvn clean package

# 运行
java -jar target/myJavaAtom-jar-with-dependencies.jar

7.4 NodeJS 插件调试

# 安装依赖
npm install

# 打包
rollup -c rollup.config.js

# 运行(需要先在根目录创建 mock 目录,将 input.json 放入)
node dist/bundle.js

7.5 Golang 插件调试

# 运行
go run main.go

# 或构建后运行
go build -o app main.go
./app

八、最佳实践

8.1 参数校验

# Python 示例
def validate_params(input_params):
    required_fields = ["repositoryUrl", "branch"]
    for field in required_fields:
        if not input_params.get(field):
            return False, f"缺少必要参数: {field}"
    return True, ""

8.2 错误处理

// Java 示例
try {
    // 业务逻辑
} catch (IOException e) {
    result.setErrorInfo(
        Status.failure,
        100002,  // 网络错误
        ErrorType.THIRD_PARTY,
        new String[]{"接口调用失败: " + e.getMessage()}
    );
}

8.3 日志输出

# Python 示例 - 使用日志分组
sdk.log.info("开始执行任务")
sdk.log.group_start("编译阶段")
sdk.log.info("正在编译...")
sdk.log.group_end("编译阶段")

8.4 敏感信息处理

  • task.json 中将敏感字段设置 isSensitive: true
  • 敏感信息不会在日志中明文显示
  • 使用插件私有设置管理账号密码等敏感数据

九、常见问题

Q: task.json 修改后流水线没有生效? A: 需要重新构建插件,并刷新流水线页面清除缓存。

Q: 如何获取上一个插件的输出变量? A: 在参数类中定义同名参数即可自动接收。

Q: 插件执行报错 "Plugin File Sha1 is wrong!"? A: 联系插件作者重新发布插件版本。

Q: 如何让插件支持多操作系统? A: 在 execution.os 中配置不同操作系统的执行命令。

Q: 插件输出变量长度限制? A: string 类型输出不能超过 4KB。


版本: 1.0.0 | 更新日期: 2025-12-26

Weekly Installs
12
First Seen
3 days ago
Installed on
claude-code8
gemini-cli8
windsurf7
opencode7
antigravity7
trae6