Skip to content

Commit

Permalink
projectRoot
Browse files Browse the repository at this point in the history
  • Loading branch information
jogold committed May 7, 2020
1 parent c4d4764 commit fb0504d
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 31 deletions.
22 changes: 14 additions & 8 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/builder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { spawnSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { findGitPath, findPkgPath } from './util';
import { findPkgPath } from './util';

/**
* Builder options
Expand Down Expand Up @@ -48,6 +48,12 @@ export interface BuilderOptions {
* @see https://hub.docker.com/_/node/?tab=tags
*/
readonly nodeDockerTag: string;

/**
* The root of the project. This will be used as the source for the volume
* mounted in the Docker container.
*/
readonly projectRoot: string;
}

/**
Expand All @@ -62,7 +68,11 @@ export class Builder {

constructor(private readonly options: BuilderOptions) {
// Original package.json
this.pkgPath = findPkgPath();
const pkgPath = findPkgPath();
if (!pkgPath) {
throw new Error('Cannot find a `package.json` in this project.');
}
this.pkgPath = pkgPath;
this.originalPkg = fs.readFileSync(this.pkgPath);
this.originalPkgJson = JSON.parse(this.originalPkg.toString());
}
Expand Down Expand Up @@ -91,18 +101,14 @@ export class Builder {
throw new Error(`[Status ${build.status}] stdout: ${build.stdout?.toString().trim()}\n\n\nstderr: ${build.stderr?.toString().trim()}`);
}

// Find the git root and mount it in the container. This allows Parcel to
// find the same modules/dependencies as the ones available "locally". It
// also supports monorepos.
const projectRoot = path.dirname(findGitPath());
const containerProjectRoot = '/project';
const containerOutDir = '/out';
const containerCacheDir = '/cache';
const containerEntryPath = path.join(containerProjectRoot, path.relative(projectRoot, path.resolve(this.options.entry)));
const containerEntryPath = path.join(containerProjectRoot, path.relative(this.options.projectRoot, path.resolve(this.options.entry)));

const dockerRunArgs = [
'run', '--rm',
'-v', `${projectRoot}:${containerProjectRoot}`,
'-v', `${this.options.projectRoot}:${containerProjectRoot}`,
'-v', `${path.resolve(this.options.outDir)}:${containerOutDir}`,
...(this.options.cacheDir ? ['-v', `${path.resolve(this.options.cacheDir)}:${containerCacheDir}`] : []),
'-w', path.dirname(containerEntryPath),
Expand Down
17 changes: 16 additions & 1 deletion packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { Builder } from './builder';
import { nodeMajorVersion, parseStackTrace } from './util';
import { findGitPath, nodeMajorVersion, parseStackTrace } from './util';

/**
* Properties for a NodejsFunction
Expand Down Expand Up @@ -74,6 +74,16 @@ export interface NodejsFunctionProps extends lambda.FunctionOptions {
* @default - the `process.versions.node` alpine image
*/
readonly nodeDockerTag?: string;

/**
* The root of the project. This will be used as the source for the volume
* mounted in the Docker container. If you specify this prop, ensure that
* this path includes `entry` and any module/dependencies used by your
* function otherwise bundling will not be possible.
*
* @default - the closest path containing a .git folder
*/
readonly projectRoot?: string;
}

/**
Expand All @@ -93,6 +103,10 @@ export class NodejsFunction extends lambda.Function {
? lambda.Runtime.NODEJS_12_X
: lambda.Runtime.NODEJS_10_X;
const runtime = props.runtime || defaultRunTime;
const projectRoot = props.projectRoot ?? findGitPath();
if (!projectRoot) {
throw new Error('Cannot find project root. Please specify it with `projectRoot`.');
}

// Build with Parcel
const builder = new Builder({
Expand All @@ -104,6 +118,7 @@ export class NodejsFunction extends lambda.Function {
cacheDir: props.cacheDir,
nodeVersion: extractVersion(runtime),
nodeDockerTag: props.nodeDockerTag || `${process.versions.node}-alpine`,
projectRoot: path.dirname(path.resolve(projectRoot)),
});
builder.build();

Expand Down
12 changes: 4 additions & 8 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function nodeMajorVersion(): number {
/**
* Finds the closest path containg a path
*/
function findClosestPathContaining(p: string): string {
function findClosestPathContaining(p: string): string | undefined {
let closestPath;

for (const nodeModulesPath of module.paths) {
Expand All @@ -63,23 +63,19 @@ function findClosestPathContaining(p: string): string {
}
}

if (!closestPath) {
throw new Error(`Cannot find path ${p}.`);
}

return closestPath;
}

/**
* Finds closest package.json path
*/
export function findPkgPath(): string {
export function findPkgPath(): string | undefined {
return findClosestPathContaining('package.json');
}

/**
* Finds closest .git/ and returns the path containing this directory
* Finds closest .git/
*/
export function findGitPath(): string {
export function findGitPath(): string | undefined {
return findClosestPathContaining(`.git${path.sep}`);
}
32 changes: 18 additions & 14 deletions packages/@aws-cdk/aws-lambda-nodejs/test/builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { Builder } from '../lib/builder';

jest.mock('child_process', () => ({
spawnSync: jest.fn((_cmd: string, args: string[]) => {
if (args.includes('/project/packages/@aws-cdk/aws-lambda-nodejs/error')) {
if (args.includes('/project/folder/error')) {
return { error: 'parcel-error' };
}

if (args.includes('/project/packages/@aws-cdk/aws-lambda-nodejs/status')) {
if (args.includes('/project/folder/status')) {
return { status: 1, stdout: Buffer.from('status-error') };
}

if (args.includes('/project/packages/@aws-cdk/aws-lambda-nodejs/no-docker')) {
if (args.includes('/project/folder/no-docker')) {
return { error: 'Error: spawnSync docker ENOENT' };
}

Expand All @@ -22,12 +22,13 @@ jest.mock('child_process', () => ({

test('calls docker with the correct args', () => {
const builder = new Builder({
entry: 'entry',
entry: '/project/folder/entry.ts',
global: 'handler',
outDir: 'out-dir',
cacheDir: 'cache-dir',
outDir: '/out-dir',
cacheDir: '/cache-dir',
nodeDockerTag: 'lts-alpine',
nodeVersion: '12',
projectRoot: '/project',
});
builder.build();

Expand All @@ -39,12 +40,12 @@ test('calls docker with the correct args', () => {
// docker run
expect(spawnSync).toHaveBeenNthCalledWith(2, 'docker', [
'run', '--rm',
'-v', expect.stringMatching(/aws-cdk:\/project$/),
'-v', `${path.join(__dirname, '../out-dir')}:/out`,
'-v', `${path.join(__dirname, '../cache-dir')}:/cache`,
'-w', '/project/packages/@aws-cdk/aws-lambda-nodejs',
'-v', '/project:/project',
'-v', '/out-dir:/out',
'-v', '/cache-dir:/cache',
'-w', '/project/folder',
'parcel-bundler',
'parcel', 'build', '/project/packages/@aws-cdk/aws-lambda-nodejs/entry',
'parcel', 'build', '/project/folder/entry.ts',
'--out-dir', '/out',
'--out-file', 'index.js',
'--global', 'handler',
Expand All @@ -59,33 +60,36 @@ test('calls docker with the correct args', () => {

test('throws in case of error', () => {
const builder = new Builder({
entry: 'error',
entry: '/project/folder/error',
global: 'handler',
outDir: 'out-dir',
nodeDockerTag: 'lts-alpine',
nodeVersion: '12',
projectRoot: '/project',
});
expect(() => builder.build()).toThrow('parcel-error');
});

test('throws if status is not 0', () => {
const builder = new Builder({
entry: 'status',
entry: '/project/folder/status',
global: 'handler',
outDir: 'out-dir',
nodeDockerTag: 'lts-alpine',
nodeVersion: '12',
projectRoot: '/project',
});
expect(() => builder.build()).toThrow('status-error');
});

test('throws if docker is not installed', () => {
const builder = new Builder({
entry: 'no-docker',
entry: '/project/folder/no-docker',
global: 'handler',
outDir: 'out-dir',
nodeDockerTag: 'lts-alpine',
nodeVersion: '12',
projectRoot: '/project',
});
expect(() => builder.build()).toThrow('Error: spawnSync docker ENOENT');
});

0 comments on commit fb0504d

Please sign in to comment.