33"""
44
55import logging
6+ import os
7+ from typing import Optional
68
79from aws_lambda_builders .actions import (
810 CleanUpAction ,
911 CopyDependenciesAction ,
1012 CopySourceAction ,
13+ LinkSinglePathAction ,
1114 MoveDependenciesAction ,
1215)
1316from aws_lambda_builders .path_resolver import PathResolver
@@ -43,7 +46,7 @@ class NodejsNpmWorkflow(BaseWorkflow):
4346 CONFIG_PROPERTY = "aws_sam"
4447
4548 DEFAULT_BUILD_DIR = BuildDirectory .ARTIFACTS
46- BUILD_IN_SOURCE_SUPPORT = BuildInSourceSupport .NOT_SUPPORTED
49+ BUILD_IN_SOURCE_SUPPORT = BuildInSourceSupport .OPTIONALLY_SUPPORTED
4750
4851 def __init__ (self , source_dir , artifacts_dir , scratch_dir , manifest_path , runtime = None , osutils = None , ** kwargs ):
4952 super (NodejsNpmWorkflow , self ).__init__ (
@@ -52,6 +55,7 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
5255
5356 if osutils is None :
5457 osutils = OSUtils ()
58+ self .osutils = osutils
5559
5660 if not osutils .file_exists (manifest_path ):
5761 LOG .warning ("package.json file not found. Continuing the build without dependencies." )
@@ -62,6 +66,8 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
6266
6367 tar_dest_dir = osutils .joinpath (scratch_dir , "unpacked" )
6468 tar_package_dir = osutils .joinpath (tar_dest_dir , "package" )
69+ # TODO: we should probably unpack straight into artifacts dir, rather than unpacking into tar_dest_dir and
70+ # then copying into artifacts. Just make sure EXCLUDED_FILES are not included, or remove them.
6571 npm_pack = NodejsNpmPackAction (
6672 tar_dest_dir , scratch_dir , manifest_path , osutils = osutils , subprocess_npm = subprocess_npm
6773 )
@@ -75,39 +81,82 @@ def __init__(self, source_dir, artifacts_dir, scratch_dir, manifest_path, runtim
7581 ]
7682
7783 if self .download_dependencies :
78- # install the dependencies into artifact folder
7984 self .actions .append (
80- NodejsNpmWorkflow .get_install_action (source_dir , artifacts_dir , subprocess_npm , osutils , self .options )
85+ NodejsNpmWorkflow .get_install_action (
86+ source_dir = source_dir ,
87+ install_dir = self .build_dir ,
88+ subprocess_npm = subprocess_npm ,
89+ osutils = osutils ,
90+ build_options = self .options ,
91+ install_links = self .build_dir == self .source_dir ,
92+ )
8193 )
8294
83- artifacts_cleanup_actions = [
84- NodejsNpmrcCleanUpAction (artifacts_dir , osutils = osutils ),
85- NodejsNpmLockFileCleanUpAction (artifacts_dir , osutils = osutils ),
86- ]
95+ if self .download_dependencies and self .build_dir == self .source_dir :
96+ self .actions += self ._actions_for_linking_source_dependencies_to_artifacts
8797
8898 # if no dependencies dir, just cleanup artifacts and we're done
8999 if not self .dependencies_dir :
90- self .actions += artifacts_cleanup_actions
100+ self .actions += self . _actions_for_cleanup
91101 return
92102
93103 # if we downloaded dependencies, update dependencies_dir
94104 if self .download_dependencies :
95- # clean up the dependencies folder first
96- self .actions .append (CleanUpAction (self .dependencies_dir ))
97- # if combine_dependencies is set, we should keep dependencies and source code in the artifact folder
98- # while copying the dependencies. Otherwise we should separate the dependencies and source code
99- dependencies_dir_update_action = (
100- CopyDependenciesAction if self .combine_dependencies else MoveDependenciesAction
101- )
102- self .actions .append (dependencies_dir_update_action (source_dir , artifacts_dir , self .dependencies_dir ))
105+ self .actions += self ._actions_for_updating_dependencies_dir
103106 # otherwise if we want to use the dependencies from dependencies_dir and we want to combine them,
104107 # then copy them into the artifacts dir
105108 elif self .combine_dependencies :
106- self .actions .append (CopySourceAction (self .dependencies_dir , artifacts_dir ))
109+ self .actions .append (
110+ CopySourceAction (
111+ self .dependencies_dir , artifacts_dir , maintain_symlinks = self .build_dir == self .source_dir
112+ )
113+ )
107114
108- # cleanup
109- self .actions += artifacts_cleanup_actions
110- self .actions .append (NodejsNpmLockFileCleanUpAction (self .dependencies_dir , osutils = osutils ))
115+ self .actions += self ._actions_for_cleanup
116+
117+ @property
118+ def _actions_for_cleanup (self ):
119+ actions = [NodejsNpmrcCleanUpAction (self .artifacts_dir , osutils = self .osutils )]
120+
121+ # we don't want to cleanup the lockfile in the source code's symlinked node_modules
122+ if self .build_dir != self .source_dir :
123+ actions .append (NodejsNpmLockFileCleanUpAction (self .artifacts_dir , osutils = self .osutils ))
124+ if self .build_dir != self .source_dir and self .dependencies_dir :
125+ actions .append (NodejsNpmLockFileCleanUpAction (self .dependencies_dir , osutils = self .osutils ))
126+
127+ return actions
128+
129+ @property
130+ def _actions_for_linking_source_dependencies_to_artifacts (self ):
131+ source_dependencies_path = os .path .join (self .source_dir , "node_modules" )
132+ artifact_dependencies_path = os .path .join (self .artifacts_dir , "node_modules" )
133+ return [LinkSinglePathAction (source = source_dependencies_path , dest = artifact_dependencies_path )]
134+
135+ @property
136+ def _actions_for_updating_dependencies_dir (self ):
137+ # clean up the dependencies folder first
138+ actions = [CleanUpAction (self .dependencies_dir )]
139+ # if combine_dependencies is set, we should keep dependencies and source code in the artifact folder
140+ # while copying the dependencies. Otherwise we should separate the dependencies and source code
141+ if self .combine_dependencies :
142+ actions .append (
143+ CopyDependenciesAction (
144+ source_dir = self .source_dir ,
145+ artifact_dir = self .artifacts_dir ,
146+ destination_dir = self .dependencies_dir ,
147+ maintain_symlinks = self .build_dir == self .source_dir ,
148+ )
149+ )
150+ else :
151+ actions .append (
152+ MoveDependenciesAction (
153+ source_dir = self .source_dir ,
154+ artifact_dir = self .artifacts_dir ,
155+ destination_dir = self .dependencies_dir ,
156+ )
157+ )
158+
159+ return actions
111160
112161 def get_resolvers (self ):
113162 """
@@ -116,30 +165,36 @@ def get_resolvers(self):
116165 return [PathResolver (runtime = self .runtime , binary = "npm" )]
117166
118167 @staticmethod
119- def get_install_action (source_dir , install_dir , subprocess_npm , osutils , build_options ):
168+ def get_install_action (
169+ source_dir : str ,
170+ install_dir : str ,
171+ subprocess_npm : SubprocessNpm ,
172+ osutils : OSUtils ,
173+ build_options : Optional [dict ],
174+ install_links : Optional [bool ] = False ,
175+ ):
120176 """
121- Get the install action used to install dependencies at artifacts_dir
122-
123- :type source_dir: str
124- :param source_dir: an existing (readable) directory containing source files
125-
126- :type install_dir: str
127- :param install_dir: Dependencies will be installed in this directory.
128-
129- :type osutils: aws_lambda_builders.workflows.nodejs_npm.utils.OSUtils
130- :param osutils: An instance of OS Utilities for file manipulation
131-
132- :type subprocess_npm: aws_lambda_builders.workflows.nodejs_npm.npm.SubprocessNpm
133- :param subprocess_npm: An instance of the NPM process wrapper
134-
135- :type build_options: Dict
136- :param build_options: Object containing build options configurations
137-
138- :type is_production: bool
139- :param is_production: NPM installation mode is production (eg --production=false to force dev dependencies)
140-
141- :rtype: BaseAction
142- :return: Install action to use
177+ Get the install action used to install dependencies.
178+
179+ Parameters
180+ ----------
181+ source_dir : str
182+ an existing (readable) directory containing source files
183+ install_dir : str
184+ Dependencies will be installed in this directory
185+ subprocess_npm : SubprocessNpm
186+ An instance of the NPM process wrapper
187+ osutils : OSUtils
188+ An instance of OS Utilities for file manipulation
189+ build_options : Optional[dict]
190+ Object containing build options configurations
191+ install_links : Optional[bool]
192+ Uses the --install-links npm option if True, by default False
193+
194+ Returns
195+ -------
196+ BaseAction
197+ Install action to use
143198 """
144199 lockfile_path = osutils .joinpath (source_dir , "package-lock.json" )
145200 shrinkwrap_path = osutils .joinpath (source_dir , "npm-shrinkwrap.json" )
@@ -149,6 +204,10 @@ def get_install_action(source_dir, install_dir, subprocess_npm, osutils, build_o
149204 npm_ci_option = build_options .get ("use_npm_ci" , False )
150205
151206 if (osutils .file_exists (lockfile_path ) or osutils .file_exists (shrinkwrap_path )) and npm_ci_option :
152- return NodejsNpmCIAction (install_dir = install_dir , subprocess_npm = subprocess_npm )
207+ return NodejsNpmCIAction (
208+ install_dir = install_dir , subprocess_npm = subprocess_npm , install_links = install_links
209+ )
153210
154- return NodejsNpmInstallAction (install_dir = install_dir , subprocess_npm = subprocess_npm )
211+ return NodejsNpmInstallAction (
212+ install_dir = install_dir , subprocess_npm = subprocess_npm , install_links = install_links
213+ )
0 commit comments