66import java .io .IOException ;
77import java .util .Arrays ;
88import java .util .HashMap ;
9+ import java .util .LinkedList ;
10+ import java .util .List ;
11+ import com .vdurmont .semver4j .Requirement ;
12+ import com .vdurmont .semver4j .Semver ;
913import org .apache .commons .io .FileUtils ;
1014import org .slf4j .Logger ;
1115import org .slf4j .LoggerFactory ;
@@ -28,6 +32,8 @@ public class NPMInstaller {
2832
2933 private final FileDownloader fileDownloader ;
3034
35+ private Requirement npmVersionRequirement ;
36+
3137 NPMInstaller (InstallConfig config , ArchiveExtractor archiveExtractor , FileDownloader fileDownloader ) {
3238 this .logger = LoggerFactory .getLogger (getClass ());
3339 this .config = config ;
@@ -78,7 +84,53 @@ public void install() throws InstallationException {
7884 if (this .npmDownloadRoot == null || this .npmDownloadRoot .isEmpty ()) {
7985 this .npmDownloadRoot = DEFAULT_NPM_DOWNLOAD_ROOT ;
8086 }
87+ if ("engines" .equals (this .npmVersion )) {
88+ try {
89+ File packageFile = new File (this .config .getWorkingDirectory (), "package.json" );
90+ HashMap <String , Object > data = new ObjectMapper ().readValue (packageFile , HashMap .class );
91+ if (data .containsKey ("engines" )) {
92+ HashMap <String , Object > engines = (HashMap <String , Object >) data .get ("engines" );
93+ if (engines .containsKey ("npm" )) {
94+ this .npmVersionRequirement = Requirement .buildNPM ((String ) engines .get ("npm" ));
95+ } else {
96+ this .logger .info ("Could not read npm from engines from package.json" );
97+ }
98+ } else {
99+ this .logger .info ("Could not read engines from package.json" );
100+ }
101+ } catch (IOException e ) {
102+ throw new InstallationException ("Could not read npm engine version from package.json" , e );
103+ }
104+ }
105+
81106 if (!npmProvided () && !npmIsAlreadyInstalled ()) {
107+ if (this .npmVersionRequirement != null ) {
108+ // download available node versions
109+ try {
110+ String downloadUrl = this .npmDownloadRoot
111+ + ".." ;
112+
113+ File archive = File .createTempFile ("npm_versions" , ".json" );
114+
115+ downloadFile (downloadUrl , archive , this .userName , this .password );
116+
117+ HashMap <String , Object > data = new ObjectMapper ().readValue (archive , HashMap .class );
118+
119+ List <String > npmVersions = new LinkedList <>();
120+ if (data .containsKey ("versions" )) {
121+ HashMap <String , Object > versions = (HashMap <String , Object >) data .get ("versions" );
122+ npmVersions .addAll (versions .keySet ());
123+ } else {
124+ this .logger .info ("Could not read versions from NPM registry" );
125+ }
126+
127+ logger .debug ("Available NPM versions: {}" , npmVersions );
128+ this .npmVersion = npmVersions .stream ().filter (version -> npmVersionRequirement .isSatisfiedBy (new Semver (version , Semver .SemverType .NPM ))).findFirst ().orElseThrow (() -> new InstallationException ("Could not find matching node version satisfying requirement " + this .npmVersionRequirement ));
129+ this .logger .info ("Found matching NPM version {} satisfying requirement {}." , this .npmVersion , this .npmVersionRequirement );
130+ } catch (IOException | DownloadException e ) {
131+ throw new InstallationException ("Could not get available node versions." , e );
132+ }
133+ }
82134 installNpm ();
83135 }
84136 copyNpmScripts ();
@@ -93,7 +145,12 @@ private boolean npmIsAlreadyInstalled() {
93145 HashMap <String , Object > data = new ObjectMapper ().readValue (npmPackageJson , HashMap .class );
94146 if (data .containsKey (VERSION )) {
95147 final String foundNpmVersion = data .get (VERSION ).toString ();
96- if (foundNpmVersion .equals (this .npmVersion )) {
148+ if (npmVersionRequirement != null && npmVersionRequirement .isSatisfiedBy (new Semver (foundNpmVersion , Semver .SemverType .NPM ))) {
149+ //update version with installed version
150+ this .nodeVersion = foundNpmVersion ;
151+ this .logger .info ("NPM {} matches required version range {} installed." , foundNpmVersion , npmVersionRequirement );
152+ return true ;
153+ } else if (foundNpmVersion .equals (this .npmVersion )) {
97154 this .logger .info ("NPM {} is already installed." , foundNpmVersion );
98155 return true ;
99156 } else {
0 commit comments