Skip to content

Commit d1ef618

Browse files
authored
fix(jsii-pacmak): retry .NET build a couple of times (#509)
Try to survive NuGet's failing due to race conditions.
1 parent 3d6415e commit d1ef618

File tree

2 files changed

+55
-27
lines changed

2 files changed

+55
-27
lines changed

packages/jsii-pacmak/lib/targets/dotnet.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ export default class Dotnet extends Target {
5353
const packageId: string = pkg.jsii.targets.dotnet.packageId;
5454
const project: string = path.join(packageId, `${packageId}.csproj`);
5555

56+
// Add retry as NuGet on Ubuntu is prone to failing due to race conditions
5657
await shell(
5758
'dotnet',
5859
[ 'build', project, '-c', 'Release' ],
59-
{ cwd: sourceDir }
60+
{ cwd: sourceDir, retry: true }
6061
);
6162

6263
await this.copyFiles(

packages/jsii-pacmak/lib/util.ts

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ import spec = require('jsii-spec');
44
import path = require('path');
55
import logging = require('./logging');
66

7+
export interface ShellOptions extends SpawnOptions {
8+
/**
9+
* Retry execution up to 3 times if it fails
10+
*
11+
* @default false
12+
*/
13+
retry?: boolean;
14+
}
15+
716
/**
817
* Given an npm package directory and a dependency name, returns the package directory of the dep.
918
* @param packageDir the root of the package declaring the dependency.
@@ -15,33 +24,51 @@ export function resolveDependencyDirectory(packageDir: string, dependencyName: s
1524
return path.dirname(require.resolve(`${dependencyName}/package.json`, { paths: lookupPaths }));
1625
}
1726

18-
export function shell(cmd: string, args: string[], options: SpawnOptions): Promise<string> {
19-
return new Promise<string>((resolve, reject) => {
20-
logging.debug(cmd, args.join(' '), JSON.stringify(options));
21-
const child = spawn(cmd, args, { ...options, shell: true, env: { ...process.env, ...options.env || {} }, stdio: ['ignore', 'pipe', 'pipe'] });
22-
const stdout = new Array<Buffer>();
23-
const stderr = new Array<Buffer>();
24-
child.stdout.on('data', chunk => {
25-
if (logging.level >= logging.LEVEL_VERBOSE) {
26-
process.stderr.write(chunk); // notice - we emit all build output to stderr
27-
}
28-
stdout.push(Buffer.from(chunk));
29-
});
30-
child.stderr.on('data', chunk => {
31-
if (logging.level >= logging.LEVEL_VERBOSE) {
32-
process.stderr.write(chunk);
33-
}
34-
stderr.push(Buffer.from(chunk));
35-
});
36-
child.once('error', reject);
37-
child.once('exit', (code, signal) => {
38-
const out = Buffer.concat(stdout).toString('utf-8');
39-
if (code === 0) { return resolve(out); }
40-
const err = Buffer.concat(stderr).toString('utf-8');
41-
if (code != null) { return reject(new Error(`Process exited with status ${code}\n${out}\n${err}`)); }
42-
reject(new Error(`Process terminated by signal ${signal}\n${out}\n${err}`));
27+
export async function shell(cmd: string, args: string[], options: ShellOptions): Promise<string> {
28+
function spawn1() {
29+
return new Promise<string>((resolve, reject) => {
30+
logging.debug(cmd, args.join(' '), JSON.stringify(options));
31+
const child = spawn(cmd, args, {
32+
...options,
33+
shell: true,
34+
env: { ...process.env, ...options.env || {} },
35+
stdio: ['ignore', 'pipe', 'pipe']
36+
});
37+
const stdout = new Array<Buffer>();
38+
const stderr = new Array<Buffer>();
39+
child.stdout.on('data', chunk => {
40+
if (logging.level >= logging.LEVEL_VERBOSE) {
41+
process.stderr.write(chunk); // notice - we emit all build output to stderr
42+
}
43+
stdout.push(Buffer.from(chunk));
44+
});
45+
child.stderr.on('data', chunk => {
46+
if (logging.level >= logging.LEVEL_VERBOSE) {
47+
process.stderr.write(chunk);
48+
}
49+
stderr.push(Buffer.from(chunk));
50+
});
51+
child.once('error', reject);
52+
child.once('exit', (code, signal) => {
53+
const out = Buffer.concat(stdout).toString('utf-8');
54+
if (code === 0) { return resolve(out); }
55+
const err = Buffer.concat(stderr).toString('utf-8');
56+
if (code != null) { return reject(new Error(`Process exited with status ${code}\n${out}\n${err}`)); }
57+
reject(new Error(`Process terminated by signal ${signal}\n${out}\n${err}`));
58+
});
4359
});
44-
});
60+
}
61+
62+
let attempts = options.retry ? 3 : 1;
63+
while (true) {
64+
attempts--;
65+
try {
66+
return spawn1();
67+
} catch (e) {
68+
if (attempts === 0) { throw e; }
69+
logging.info(`${e.message} (retrying)`);
70+
}
71+
}
4572
}
4673

4774
/**

0 commit comments

Comments
 (0)