Skip to content

Commit 52b7ebc

Browse files
committed
feat(restore): add cifs client to unc paths usage to work on any OS
1 parent 8ac8b2f commit 52b7ebc

File tree

8 files changed

+335
-68
lines changed

8 files changed

+335
-68
lines changed

cmf-cli/Commands/assemble/AssembleCommand.cs

+23-16
Original file line numberDiff line numberDiff line change
@@ -209,20 +209,14 @@ private void AssembleTestPackages(IDirectoryInfo outputDir, IDirectoryInfo[] rep
209209
// Load test package from repo if is not loaded yet
210210
if (testPackage.CmfPackage == null || (testPackage.CmfPackage != null && testPackage.CmfPackage.Uri == null))
211211
{
212-
string _dependencyFileName = $"{testPackage.Id}.{testPackage.Version}.zip";
213-
214-
IFileInfo dependencyFile = repoDirectories?
215-
.Select(r => r.GetFiles(_dependencyFileName).FirstOrDefault())
216-
.Where(r => r != null)
217-
.FirstOrDefault();
218-
219-
if (dependencyFile != null)
220-
{
221-
testPackage.CmfPackage = new(testPackage.Id, testPackage.Version, new(dependencyFile.FullName));
222-
}
223-
else
212+
string packageName = $"{testPackage.Id}.{testPackage.Version}";
213+
testPackage.CmfPackage = ExecutionContext.Instance.RunningOnWindows
214+
? CmfPackage.LoadFromRepo(repoDirectories, testPackage.Id, testPackage.Version, fromManifest: false)
215+
: CmfPackage.LoadFromCIFSShare(testPackage.Id, testPackage.Version, fromManifest: false);
216+
if(testPackage.CmfPackage == null)
224217
{
225-
throw new CliException(string.Format(CliMessages.SomePackagesNotFound, _dependencyFileName));
218+
string errorMessage = string.Format(CoreMessages.NotFound, packageName);
219+
throw new CliException(errorMessage);
226220
}
227221
}
228222

@@ -270,7 +264,7 @@ private void AssembleDependencies(IDirectoryInfo outputDir, Uri ciRepo, IDirecto
270264
// Save all external dependencies and locations in a dictionary
271265
else
272266
{
273-
packagesLocation[$"{dependency.Id}@{dependency.Version}"] = dependency.CmfPackage.Uri.GetFileName();
267+
packagesLocation[$"{dependency.Id}@{dependency.Version}"] = ExecutionContext.Instance.RunningOnWindows ? dependency.CmfPackage.Uri.GetFileName() : dependency.CmfPackage.Uri.LocalPath;
274268
}
275269

276270
AssembleDependencies(outputDir, ciRepo, repoDirectories, dependency.CmfPackage, assembledDependencies, includeTestPackages);
@@ -293,7 +287,9 @@ private void AssemblePackage(IDirectoryInfo outputDir, IDirectoryInfo[] repoDire
293287
if (cmfPackage == null || (cmfPackage != null && cmfPackage.Uri == null))
294288
{
295289
string packageName = cmfPackage.PackageName;
296-
cmfPackage = CmfPackage.LoadFromRepo(repoDirectories, cmfPackage.PackageId, cmfPackage.Version);
290+
cmfPackage = ExecutionContext.Instance.RunningOnWindows
291+
? CmfPackage.LoadFromRepo(repoDirectories, cmfPackage.PackageId, cmfPackage.Version)
292+
: CmfPackage.LoadFromCIFSShare(cmfPackage.PackageId, cmfPackage.Version);
297293
if(cmfPackage == null)
298294
{
299295
string errorMessage = string.Format(CoreMessages.NotFound, packageName);
@@ -308,7 +304,18 @@ private void AssemblePackage(IDirectoryInfo outputDir, IDirectoryInfo[] repoDire
308304
}
309305

310306
Log.Information(string.Format(CliMessages.GetPackage, cmfPackage.PackageId, cmfPackage.Version));
311-
cmfPackage.Uri.GetFile().CopyTo(destinationFile);
307+
if(ExecutionContext.Instance.RunningOnWindows)
308+
{
309+
cmfPackage.Uri.GetFile().CopyTo(destinationFile);
310+
}
311+
else if(cmfPackage.SharedFolder != null)
312+
{
313+
var file = cmfPackage.SharedFolder.GetFile(cmfPackage.ZipPackageName);
314+
using var fileStream = file.Item2;
315+
fileStream.Position = 0; // Reset stream position to the beginning
316+
using var fileStreamOutput = fileSystem.File.Create(destinationFile);
317+
fileStream.CopyTo(fileStreamOutput);
318+
}
312319

313320
// Assemble Tests
314321
if (includeTestPackages)

cmf-cli/Handlers/PackageType/PackageTypeHandler.cs

+45-35
Original file line numberDiff line numberDiff line change
@@ -662,50 +662,60 @@ public virtual void RestoreDependencies(Uri[] repoUris)
662662
var identifier = $"{dependency.Id}@{dependency.Version}";
663663
Log.Debug($"Processing dependency {identifier}...");
664664
Log.Debug($"Found package {identifier} at {dependency.CmfPackage.Uri.AbsoluteUri}");
665-
if (dependency.CmfPackage.Uri.IsDirectory())
665+
if(dependency.CmfPackage.SharedFolder != null)
666+
{
667+
var file = dependency.CmfPackage.SharedFolder.GetFile(dependency.CmfPackage.ZipPackageName);
668+
ExtractZip(file.Item2, identifier);
669+
}
670+
else if (dependency.CmfPackage.Uri.IsDirectory())
666671
{
667672
using (Stream zipToOpen = this.fileSystem.FileInfo.New(dependency.CmfPackage.Uri.LocalPath).OpenRead())
668673
{
669-
using (ZipArchive zip = new(zipToOpen, ZipArchiveMode.Read))
670-
{
671-
// these tuples allow us to rewrite entry paths
672-
var entriesToExtract = new List<Tuple<ZipArchiveEntry, string>>();
673-
entriesToExtract.AddRange(zip.Entries.Select(entry => new Tuple<ZipArchiveEntry, string>(entry, entry.FullName)));
674-
675-
foreach (var entry in entriesToExtract)
676-
{
677-
var target = this.fileSystem.Path.Join(this.DependenciesFolder.FullName, omitIdentifier ? null : identifier, entry.Item2);
678-
var targetDir = this.fileSystem.Path.GetDirectoryName(target);
679-
if (target.EndsWith("/"))
680-
{
681-
// this a dotnet bug: if a folder contains a ., the library assumes it's a file and adds it as an entry
682-
// however, afterwards all folder contents are separate entries, so we can just skip these
683-
continue;
684-
}
685-
686-
if (!fileSystem.File.Exists(target)) // TODO: support overwriting if requested
687-
{
688-
var overwrite = false;
689-
Log.Debug($"Extracting {entry.Item1.FullName} to {target}");
690-
if (!string.IsNullOrEmpty(targetDir))
691-
{
692-
fileSystem.Directory.CreateDirectory(targetDir);
693-
}
694-
695-
entry.Item1.ExtractToFile(target, overwrite, fileSystem);
696-
}
697-
else
698-
{
699-
Log.Debug($"Skipping {target}, file exists");
700-
}
701-
}
702-
}
674+
ExtractZip(zipToOpen, identifier);
703675
}
704676
}
705677
}
706678
});
707679
}
708680

681+
private void ExtractZip(Stream zipToOpen, string identifier)
682+
{
683+
using (ZipArchive zip = new(zipToOpen, ZipArchiveMode.Read))
684+
{
685+
// these tuples allow us to rewrite entry paths
686+
var entriesToExtract = new List<Tuple<ZipArchiveEntry, string>>();
687+
entriesToExtract.AddRange(zip.Entries.Select(entry => new Tuple<ZipArchiveEntry, string>(entry, entry.FullName)));
688+
689+
foreach (var entry in entriesToExtract)
690+
{
691+
var target = this.fileSystem.Path.Join(this.DependenciesFolder.FullName, omitIdentifier ? null : identifier, entry.Item2);
692+
var targetDir = this.fileSystem.Path.GetDirectoryName(target);
693+
if (target.EndsWith("/"))
694+
{
695+
// this a dotnet bug: if a folder contains a ., the library assumes it's a file and adds it as an entry
696+
// however, afterwards all folder contents are separate entries, so we can just skip these
697+
continue;
698+
}
699+
700+
if (!fileSystem.File.Exists(target)) // TODO: support overwriting if requested
701+
{
702+
var overwrite = false;
703+
Log.Debug($"Extracting {entry.Item1.FullName} to {target}");
704+
if (!string.IsNullOrEmpty(targetDir))
705+
{
706+
fileSystem.Directory.CreateDirectory(targetDir);
707+
}
708+
709+
entry.Item1.ExtractToFile(target, overwrite, fileSystem);
710+
}
711+
else
712+
{
713+
Log.Debug($"Skipping {target}, file exists");
714+
}
715+
}
716+
}
717+
}
718+
709719
#endregion Public Methods
710720
}
711721
}

core/Interfaces/ICIFSClient.cs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections.Generic;
2+
using Core.Objects;
3+
4+
namespace Cmf.CLI.Core.Interfaces
5+
{
6+
public interface ICIFSClient
7+
{
8+
public string Server { get; }
9+
public List<SharedFolder> SharedFolders { get;}
10+
public bool IsConnected { get; }
11+
public void Connect();
12+
public void Disconnect();
13+
}
14+
}

core/Interfaces/ISharedFolder.cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace Cmf.CLI.Core.Interfaces
5+
{
6+
public interface ISharedFolder
7+
{
8+
public Tuple<Uri, Stream> GetFile(string fileName);
9+
}
10+
}

core/Objects/CIFSClient.cs

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Cmf.CLI.Core;
5+
using Cmf.CLI.Core.Interfaces;
6+
using Cmf.CLI.Utilities;
7+
using Microsoft.TemplateEngine.Utils;
8+
using SMBLibrary;
9+
using SMBLibrary.Client;
10+
11+
namespace Core.Objects
12+
{
13+
public class CIFSClient : ICIFSClient
14+
{
15+
public string Server { get; private set; }
16+
public List<SharedFolder> SharedFolders { get; private set; }
17+
public bool IsConnected { get; private set; }
18+
19+
private ISMBClient _smbClient;
20+
private string _domain;
21+
private string _username;
22+
private string _password;
23+
24+
public CIFSClient(string server, IEnumerable<Uri> uris)
25+
{
26+
Server = server;
27+
_smbClient = new SMB2Client();
28+
_domain = Environment.GetEnvironmentVariable("CIFS_DOMAIN");
29+
_username = Environment.GetEnvironmentVariable("CIFS_USERNAME");
30+
_password = Environment.GetEnvironmentVariable("CIFS_PASSWORD");
31+
if(string.IsNullOrEmpty(_domain) || string.IsNullOrEmpty(_username) || string.IsNullOrEmpty(_password))
32+
{
33+
throw new CliException("CIFS credentials not found in environment variables");
34+
}
35+
36+
Connect();
37+
38+
if(IsConnected)
39+
{
40+
SharedFolders = [];
41+
uris.ForEach(uri => SharedFolders.Add(new SharedFolder(uri, _smbClient)));
42+
}
43+
}
44+
45+
public void Connect()
46+
{
47+
Log.Debug($"Connecting to SMB server {Server} with username {_username}");
48+
IsConnected = _smbClient.Connect(Server, SMBTransportType.DirectTCPTransport);
49+
if (!IsConnected)
50+
{
51+
Log.Debug($"Failed to connect to {Server}");
52+
Log.Warning($"Failed to connect to {Server}");
53+
}
54+
55+
var status = _smbClient.Login(_domain, _username, _password);
56+
if (status != NTStatus.STATUS_SUCCESS)
57+
{
58+
Log.Debug($"Fail status {status}");
59+
Log.Warning($"Failed to login to {Server} with username {_username}");
60+
}
61+
}
62+
63+
public void Disconnect()
64+
{
65+
// Implement disconnection logic here
66+
Console.WriteLine("Disconnecting from SMB server");
67+
}
68+
}
69+
70+
public class SharedFolder : ISharedFolder
71+
{
72+
public bool Exists { get; private set; }
73+
private ISMBFileStore _smbFileStore { get; set; }
74+
private ISMBClient _client { get; set; }
75+
private string _server { get; set; }
76+
private string _share { get; set; }
77+
private string _path { get; set; }
78+
private Uri _uri { get; set; }
79+
80+
public SharedFolder(Uri uri, ISMBClient client)
81+
{
82+
_client = client;
83+
_server = uri.Host;
84+
_share = uri.PathAndQuery.Split("/")[1];
85+
_path = uri.PathAndQuery.Replace($"/{_share}", "").Substring(1);
86+
_uri = uri;
87+
Load();
88+
}
89+
90+
private void Load()
91+
{
92+
_smbFileStore = _client.TreeConnect(_share, out NTStatus status);
93+
if (status != NTStatus.STATUS_SUCCESS)
94+
{
95+
Log.Debug($"Fail status {status}");
96+
Log.Warning($"Failed to connect to share {_share} on {_server}");
97+
Exists = false;
98+
}
99+
else
100+
{
101+
Exists = true;
102+
}
103+
}
104+
105+
public Tuple<Uri, Stream> GetFile(string fileName)
106+
{
107+
Tuple<Uri, Stream> fileStream = null;
108+
var filepath = $"{_path}/{fileName}";
109+
var status = _smbFileStore.CreateFile(out object fileHandle, out FileStatus fileStatus, filepath, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Normal, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
110+
if (status == NTStatus.STATUS_SUCCESS)
111+
{
112+
var stream = new MemoryStream();
113+
long bytesRead = 0;
114+
while (true)
115+
{
116+
status = _smbFileStore.ReadFile(out byte[] data, fileHandle, bytesRead, (int)_client.MaxReadSize);
117+
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
118+
{
119+
throw new Exception($"Failed to read file {filepath}");
120+
}
121+
122+
if (status == NTStatus.STATUS_END_OF_FILE || data.Length == 0)
123+
{
124+
break;
125+
}
126+
bytesRead += data.Length;
127+
stream.Write(data, 0, data.Length);
128+
}
129+
var uri = new Uri(_uri, filepath);
130+
fileStream = new Tuple<Uri, Stream>(uri, stream);
131+
}
132+
133+
return fileStream;
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)