@@ -18,10 +18,15 @@ public class ModuleDependencyHandler : IDisposable
1818 private Runspace runspace ;
1919 private readonly string moduleRepository ;
2020 private string tempDirPath ;
21+ private string localPSModulePath ;
2122 Dictionary < string , PSObject > modulesFound ;
22- HashSet < string > modulesSaved ;
23- private string oldPSModulePath ;
24- private string currentModulePath ;
23+ HashSet < string > modulesSavedInModulePath ;
24+ HashSet < string > modulesSavedInTempPath ;
25+ private string localAppdataPath ;
26+ private string pssaAppdataPath ;
27+ private const string symLinkName = "TempModuleDir" ;
28+ private const string tempPrefix = "PSSAModules-" ;
29+ private string symLinkPath ;
2530
2631 #endregion Private Variables
2732
@@ -36,7 +41,7 @@ public Runspace Runspace
3641 get { return runspace ; }
3742 }
3843
39- #endregion
44+ #endregion Properties
4045
4146 #region Private Methods
4247 private static void ThrowIfNull < T > ( T obj , string name )
@@ -46,44 +51,96 @@ private static void ThrowIfNull<T>(T obj, string name)
4651 throw new ArgumentNullException ( name ) ;
4752 }
4853 }
54+
55+ private void SetupCache ( )
56+ {
57+ // check if pssa exists in local appdata
58+ if ( Directory . Exists ( pssaAppdataPath ) )
59+ {
60+ // check if there is a link
61+ if ( File . Exists ( symLinkPath ) )
62+ {
63+ tempDirPath = GetTempDirPath ( symLinkPath ) ;
64+
65+ // check if the temp dir exists
66+ if ( tempDirPath != null
67+ && Directory . Exists ( tempDirPath ) )
68+ {
69+ SetModulesInTempPath ( ) ;
70+ return ;
71+ }
72+ }
73+ SetupTempDir ( ) ;
74+ }
75+ else
76+ {
77+ Directory . CreateDirectory ( pssaAppdataPath ) ;
78+ SetupTempDir ( ) ;
79+ }
80+ }
81+
82+ private void SetModulesInTempPath ( )
83+ {
84+ // we assume the modules have not been tampered with
85+ foreach ( var dir in Directory . EnumerateDirectories ( tempDirPath ) )
86+ {
87+ modulesSavedInTempPath . Add ( Path . GetFileName ( dir ) ) ;
88+ }
89+ }
90+
4991 private void SetupTempDir ( )
5092 {
51- //var tempPath = Path.GetTempPath();
52- //do
53- //{
54- // tempDirPath = Path.Combine(
55- // tempPath,
56- // Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
57- //} while (Directory.Exists(tempDirPath));
58- //Directory.CreateDirectory(tempDirPath);
59- tempDirPath = "C:\\ Users\\ kabawany\\ tmp\\ modules\\ " ;
93+ CreateTempDir ( ) ;
94+ UpdateSymLinkFile ( ) ;
6095 }
6196
62- private void RemoveTempDir ( )
97+ private void UpdateSymLinkFile ( )
6398 {
64- //Directory.Delete(tempDirPath, true );
99+ File . WriteAllLines ( symLinkPath , new string [ ] { tempDirPath } ) ;
65100 }
66101
67- private void SetupPSModulePath ( )
102+ private void CreateTempDir ( )
68103 {
69- oldPSModulePath = Environment . GetEnvironmentVariable ( "PSModulePath" , EnvironmentVariableTarget . Process ) ;
70- var sb = new StringBuilder ( ) ;
71- sb . Append ( oldPSModulePath )
72- . Append ( Path . DirectorySeparatorChar )
73- . Append ( tempDirPath ) ;
74- currentModulePath = sb . ToString ( ) ;
104+ tempDirPath = GetTempDirPath ( ) ;
105+ Directory . CreateDirectory ( tempDirPath ) ;
106+ }
107+
108+ private string GetTempDirPath ( )
109+ {
110+ var tempPathRoot = Path . GetTempPath ( ) ;
111+ string tempPath ;
112+ do
113+ {
114+ tempPath = Path . Combine (
115+ tempPathRoot ,
116+ tempPrefix + Path . GetFileNameWithoutExtension ( Path . GetRandomFileName ( ) ) ) ;
117+ } while ( Directory . Exists ( tempPath ) ) ;
118+ return tempPath ;
119+ }
120+
121+ // Return the first line of the file
122+ private string GetTempDirPath ( string symLinkPath )
123+ {
124+ var symLinkLines = File . ReadAllLines ( symLinkPath ) ;
125+ if ( symLinkLines . Length != 1 )
126+ {
127+ return null ;
128+ }
129+ return symLinkLines [ 0 ] ;
75130 }
76131
77132 private void CleanUp ( )
78133 {
79134 runspace . Dispose ( ) ;
80- RemoveTempDir ( ) ;
81- RestorePSModulePath ( ) ;
82- }
83135
84- private void RestorePSModulePath ( )
85- {
86- Environment . SetEnvironmentVariable ( "PSModulePath" , oldPSModulePath , EnvironmentVariableTarget . Process ) ;
136+ // remove the modules from local psmodule path
137+ foreach ( var dir in Directory . EnumerateDirectories ( localPSModulePath ) )
138+ {
139+ if ( modulesSavedInModulePath . Contains ( Path . GetFileName ( dir ) ) )
140+ {
141+ Directory . Delete ( dir , true ) ;
142+ }
143+ }
87144 }
88145
89146 private void SaveModule ( PSObject module )
@@ -98,6 +155,33 @@ private void SaveModule(PSObject module)
98155 ps . Invoke ( ) ;
99156 }
100157
158+ // TODO Use powershell copy-item
159+ private void CopyDir ( string srcParentPath ,
160+ string srcName ,
161+ string dstParentPath ,
162+ string dstName = null ,
163+ bool recurse = false )
164+ {
165+ if ( dstName == null )
166+ {
167+ dstName = srcName ;
168+ }
169+ var srcPath = Path . Combine ( srcParentPath , srcName ) ;
170+ var dstPath = Path . Combine ( dstParentPath , dstName ) ;
171+ Directory . CreateDirectory ( dstPath ) ;
172+ foreach ( var file in Directory . EnumerateFiles ( srcPath ) )
173+ {
174+ File . Copy ( file , Path . Combine ( dstPath , Path . GetFileName ( file ) ) ) ;
175+ }
176+ foreach ( var dir in Directory . EnumerateDirectories ( srcPath ) )
177+ {
178+ CopyDir ( srcPath ,
179+ Path . GetFileName ( dir ) ,
180+ dstPath ,
181+ recurse : true ) ;
182+ }
183+ }
184+
101185 #endregion Private Methods
102186
103187 #region Public Methods
@@ -106,10 +190,21 @@ public ModuleDependencyHandler()
106190 {
107191 runspace = null ;
108192 moduleRepository = "PSGallery" ;
109- modulesSaved = new HashSet < string > ( ) ;
193+ modulesSavedInModulePath = new HashSet < string > ( ) ;
194+ modulesSavedInTempPath = new HashSet < string > ( ) ;
110195 modulesFound = new Dictionary < string , PSObject > ( ) ;
111- SetupTempDir ( ) ;
112- //SetupPSModulePath();
196+
197+ // TODO search it in the $psmodulepath instead of constructing it
198+ localPSModulePath = Path . Combine (
199+ Environment . GetEnvironmentVariable ( "USERPROFILE" ) ,
200+ "Documents\\ WindowsPowerShell\\ Modules" ) ;
201+ localAppdataPath = Environment . GetEnvironmentVariable ( "LOCALAPPDATA" ) ;
202+
203+ // TODO Add PSSA Version in the path
204+ pssaAppdataPath = Path . Combine ( localAppdataPath , "PSScriptAnalyzer" ) ;
205+ symLinkPath = Path . Combine ( pssaAppdataPath , symLinkName ) ;
206+
207+ SetupCache ( ) ;
113208 }
114209
115210 public ModuleDependencyHandler ( Runspace runspace ) : this ( )
@@ -163,13 +258,28 @@ public PSObject FindModule(string moduleName)
163258 return module ;
164259 }
165260
261+
262+ public bool ModuleExists ( string moduleName )
263+ {
264+ throw new NotImplementedException ( ) ;
265+ }
266+
267+
166268 public void SaveModule ( string moduleName )
167269 {
168270 ThrowIfNull ( moduleName , "moduleName" ) ;
169- if ( modulesSaved . Contains ( moduleName ) )
271+ if ( modulesSavedInModulePath . Contains ( moduleName ) )
272+ {
273+ return ;
274+ }
275+ if ( modulesSavedInTempPath . Contains ( moduleName ) )
170276 {
277+ // copy to local ps module path
278+ CopyDir ( tempDirPath , moduleName , localPSModulePath , recurse : true ) ;
279+ modulesSavedInModulePath . Add ( moduleName ) ;
171280 return ;
172281 }
282+
173283 var module = FindModule ( moduleName ) ;
174284 if ( module == null )
175285 {
@@ -180,7 +290,11 @@ public void SaveModule(string moduleName)
180290 moduleRepository ) ) ;
181291 }
182292 SaveModule ( module ) ;
183- modulesSaved . Add ( moduleName ) ;
293+ modulesSavedInTempPath . Add ( moduleName ) ;
294+
295+ // copy to local ps module path
296+ CopyDir ( tempDirPath , moduleName , localPSModulePath , recurse : true ) ;
297+ modulesSavedInModulePath . Add ( moduleName ) ;
184298 }
185299
186300 public static string GetModuleNameFromErrorExtent ( ParseError error , ScriptBlockAst ast )
@@ -229,4 +343,4 @@ public void Dispose()
229343
230344 #endregion Public Methods
231345 }
232- }
346+ }
0 commit comments