feat: create stage

This commit is contained in:
wancat committed 2024-03-08 00:38:49 +08:00
1 parent 65ad72223b
commit 77fe2d37b3
11 files changed
+67 -18

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
import { IStage, ICommand } from './types'
import { Condition } from './condition/Condition'
export class Stage {
id: string
name: string
condition: Condition
constructor({ id, name, condition }: IStage) {
this.id = id
this.name = name
this.condition = new Condition(condition)
}
satisfies(command: ICommand): boolean {
return this.condition.match(command)
}
}
@@ -1,4 +1,4 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
export class CommandMatcher implements Matcher { export class CommandMatcher implements Matcher {
@@ -7,7 +7,7 @@ export class CommandMatcher implements Matcher {
this.matcher = new RegExp(command, flags) this.matcher = new RegExp(command, flags)
} }
match(command: Command): boolean { match(command: ICommand): boolean {
return !!command.command && this.matcher.test(command.command) return !!command.command && this.matcher.test(command.command)
} }
} }
+2 -2
View File
@@ -1,5 +1,5 @@
import { import {
Command, ICommand,
ICondition, ICondition,
IFileExistenceChecker, IFileExistenceChecker,
FileExistenceInput, FileExistenceInput,
@@ -30,7 +30,7 @@ export class Condition {
this.matchers.push(new ErrorMatcher(error)) // always check unexpected error this.matchers.push(new ErrorMatcher(error)) // always check unexpected error
} }
match(command: Command): boolean { match(command: ICommand): boolean {
return this.matchers.every((matcher) => { return this.matchers.every((matcher) => {
return matcher.match(command) return matcher.match(command)
}) })
+2 -2
View File
@@ -1,5 +1,5 @@
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
import { Command } from '../types.js' import { ICommand } from '../types.js'
export class ErrorMatcher implements Matcher { export class ErrorMatcher implements Matcher {
private matcher?: RegExp private matcher?: RegExp
@@ -9,7 +9,7 @@ export class ErrorMatcher implements Matcher {
} }
} }
match(command: Command): boolean { match(command: ICommand): boolean {
if (!this.matcher) { if (!this.matcher) {
return !command.error return !command.error
} }
+2 -2
View File
@@ -1,5 +1,5 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
export interface Matcher { export interface Matcher {
match(command: Command): boolean match(command: ICommand): boolean
} }
+2 -2
View File
@@ -1,10 +1,10 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
export class NotMatcher implements Matcher { export class NotMatcher implements Matcher {
constructor(private condition: Matcher) {} constructor(private condition: Matcher) {}
match(command: Command): boolean { match(command: ICommand): boolean {
return !this.condition.match(command) return !this.condition.match(command)
} }
} }
+2 -2
View File
@@ -1,10 +1,10 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
export class OrMatcher implements Matcher { export class OrMatcher implements Matcher {
constructor(private conditions: Matcher[]) {} constructor(private conditions: Matcher[]) {}
match(command: Command): boolean { match(command: ICommand): boolean {
return this.conditions.some((condition) => condition.match(command)) return this.conditions.some((condition) => condition.match(command))
} }
} }
+2 -2
View File
@@ -1,4 +1,4 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
export class OutputMatcher implements Matcher { export class OutputMatcher implements Matcher {
@@ -7,7 +7,7 @@ export class OutputMatcher implements Matcher {
this.matcher = new RegExp(pattern, args) this.matcher = new RegExp(pattern, args)
} }
match(command: Command): boolean { match(command: ICommand): boolean {
return !!command.output && this.matcher.test(command.output.trim()) return !!command.output && this.matcher.test(command.output.trim())
} }
} }
+2 -2
View File
@@ -1,10 +1,10 @@
import { Command } from '../types.js' import { ICommand } from '../types.js'
import { Matcher } from './Matcher.js' import { Matcher } from './Matcher.js'
export class PwdMatcher implements Matcher { export class PwdMatcher implements Matcher {
constructor(private pwd: string) {} constructor(private pwd: string) {}
match(command: Command): boolean { match(command: ICommand): boolean {
return command.pwd === this.pwd return command.pwd === this.pwd
} }
} }
+8 -2
View File
@@ -1,4 +1,4 @@
export interface Command { export interface ICommand {
command?: string command?: string
output?: string output?: string
error?: string error?: string
@@ -12,7 +12,7 @@ export interface ICondition {
pwd?: string pwd?: string
or?: ICondition[] or?: ICondition[]
not?: ICondition not?: ICondition
files: FileExistenceInput[] files?: FileExistenceInput[]
} }
export enum FileType { export enum FileType {
@@ -32,3 +32,9 @@ export interface FileExistenceInput extends FileInput {
export interface IFileExistenceChecker { export interface IFileExistenceChecker {
exists(file: FileInput): Promise<boolean> exists(file: FileInput): Promise<boolean>
} }
export interface IStage {
id: string
name: string
condition: ICondition
}
+25
View File
@@ -0,0 +1,25 @@
import { test, expect } from '@jest/globals'
import { Stage } from '../src/Stage'
test('check stage condition', () => {
const stage = new Stage({
id: 'stage1',
name: 'List the files using `ls`',
condition: {
command: 'ls',
pwd: '/home/user',
},
})
expect(
stage.satisfies({
command: 'ls',
pwd: '/home/user',
})
).toBe(true)
expect(
stage.satisfies({
command: 'ls',
pwd: '/home/user/Downloads',
})
).toBe(false)
})