airport2 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ import { join } from 'path'
2
+ import { name, version } from '../package.json'
3
+ import { PipeLine, remote, logError } from '../src/airport'
4
+
5
+ const file = [name, version].join('-') + '.tgz'
6
+
7
+ // 创建部署任务
8
+ const deployTask = {
9
+ name: '部署应用',
10
+ steps: [
11
+ {
12
+ name: '构建项目',
13
+ run: [
14
+ 'mkdir -p .cache',
15
+ 'mv `npm pack` .cache/',
16
+ ],
17
+ },
18
+ {
19
+ name: '发布到远程服务器',
20
+ async run() {
21
+ const ssh = remote('gavin@192.168.5.121')
22
+ const dir = (path?: string) => join('/home/gavin/test', path ?? '')
23
+ try {
24
+ await ssh.run('rm -f', dir('latest'))
25
+ await ssh.run('mkdir -p', dir(version))
26
+ await ssh.scp(`.cache/${file}`, dir())
27
+ await ssh.run('tar -zxvf', dir(file), '-C', dir(version))
28
+ await ssh.run('ln -s', dir(version), dir('latest'))
29
+ }
30
+ catch (e) {
31
+ logError(e)
32
+ }
33
+ },
34
+ },
35
+ ],
36
+ }
37
+
38
+ // 执行流水线
39
+ PipeLine.run([deployTask])
@@ -0,0 +1,39 @@
1
+ import { join } from 'path'
2
+ import { name, version } from '../package.json'
3
+ import { PipeLine, Task, remote, logError } from '../src/airport'
4
+
5
+ const file = [name, version].join('-') + '.tgz'
6
+
7
+ // 创建部署任务
8
+ const deployTask = new Task({
9
+ name: '部署应用',
10
+ steps: [
11
+ {
12
+ name: '构建项目',
13
+ run: [
14
+ 'mkdir -p .cache',
15
+ 'mv `npm pack` .cache/',
16
+ ],
17
+ },
18
+ {
19
+ name: '发布到远程服务器',
20
+ async run() {
21
+ const ssh = remote('gavin@192.168.5.121')
22
+ const dir = (path?: string) => join('/home/gavin/test', path ?? '')
23
+ try {
24
+ await ssh.run('rm -f', dir('latest'))
25
+ await ssh.run('mkdir -p', dir(version))
26
+ await ssh.scp(`.cache/${file}`, dir())
27
+ await ssh.run('tar -zxvf', dir(file), '-C', dir(version))
28
+ await ssh.run('ln -s', dir(version), dir('latest'))
29
+ }
30
+ catch (e) {
31
+ logError(e)
32
+ }
33
+ },
34
+ },
35
+ ],
36
+ })
37
+
38
+ // 执行流水线
39
+ PipeLine.test([deployTask])
@@ -26,6 +26,7 @@ const deployTask = new Task({
26
26
  await ssh.scp(`.cache/${file}`, dir())
27
27
  await ssh.run('tar -zxvf', dir(file), '-C', dir(version))
28
28
  await ssh.run('ln -s', dir(version), dir('latest'))
29
+ await ssh.run('rm -f', dir(file))
29
30
  }
30
31
  catch (e) {
31
32
  logError(e)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airport2",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "main": "src/index.ts",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,36 +1,58 @@
1
1
  import { Task } from './task'
2
- import { log, purple } from '../lib'
3
- import {
4
- START,
5
- END,
6
- PIPE_TOP_LEFT,
7
- PIPE_TOP_RIGHT,
8
- PIPE_BOTTOM_LEFT,
9
- PIPE_BOTTOM_RIGHT,
10
- } from '../conf'
2
+ import { logPipeStart, logPipeEnd } from '../lib'
3
+ import type { TaskRaw } from '../types'
11
4
 
12
5
  export class PipeLine {
13
6
  public name?: string
7
+ // public log: boolean = true // 决定是否打印日志
14
8
  private tasks: Task[]
15
9
 
16
- static run(tasks: Task[]) {
10
+ static run(tasks: Task[] | TaskRaw[]) {
17
11
  return new PipeLine(tasks).run()
18
12
  }
19
13
 
20
- constructor(tasks: Task[]) {
21
- this.tasks = tasks
14
+ static test(tasks: Task[] | TaskRaw[]) {
15
+ return new PipeLine(tasks).test()
16
+ }
17
+
18
+ constructor(tasks: Task[] | TaskRaw[]) {
19
+ if (tasks.length > 0 && tasks[0] instanceof Task) {
20
+ this.tasks = tasks as Task[]
21
+ } else {
22
+ this.tasks = (tasks as TaskRaw[]).map((task) => new Task(task))
23
+ }
22
24
  }
23
25
 
24
26
  /**
25
27
  * 执行任务中的所有步骤
26
28
  */
27
29
  async run() {
28
- log(PIPE_TOP_LEFT, purple('Airport PipeLine'), START, PIPE_TOP_RIGHT)
30
+ this.beforeRun()
31
+ for (const [index, task] of this.tasks.entries()) {
32
+ if (!task.skip) {
33
+ task.index = index + 1
34
+ await task._runPipeLine()
35
+ }
36
+ }
37
+ this.afterRun()
38
+ }
29
39
 
30
- for (const task of this.tasks) {
31
- await task.run()
40
+ test() {
41
+ this.beforeRun()
42
+ for (const [index, task] of this.tasks.entries()) {
43
+ if (!task.skip) {
44
+ task.index = index + 1
45
+ task.test()
46
+ }
32
47
  }
48
+ this.afterRun()
49
+ }
50
+
51
+ private beforeRun() {
52
+ logPipeStart()
53
+ }
33
54
 
34
- log(PIPE_BOTTOM_LEFT, purple('Airport PipeLine'), END, PIPE_BOTTOM_RIGHT)
55
+ private afterRun() {
56
+ logPipeEnd()
35
57
  }
36
58
  }
@@ -1,35 +1,48 @@
1
- import { log, run } from '../lib'
2
1
  import type { StepRaw } from '../types'
2
+ import { log, run, logStepStart, logStepEnd, logTesting } from '../lib'
3
3
 
4
4
  export class Step {
5
5
  public name: string
6
6
  public skip: boolean = false
7
+ public index: number = 1 // 当前索引
7
8
  private step: StepRaw
8
- private test: boolean
9
+ private testing: boolean = false
9
10
 
10
- constructor(step: StepRaw, test: boolean = false) {
11
- this.test = test
11
+ constructor(step: StepRaw) {
12
12
  this.step = step
13
13
  this.name = step.name
14
14
  this.skip = step.skip ?? false
15
15
  }
16
16
 
17
+ async run() {
18
+ this.beforeRun()
19
+ await this.exec()
20
+ this.afterRun()
21
+ }
22
+
23
+ async test() {
24
+ this.testing = true
25
+ this.beforeRun()
26
+ if (typeof this.step.run === 'string') {
27
+ this.runStringTesting()
28
+ } else if (Array.isArray(this.step.run)) {
29
+ this.runArrayTesting()
30
+ } else {
31
+ logTesting('[async function run]')
32
+ }
33
+ this.afterRun()
34
+ }
35
+
17
36
  /**
18
37
  * 执行步骤
19
38
  */
20
- async run(): Promise<any> {
39
+ private async exec(): Promise<any> {
21
40
  if (typeof this.step.run === 'string') {
22
41
  return this.runString()
23
42
  } else if (Array.isArray(this.step.run)) {
24
43
  return this.runArray()
25
44
  } else {
26
- // 函数类型的步骤
27
- if (this.test) {
28
- log(`[TEST MODE] Would execute function: ${this.step.run.name}`)
29
- } else {
30
- // 实际执行函数
31
- return await this.step.run()
32
- }
45
+ return await this.step.run()
33
46
  }
34
47
  }
35
48
 
@@ -38,11 +51,7 @@ export class Step {
38
51
  */
39
52
  private async runString() {
40
53
  const runString = this.step.run as string
41
- if (this.test) {
42
- log(`[TEST MODE] Would execute: ${runString}`)
43
- } else {
44
- return await run(runString)
45
- }
54
+ return await run(runString)
46
55
  }
47
56
 
48
57
  /**
@@ -50,13 +59,28 @@ export class Step {
50
59
  */
51
60
  private async runArray() {
52
61
  const runArray = this.step.run as string[]
53
- if (this.test) {
54
- log(`[TEST MODE] Would execute array: ${runArray}`)
55
- } else {
56
- for (const cmd of runArray) {
57
- await run(cmd)
58
- }
62
+ for (const cmd of runArray) {
63
+ await run(cmd)
59
64
  }
60
65
  }
61
- }
62
66
 
67
+ private runStringTesting() {
68
+ const runString = this.step.run as string
69
+ logTesting(runString)
70
+ }
71
+
72
+ private runArrayTesting() {
73
+ const runArray = this.step.run as string[]
74
+ for (const cmd of runArray) {
75
+ logTesting(cmd)
76
+ }
77
+ }
78
+
79
+ private beforeRun() {
80
+ logStepStart(this)
81
+ }
82
+
83
+ private afterRun() {
84
+ logStepEnd(this)
85
+ }
86
+ }
@@ -1,35 +1,74 @@
1
1
  import { Step } from './step'
2
- import { logStart, purple, blue } from '../lib'
3
- import { START, END } from '../conf'
4
- import type { TaskOptions, StepRaw } from '../types'
2
+ import { logTaskStart, logTaskEnd } from '../lib'
3
+ import type { TaskRaw, StepRaw } from '../types'
5
4
 
6
5
  export class Task {
6
+ // 任务名称
7
7
  public name: string
8
- public opt: TaskOptions
8
+
9
+ /** Task元数据 */
10
+ public opt: TaskRaw
11
+
12
+ /** 在流水线中的任务索引 */
13
+ public index: number = 1 // 当前索引
14
+
15
+ /** 流水线中是否跳过当前任务 */
16
+ public skip: boolean = false
17
+
18
+ // 是否无头模式,是否自调用
19
+ // true: PipeLine模式,Task由PipeLine.run调用
20
+ // false: 自调用,独立任务模式,外部直接调用Task.run
21
+ public inPipeLine: boolean = false
22
+
23
+ // 任务步骤
9
24
  private steps: StepRaw[]
10
25
 
11
- constructor(opt: TaskOptions) {
26
+ constructor(opt: TaskRaw) {
12
27
  this.opt = opt
13
28
  this.name = opt.name
14
29
  this.steps = opt.steps
30
+ this.skip = opt.skip ?? false
15
31
  }
16
32
 
17
33
  /**
18
34
  * 执行任务中的所有步骤
19
35
  */
20
- async run(test: boolean = false) {
21
- const steps = this.steps.map((step) => new Step(step, test))
22
-
23
- logStart(purple('T'), START, blue(this.name))
36
+ async run() {
37
+ this.beforeRun()
38
+ const steps = this.steps.map((step) => new Step(step))
24
39
 
25
- for (const step of steps) {
40
+ for (const [index, step] of steps.entries()) {
26
41
  if (!step.skip) {
27
- logStart(purple('S'), START, blue(step.name))
42
+ step.index = index + 1
28
43
  step.skip || (await step.run())
29
- logStart(purple('S'), END, blue(step.name))
30
44
  }
31
45
  }
46
+ this.afterRun()
47
+ }
48
+
49
+ test() {
50
+ this.inPipeLine = true
51
+ this.beforeRun()
52
+ const steps = this.steps.map((step) => new Step(step))
53
+ for (const [index, step] of steps.entries()) {
54
+ if (!step.skip) {
55
+ step.index = index + 1
56
+ step.skip || (step.test())
57
+ }
58
+ }
59
+ this.afterRun()
60
+ }
61
+
62
+ async _runPipeLine() {
63
+ this.inPipeLine = true
64
+ await this.run()
65
+ }
66
+
67
+ private beforeRun() {
68
+ logTaskStart(this)
69
+ }
32
70
 
33
- logStart(purple('T'), END, blue(this.name))
71
+ private afterRun() {
72
+ logTaskEnd(this)
34
73
  }
35
74
  }
@@ -1,7 +1,15 @@
1
1
  import { Writable } from 'stream'
2
- import { gray } from './color'
2
+ import { blue, gray, purple } from './color'
3
3
  import { formatTimestamp } from './date'
4
- import { PIPE_PREFIX } from '../conf'
4
+ import {
5
+ START,
6
+ END,
7
+ PIPE_PREFIX,
8
+ PIPE_TOP_LEFT,
9
+ PIPE_TOP_RIGHT,
10
+ PIPE_BOTTOM_LEFT,
11
+ PIPE_BOTTOM_RIGHT,
12
+ } from '../conf'
5
13
 
6
14
  export const log = (...args: any[]) => {
7
15
  console.log(gray(`[${formatTimestamp()}]`), ...args)
@@ -19,6 +27,11 @@ export const logErrorStart = (...args: any[]) => {
19
27
  logError(PIPE_PREFIX, ...args)
20
28
  }
21
29
 
30
+ export const logTesting = (...args: any[]) => {
31
+ logStart(gray('Testing'), ...args)
32
+ }
33
+
34
+ // 日志流、用于子进程直接输出日志到控制台
22
35
  export const createLogStream = (prefix?: string) =>
23
36
  new Writable({
24
37
  write(chunk: Buffer, _encoding, callback) {
@@ -39,3 +52,42 @@ export const createLogStream = (prefix?: string) =>
39
52
  callback()
40
53
  },
41
54
  })
55
+
56
+
57
+ // 流水线开始时日志
58
+ export const logPipeStart = () => {
59
+ log(PIPE_TOP_LEFT, purple('Airport PipeLine'), START, PIPE_TOP_RIGHT)
60
+ }
61
+
62
+ // 流水线结束时日志
63
+ export const logPipeEnd = () => {
64
+ log(PIPE_BOTTOM_LEFT, purple('Airport PipeLine'), END, PIPE_BOTTOM_RIGHT)
65
+ }
66
+
67
+
68
+ // 任务开始时日志
69
+ export const logTaskStart = ({ name, index, inPipeLine }) => {
70
+ if (!inPipeLine) {
71
+ logPipeStart()
72
+ }
73
+ logStart(purple('T' + index), START, blue(name))
74
+ }
75
+
76
+ // 任务结束时日志
77
+ export const logTaskEnd = ({ name, index, inPipeLine }) => {
78
+ logStart(purple('T' + index), END, blue(name))
79
+ if (!inPipeLine) {
80
+ logPipeEnd()
81
+ }
82
+ }
83
+
84
+
85
+ // 步骤开始时日志
86
+ export const logStepStart = ({ name, index }) => {
87
+ logStart(purple('S'+ index), START, blue(name))
88
+ }
89
+
90
+ // 步骤结束时日志
91
+ export const logStepEnd = ({ name, index }) => {
92
+ logStart(purple('S'+ index), END, blue(name))
93
+ }
@@ -6,30 +6,33 @@ export interface StepResult {
6
6
  output: string
7
7
  }
8
8
 
9
+
9
10
  /**
10
- * 步骤配置选项
11
+ * 任务执行结果
11
12
  */
12
- export interface StepRaw {
13
+ export interface TaskResult {
14
+ success: boolean
15
+ stepResults: StepResult[]
13
16
  name: string
14
- skip?: boolean
15
- run: string | string[] | ((...args: any[]) => Promise<any>)
16
17
  }
17
18
 
18
19
 
19
20
  /**
20
- * 任务执行结果
21
+ * 步骤配置选项
21
22
  */
22
- export interface TaskResult {
23
- success: boolean
24
- stepResults: StepResult[]
23
+ export interface StepRaw {
25
24
  name: string
25
+ skip?: boolean
26
+ run: string | string[] | ((...args: any[]) => Promise<any>)
26
27
  }
27
28
 
29
+
28
30
  /**
29
31
  * 任务配置选项
30
32
  */
31
- export interface TaskOptions {
33
+ export interface TaskRaw {
32
34
  name: string
33
35
  steps: StepRaw[]
34
36
  test?: boolean
37
+ skip?: boolean
35
38
  }