mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-08 03:20:12 +08:00
feat: able to test files
This commit is contained in:
1 parent
af27780763
commit
625c1a80da
5 files changed
+99
-28
No files matched your search
@@ -65,7 +65,6 @@ function printLine(content, delay, color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function printResponses(responses, delay = 100) {
|
async function printResponses(responses, delay = 100) {
|
||||||
await waitTime(1000)
|
|
||||||
console.log()
|
console.log()
|
||||||
console.log(colorize('-------------', 'white'))
|
console.log(colorize('-------------', 'white'))
|
||||||
for (const response of responses) {
|
for (const response of responses) {
|
||||||
|
|||||||
@@ -11,9 +11,15 @@ const responseSchema = new Schema({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const fileConditionSchema = new Schema({
|
const fileConditionSchema = new Schema({
|
||||||
path: String,
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
type: String,
|
type: String,
|
||||||
exists: Boolean,
|
exists: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const conditionSchema = new Schema({
|
const conditionSchema = new Schema({
|
||||||
|
|||||||
@@ -136,22 +136,16 @@ stages:
|
|||||||
content:
|
content:
|
||||||
- You are using `less` or the output contains `wow`.
|
- You are using `less` or the output contains `wow`.
|
||||||
|
|
||||||
- id: nested-condition
|
- id: test-file-exists
|
||||||
task: Test nested condition
|
task: Test file exists
|
||||||
repeatable: true
|
repeatable: true
|
||||||
condition:
|
condition:
|
||||||
$or:
|
command:
|
||||||
- pwd:
|
- ls
|
||||||
- '^/etc/zsh$'
|
files:
|
||||||
$not:
|
- path: /home/commander/jail
|
||||||
command:
|
type: folder
|
||||||
- ls
|
|
||||||
- $not:
|
|
||||||
pwd:
|
|
||||||
- '^/etc/zsh$'
|
|
||||||
command:
|
|
||||||
- ls
|
|
||||||
responses:
|
responses:
|
||||||
- type: narrative
|
- type: narrative
|
||||||
content:
|
content:
|
||||||
- You are in `/etc/zsh` and not using `ls`, or you are not in `/etc/zsh` and using `ls`.
|
- There is a jail
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { PassThrough } from 'stream'
|
||||||
import Docker from 'dockerode'
|
import Docker from 'dockerode'
|
||||||
|
import { buildFileCheckCmd } from '@linux-odyssey/utils'
|
||||||
import config from '../config.js'
|
import config from '../config.js'
|
||||||
|
|
||||||
const engine = new Docker()
|
const engine = new Docker()
|
||||||
@@ -77,3 +79,47 @@ export async function deleteContainer(id) {
|
|||||||
}
|
}
|
||||||
await container.remove()
|
await container.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utility function to convert a stream to a string
|
||||||
|
function streamToString(stream) {
|
||||||
|
const chunks = []
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
stream.on('data', (chunk) => chunks.push(chunk))
|
||||||
|
stream.on('error', reject)
|
||||||
|
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
|
||||||
|
setTimeout(() => resolve(Buffer.concat(chunks).toString('utf8')), 100)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkFiles(id, files) {
|
||||||
|
const container = engine.getContainer(id)
|
||||||
|
if (!container) {
|
||||||
|
throw new Error('Container not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd = buildFileCheckCmd(files)
|
||||||
|
console.log(cmd)
|
||||||
|
const exec = await container.exec({
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
|
Cmd: ['sh', '-c', cmd],
|
||||||
|
})
|
||||||
|
const execStream = await exec.start()
|
||||||
|
const outStream = new PassThrough()
|
||||||
|
const errStream = new PassThrough()
|
||||||
|
|
||||||
|
console.log('exec started')
|
||||||
|
|
||||||
|
exec.modem.demuxStream(execStream, outStream, errStream)
|
||||||
|
const [output, errorOutput] = await Promise.all([
|
||||||
|
streamToString(outStream),
|
||||||
|
streamToString(errStream),
|
||||||
|
])
|
||||||
|
|
||||||
|
console.log('output', output, 'errorOutput', errorOutput)
|
||||||
|
|
||||||
|
const execOutput = await exec.inspect()
|
||||||
|
console.log('exit code', execOutput.ExitCode)
|
||||||
|
console.log('exit code === 0', execOutput.ExitCode === 0)
|
||||||
|
return execOutput.ExitCode === 0
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { FileGraph } from '@linux-odyssey/file-graph'
|
|||||||
import { Quest } from '@linux-odyssey/models'
|
import { Quest } from '@linux-odyssey/models'
|
||||||
import { pushToSession } from '../api/socket.js'
|
import { pushToSession } from '../api/socket.js'
|
||||||
import SessionHandler from './sessionHandler.js'
|
import SessionHandler from './sessionHandler.js'
|
||||||
|
import { checkFiles } from '../containers/docker.js'
|
||||||
|
|
||||||
// 檢查 pattern 是否符合 input
|
// 檢查 pattern 是否符合 input
|
||||||
const checkMatch = (pattern, input) => {
|
const checkMatch = (pattern, input) => {
|
||||||
@@ -53,15 +54,31 @@ export default class CommandHandler extends SessionHandler {
|
|||||||
this.session.graph = graph
|
this.session.graph = graph
|
||||||
}
|
}
|
||||||
|
|
||||||
isMatch(condition) {
|
async isMatch(condition) {
|
||||||
const keys = ['command', 'output', 'error', 'pwd']
|
const keys = ['command', 'output', 'error', 'pwd']
|
||||||
console.log('condition', condition)
|
if (keys.some((k) => !checkMatch(condition[k], this.commandInput[k]))) {
|
||||||
return (
|
return false
|
||||||
keys.every((k) => checkMatch(condition[k], this.commandInput[k])) &&
|
}
|
||||||
(condition.$or.length === 0 ||
|
if (condition.files.length > 0) {
|
||||||
condition.$or.some((c) => this.isMatch(c))) &&
|
const match = await this.checkFiles(condition.files)
|
||||||
(!condition.$not || !this.isMatch(condition.$not))
|
if (!match) return false
|
||||||
)
|
}
|
||||||
|
if (condition.$not) {
|
||||||
|
const match = await this.isMatch(condition.$not)
|
||||||
|
if (match) return false
|
||||||
|
}
|
||||||
|
if (condition.$or.length > 0) {
|
||||||
|
const matches = await Promise.all(
|
||||||
|
condition.$or.map((c) => this.isMatch(c))
|
||||||
|
)
|
||||||
|
if (matches.every((m) => m === false)) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFiles(files) {
|
||||||
|
console.log('checkFiles', files)
|
||||||
|
return checkFiles(this.session.containerId, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
@@ -75,9 +92,18 @@ export default class CommandHandler extends SessionHandler {
|
|||||||
this.handleEvent()
|
this.handleEvent()
|
||||||
this.handleCommand()
|
this.handleCommand()
|
||||||
|
|
||||||
const response = stages
|
const matches = await Promise.all(
|
||||||
.filter((s) => this.isMatch(s.condition))
|
stages.map((stage) => this.isMatch(stage.condition))
|
||||||
.reduce((r, s) => ({ ...r, ...this.execute(s) }), {})
|
)
|
||||||
|
|
||||||
|
console.log('matches', matches)
|
||||||
|
|
||||||
|
const filteredStages = stages.filter((_, i) => matches[i])
|
||||||
|
console.log('filteredStages', filteredStages)
|
||||||
|
const response = filteredStages.reduce(
|
||||||
|
(r, s) => ({ ...r, ...this.execute(s) }),
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
end: this.session.status === 'finished',
|
end: this.session.status === 'finished',
|
||||||
|
|||||||
Reference in new issue
Block a user