Skip to content

Commit c17b01a

Browse files
Merge branch 'develop'
2 parents 404ee7b + 8bc62be commit c17b01a

File tree

13 files changed

+157
-42
lines changed

13 files changed

+157
-42
lines changed

.github/workflows/pr-labeler.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
pull-requests: write
1818
runs-on: ubuntu-latest
1919
steps:
20-
- uses: actions/github-script@v6
20+
- uses: actions/github-script@v7
2121
with:
2222
github-token: ${{secrets.GITHUB_TOKEN}}
2323
script: |

aws_lambda_builders/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
# Changing version will trigger a new release!
66
# Please make the version change as the last step of your development.
77

8-
__version__ = "1.42.0"
8+
__version__ = "1.43.0"
99
RPC_PROTOCOL_VERSION = "0.3"

aws_lambda_builders/actions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class _ActionMetaClass(type):
5757
def __new__(mcs, name, bases, class_dict):
5858
cls = type.__new__(mcs, name, bases, class_dict)
5959

60-
if cls.__name__ == "BaseAction":
60+
if cls.__name__ in ["BaseAction", "NodejsNpmInstallOrUpdateBaseAction"]:
6161
return cls
6262

6363
# Validate class variables
@@ -156,7 +156,12 @@ def __init__(self, source: Union[str, os.PathLike], dest: Union[str, os.PathLike
156156
self._dest = dest
157157

158158
def execute(self):
159+
source_path = Path(self._source)
159160
destination_path = Path(self._dest)
161+
if not source_path.exists():
162+
# Source path doesn't exist, nothing to symlink
163+
LOG.debug("Source path %s does not exist, skipping generating symlink", source_path)
164+
return
160165
if not destination_path.exists():
161166
os.makedirs(destination_path.parent, exist_ok=True)
162167
utils.create_symlink_or_copy(str(self._source), str(destination_path))

aws_lambda_builders/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ def create_symlink_or_copy(source: str, destination: str) -> None:
198198
"""Tries to create symlink, if it fails it will copy source into destination"""
199199
LOG.debug("Creating symlink; source: %s, destination: %s", source, destination)
200200
try:
201+
if Path(destination).exists() and Path(destination).is_symlink():
202+
# The symlink is already in place, don't try re-creating it
203+
LOG.debug("Symlink between %s and %s already exists, skipping generating symlink", source, destination)
204+
return
201205
os.symlink(Path(source).absolute(), Path(destination).absolute())
202206
except OSError as ex:
203207
LOG.warning(

aws_lambda_builders/workflows/nodejs_npm/actions.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,32 +72,35 @@ def execute(self):
7272
raise ActionFailedError(str(ex))
7373

7474

75-
class NodejsNpmInstallAction(BaseAction):
76-
75+
class NodejsNpmInstallOrUpdateBaseAction(BaseAction):
7776
"""
78-
A Lambda Builder Action that installs NPM project dependencies
77+
A base Lambda Builder Action that is used for installs or updating NPM project dependencies
7978
"""
8079

81-
NAME = "NpmInstall"
82-
DESCRIPTION = "Installing dependencies from NPM"
8380
PURPOSE = Purpose.RESOLVE_DEPENDENCIES
8481

85-
def __init__(self, install_dir: str, subprocess_npm: SubprocessNpm, install_links: Optional[bool] = False):
82+
def __init__(self, install_dir: str, subprocess_npm: SubprocessNpm):
8683
"""
8784
Parameters
8885
----------
8986
install_dir : str
9087
Dependencies will be installed in this directory.
9188
subprocess_npm : SubprocessNpm
9289
An instance of the NPM process wrapper
93-
install_links : Optional[bool]
94-
Uses the --install-links npm option if True, by default False
9590
"""
9691

97-
super(NodejsNpmInstallAction, self).__init__()
92+
super().__init__()
9893
self.install_dir = install_dir
9994
self.subprocess_npm = subprocess_npm
100-
self.install_links = install_links
95+
96+
97+
class NodejsNpmInstallAction(NodejsNpmInstallOrUpdateBaseAction):
98+
"""
99+
A Lambda Builder Action that installs NPM project dependencies
100+
"""
101+
102+
NAME = "NpmInstall"
103+
DESCRIPTION = "Installing dependencies from NPM"
101104

102105
def execute(self):
103106
"""
@@ -109,9 +112,38 @@ def execute(self):
109112
LOG.debug("NODEJS installing in: %s", self.install_dir)
110113

111114
command = ["install", "-q", "--no-audit", "--no-save", "--unsafe-perm", "--production"]
112-
if self.install_links:
113-
command.append("--install-links")
115+
self.subprocess_npm.run(command, cwd=self.install_dir)
116+
117+
except NpmExecutionError as ex:
118+
raise ActionFailedError(str(ex))
119+
120+
121+
class NodejsNpmUpdateAction(NodejsNpmInstallOrUpdateBaseAction):
122+
"""
123+
A Lambda Builder Action that installs NPM project dependencies
124+
"""
114125

126+
NAME = "NpmUpdate"
127+
DESCRIPTION = "Updating dependencies from NPM"
128+
129+
def execute(self):
130+
"""
131+
Runs the action.
132+
133+
:raises lambda_builders.actions.ActionFailedError: when NPM execution fails
134+
"""
135+
try:
136+
LOG.debug("NODEJS updating in: %s", self.install_dir)
137+
138+
command = [
139+
"update",
140+
"--no-audit",
141+
"--no-save",
142+
"--unsafe-perm",
143+
"--production",
144+
"--no-package-lock",
145+
"--install-links",
146+
]
115147
self.subprocess_npm.run(command, cwd=self.install_dir)
116148

117149
except NpmExecutionError as ex:

aws_lambda_builders/workflows/nodejs_npm/workflow.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
NodejsNpmPackAction,
2323
NodejsNpmrcAndLockfileCopyAction,
2424
NodejsNpmrcCleanUpAction,
25+
NodejsNpmUpdateAction,
2526
)
2627
from aws_lambda_builders.workflows.nodejs_npm.npm import SubprocessNpm
2728
from aws_lambda_builders.workflows.nodejs_npm.utils import OSUtils
@@ -119,7 +120,7 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
119120
subprocess_npm=subprocess_npm,
120121
osutils=osutils,
121122
build_options=self.options,
122-
install_links=is_building_in_source,
123+
is_building_in_source=is_building_in_source,
123124
)
124125
)
125126

@@ -211,7 +212,7 @@ def get_install_action(
211212
subprocess_npm: SubprocessNpm,
212213
osutils: OSUtils,
213214
build_options: Optional[dict],
214-
install_links: Optional[bool] = False,
215+
is_building_in_source: Optional[bool] = False,
215216
):
216217
"""
217218
Get the install action used to install dependencies.
@@ -228,8 +229,8 @@ def get_install_action(
228229
An instance of OS Utilities for file manipulation
229230
build_options : Optional[dict]
230231
Object containing build options configurations
231-
install_links : Optional[bool]
232-
Uses the --install-links npm option if True, by default False
232+
is_building_in_source : Optional[bool]
233+
States whether --build-in-source flag is set or not
233234
234235
Returns
235236
-------
@@ -245,12 +246,13 @@ def get_install_action(
245246

246247
if (osutils.file_exists(lockfile_path) or osutils.file_exists(shrinkwrap_path)) and npm_ci_option:
247248
return NodejsNpmCIAction(
248-
install_dir=install_dir, subprocess_npm=subprocess_npm, install_links=install_links
249+
install_dir=install_dir, subprocess_npm=subprocess_npm, install_links=is_building_in_source
249250
)
250251

251-
return NodejsNpmInstallAction(
252-
install_dir=install_dir, subprocess_npm=subprocess_npm, install_links=install_links
253-
)
252+
if is_building_in_source:
253+
return NodejsNpmUpdateAction(install_dir=install_dir, subprocess_npm=subprocess_npm)
254+
255+
return NodejsNpmInstallAction(install_dir=install_dir, subprocess_npm=subprocess_npm)
254256

255257
@staticmethod
256258
def can_use_install_links(npm_process: SubprocessNpm) -> bool:

aws_lambda_builders/workflows/nodejs_npm_esbuild/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
118118
subprocess_npm=self.subprocess_npm,
119119
osutils=self.osutils,
120120
build_options=self.options,
121-
install_links=is_building_in_source,
121+
is_building_in_source=is_building_in_source,
122122
)
123123
)
124124

requirements/dev.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ pyelftools~=0.30 # Used to verify the generated Go binary architecture in integr
1313

1414
# formatter
1515
black==22.6.0; python_version < "3.8"
16-
black==23.10.1; python_version >= "3.8"
17-
ruff==0.1.4
16+
black==23.11.0; python_version >= "3.8"
17+
ruff==0.1.5

tests/integration/workflows/nodejs_npm/test_nodejs_npm.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,46 @@ def test_build_in_source_with_download_dependencies(self, runtime):
315315
output_files = set(os.listdir(self.artifacts_dir))
316316
self.assertEqual(expected_files, output_files)
317317

318+
@parameterized.expand([("nodejs12.x",), ("nodejs14.x",), ("nodejs16.x",), ("nodejs18.x",), ("nodejs20.x",)])
319+
def test_build_in_source_with_removed_dependencies(self, runtime):
320+
# run a build with default requirements and confirm dependencies are downloaded
321+
source_dir = os.path.join(self.temp_testdata_dir, "npm-deps")
322+
323+
self.builder.build(
324+
source_dir,
325+
self.artifacts_dir,
326+
self.scratch_dir,
327+
os.path.join(source_dir, "package.json"),
328+
runtime=runtime,
329+
build_in_source=True,
330+
)
331+
332+
# dependencies installed in source folder
333+
source_node_modules = os.path.join(source_dir, "node_modules")
334+
self.assertTrue(os.path.isdir(source_node_modules))
335+
expected_node_modules_contents = {"minimal-request-promise", ".package-lock.json"}
336+
self.assertEqual(set(os.listdir(source_node_modules)), expected_node_modules_contents)
337+
338+
# update package.json with empty one and re-run the build then confirm node_modules are cleared up
339+
shutil.copy2(
340+
os.path.join(self.temp_testdata_dir, "no-deps", "package.json"),
341+
os.path.join(self.temp_testdata_dir, "npm-deps", "package.json"),
342+
)
343+
344+
self.builder.build(
345+
source_dir,
346+
self.artifacts_dir,
347+
self.scratch_dir,
348+
os.path.join(source_dir, "package.json"),
349+
runtime=runtime,
350+
build_in_source=True,
351+
)
352+
# dependencies installed in source folder
353+
source_node_modules = os.path.join(source_dir, "node_modules")
354+
self.assertTrue(os.path.isdir(source_node_modules))
355+
self.assertIn(".package-lock.json", set(os.listdir(source_node_modules)))
356+
self.assertNotIn("minimal-request-promise", set(os.listdir(source_node_modules)))
357+
318358
@parameterized.expand([("nodejs12.x",), ("nodejs14.x",), ("nodejs16.x",), ("nodejs18.x",), ("nodejs20.x",)])
319359
def test_build_in_source_with_download_dependencies_local_dependency(self, runtime):
320360
source_dir = os.path.join(self.temp_testdata_dir, "with-local-dependency")

tests/unit/test_actions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
MoveDependenciesAction,
1313
CleanUpAction,
1414
DependencyManager,
15+
LinkSinglePathAction,
1516
)
1617

1718

@@ -262,3 +263,15 @@ def test_excludes_dependencies_from_source(
262263
@staticmethod
263264
def _convert_strings_to_paths(source_dest_list):
264265
return map(lambda item: (Path(item[0]), Path(item[1])), source_dest_list)
266+
267+
268+
class TestLinkSinglePathAction(TestCase):
269+
@patch("aws_lambda_builders.actions.os.makedirs")
270+
@patch("aws_lambda_builders.utils.create_symlink_or_copy")
271+
def test_skips_non_existent_source(self, mock_create_symlink_or_copy, mock_makedirs):
272+
src = "src/path"
273+
dest = "dest/path"
274+
275+
LinkSinglePathAction(source=src, dest=dest).execute()
276+
mock_create_symlink_or_copy.assert_not_called()
277+
mock_makedirs.assert_not_called()

0 commit comments

Comments
 (0)