diff options
author | Rob Mensching <rob@firegiant.com> | 2020-06-08 16:26:59 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2020-06-08 16:37:14 -0700 |
commit | 02cdf55197d599d4d1fd611ad749d01f5c47a01f (patch) | |
tree | 76fcd573827fdb6b142197baa3237e6310a7bd4d /src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs | |
parent | 04b8976ca565ce95cf32a58c8725843618724383 (diff) | |
download | wix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.tar.gz wix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.tar.bz2 wix-02cdf55197d599d4d1fd611ad749d01f5c47a01f.zip |
Add "extension" command
Diffstat (limited to 'src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs')
-rw-r--r-- | src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs b/src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs new file mode 100644 index 00000000..3ec6451e --- /dev/null +++ b/src/WixToolset.Core.ExtensionCache/ExtensionCacheManager.cs | |||
@@ -0,0 +1,252 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.ExtensionCache | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Threading; | ||
10 | using System.Threading.Tasks; | ||
11 | using NuGet.Common; | ||
12 | using NuGet.Configuration; | ||
13 | using NuGet.Credentials; | ||
14 | using NuGet.Packaging; | ||
15 | using NuGet.Protocol; | ||
16 | using NuGet.Protocol.Core.Types; | ||
17 | using NuGet.Versioning; | ||
18 | |||
19 | /// <summary> | ||
20 | /// Extension cache manager. | ||
21 | /// </summary> | ||
22 | public class ExtensionCacheManager | ||
23 | { | ||
24 | public string CacheFolder(bool global) => global ? this.GlobalCacheFolder() : this.LocalCacheFolder(); | ||
25 | |||
26 | public string LocalCacheFolder() => Path.Combine(Environment.CurrentDirectory, @".wix\extensions\"); | ||
27 | |||
28 | public string GlobalCacheFolder() | ||
29 | { | ||
30 | var baseFolder = Environment.GetEnvironmentVariable("WIX_EXTENSIONS") ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); | ||
31 | return Path.Combine(baseFolder, @".wix\extensions\"); | ||
32 | } | ||
33 | |||
34 | public async Task<bool> AddAsync(bool global, string extension, CancellationToken cancellationToken) | ||
35 | { | ||
36 | if (String.IsNullOrEmpty(extension)) | ||
37 | { | ||
38 | throw new ArgumentNullException(nameof(extension)); | ||
39 | } | ||
40 | |||
41 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); | ||
42 | |||
43 | var result = await this.DownloadAndExtractAsync(global, extensionId, extensionVersion, cancellationToken); | ||
44 | |||
45 | return result; | ||
46 | } | ||
47 | |||
48 | public Task<bool> RemoveAsync(bool global, string extension, CancellationToken cancellationToken) | ||
49 | { | ||
50 | if (String.IsNullOrEmpty(extension)) | ||
51 | { | ||
52 | throw new ArgumentNullException(nameof(extension)); | ||
53 | } | ||
54 | |||
55 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); | ||
56 | |||
57 | var cacheFolder = this.CacheFolder(global); | ||
58 | |||
59 | cacheFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); | ||
60 | |||
61 | if (Directory.Exists(cacheFolder)) | ||
62 | { | ||
63 | cancellationToken.ThrowIfCancellationRequested(); | ||
64 | |||
65 | Directory.Delete(cacheFolder, true); | ||
66 | return Task.FromResult(true); | ||
67 | } | ||
68 | |||
69 | return Task.FromResult(false); | ||
70 | } | ||
71 | |||
72 | public Task<IEnumerable<CachedExtension>> ListAsync(bool global, string extension, CancellationToken cancellationToken) | ||
73 | { | ||
74 | var found = new List<CachedExtension>(); | ||
75 | |||
76 | (var extensionId, var extensionVersion) = ParseExtensionReference(extension); | ||
77 | |||
78 | var cacheFolder = this.CacheFolder(global); | ||
79 | |||
80 | var searchFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); | ||
81 | |||
82 | if (!Directory.Exists(searchFolder)) | ||
83 | { | ||
84 | } | ||
85 | else if (!String.IsNullOrEmpty(extensionVersion)) // looking for an explicit version of an extension. | ||
86 | { | ||
87 | var extensionFolder = Path.Combine(cacheFolder, extensionId, extensionVersion); | ||
88 | if (Directory.Exists(extensionFolder)) | ||
89 | { | ||
90 | var present = ExtensionFileExists(cacheFolder, extensionId, extensionVersion); | ||
91 | found.Add(new CachedExtension(extensionId, extensionVersion, !present)); | ||
92 | } | ||
93 | } | ||
94 | else // looking for all versions of an extension or all versions of all extensions. | ||
95 | { | ||
96 | IEnumerable<string> foundExtensionIds; | ||
97 | |||
98 | if (String.IsNullOrEmpty(extensionId)) | ||
99 | { | ||
100 | // Looking for all versions of all extensions. | ||
101 | foundExtensionIds = Directory.GetDirectories(cacheFolder).Select(folder => Path.GetFileName(folder)).ToList(); | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | // Looking for all versions of a single extension. | ||
106 | var extensionFolder = Path.Combine(cacheFolder, extensionId); | ||
107 | foundExtensionIds = Directory.Exists(extensionFolder) ? new[] { extensionId } : Array.Empty<string>(); | ||
108 | } | ||
109 | |||
110 | foreach (var foundExtensionId in foundExtensionIds) | ||
111 | { | ||
112 | var extensionFolder = Path.Combine(cacheFolder, foundExtensionId); | ||
113 | |||
114 | foreach (var folder in Directory.GetDirectories(extensionFolder)) | ||
115 | { | ||
116 | cancellationToken.ThrowIfCancellationRequested(); | ||
117 | |||
118 | var foundExtensionVersion = Path.GetFileName(folder); | ||
119 | |||
120 | if (!NuGetVersion.TryParse(foundExtensionVersion, out _)) | ||
121 | { | ||
122 | continue; | ||
123 | } | ||
124 | |||
125 | var present = ExtensionFileExists(cacheFolder, foundExtensionId, foundExtensionVersion); | ||
126 | found.Add(new CachedExtension(foundExtensionId, foundExtensionVersion, !present)); | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | return Task.FromResult((IEnumerable<CachedExtension>)found); | ||
132 | } | ||
133 | |||
134 | private async Task<bool> DownloadAndExtractAsync(bool global, string id, string version, CancellationToken cancellationToken) | ||
135 | { | ||
136 | var logger = NullLogger.Instance; | ||
137 | |||
138 | DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, nonInteractive: false); | ||
139 | |||
140 | var settings = Settings.LoadDefaultSettings(root: Environment.CurrentDirectory); | ||
141 | var sources = PackageSourceProvider.LoadPackageSources(settings).Where(s => s.IsEnabled); | ||
142 | |||
143 | using (var cache = new SourceCacheContext()) | ||
144 | { | ||
145 | PackageSource versionSource = null; | ||
146 | |||
147 | var nugetVersion = String.IsNullOrEmpty(version) ? null : new NuGetVersion(version); | ||
148 | |||
149 | if (nugetVersion is null) | ||
150 | { | ||
151 | foreach (var source in sources) | ||
152 | { | ||
153 | var repository = Repository.Factory.GetCoreV3(source.Source); | ||
154 | var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); | ||
155 | |||
156 | var availableVersions = await resource.GetAllVersionsAsync(id, cache, logger, cancellationToken); | ||
157 | foreach (var availableVersion in availableVersions) | ||
158 | { | ||
159 | if (nugetVersion is null || nugetVersion < availableVersion) | ||
160 | { | ||
161 | nugetVersion = availableVersion; | ||
162 | versionSource = source; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | if (nugetVersion is null) | ||
168 | { | ||
169 | return false; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | var searchSources = versionSource is null ? sources : new[] { versionSource }; | ||
174 | |||
175 | var extensionFolder = Path.Combine(this.CacheFolder(global), id, nugetVersion.ToString()); | ||
176 | |||
177 | foreach (var source in searchSources) | ||
178 | { | ||
179 | var repository = Repository.Factory.GetCoreV3(source.Source); | ||
180 | var resource = await repository.GetResourceAsync<FindPackageByIdResource>(); | ||
181 | |||
182 | using (var stream = new MemoryStream()) | ||
183 | { | ||
184 | var downloaded = await resource.CopyNupkgToStreamAsync(id, nugetVersion, stream, cache, logger, cancellationToken); | ||
185 | |||
186 | if (downloaded) | ||
187 | { | ||
188 | stream.Position = 0; | ||
189 | |||
190 | using (var archive = new PackageArchiveReader(stream)) | ||
191 | { | ||
192 | var files = PackagingConstants.Folders.Known.SelectMany(folder => archive.GetFiles(folder)).Distinct(StringComparer.OrdinalIgnoreCase); | ||
193 | await archive.CopyFilesAsync(extensionFolder, files, this.ExtractProgress, logger, cancellationToken); | ||
194 | } | ||
195 | |||
196 | return true; | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | return false; | ||
203 | } | ||
204 | |||
205 | private string ExtractProgress(string sourceFile, string targetPath, Stream fileStream) => fileStream.CopyToFile(targetPath); | ||
206 | |||
207 | private static (string extensionId, string extensionVersion) ParseExtensionReference(string extensionReference) | ||
208 | { | ||
209 | var extensionId = extensionReference ?? String.Empty; | ||
210 | var extensionVersion = String.Empty; | ||
211 | |||
212 | var index = extensionId.LastIndexOf('/'); | ||
213 | if (index > 0) | ||
214 | { | ||
215 | extensionVersion = extensionReference.Substring(index + 1); | ||
216 | extensionId = extensionReference.Substring(0, index); | ||
217 | |||
218 | if (!NuGetVersion.TryParse(extensionVersion, out _)) | ||
219 | { | ||
220 | throw new ArgumentException($"Invalid extension version in {extensionReference}"); | ||
221 | } | ||
222 | |||
223 | if (String.IsNullOrEmpty(extensionId)) | ||
224 | { | ||
225 | throw new ArgumentException($"Invalid extension id in {extensionReference}"); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return (extensionId, extensionVersion); | ||
230 | } | ||
231 | |||
232 | private static bool ExtensionFileExists(string baseFolder, string extensionId, string extensionVersion) | ||
233 | { | ||
234 | var toolsFolder = Path.Combine(baseFolder, extensionId, extensionVersion, "tools"); | ||
235 | if (!Directory.Exists(toolsFolder)) | ||
236 | { | ||
237 | return false; | ||
238 | } | ||
239 | |||
240 | var extensionAssembly = Path.Combine(toolsFolder, extensionId + ".dll"); | ||
241 | |||
242 | var present = File.Exists(extensionAssembly); | ||
243 | if (!present) | ||
244 | { | ||
245 | extensionAssembly = Path.Combine(toolsFolder, extensionId + ".exe"); | ||
246 | present = File.Exists(extensionAssembly); | ||
247 | } | ||
248 | |||
249 | return present; | ||
250 | } | ||
251 | } | ||
252 | } | ||