1
1
using System ;
2
2
using System . IO ;
3
- using System . Linq ;
3
+ using System . Security . Cryptography ;
4
+ using System . Text ;
4
5
using System . Threading . Tasks ;
5
- using Dotnet . Script . DependencyModel . Environment ;
6
6
using Dotnet . Script . DependencyModel . Logging ;
7
7
using Dotnet . Script . DependencyModel . ProjectSystem ;
8
- using Dotnet . Script . DependencyModel . Runtime ;
9
8
10
9
namespace Dotnet . Script . Core . Commands
11
10
{
12
- public class ExecuteScriptCommand : IFileCommand
11
+ public class ExecuteScriptCommand
13
12
{
14
13
private readonly ScriptConsole _scriptConsole ;
15
14
private readonly LogFactory _logFactory ;
15
+ private readonly Logger _logger ;
16
16
17
17
public ExecuteScriptCommand ( ScriptConsole scriptConsole , LogFactory logFactory )
18
18
{
19
19
_scriptConsole = scriptConsole ;
20
20
_logFactory = logFactory ;
21
+ _logger = logFactory . CreateLogger < ExecuteScriptCommand > ( ) ;
22
+
21
23
}
22
24
23
25
public async Task < TReturn > Run < TReturn , THost > ( ExecuteScriptCommandOptions options )
@@ -27,7 +29,7 @@ public async Task<TReturn> Run<TReturn, THost>(ExecuteScriptCommandOptions optio
27
29
return await DownloadAndRunCode < TReturn > ( options ) ;
28
30
}
29
31
30
- var pathToLibrary = CreateLibrary ( options ) ;
32
+ var pathToLibrary = GetLibrary ( options ) ;
31
33
return await ExecuteLibrary < TReturn > ( pathToLibrary , options . Arguments , options . NoCache ) ;
32
34
}
33
35
@@ -39,13 +41,90 @@ private async Task<TReturn> DownloadAndRunCode<TReturn>(ExecuteScriptCommandOpti
39
41
return await new ExecuteCodeCommand ( _scriptConsole , _logFactory ) . Execute < TReturn > ( options ) ;
40
42
}
41
43
42
- private string CreateLibrary ( ExecuteScriptCommandOptions executeOptions )
44
+
45
+ private string GetLibrary ( ExecuteScriptCommandOptions executeOptions )
43
46
{
44
47
var projectFolder = FileUtils . GetPathToTempFolder ( Path . GetDirectoryName ( executeOptions . File . Path ) ) ;
45
48
var publishDirectory = Path . Combine ( projectFolder , "publish" ) ;
46
- var options = new PublishCommandOptions ( executeOptions . File , publishDirectory , "script" , PublishType . Library , executeOptions . OptimizationLevel , null , executeOptions . NoCache ) ;
47
- new PublishCommand ( _scriptConsole , _logFactory ) . Execute ( options ) ;
48
- return Path . Combine ( publishDirectory , "script.dll" ) ;
49
+ var pathToLibrary = Path . Combine ( publishDirectory , "script.dll" ) ;
50
+
51
+ if ( ! TryCreateHash ( executeOptions , out var hash ) || ! TryGetHash ( publishDirectory , out var cachedHash ) )
52
+ {
53
+ return CreateLibrary ( ) ;
54
+ }
55
+
56
+ if ( ! string . Equals ( hash , cachedHash ) )
57
+ {
58
+ return CreateLibrary ( ) ;
59
+ }
60
+
61
+ return pathToLibrary ;
62
+
63
+ string CreateLibrary ( )
64
+ {
65
+ var options = new PublishCommandOptions ( executeOptions . File , publishDirectory , "script" , PublishType . Library , executeOptions . OptimizationLevel , null , executeOptions . NoCache ) ;
66
+ new PublishCommand ( _scriptConsole , _logFactory ) . Execute ( options ) ;
67
+ if ( hash != null )
68
+ {
69
+ File . WriteAllText ( Path . Combine ( publishDirectory , "script.sha256" ) , hash ) ;
70
+ }
71
+ return Path . Combine ( publishDirectory , "script.dll" ) ;
72
+ }
73
+ }
74
+
75
+ public bool TryCreateHash ( ExecuteScriptCommandOptions options , out string hash )
76
+ {
77
+ if ( options . NoCache )
78
+ {
79
+ _logger . Debug ( $ "The script { options . File . Path } was executed with the '--nocache' flag. Skipping cache.") ;
80
+ hash = null ;
81
+ return false ;
82
+ }
83
+
84
+ var projectFolder = FileUtils . GetPathToTempFolder ( Path . GetDirectoryName ( options . File . Path ) ) ;
85
+ var pathToProjectFile = Path . Combine ( projectFolder , "script.csproj" ) ;
86
+ var projectFile = new ProjectFile ( File . ReadAllText ( pathToProjectFile ) ) ;
87
+ if ( ! projectFile . IsCacheable )
88
+ {
89
+ _logger . Warning ( $ "The script { options . File . Path } is not cacheable. For caching and optimal performance, ensure that the script only contains NuGet references with pinned/exact versions.") ;
90
+ hash = null ;
91
+ return false ;
92
+ }
93
+
94
+ var scriptFilesProvider = new ScriptFilesResolver ( ) ;
95
+ var allScriptFiles = scriptFilesProvider . GetScriptFiles ( options . File . Path ) ;
96
+ IncrementalHash incrementalHash = IncrementalHash . CreateHash ( HashAlgorithmName . SHA256 ) ;
97
+ foreach ( var scriptFile in allScriptFiles )
98
+ {
99
+ incrementalHash . AppendData ( File . ReadAllBytes ( scriptFile ) ) ;
100
+ }
101
+
102
+ var configuration = options . OptimizationLevel . ToString ( ) ;
103
+ incrementalHash . AppendData ( Encoding . UTF8 . GetBytes ( configuration ) ) ;
104
+
105
+ hash = Convert . ToBase64String ( incrementalHash . GetHashAndReset ( ) ) ;
106
+ return true ;
107
+ }
108
+
109
+
110
+ public bool TryGetHash ( string cacheFolder , out string hash )
111
+ {
112
+ if ( ! Directory . Exists ( cacheFolder ) )
113
+ {
114
+ hash = null ;
115
+ return false ;
116
+ }
117
+
118
+ var pathToHashFile = Path . Combine ( cacheFolder , "script.sha256" ) ;
119
+
120
+ if ( ! File . Exists ( Path . Combine ( cacheFolder , "script.sha256" ) ) )
121
+ {
122
+ hash = null ;
123
+ return false ;
124
+ }
125
+
126
+ hash = File . ReadAllText ( pathToHashFile ) ;
127
+ return true ;
49
128
}
50
129
51
130
private async Task < TReturn > ExecuteLibrary < TReturn > ( string pathToLibrary , string [ ] arguments , bool noCache )
0 commit comments