Skip to content

Commit a4b688e

Browse files
committed
fix: Scripts making pipelined requests were not waited on to finish before loading the next script.
I observed a bug when using Azure app-configuration to get app configuration values from Azure's App Configuration service to create a robot.config object for other scripts to get values from. appConfigClient.getConfigurationSetting pipelines requests and the other scripts (which depended on this one to complete) were being loaded before getConfigurationSetting returned, resulting in a race condition where other scripts failed to load because they depended on robot.config to have the values from App Configuration.
1 parent 6eb73e3 commit a4b688e

4 files changed

Lines changed: 23 additions & 12 deletions

File tree

src/GenHubot.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function runCommands (hubotDirectory, options) {
4949
// This is a test script.
5050
//
5151
52-
export default (robot) => {
52+
export default async (robot) => {
5353
robot.respond(/helo$/, async res => {
5454
await res.reply("HELO World! I'm Dumbotheelephant.")
5555
})
@@ -92,7 +92,7 @@ export default (robot) => {
9292
this.robot.emit('play', envelope, ...strings)
9393
}
9494
95-
run () {
95+
async run () {
9696
// This is required to get the scripts loaded
9797
this.emit('connected')
9898
}
@@ -108,7 +108,7 @@ export default (robot) => {
108108
}
109109
}
110110
export default {
111-
use (robot) {
111+
async use (robot) {
112112
return new DummyAdapter(robot)
113113
}
114114
}
@@ -128,12 +128,14 @@ export default (robot) => {
128128
describe('Xample testing Hubot scripts', () => {
129129
let robot = null
130130
beforeEach(async () => {
131+
process.env.EXPRESS_PORT = 0
131132
robot = new Robot(dummyRobot, true, 'Dumbotheelephant')
132133
await robot.loadAdapter()
133134
await robot.run()
134135
await robot.loadFile('./scripts', 'Xample.mjs')
135136
})
136137
afterEach(() => {
138+
delete process.env.EXPRESS_PORT
137139
robot.shutdown()
138140
})
139141
it('should handle /helo request', async () => {

src/Robot.mjs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,25 @@ class Robot {
332332
async loadmjs (filePath) {
333333
const forImport = this.prepareForImport(filePath)
334334
const script = await import(forImport)
335+
let result = null
335336
if (typeof script?.default === 'function') {
336-
script.default(this)
337+
result = await script.default(this)
337338
} else {
338339
this.logger.warning(`Expected ${filePath} (after preparing for import ${forImport}) to assign a function to export default, got ${typeof script}`)
339340
}
341+
return result
340342
}
341343

342344
async loadjs (filePath) {
343345
const forImport = this.prepareForImport(filePath)
344346
const script = (await import(forImport)).default
347+
let result = null
345348
if (typeof script === 'function') {
346-
script(this)
349+
result = await script(this)
347350
} else {
348351
this.logger.warning(`Expected ${filePath} (after preparing for import ${forImport}) to assign a function to module.exports, got ${typeof script}`)
349352
}
353+
return result
350354
}
351355

352356
// Public: Loads a file in path.
@@ -362,16 +366,17 @@ class Robot {
362366
// see https://github.com/hubotio/hubot/issues/1355
363367
if (['js', 'mjs'].indexOf(ext) === -1) {
364368
this.logger.debug(`Skipping unsupported file type ${full}`)
365-
return
369+
return null
366370
}
367-
371+
let result = null
368372
try {
369-
await this[`load${ext}`](full)
373+
result = await this[`load${ext}`](full)
370374
this.parseHelp(full)
371375
} catch (error) {
372376
this.logger.error(`Unable to load ${full}: ${error.stack}`)
373377
throw error
374378
}
379+
return result
375380
}
376381

377382
// Public: Loads every script in the given path.
@@ -381,19 +386,22 @@ class Robot {
381386
// Returns nothing.
382387
async load (path) {
383388
this.logger.debug(`Loading scripts from ${path}`)
389+
const results = []
384390
try {
385391
const folder = await File.readdir(path, { withFileTypes: true })
386392
for await (const file of folder) {
387393
if (file.isDirectory()) continue
388394
try {
389-
await this.loadFile(path, file.name)
395+
const result = await this.loadFile(path, file.name)
396+
results.push(result)
390397
} catch (e) {
391398
this.logger.error(`Error loading file ${file.name} - ${e.stack}`)
392399
}
393400
}
394401
} catch (e) {
395402
this.logger.error(`Path ${path} does not exist`)
396403
}
404+
return results
397405
}
398406

399407
// Public: Load scripts from packages specified in the
@@ -460,7 +468,7 @@ class Robot {
460468
if (stat) {
461469
app.use(express.static(stat))
462470
}
463-
const p = new Promise((resolve, reject) => {
471+
return new Promise((resolve, reject) => {
464472
try {
465473
this.server = app.listen(port, address, () => {
466474
this.router = app
@@ -471,7 +479,6 @@ class Robot {
471479
reject(err)
472480
}
473481
})
474-
return p
475482
}
476483

477484
// Setup an empty router object

test/XampleTest.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import dummyRobot from './doubles/DummyAdapter.mjs'
1313
describe('Xample testing Hubot scripts', () => {
1414
let robot = null
1515
beforeEach(async () => {
16+
process.env.EXPRESS_PORT = 0
1617
robot = new Robot(dummyRobot, true, 'Dumbotheelephant')
1718
await robot.loadAdapter()
1819
await robot.run()
1920
await robot.loadFile('./test/scripts', 'Xample.mjs')
2021
})
2122
afterEach(() => {
23+
delete process.env.EXPRESS_PORT
2224
robot.shutdown()
2325
})
2426
it('should handle /helo request', async () => {

test/doubles/DummyAdapter.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class DummyAdapter extends Adapter {
4545
}
4646
}
4747
export default {
48-
use (robot) {
48+
async use (robot) {
4949
return new DummyAdapter(robot)
5050
}
5151
}

0 commit comments

Comments
 (0)