refactor: move

This commit is contained in:
xiaojunnuo
2021-02-08 00:21:36 +08:00
parent cfb1034450
commit 82f86d9556
150 changed files with 14691 additions and 2059 deletions
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "standard",
"env": {
"mocha": true
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
]
}
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@certd/executor",
"version": "0.1.14",
"description": "",
"main": "src/index.js",
"scripts": {
"test": "echo \\\"Error: no test specified\\\" && exit 1",
"build": "webpack --config webpack.config.cjs ",
"rollup": "rollup --config rollup.config.js"
},
"type": "module",
"dependencies": {
"@certd/api": "^0.1.13",
"@certd/certd": "^0.1.13",
"@certd/dns-providers": "^0.1.13",
"@certd/plugins": "^0.1.13",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.1",
"chai": "^4.2.0",
"eslint": "^7.15.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"mocha": "^8.2.1",
"rollup": "^2.35.1",
"rollup-plugin-terser": "^7.0.2"
},
"author": "Greper",
"license": "MIT",
"sideEffects": false,
"gitHead": "4a421d5b142d453203c68ce6d1036e168ea2455b"
}
+21
View File
@@ -0,0 +1,21 @@
import json from '@rollup/plugin-json'
import { terser } from 'rollup-plugin-terser'
import commonjs from '@rollup/plugin-commonjs'
import { nodeResolve } from '@rollup/plugin-node-resolve'
export default {
input: 'src/index.js',
output: [
{
file: 'bundle.js',
format: 'es'
},
{
file: 'bundle.min.js',
format: 'iife',
name: 'version',
plugins: [terser()]
}
],
plugins: [json(), commonjs(), nodeResolve()]
}
+173
View File
@@ -0,0 +1,173 @@
import { Certd } from '@certd/certd'
import { pluginRegistry, util } from '@certd/api'
import _ from 'lodash-es'
import dayjs from 'dayjs'
import { Trace } from './trace.js'
import PluginAliyun from '@certd/plugin-aliyun'
import PluginTencent from '@certd/plugin-tencent'
import PluginHost from '@certd/plugin-host'
// 安装默认插件和授权提供者
PluginAliyun.install()
PluginTencent.install()
PluginHost.install()
const logger = util.logger
function createDefaultOptions () {
return {
args: {
forceCert: false,
forceDeploy: true,
forceRedeploy: false,
doNotThrowError: false // 部署流程执行有错误时,不抛异常,此时整个任务执行完毕后,可以返回结果,你可以在返回结果中处理
}
}
}
export class Executor {
constructor () {
this.trace = new Trace()
}
async run (options) {
logger.info('------------------- Cert-D ---------------------')
try {
this.transferToSafetyOptions(options)
options = _.merge(createDefaultOptions(), options)
return await this.doRun(options)
} catch (e) {
logger.error('任务执行出错:' + e.message, e)
throw e
}
}
async doRun (options) {
// 申请证书
logger.info('任务开始')
const certd = new Certd(options)
const cert = await this.runCertd(certd)
if (cert == null) {
throw new Error('申请证书失败')
}
logger.info('证书保存路径:', cert.certDir)
logger.info('----------------------')
if (!cert.isNew) {
// 如果没有更新
if (!options.args.forceDeploy && !options.args.forceRedeploy) {
// 且不需要强制运行deploy
logger.info('证书无更新,无需重新部署')
logger.info('任务完成')
return { cert }
}
}
// 读取上次执行进度
let context = {}
const contextJson = await certd.certStore.getCurrentFile('context.json')
if (contextJson) {
context = JSON.parse(contextJson)
}
context.certIsNew = !!cert.isNew
const trace = new Trace(context)
const resultTrace = trace.getInstance({ type: 'result' })
// 运行部署任务
try {
await this.runDeploys({ options, cert, context, trace })
} finally {
await certd.certStore.setCurrentFile('context.json', JSON.stringify(context))
}
logger.info('任务完成')
trace.print()
const result = resultTrace.get({ })
const returnData = {
cert,
context,
result
}
if (result.status === 'error' && options.args.doNotThrowError === false) {
throw new Error(result.remark)
}
return returnData
}
async runCertd (certd) {
logger.info(`证书任务 ${JSON.stringify(certd.options.cert.domains)} 开始`)
const cert = await certd.certApply()
logger.info(`证书任务 ${JSON.stringify(certd.options.cert.domains)} 完成`)
return cert
}
async runDeploys ({ options, cert, context, trace }) {
if (cert == null) {
const certd = new Certd(options)
cert = await certd.readCurrentCert()
}
logger.info('部署任务开始')
for (const deploy of options.deploy) {
const deployName = deploy.deployName
logger.info(`------------【${deployName}】-----------`)
const deployTrace = trace.getInstance({ type: 'deploy', deployName })
if (deploy.disabled === true) {
logger.info('此流程已被禁用,跳过')
logger.info('')
deployTrace.set({ value: { current: 'skip', status: 'disabled', remark: '流程禁用' } })
continue
}
try {
for (const task of deploy.tasks) {
if (context[deployName] == null) {
context[deployName] = {}
}
const taskContext = context[deployName]
// 开始执行任务列表
await this.runTask({ options, cert, task, context: taskContext, deploy, trace })
}
deployTrace.set({ value: { status: 'success', remark: '执行成功' } })
trace.set({ type: 'result', value: { status: 'success', remark: '执行成功' } })
} catch (e) {
deployTrace.set({ value: { status: 'error', remark: '执行失败:' + e.message } })
trace.set({ type: 'result', value: { status: 'error', remark: deployName + '执行失败:' + e.message } })
logger.error('流程执行失败', e)
}
logger.info('')
}
}
async runTask ({ options, task, cert, context, deploy, trace }) {
const taskType = task.type
const Plugin = pluginRegistry.get(taskType)
const deployName = deploy.deployName
const taskName = task.taskName
if (Plugin == null) {
throw new Error(`插件:${taskType}还未安装`)
}
let instance = Plugin
if (Plugin instanceof Function) {
instance = new Plugin({ accessProviders: options.accessProviders })
}
const taskTrace = trace.getInstance({ type: 'deploy', deployName, taskName })
const traceStatus = taskTrace.get({})
if (traceStatus && traceStatus.status === 'success' && !options.args.forceRedeploy) {
logger.info(`----【${taskName}】已经执行完成,跳过此任务`)
taskTrace.set({ value: { current: 'skip', status: 'success', remark: '已执行成功过,本次跳过' } })
return
}
logger.info(`----【${taskName}】开始执行`)
try {
// 执行任务
await instance.execute({ cert, props: task.props, context })
taskTrace.set({ value: { current: 'success', status: 'success', remark: '执行成功', time: dayjs().format() } })
} catch (e) {
taskTrace.set({ value: { current: 'error', status: 'error', remark: e.message, time: dayjs().format() } })
throw e
}
logger.info(`----任务【${taskName}】执行完成`)
logger.info('')
}
}
+94
View File
@@ -0,0 +1,94 @@
import { util } from '@certd/api'
import _ from 'lodash-es'
const logger = util.logger
export class Trace {
constructor (context) {
this.context = context
}
getInstance ({ type, deployName, taskName }) {
return {
get: ({ prop }) => {
return this.get({ type, deployName, taskName, prop })
},
set: ({ prop, value }) => {
this.set({ type, deployName, taskName, prop, value })
}
}
}
set ({ type, deployName, taskName, prop, value }) {
const key = this.buildTraceKey({ type, deployName, taskName, prop })
const oldValue = _.get(this.context, key) || {}
_.merge(oldValue, value)
_.set(this.context, key, oldValue)
}
get ({ type, deployName, taskName, prop }) {
return _.get(this.context, this.buildTraceKey({ type, deployName, taskName, prop }))
}
buildTraceKey ({ type = 'default', deployName, taskName, prop }) {
let key = '__trace__.' + type
if (deployName) {
key += '.'
key += deployName.replace(/\./g, '_')
}
if (taskName) {
key += '.tasks.'
key += taskName.replace(/\./g, '_')
}
if (prop) {
key += '.' + prop
}
return key
}
getStringLength (str) {
const enLength = str.replace(/[\u0391-\uFFE5]/g, '').length // 先把中文替换成两个字节的英文,再计算长度
return Math.floor((str.length - enLength) * 1.5) + enLength
}
print () {
const context = this.context
logger.info('---------------------------任务结果总览--------------------------')
if (context.certIsNew) {
this.printTraceLine({ current: 'success', remark: '证书更新成功' }, '更新证书')
} else {
this.printTraceLine({ current: 'skip', remark: '还未到过期时间,跳过' }, '更新证书')
}
const trace = this.get({ type: 'deploy' })
// logger.info('trace', trace)
for (const deployName in trace) {
if (trace[deployName] == null) {
trace[deployName] = {}
}
const traceStatus = this.printTraceLine(trace[deployName], deployName)
const tasks = traceStatus.tasks
if (tasks) {
for (const taskName in tasks) {
if (tasks[taskName] == null) {
tasks[taskName] = {}
}
this.printTraceLine(tasks[taskName], taskName, ' └')
}
}
}
const result = this.get({ type: 'result' })
this.printTraceLine(result, 'result', '')
const mainContext = {}
_.merge(mainContext, context)
delete mainContext.__trace__
logger.info('【context】', JSON.stringify(mainContext))
}
printTraceLine (traceStatus, name, prefix = '') {
const length = this.getStringLength(name)
const endPad = _.repeat('-', 45 - prefix.length - length) + '\t'
const status = traceStatus.current || traceStatus.status || ''
const remark = traceStatus.remark || ''
logger.info(`${prefix}${name}${endPad}[${status}] \t${remark}`)
return traceStatus
}
}
+34
View File
@@ -0,0 +1,34 @@
import pkg from 'chai'
import { Executor } from '../src'
import { createOptions } from '../../../../test/options.js'
const { expect } = pkg
describe('AutoDeploy', function () {
it('#run', async function () {
this.timeout(120000)
const options = createOptions()
const executor = new Executor()
const ret = await executor.run(options)
expect(ret).ok
expect(ret.cert).ok
})
it('#forceCert', async function () {
this.timeout(120000)
const executor = new Executor()
const options = createOptions()
options.args.forceCert = true
options.args.forceDeploy = true
const ret = await executor.run(options)
expect(ret).ok
expect(ret.cert).ok
})
it('#forceDeploy', async function () {
this.timeout(120000)
const executor = new Executor()
const options = createOptions()
const ret = await executor.run(options, { forceCert: false, forceDeploy: true, forceRedeploy: true })
expect(ret).ok
expect(ret.cert).ok
})
})
+23
View File
@@ -0,0 +1,23 @@
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
console.log(CleanWebpackPlugin)
module.exports = {
devtool: 'source-map',
target: 'node',
entry: './src/index.js',
output: {
filename: 'executor.js',
path: path.resolve(__dirname, 'dist'),
library: 'certdExecutor',
libraryTarget: 'umd'
},
plugins: [
new CleanWebpackPlugin()
],
mode: 'production'
// mode: 'development',
// optimization: {
// usedExports: true
// }
}
File diff suppressed because it is too large Load Diff