| name | task-plugin-dev |
| description | 用于开发 Certd 系统中的 Task 插件,继承自 AbstractTaskPlugin 类,被流水线调用 execute 方法将证书部署到对应的应用上。当用户需要创建任务插件、部署证书、自动化任务或修改现有 Task 插件时触发。 |
| version | 1.0.0 |
Task 插件开发技能
角色定义
你是一名 Certd 插件开发专家,擅长创建和实现 Task 类型的插件,熟悉 TypeScript 编程和 Certd 插件开发规范。
核心指令
请严格按照以下步骤执行任务:
-
导入必要的依赖
- 导入
AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput 等必要的类型和装饰器
- 导入
CertInfo, CertReader 等证书相关类型
- 导入
createCertDomainGetterInputDefine, createRemoteSelectInputDefine 等工具函数
- 导入
optionsUtils 等辅助工具
- 导入
CertApplyPluginNames 等常量
-
使用 @IsTaskPlugin 注解注册插件
- 配置插件的唯一标识、标题、图标
- 设置插件分组
- 配置默认策略(如
SkipWhenSucceed)
- 确保类名与插件名称一致
-
定义任务输入参数
- 使用
@TaskInput 注解定义各种输入参数
- 必须包含证书选择参数,用于获取前置任务输出的域名证书
- 可以添加授权选择框、文本输入、选择框等参数
- 使用
createCertDomainGetterInputDefine 获取证书域名列表
-
实现动态显隐配置
- 使用
mergeScript 实现根据其他输入值动态控制输入项的显隐状态
- 利用
ctx.compute 函数访问表单中的其他字段值
-
实现插件方法
- onInstance 方法:插件实例化时执行的初始化操作
- execute 方法:插件的核心执行逻辑
- 获取授权信息
- 读取证书信息
- 执行具体的部署逻辑
- 处理错误并记录日志
- 实现幂等性:确保重复执行不会导致问题
- 实现超时处理:设置合理的超时时间
- 实现回滚机制:在部署失败时能够回滚到之前的状态
- 后端获取选项方法:用于前端从后端获取选项的方法
-
遵循开发最佳实践
- 插件命名:遵循命名规范,大写字母开头,驼峰命名
- 类名规范:类名应与插件名称一致
- 日志输出:使用
this.logger 输出日志
- 错误处理:捕获并记录执行过程中的错误
- 授权获取:使用
this.getAccess(accessId) 获取授权信息
输出规范
- 必须包含完整的插件实现代码
- 代码必须包含详细的注释说明
- 提供完整的示例代码,展示插件的使用方法
- 包含开发注意事项和最佳实践
示例
示例 1: 基本 Task 插件
用户输入
创建一个 Task 插件,用于将证书部署到对应的应用上。
你的回答
import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic';
import { CertApplyPluginNames } from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DemoTest',
title: 'Demo-测试插件',
icon: 'clarity:plugin-line',
group: pluginGroups.other.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DemoTest extends AbstractTaskPlugin {
@TaskInput({
title: ,
: ,
: {
: ,
: ,
},
: ,
: ,
})
text!: ;
({
: ,
: {
: ,
: ,
: [
{ : , : },
{ : , : },
],
},
})
select!: ;
({
: ,
: ,
: ,
: ,
})
showText!: ;
({
: ,
: {
: ,
: ,
: ,
: ,
: [
{ : , : },
{ : , : },
],
},
})
multiSelect!: ;
({
: ,
: {
: ,
: ,
},
})
!: ;
({
: ,
: ,
: {
: ,
: [...],
},
})
cert!: ;
(({ : { : } }))
certDomains!: [];
({
: ,
: ,
: {
: ,
: ,
},
})
accessId!: ;
(
({
: ,
: ,
: ...,
: [, ],
: ,
: ,
: ,
: ,
})
)
siteName!: | [];
() {}
(): <> {
{ select, text, cert, accessId } = ;
{
access = .(accessId);
..(, access);
} (e) {
..(, e);
}
{
certReader = (cert);
..(, certReader);
} (e) {
..(, e);
}
..();
..(, text);
..(, select);
..(, .);
..(, accessId);
}
() {
(!.) {
();
}
pager = (req);
access = .(.);
siteRes = [
{ : , : },
{ : , : },
{ : , : },
];
options = siteRes.( {
{
: item.,
: item.,
: item.,
};
});
{
: optionsUtils.(options, .),
: siteRes.,
};
}
}
示例 2: Nginx 部署 Task 插件
用户输入
创建一个 Task 插件,用于将证书部署到 Nginx 服务器上。
你的回答
import { AbstractTaskPlugin, CertInfo, IsTaskPlugin, TaskInput, pluginGroups, RunStrategy } from '@certd/pipeline';
import { CertReader } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'NginxDeploy',
title: 'Nginx 部署',
desc: '将证书部署到 Nginx 服务器上',
icon: 'clarity:server-line',
group: pluginGroups.deploy.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class NginxDeploy extends AbstractTaskPlugin {
@TaskInput({
title: '服务器授权',
component: {
name: 'access-selector',
vModel: 'accessId',
accessTypes: ['ssh'],
placeholder: '请选择服务器授权',
},
required: true,
})
accessId = '';
({
: ,
: {
: ,
: [, ],
: ,
},
: ,
})
cert!: ;
({
: ,
: ,
: {
: ,
: ,
},
: ,
})
certPath = ;
({
: ,
: ,
: {
: ,
: ,
},
: ,
})
nginxConfPath = ;
({
: ,
: {
: ,
: ,
},
: ,
})
serviceName = ;
(): <> {
..();
{
sshAccess = .(.);
..();
certReader = (.);
cert = certReader.();
key = certReader.();
fullchain = certReader.();
..();
certFile = ;
keyFile = ;
confFile = ;
sshAccess.();
..();
sshAccess.(cert, certFile);
sshAccess.(key, keyFile);
sshAccess.(fullchain, );
..();
nginxConf = ;
sshAccess.(nginxConf, confFile);
..();
testResult = sshAccess.();
(testResult.()) {
..();
} {
();
}
sshAccess.();
..();
..();
} (error) {
..(, error);
();
}
}
}
();
示例 3: 阿里云 OSS 部署 Task 插件
用户输入
创建一个 Task 插件,用于将证书部署到阿里云 OSS 上。
你的回答
import { AbstractTaskPlugin, CertInfo, IsTaskPlugin, TaskInput, pluginGroups, RunStrategy } from '@certd/pipeline';
import { CertReader } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'AliyunOSSDeploy',
title: '阿里云 OSS 部署',
desc: '将证书部署到阿里云 OSS 存储上',
icon: 'clarity:cloud-line',
group: pluginGroups.deploy.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AliyunOSSDeploy extends AbstractTaskPlugin {
@TaskInput({
title: '阿里云授权',
component: {
name: 'access-selector',
vModel: 'accessId',
accessTypes: ['aliyun'],
placeholder: '请选择阿里云授权',
},
required: true,
})
accessId = '';
({
: ,
: {
: ,
: [, ],
: ,
},
: ,
})
cert!: ;
({
: ,
: {
: ,
: ,
},
: ,
})
bucketName = ;
({
: ,
: ,
: {
: ,
: ,
},
})
storagePath = ;
(): <> {
..();
{
aliyunAccess = .(.);
..();
certReader = (.);
cert = certReader.();
key = certReader.();
fullchain = certReader.();
..();
basePath = ..() ? . : ;
certFileName = ;
keyFileName = ;
fullchainFileName = ;
aliyunAccess.({
: .,
: certFileName,
: cert,
});
..();
aliyunAccess.({
: .,
: keyFileName,
: key,
});
..();
aliyunAccess.({
: .,
: fullchainFileName,
: fullchain,
});
..();
aliyunAccess.({
: .,
: certFileName,
: ,
});
aliyunAccess.({
: .,
: keyFileName,
: ,
});
aliyunAccess.({
: .,
: fullchainFileName,
: ,
});
..();
..();
} (error) {
..(, error);
();
}
}
}
();
注意事项
- 插件命名:插件名称应遵循命名规范,大写字母开头,驼峰命名。
- 类名规范:类名应与插件名称(name)一致。
- 证书选择:必须包含证书选择参数,用于获取前置任务输出的域名证书。
- 日志输出:使用
this.logger 输出日志,而不是 console,参数文本化,不要传对象,否则会输出[object Object]}。
- 错误处理:执行过程中的错误应被捕获并记录。
- 授权获取:使用
this.getAccess(accessId) 获取授权信息。
- 旧版数据兼容: 新增@TaskInput注解的插件参数,必须要考虑旧版数据兼容,比如新增一个deployType参数,有两种值:
default和custom,需要在使用时判空,走旧版逻辑。
部署逻辑注意事项
-
部署接口逻辑:
- 研究应用的部署接口逻辑,一般有两种:
a. 用户选择网站ID,给网站部署新证书
b. 用户选择证书ID,只需要更新证书即可
- 保证多次执行都能针对同一个对象部署证书
- 确保出错后重新运行能够回归到正常状态
-
前置证书选择:
- 前置证书可以是原始的
certInfo 类型,也可能是上传到平台之后返回的证书id
- 根据接口要求选择合适的证书类型:
a. 如果接口需要上传后的证书id,那么部署时要先将证书上传,再部署
b. 如果接口需要原始的
certInfo 类型,那么直接使用 certInfo 部署证书
c. 当两者都支持时,判断用户选择的证书类型,再考虑优先上传再部署
-
证书清理:
- 如果是先上传再部署的,那么在部署完成后,可能需要考虑清理证书