本文介绍了如何使用腾讯云 CNB(Cloud Native Build)构建和部署 Maven 项目,通过 .cnb.yml 文件配置流水线任务,结合 YAML 锚点与并行任务机制,实现对 Maven 项目的自动化构建与部署。

前言

​CNB(Cloud Native Build) 是腾讯云 CODING 团队推出的全新产品 “云原生构建”,对标 GitHub;内置加速服务,可快速访问 GitHub、DockerHub 等资源;还能在 Pipeline 中将代码同步至其他平台,非常适合国内开发者。CNB 基于 Docker 生态,与 Github 等平台类似,开发者通过编写 yml 文件声明自己的流水线。

​本文将以 maven 项目为例介绍在 cnb 上自定义一个简单的流水线。

准备工作

流程分析

​一个简化的流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
st=>start: 开发者提交代码
op1=>operation: 代码推送到 Git 仓库
op2=>operation: 触发 CI 工具
op3=>operation: 代码编译
op4=>operation: 构建产物
op5=>operation: 部署产物
cond1=>condition: 部署成功?
op6=>operation: 失败通知
op7=>operation: 发布完成
e=>end: 流程结束

st->op1->op2->op3->op4->op5->cond1
cond1(yes)->op7->e
cond1(no)->op6->e

示例项目结构

假如你的项目结构是这样子的:

::: file-tree

  • Demo 带有两个子项目
    • Demo-Backend 后台
      • src
        • main
          • java/
          • resources
            • application.yml
      • pom.xml
    • Demo-Frontend 前台
      • src
        • main
          • java/
          • resources
            • application.yml
      • pom.xml
    • docker
      • Demo-Backend
        • application.yml 要部署的环境的配置
      • Demo-Frontend
        • application.yml 要部署的环境的配置
      • Dockerfile
      • cache.Dockerfile
      • deploy.sh 部署脚本
      • entrypoint.sh
    • pom.xml
    • .gitignore
    • README.md

    • :::

示例流水线文件

此处需注意yml语法,详见 YAML 语言教程- 阮一峰的网络日志

.cnb.yml 文件

在根目录创建 .cnb.yml 文件如下(可暂时跳转到分步说明阅读详细步骤):

其中 & 代表锚点, * 代表别名,可以用来引用; & 用来建立锚点,<< 表示合并到当前数据,* 用来引用锚点。

::: collapse

  • .cnb.yml 文件 (点击展开)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    # .cnb.yml
    # ----------------------
    # 各阶段 Job 定义模板
    # ----------------------
    # 准备阶段
    prepare: &prepare
    script: |
    POMS=$(find . -name "pom.xml" | sort | paste -sd "," -)
    if [ "${CNB_IS_TAG}" = "true" ]; then
    TAG1=${CNB_BRANCH}
    TAG2=latest
    else
    TAG1=${CNB_BRANCH}-${CNB_COMMIT_SHORT}
    TAG2=${CNB_BRANCH}
    fi
    printf "##[set-output poms=%s]\n" "$POMS"
    printf "##[set-output tag1=%s]\n" "$TAG1"
    printf "##[set-output tag2=%s]\n" "$TAG2"
    exports:
    poms: POMS
    tag1: TAG1
    tag2: TAG2
    #构建缓存镜像
    docker_cache: &docker_cache
    image: maven:3.9.9-eclipse-temurin-17
    type: docker:cache
    options:
    dockerfile: ./docker/cache.Dockerfile
    by: $POMS
    exports:
    name: DOCKER_CACHE_IMAGE
    # Maven 打包
    maven_package: &maven_package
    image: $DOCKER_CACHE_IMAGE_NAME
    volumes:
    - /root/.m2:copy-on-write
    script: |
    mvn versions:set -DnewVersion=${TAG1} -DgenerateBackupPoms=false
    mvn clean -B package -DskipTests
    # Docker build && Docker push
    build_push: &build_push
    script: |
    IMAGE_BASE=${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}/$ {ARTIFACT_ID_LOWERCASE}
    docker build -f docker/Dockerfile -t ${IMAGE_BASE}:${TAG1} -t $ {IMAGE_BASE}:${TAG2} --build-arg ARTIFACT_ID=${ARTIFACT_ID}--build-arg VERSION=${TAG1} .
    docker push ${IMAGE_BASE}:${TAG1}
    docker push ${IMAGE_BASE}:${TAG2}
    # SSH 部署
    ssh_deploy: &ssh_deploy
    image: docker.cnb.cool/falling42/ssh-deploy:v0.1.0
    imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
    settings:
    use_screen: 'no'
    use_jump_host: 'no'
    ssh_host: ${your_ssh_host}
    ssh_user: ${your_ssh_user}
    ssh_private_key: ${your_ssh_private_key}
    execute_remote_script: 'yes'
    transfer_files: 'no'
    copy_script: 'yes'
    source_script: './docker/deploy.sh'
    deploy_script: '/opt/ops/deploy-demo.sh'
    service_name: ${ARTIFACT_ID}
    service_version: "${TAG1}"
    #失败通知:wechat-bot
    notify_wechat_bot: &notify_wechat_bot
    image: tencentcom/wecom-message
    settings:
    imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenvyml
    robot: ${your_webhook_url}
    msgType: text
    content: |
    🚨构 建 失 败 通 知🚨
    📦仓 库: ${CNB_REPO_SLUG_LOWERCASE}
    👤发 起 人: ${CNB_BUILD_USER}
    🛠️失败任务: ${CNB_BUILD_FAILED_STAGE_NAME}
    👉查看详情: ${CNB_BUILD_WEB_URL}
    #失败通知:wechat
    notify_wechat: &notify_wechat
    imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
    image: clem109/drone-wechat
    settings:
    corpid: ${your_corpid}
    corp_secret: ${your_corp_secret}
    agent_id: ${your_agent_id}
    to_user: ${your_user_id}
    msg_url: ${CNB_BUILD_WEB_URL}
    safe: 0
    btn_txt: 查看详情
    title: ${CNB_REPO_SLUG_LOWERCASE} 构建失败通知
    description: "发起人: ${CNB_BUILD_USER}\n失败任务: $ {CNB_BUILD_FAILED_STAGE_NAME}\n点击查看详情: ${CNB_BUILD_WEB_URL}\n"
    # 失败通知:serverchan
    notify_serverchan: &notify_serverchan
    image: yakumioto/drone-serverchan
    imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
    settings:
    key: ${your_sct_key}
    text: 🚨 构建失败通知
    desp: |
    > **📦 仓库:** ${CNB_REPO_SLUG_LOWERCASE}
    > **👤 发起人:** ${CNB_BUILD_USER}
    > **🛠️ 失败任务:** ${CNB_BUILD_FAILED_STAGE_NAME}
    > **[👉 点击查看完整构建日志](${CNB_BUILD_WEB_URL})**
    # 失败通知:email
    notify_email: &notify_email
    image: drillster/drone-email
    imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
    settings:
    host: ${your_smtp_host}$
    port: 465
    recipients: ${CNB_COMMITTER_EMAIL}
    username: ${your_smtp_user}
    password: ${your_smtp_password}
    from.address: ${your_smtp_user}
    from.name: CNB构建通知
    subject: ${CNB_REPO_SLUG_LOWERCASE} 构建失败通知
    body: |
    <div style="font-family: Arial, sans-serif; padding: 12px; border:1px solid #eee;">
    <h2 style="color: #D32F2F;">🚨 构建失败通知</h2>
    <p><strong>📦 仓库:</strong> ${CNB_REPO_SLUG_LOWERCASE}</p>
    <p><strong>👤 发起人:</strong> ${CNB_BUILD_USER}(ID: $ {CNB_BUILD_USER_ID})</p>
    <p><strong>🛠️ 失败任务:</strong> ${CNB_BUILD_FAILED_STAGE_NAME}<p>
    <p style="margin-top: 20px;">
    👉 <a href="${CNB_BUILD_WEB_URL}" style="color: #1976D2; text-decoration: none;">
    点击查看完整构建日志</a>
    </p>
    <hr style="margin-top: 24px;"/>
    <p style="font-size: 12px; color: #888;">
    来自 CNB构建通知
    </p>
    </div>
    # 定义要并行构建的模块
    Frontend: &Frontend
    ARTIFACT_ID: Demo-Frontend
    ARTIFACT_ID_LOWERCASE: demo-frontend
    Backend: &Backend
    ARTIFACT_ID: Demo-Backend
    ARTIFACT_ID_LOWERCASE: demo-backend
    # ----------------------
    # 并行 Jobs 定义模板
    # ----------------------
    build_push_jobs: &build_push_jobs
    Frontend: { <<: *build_push, env: { <<: *Frontend } }
    Backend: { <<: *build_push, env: { <<: *Backend } }
    deploy_jobs: &deploy_jobs
    Frontend: { <<: *ssh_deploy, env: { <<: *Frontend } }
    Backend: { <<: *ssh_deploy, env: { <<: *Backend } }
    # ----------------------
    # 主 Pipeline 定义模板
    # ----------------------
    # ----------------------
    pipeline: &pipeline
    name: Demo
    runner:
    tags: cnb:arch:amd64
    cpus: 4
    services:
    - docker
    stages:
    - name: prepare
    <<: *prepare
    - name: build cache image
    <<: *docker_cache
    - name: maven package
    <<: *maven_package
    - name: build push
    jobs:
    <<: *build_push_jobs
    - name: ssh deploy
    jobs:
    <<: *deploy_jobs
    failStages:
    - name: notify
    jobs:
    notify-email:
    <<: *notify_email
    notify-wechat:
    <<: *notify_wechat
    notify-wechat-bot:
    <<: *notify_wechat_bot
    notify-serverchan:
    <<: *notify_serverchan
    # ----------------------
    # 分支触发定义
    # ----------------------
    main:
    push:
    - <<: *pipeline
    "**":
    web_trigger_one:
    - <<: *pipeline
    $:
    tag_push:
    - <<: *pipeline

:::

.cnb/web_trigger.yml 文件

在 项目根目录创建 .cnb/web_trigger.yml 文件,用于给云原生构建的项目首页配置构建按钮

注意event的名字要与 .cnb.yml 文件中的触发条件相对应

更多个性化配置详见 手动触发流水线

1
2
3
4
5
6
# .cnb/web_trigger.yml
branch:
- buttons: # 自定义按钮
- name: 手动构建
description: 手动构建
event: web_trigger_one

分步说明

一个流水线的执行过程是:

仓库发生事件 -> 确定所属分支 -> 确定事件名 -> 执行流水线 -> 执行任务 -> 失败时的任务

触发事件

这部分算是整个流水线的入口,决定何时触发流水线任务

更多触发事件请查看 触发事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Branch 事件:远端代码分支变动触发的事件。
main: # 分支名称
push: # git 仓库事件,分支 push 时触发。
- <<: *pipeline # 流水线配置
## 注意这个'-' 因为将pipeline合并到当前数据时候pipeline的配置无'-',这里不加'-'则无法解析流水线任务
# web_trigger 自定义事件
"**": # ** 代表匹配所有分支名
web_trigger_one: # 名字随意,不重复即可
- <<: *pipeline

# Tag 事件:由远端代码和页面 Tag 相关操作触发的事件。
$: # 对所有 tag 生效
tag_push: # 页面或者 git 创建并推送新 tag 时触发
- <<: *pipeline

流水线配置

这部分包括配置需要的构建环境以及配置流水线步骤

更多配置请查看 Pipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pipeline: &pipeline
name: Demo #流水线名字
# --- 构建环境配置 ---
runner:
tags: cnb:arch:amd64 # 指定使用具备哪些标签的构建节点。例如cnb:arch:arm64:v8
cpus: 4 # 指定构建需使用的最大 cpu 核数(memory = cpu 核数 * 2 G)
services: # 用于声明构建时需要的服务,格式:name:[version], version 是可选的。
- docker # 用于开启 dind 服务,当构建过程中需要使用 docker build,docker login 等操作时声明, 会自动在环境注入 docker daemon 和 docker cli。
# --- 构建环境配置 结束---
# --- 执行任务(s) ---
stages: # 定义一组阶段任务,每个阶段串行运行。Stage 表示一个构建阶段,可以由一个或者多个 Job 组成。每一个"-"开头就代表一个stage(在yml中一组连词线开头的行,构成一个数组。)
- name: prepare # 只有一个 Job,省略 "jobs:"(省掉 Stage 直接书写这个 Job)
<<: *prepare
- name: build cache image
<<: *docker_cache
- name: maven package
<<: *maven_package
- name: build push
jobs: # 定义一组任务,每个任务串行/并行运行。本文这里使用并行运行。请见后面的说明。
<<: *build_push_jobs
- name: ssh deploy
jobs: # 定义一组任务,每个任务串行/并行运行。本文这里使用并行运行。请见后面的说明。
<<: *deploy_jobs
# --- 执行任务(s) 结束 ---
# --- 在执行任务失败时执行的任务 ---
failStages: # 定义一组失败阶段任务。当正常流程失败,会依次执行此阶段任务。
- name: notify
jobs: # 定义一组任务,每个任务串行/并行运行。本文这里使用并行运行。当值为对象(无序)时,那么这组 Job 会并行执行。(对象的一组键值对,使用冒号结构表示。)
notify-email:
<<: *notify_email
notify-wechat:
<<: *notify_wechat
notify-wechat-bot:
<<: *notify_wechat_bot
notify-serverchan:
<<: *notify_serverchan

env

由于本文的示例项目使用了两个子项目,为了避免重复配置,本文使用了 yml 的引用结合 CNB 的 env 来声明要构建的多个项目。如果只是一个单体项目,按照此逻辑直接声明一个锚点即可。

1
2
3
4
5
6
7
# 定义要构建的项目, 下方的yml对象声明了每个项目的env,便于在后续流程中使用
Frontend: &Frontend
ARTIFACT_ID: Demo-Frontend
ARTIFACT_ID_LOWERCASE: demo-frontend
Backend: &Backend
ARTIFACT_ID: Demo-Backend
ARTIFACT_ID_LOWERCASE: demo-backend

准备阶段

这里使用一系列脚本任务,目的是为了统一构建产物的版本,其中:

POMS=$(find . -name "pom.xml" | sort | paste -sd "," -)

是将项目当中所有依赖文件找到存到一个变量里以便后续使用,单体项目可删去

1
2
3
4
5
6
7
8
9
if [ "${CNB_IS_TAG}" = "true" ]; then
#判断是否为 tag_push 是则版本设置为tag和latest
TAG1=${CNB_BRANCH}
TAG2=latest
else
# 否则版本设置为 分支名-提交短哈希 (例如 dev-6bf073ba) 和 分支名 (例如 dev)
TAG1=${CNB_BRANCH}-${CNB_COMMIT_SHORT}
TAG2=${CNB_BRANCH}
fi

是设置 maven 构建出的产物的版本 $TAG1 和 docker 镜像的 tag 标签 $TAG1$TAG2

后续的 printf 是根据 CNB 导出环境变量的标准把变量通过 exports 导出

可使用 printf “%s” “hello\nworld” 来输出变量,以消除标准输出流最后的换行符,同时保留 \n 等转义字符。

详细信息请看 导出环境变量

完整步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
prepare: &prepare
script: |
POMS=$(find . -name "pom.xml" | sort | paste -sd "," -)
if [ "${CNB_IS_TAG}" = "true" ]; then
TAG1=${CNB_BRANCH}
TAG2=latest
else
TAG1=${CNB_BRANCH}-${CNB_COMMIT_SHORT}
TAG2=${CNB_BRANCH}
fi
printf "##[set-output poms=%s]\n" "$POMS"
printf "##[set-output tag1=%s]\n" "$TAG1"
printf "##[set-output tag2=%s]\n" "$TAG2"
exports:
poms: POMS
tag1: TAG1
tag2: TAG2

构建缓存镜像

更多信息请看 流水线缓存docker:cache

1
2
3
4
5
6
7
8
docker_cache: &docker_cache
image: maven:3.9.9-eclipse-temurin-17 # 基础镜像
type: docker:cache # 设置内置任务为 docker:cache
options:
dockerfile: ./docker/cache.Dockerfile # 用于构建缓存镜像的 Dockerfile 路径。
by: $POMS # 用来声明缓存镜像构建过程中依赖的文件列表。注意:未出现在 by 列表中的文件,除了 Dockerfile,其他在构建镜像过程中,都当不存在处理。这里本文使用准备阶段找到的pom.xml文件(s),单体项目直接写根目录pom.xml即可
exports: # 把镜像名字导出供后续使用
name: DOCKER_CACHE_IMAGE

./docker/cache.Dockerfile 文件:

注意:

  • 基础镜像根据自己项目修改
  • mvn 命令 -P pro 需要根据自己项目删改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用带 Maven 和 JDK 17 的基础镜像
FROM maven:3.9.9-eclipse-temurin-17

# 复制项目代码到容器中
COPY . .

# 根据 COPY 过来的文件进行依赖的安装
RUN mvn -B \
--file pom.xml \
-P pro \
-DskipTests \
dependency:resolve-plugins dependency:go-offline \
assembly:help compiler:help enforcer:help exec:help failsafe:help \
install:help jar:help resources:help surefire:help \
clean:help dependency:help site:help

# 设置好需要的环境变量(本文实际尚未使用)
ENV M2_PATH=/root/.m2

构建 maven 项目

1
2
3
4
5
6
7
maven_package: &maven_package
image: $DOCKER_CACHE_IMAGE_NAME # 使用缓存镜像加快构建速度
volumes: # 声明数据卷
- /root/.m2:copy-on-write # 用于缓存场景,支持并发构建
script: |
mvn versions:set -DnewVersion=${TAG1} -DgenerateBackupPoms=false # 设置版本
mvn clean -B package -DskipTests # 打包(跳过测试)

构建并推送 docker 镜像

一次 build,两个标签,两次 push,其中 ${TAG1} 是每次触发的不一样的版本,${TAG2} 用于固定(分支的)最新版

--build-arg ARTIFACT_ID=${ARTIFACT_ID} --build-arg VERSION=${TAG1}

根据自己情况修改构建参数

1
2
3
4
5
6
build_push: &build_push
script: |
IMAGE_BASE=${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}/${ARTIFACT_ID_LOWERCASE}
docker build -f docker/Dockerfile -t ${IMAGE_BASE}:${TAG1} -t ${IMAGE_BASE}:${TAG2} --build-arg ARTIFACT_ID=${ARTIFACT_ID} --build-arg VERSION=${TAG1} .
docker push ${IMAGE_BASE}:${TAG1}
docker push ${IMAGE_BASE}:${TAG2}

因为本文是一次性构建了所有子项目,所以后续的构建镜像、运行镜像部分所有的子项目job要并行执行节省时间,单体项目填一个即可

1
2
3
build_push_jobs: &build_push_jobs # 一行一个对象,并行执行,注意合并env
Frontend: { <<: *build_push, env: { <<: *Frontend } }
Backend: { <<: *build_push, env: { <<: *Backend } }

docker/Dockerfile 示例,根据项目修改,注意版本的统一,本文统一版本的操作在准备阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
FROM eclipse-temurin:17

# 注意构建时传递参数
ARG ARTIFACT_ID
ARG VERSION

ENV VERSION=${VERSION} \
ARTIFACT_ID=${ARTIFACT_ID} \
TZ=Asia/Shanghai \
JAR_NAME=${ARTIFACT_ID}-${VERSION}.jar

# 设置时区、复制 jar 和 entrypoint 脚本并授权,全合并到一个 RUN 层中
WORKDIR /app

# 注意单体项目没有${ARTIFACT_ID}/
COPY ./${ARTIFACT_ID}/target/${JAR_NAME} /app/app.jar
COPY ./docker/entrypoint.sh /entrypoint.sh

RUN set -eux; \
apt-get update && apt-get install -y --no-install-recommends tzdata && \
ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone && \
dpkg-reconfigure -f noninteractive tzdata && \
chmod +x /entrypoint.sh && \
apt-get clean && rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/entrypoint.sh"]

docker/entrypoint.sh 示例,根据项目修改

1
2
3
#!/bin/bash
set -eux
exec java -Xmx2048m -Xms1024m -jar /app/app.jar "$@"

在目标环境中运行 docker 镜像

(不推荐)部署这里本文使用了自己制作的部署工具,详细信息在这里 ssh-deploy

(推荐)也可以使用官方的ssh插件 ssh

其中小写字母的主机凭据变量要通过密钥仓库引入并配置好权限,详见 imports 权限检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ssh_deploy: &ssh_deploy
image: docker.cnb.cool/falling42/ssh-deploy:v0.1.0
imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
# 导入密钥仓库变量
settings:
use_screen: 'no'
use_jump_host: 'no'
ssh_host: ${your_ssh_host}
ssh_user: ${your_ssh_user}
ssh_private_key: ${your_ssh_private_key}
execute_remote_script: 'yes'
transfer_files: 'no'
copy_script: 'yes'
source_script: './docker/deploy.sh'
deploy_script: '/opt/ops/deploy-demo.sh'
service_name: ${ARTIFACT_ID}
service_version: "${TAG1}"

因为本文是一次性构建了所有子项目,所以后续的构建镜像、运行镜像部分所有的子项目job要并行执行节省时间,单体项目填一个即可

1
2
3
deploy_jobs: &deploy_jobs # 一行一个对象,并行执行,注意合并env
Frontend: { <<: *ssh_deploy, env: { <<: *Frontend } }
Backend: { <<: *ssh_deploy, env: { <<: *Backend } }

失败通知

其中小写字母的变量要通过密钥仓库引入并配置好权限,详见 imports 权限检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#失败通知:wechat-bot
notify_wechat_bot: &notify_wechat_bot
image: tencentcom/wecom-message
settings:
imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
robot: ${your_webhook_url}
msgType: text
content: |
🚨构 建 失 败 通 知🚨
📦仓 库: ${CNB_REPO_SLUG_LOWERCASE}
👤发 起 人: ${CNB_BUILD_USER}
🛠️失败任务: ${CNB_BUILD_FAILED_STAGE_NAME}
👉查看详情: ${CNB_BUILD_WEB_URL}

#失败通知:wechat
notify_wechat: &notify_wechat
imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
image: clem109/drone-wechat
settings:
corpid: ${your_corpid}
corp_secret: ${your_corp_secret}
agent_id: ${your_agent_id}
to_user: ${your_user_id}
msg_url: ${CNB_BUILD_WEB_URL}
safe: 0
btn_txt: 查看详情
title: ${CNB_REPO_SLUG_LOWERCASE} 构建失败通知
description: "发起人: ${CNB_BUILD_USER}\n失败任务: ${CNB_BUILD_FAILED_STAGE_NAME}\n点击查看详情: ${CNB_BUILD_WEB_URL}\n"

# 失败通知:serverchan
notify_serverchan: &notify_serverchan
image: yakumioto/drone-serverchan
imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
settings:
key: ${your_sct_key}
text: 🚨 构建失败通知
desp: |
> **📦 仓库:** ${CNB_REPO_SLUG_LOWERCASE}
> **👤 发起人:** ${CNB_BUILD_USER}
> **🛠️ 失败任务:** ${CNB_BUILD_FAILED_STAGE_NAME}
> **[👉 点击查看完整构建日志](${CNB_BUILD_WEB_URL})**

# 失败通知:email
notify_email: &notify_email
image: drillster/drone-email
imports: https://cnb.cool/<your_org>/<your_repo>/-/blob/main/yourenv.yml
settings:
host: ${your_smtp_host}$
port: 465
recipients: ${CNB_COMMITTER_EMAIL}
username: ${your_smtp_user}
password: ${your_smtp_password}
from.address: ${your_smtp_user}
from.name: CNB构建通知
subject: ${CNB_REPO_SLUG_LOWERCASE} 构建失败通知
body: |
<div style="font-family: Arial, sans-serif; padding: 12px; border: 1px solid #eee;">
<h2 style="color: #D32F2F;">🚨 构建失败通知</h2>
<p><strong>📦 仓库:</strong> ${CNB_REPO_SLUG_LOWERCASE}</p>
<p><strong>👤 发起人:</strong> ${CNB_BUILD_USER}(ID: ${CNB_BUILD_USER_ID})</p>
<p><strong>🛠️ 失败任务:</strong> ${CNB_BUILD_FAILED_STAGE_NAME}</p>
<p style="margin-top: 20px;">
👉 <a href="${CNB_BUILD_WEB_URL}" style="color: #1976D2; text-decoration: none;">
点击查看完整构建日志</a>
</p>
<hr style="margin-top: 24px;"/>
<p style="font-size: 12px; color: #888;">
来自 CNB构建通知
</p>
</div>