Skip to content

Commit 3f84a2b

Browse files
authored
feat: use build_dir in esbuild workflow to support building in source (#437)
1 parent 755a99b commit 3f84a2b

File tree

5 files changed

+80
-27
lines changed

5 files changed

+80
-27
lines changed

aws_lambda_builders/workflows/nodejs_npm/actions.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,10 @@ class NodejsNpmInstallAction(BaseAction):
8181
DESCRIPTION = "Installing dependencies from NPM"
8282
PURPOSE = Purpose.RESOLVE_DEPENDENCIES
8383

84-
def __init__(self, artifacts_dir, subprocess_npm):
84+
def __init__(self, install_dir, subprocess_npm):
8585
"""
86-
:type artifacts_dir: str
87-
:param artifacts_dir: an existing (writable) directory with project source files.
88-
Dependencies will be installed in this directory.
86+
:type install_dir: str
87+
:param install_dir: Dependencies will be installed in this directory.
8988
9089
:type subprocess_npm: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm
9190
:param subprocess_npm: An instance of the NPM process wrapper
@@ -95,7 +94,7 @@ def __init__(self, artifacts_dir, subprocess_npm):
9594
"""
9695

9796
super(NodejsNpmInstallAction, self).__init__()
98-
self.artifacts_dir = artifacts_dir
97+
self.install_dir = install_dir
9998
self.subprocess_npm = subprocess_npm
10099

101100
def execute(self):
@@ -105,10 +104,10 @@ def execute(self):
105104
:raises lambda_builders.actions.ActionFailedError: when NPM execution fails
106105
"""
107106
try:
108-
LOG.debug("NODEJS installing in: %s", self.artifacts_dir)
107+
LOG.debug("NODEJS installing in: %s", self.install_dir)
109108

110109
self.subprocess_npm.run(
111-
["install", "-q", "--no-audit", "--no-save", "--unsafe-perm", "--production"], cwd=self.artifacts_dir
110+
["install", "-q", "--no-audit", "--no-save", "--unsafe-perm", "--production"], cwd=self.install_dir
112111
)
113112

114113
except NpmExecutionError as ex:
@@ -128,18 +127,17 @@ class NodejsNpmCIAction(BaseAction):
128127
DESCRIPTION = "Installing dependencies from NPM using the CI method"
129128
PURPOSE = Purpose.RESOLVE_DEPENDENCIES
130129

131-
def __init__(self, artifacts_dir, subprocess_npm):
130+
def __init__(self, install_dir, subprocess_npm):
132131
"""
133-
:type artifacts_dir: str
134-
:param artifacts_dir: an existing (writable) directory with project source files.
135-
Dependencies will be installed in this directory.
132+
:type install_dir: str
133+
:param install_dir: Dependencies will be installed in this directory.
136134
137135
:type subprocess_npm: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm
138136
:param subprocess_npm: An instance of the NPM process wrapper
139137
"""
140138

141139
super(NodejsNpmCIAction, self).__init__()
142-
self.artifacts_dir = artifacts_dir
140+
self.install_dir = install_dir
143141
self.subprocess_npm = subprocess_npm
144142

145143
def execute(self):
@@ -150,9 +148,9 @@ def execute(self):
150148
"""
151149

152150
try:
153-
LOG.debug("NODEJS installing ci in: %s", self.artifacts_dir)
151+
LOG.debug("NODEJS installing ci in: %s", self.install_dir)
154152

155-
self.subprocess_npm.run(["ci"], cwd=self.artifacts_dir)
153+
self.subprocess_npm.run(["ci"], cwd=self.install_dir)
156154

157155
except NpmExecutionError as ex:
158156
raise ActionFailedError(str(ex))

aws_lambda_builders/workflows/nodejs_npm/workflow.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,15 @@ def get_resolvers(self):
148148
return [PathResolver(runtime=self.runtime, binary="npm")]
149149

150150
@staticmethod
151-
def get_install_action(source_dir, artifacts_dir, subprocess_npm, osutils, build_options):
151+
def get_install_action(source_dir, install_dir, subprocess_npm, osutils, build_options):
152152
"""
153153
Get the install action used to install dependencies at artifacts_dir
154154
155155
:type source_dir: str
156156
:param source_dir: an existing (readable) directory containing source files
157157
158-
:type artifacts_dir: str
159-
:param artifacts_dir: Dependencies will be installed in this directory.
158+
:type install_dir: str
159+
:param install_dir: Dependencies will be installed in this directory.
160160
161161
:type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
162162
:param osutils: An instance of OS Utilities for file manipulation
@@ -181,6 +181,6 @@ def get_install_action(source_dir, artifacts_dir, subprocess_npm, osutils, build
181181
npm_ci_option = build_options.get("use_npm_ci", False)
182182

183183
if (osutils.file_exists(lockfile_path) or osutils.file_exists(shrinkwrap_path)) and npm_ci_option:
184-
return NodejsNpmCIAction(artifacts_dir, subprocess_npm=subprocess_npm)
184+
return NodejsNpmCIAction(install_dir=install_dir, subprocess_npm=subprocess_npm)
185185

186-
return NodejsNpmInstallAction(artifacts_dir, subprocess_npm=subprocess_npm)
186+
return NodejsNpmInstallAction(install_dir=install_dir, subprocess_npm=subprocess_npm)

aws_lambda_builders/workflows/nodejs_npm_esbuild/workflow.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class NodejsNpmEsbuildWorkflow(BaseWorkflow):
4242
CONFIG_PROPERTY = "aws_sam"
4343

4444
DEFAULT_BUILD_DIR = BuildDirectory.SCRATCH
45-
BUILD_IN_SOURCE_SUPPORT = BuildInSourceSupport.NOT_SUPPORTED
45+
BUILD_IN_SOURCE_SUPPORT = BuildInSourceSupport.OPTIONALLY_SUPPORTED
4646

4747
def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtime=None, osutils=None, **kwargs):
4848

@@ -77,23 +77,26 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
7777
"dependencies directory was not provided and downloading dependencies is disabled."
7878
)
7979

80-
self.actions = [
81-
CopySourceAction(source_dir=self.source_dir, dest_dir=self.scratch_dir, excludes=self.EXCLUDED_FILES)
82-
]
80+
# if we're building in the source directory, we don't have to copy the source code
81+
self.actions = (
82+
[]
83+
if self.build_dir == self.source_dir
84+
else [CopySourceAction(source_dir=self.source_dir, dest_dir=self.build_dir, excludes=self.EXCLUDED_FILES)]
85+
)
8386

8487
if self.download_dependencies:
8588
self.actions.append(
8689
NodejsNpmWorkflow.get_install_action(
8790
source_dir=source_dir,
88-
artifacts_dir=self.scratch_dir,
91+
install_dir=self.build_dir,
8992
subprocess_npm=self.subprocess_npm,
9093
osutils=self.osutils,
9194
build_options=self.options,
9295
)
9396
)
9497

9598
bundle_action = EsbuildBundleAction(
96-
working_directory=self.scratch_dir,
99+
working_directory=self.build_dir,
97100
output_directory=self.artifacts_dir,
98101
bundler_config=bundler_config,
99102
osutils=self.osutils,
@@ -103,7 +106,9 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
103106
)
104107

105108
# If there's no dependencies_dir, just bundle and we're done.
106-
if not self.dependencies_dir:
109+
# Same thing if we're building in the source directory (since the dependencies persist in
110+
# the source directory, we don't want to move them or symlink them back to the source)
111+
if not self.dependencies_dir or self.build_dir == self.source_dir:
107112
self.actions.append(bundle_action)
108113
return
109114

tests/integration/workflows/nodejs_npm_esbuild/test_nodejs_npm_with_esbuild.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class TestNodejsNpmWorkflowWithEsbuild(TestCase):
2020
TEST_DATA_FOLDER = os.path.join(os.path.dirname(__file__), "testdata")
2121

2222
def setUp(self):
23+
self.source_dir = os.path.join(self.TEST_DATA_FOLDER, "with-deps-esbuild")
2324
self.artifacts_dir = tempfile.mkdtemp()
2425
self.scratch_dir = tempfile.mkdtemp()
2526
self.dependencies_dir = tempfile.mkdtemp()
@@ -39,6 +40,11 @@ def tearDown(self):
3940
shutil.rmtree(self.artifacts_dir)
4041
shutil.rmtree(self.scratch_dir)
4142

43+
# clean up dependencies that were installed in source dir
44+
source_dependencies = os.path.join(self.source_dir, "node_modules")
45+
if os.path.exists(source_dependencies):
46+
shutil.rmtree(source_dependencies)
47+
4248
@parameterized.expand([("nodejs12.x",), ("nodejs14.x",), ("nodejs16.x",), ("nodejs18.x",)])
4349
def test_builds_javascript_project_with_dependencies(self, runtime):
4450
source_dir = os.path.join(self.TEST_DATA_FOLDER, "with-deps-esbuild")
@@ -407,3 +413,29 @@ def test_esbuild_produces_mjs_output_files(self, runtime):
407413
expected_files = {"included.mjs", "included.mjs.map"}
408414
output_files = set(os.listdir(self.artifacts_dir))
409415
self.assertEqual(expected_files, output_files)
416+
417+
@parameterized.expand([("nodejs12.x",), ("nodejs14.x",), ("nodejs16.x",), ("nodejs18.x",)])
418+
def test_esbuild_can_build_in_source(self, runtime):
419+
options = {"entry_points": ["included.js"]}
420+
421+
self.builder.build(
422+
self.source_dir,
423+
self.artifacts_dir,
424+
self.scratch_dir,
425+
os.path.join(self.source_dir, "package.json"),
426+
runtime=runtime,
427+
options=options,
428+
executable_search_paths=[self.binpath],
429+
build_in_source=True,
430+
)
431+
432+
# dependencies installed in source folder
433+
self.assertIn("node_modules", os.listdir(self.source_dir))
434+
435+
# dependencies not in scratch
436+
self.assertNotIn("node_modules", os.listdir(self.scratch_dir))
437+
438+
# bundle is in artifacts
439+
expected_files = {"included.js"}
440+
output_files = set(os.listdir(self.artifacts_dir))
441+
self.assertEqual(expected_files, output_files)

tests/unit/workflows/nodejs_npm_esbuild/test_workflow.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def test_workflow_uses_production_npm_version(self, get_workflow_mock):
313313
self.assertIsInstance(workflow.actions[2], EsbuildBundleAction)
314314

315315
get_workflow_mock.get_install_action.assert_called_with(
316-
source_dir="source", artifacts_dir="scratch_dir", subprocess_npm=ANY, osutils=ANY, build_options=None
316+
source_dir="source", install_dir="scratch_dir", subprocess_npm=ANY, osutils=ANY, build_options=None
317317
)
318318

319319
@patch("aws_lambda_builders.workflows.nodejs_npm_esbuild.workflow.SubprocessNpm")
@@ -342,3 +342,21 @@ def test_no_download_dependencies_and_no_dependencies_dir_fails(self):
342342
osutils=self.osutils,
343343
download_dependencies=False,
344344
)
345+
346+
def test_build_in_source(self):
347+
source_dir = "source"
348+
workflow = NodejsNpmEsbuildWorkflow(
349+
source_dir=source_dir,
350+
artifacts_dir="artifacts",
351+
scratch_dir="scratch_dir",
352+
manifest_path="manifest",
353+
osutils=self.osutils,
354+
build_in_source=True,
355+
)
356+
357+
self.assertEqual(len(workflow.actions), 2)
358+
359+
self.assertIsInstance(workflow.actions[0], NodejsNpmInstallAction)
360+
self.assertEqual(workflow.actions[0].install_dir, source_dir)
361+
self.assertIsInstance(workflow.actions[1], EsbuildBundleAction)
362+
self.assertEqual(workflow.actions[1]._working_directory, source_dir)

0 commit comments

Comments
 (0)