Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.

Commit 1583d66

Browse files
committed
add cake process and filesystem structure
remove System.CommandLine fix cli quotation problems
1 parent f6d8e16 commit 1583d66

36 files changed

+3217
-187
lines changed

src/LocalStack.AwsLocal/CommandDispatcher.cs

Lines changed: 104 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Reflection;
6+
using System.Runtime.InteropServices;
57
using LocalStack.AwsLocal.AmbientContexts;
68
using LocalStack.AwsLocal.Contracts;
79
using LocalStack.AwsLocal.Extensions;
10+
using LocalStack.AwsLocal.ProcessCore;
11+
using LocalStack.AwsLocal.ProcessCore.IO;
812
using LocalStack.Client.Contracts;
913
using LocalStack.Client.Models;
1014

@@ -14,18 +18,16 @@ public class CommandDispatcher
1418
{
1519
private const string UsageResource = "LocalStack.AwsLocal.Docs.Usage.txt";
1620

17-
private readonly IProcessHelper _processHelper;
21+
private readonly IProcessRunner _processRunner;
1822
private readonly IConfig _config;
23+
private readonly IFileSystem _fileSystem;
1924
private readonly string[] _args;
2025

21-
private CommandDispatcher()
26+
public CommandDispatcher(IProcessRunner processRunner, IConfig config, IFileSystem fileSystem, string[] args)
2227
{
23-
}
24-
25-
public CommandDispatcher(IProcessHelper processHelper, IConfig config, string[] args)
26-
{
27-
_processHelper = processHelper;
28+
_processRunner = processRunner;
2829
_config = config;
30+
_fileSystem = fileSystem;
2931
_args = args;
3032
}
3133

@@ -57,31 +59,113 @@ public void Run()
5759
return;
5860
}
5961

60-
string cliCommand = _args.GetCliCommand(awsServiceEndpoint.ServiceUrl);
62+
var processSettings = new ProcessSettings
63+
{ NoWorkingDirectory = true, Silent = true, EnvironmentVariables = new Dictionary<string, string>() };
64+
processSettings.EnvironmentVariables.Add("AWS_DEFAULT_REGION", Environment.GetEnvironmentVariable("AWS_DEFAULT_REGION") ?? "us-east-1");
65+
processSettings.EnvironmentVariables.Add("AWS_ACCESS_KEY_ID", Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID") ?? "_not_needed_locally_");
66+
processSettings.EnvironmentVariables.Add("AWS_SECRET_ACCESS_KEY", Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") ?? "_not_needed_locally_");
67+
68+
69+
var builder = new ProcessArgumentBuilder();
70+
builder.Append(_args[0]);
71+
builder.AppendSwitch("--endpoint-url", "=", awsServiceEndpoint.ServiceUrl);
72+
73+
if (awsServiceEndpoint.ServiceUrl.StartsWith("https"))
74+
{
75+
builder.Append("--no-verify-ssl");
76+
}
77+
78+
var passToNextArgument = false;
79+
for (var i = 0; i < _args.Length; i++)
80+
{
81+
string argument = _args[i];
82+
83+
if (argument == _args[0])
84+
{
85+
continue;
86+
}
87+
88+
if (passToNextArgument)
89+
{
90+
passToNextArgument = false;
91+
continue;
92+
}
93+
94+
95+
if (argument.StartsWith("--") && !argument.Contains("=") && i + 1 < _args.Length) // switch argument
96+
{
97+
string nextArgument = _args[i + 1];
98+
builder.AppendSwitchQuoted(argument, " ", nextArgument);
99+
100+
passToNextArgument = true;
101+
continue;
102+
}
103+
104+
builder.Append(argument);
105+
}
106+
61107

62-
string awsDefaultRegion = Environment.GetEnvironmentVariable("AWS_DEFAULT_REGION") ?? "us-east-1";
63-
string awsAccessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID") ?? "_not_needed_locally_";
64-
string awsSecretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") ?? "_not_needed_locally_";
108+
processSettings.Arguments = builder;
65109

66-
_processHelper.CmdExecute(cliCommand, null, true, true, new Dictionary<string, string>
110+
string awsExec = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "aws.cmd" : "aws";
111+
FilePath? awsPath = GetAwsPath(awsExec);
112+
113+
if (awsPath == null)
67114
{
68-
{"AWS_DEFAULT_REGION", awsDefaultRegion},
69-
{"AWS_ACCESS_KEY_ID", awsAccessKeyId},
70-
{"AWS_SECRET_ACCESS_KEY", awsSecretAccessKey}
71-
});
115+
ConsoleContext.Current.WriteLine($"ERROR: Unable to find aws cli");
116+
EnvironmentContext.Current.Exit(1);
117+
return;
118+
}
119+
120+
IProcess? process = _processRunner.Start(awsPath, processSettings);
121+
122+
process?.WaitForExit();
72123
}
73124

74125
private static string GetUsageInfo()
75126
{
76-
using (Stream stream = Assembly.GetCallingAssembly().GetManifestResourceStream(UsageResource))
127+
using Stream? stream = Assembly.GetCallingAssembly().GetManifestResourceStream(UsageResource);
128+
using var reader = new StreamReader(stream ?? throw new NullReferenceException(nameof(stream)));
129+
string result = reader.ReadToEnd();
130+
131+
return result;
132+
}
133+
134+
private FilePath? GetAwsPath(params string[] awsPaths)
135+
{
136+
string[]? pathDirs = null;
137+
138+
// Look for each possible executable name in various places.
139+
foreach (string toolExeName in awsPaths)
77140
{
78-
using (var reader = new StreamReader(stream))
141+
142+
// Cache the PATH directory list if we didn't already.
143+
if (pathDirs == null)
79144
{
80-
string result = reader.ReadToEnd();
145+
string? pathEnv = Environment.GetEnvironmentVariable("PATH");
146+
if (!string.IsNullOrEmpty(pathEnv))
147+
{
148+
pathDirs = pathEnv.Split(new[] { !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ':' : ';' },
149+
StringSplitOptions.RemoveEmptyEntries);
150+
}
151+
else
152+
{
153+
pathDirs = new string[] { };
154+
}
155+
}
81156

82-
return result;
157+
// Look in every PATH directory for the file.
158+
foreach (var pathDir in pathDirs)
159+
{
160+
FilePath file = new DirectoryPath(pathDir).CombineWithFilePath(toolExeName);
161+
if (_fileSystem.Exist(file))
162+
{
163+
return file.FullPath;
164+
}
83165
}
84166
}
167+
168+
return null;
85169
}
86170
}
87171
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/***************************************************************************************
2+
* Title: cake-build/cake
3+
* Author: Mattias Karlsson, Patrik Svensson, Gary Ewan Park, Dave Glick
4+
* Date: 01.05.2020
5+
* Code version: commit: 0c46856abe976f6c122f05a5e82ccb854fd923c1
6+
* Availability: https://github.com/cake-build/cake/blob/develop/src/Cake.Core/IO/IDirectory.cs
7+
*
8+
***************************************************************************************/
9+
10+
using System.Collections.Generic;
11+
using LocalStack.AwsLocal.ProcessCore.IO;
12+
13+
namespace LocalStack.AwsLocal.Contracts
14+
{
15+
/// Represents a directory.
16+
public interface IDirectory : IFileSystemInfo
17+
{
18+
/// <summary>
19+
/// Gets the path to the directory.
20+
/// </summary>
21+
/// <value>The path.</value>
22+
new DirectoryPath? Path { get; }
23+
24+
/// <summary>
25+
/// Creates the directory.
26+
/// </summary>
27+
void Create();
28+
29+
/// <summary>
30+
/// Moves the directory to the specified destination path.
31+
/// </summary>
32+
/// <param name="destination">The destination path.</param>
33+
void Move(DirectoryPath destination);
34+
35+
/// <summary>
36+
/// Deletes the directory.
37+
/// </summary>
38+
/// <param name="recursive">Will perform a recursive delete if set to <c>true</c>.</param>
39+
void Delete(bool recursive);
40+
41+
/// <summary>
42+
/// Gets directories matching the specified filter and scope.
43+
/// </summary>
44+
/// <param name="filter">The filter.</param>
45+
/// <param name="scope">The search scope.</param>
46+
/// <returns>Directories matching the filter and scope.</returns>
47+
IEnumerable<IDirectory> GetDirectories(string filter, SearchScope scope);
48+
49+
/// <summary>
50+
/// Gets files matching the specified filter and scope.
51+
/// </summary>
52+
/// <param name="filter">The filter.</param>
53+
/// <param name="scope">The search scope.</param>
54+
/// <returns>Files matching the specified filter and scope.</returns>
55+
IEnumerable<IFile> GetFiles(string filter, SearchScope scope);
56+
}
57+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/***************************************************************************************
2+
* Title: cake-build/cake
3+
* Author: Mattias Karlsson, Patrik Svensson, Gary Ewan Park, Dmytro Dziuma
4+
* Date: 01.05.2020
5+
* Code version: commit: 0c46856abe976f6c122f05a5e82ccb854fd923c1
6+
* Availability: https://github.com/cake-build/cake/blob/develop/src/Cake.Core/IO/IFile.cs
7+
*
8+
***************************************************************************************/
9+
10+
using System.IO;
11+
using LocalStack.AwsLocal.ProcessCore.IO;
12+
13+
namespace LocalStack.AwsLocal.Contracts
14+
{
15+
/// <summary>
16+
/// Represents a file.
17+
/// </summary>
18+
public interface IFile : IFileSystemInfo
19+
{
20+
/// <summary>
21+
/// Gets the path to the file.
22+
/// </summary>
23+
/// <value>The path.</value>
24+
new FilePath? Path { get; }
25+
26+
/// <summary>
27+
/// Gets the length of the file.
28+
/// </summary>
29+
/// <value>The length of the file.</value>
30+
long Length { get; }
31+
32+
/// <summary>
33+
/// Gets or sets the file attributes.
34+
/// </summary>
35+
/// <value>The file attributes.</value>
36+
FileAttributes Attributes { get; set; }
37+
38+
/// <summary>
39+
/// Copies the file to the specified destination path.
40+
/// </summary>
41+
/// <param name="destination">The destination path.</param>
42+
/// <param name="overwrite">Will overwrite existing destination file if set to <c>true</c>.</param>
43+
void Copy(FilePath destination, bool overwrite);
44+
45+
/// <summary>
46+
/// Moves the file to the specified destination path.
47+
/// </summary>
48+
/// <param name="destination">The destination path.</param>
49+
void Move(FilePath destination);
50+
51+
/// <summary>
52+
/// Deletes the file.
53+
/// </summary>
54+
void Delete();
55+
56+
/// <summary>
57+
/// Opens the file using the specified options.
58+
/// </summary>
59+
/// <param name="fileMode">The file mode.</param>
60+
/// <param name="fileAccess">The file access.</param>
61+
/// <param name="fileShare">The file share.</param>
62+
/// <returns>A <see cref="Stream"/> to the file.</returns>
63+
Stream Open(FileMode fileMode, FileAccess fileAccess, FileShare fileShare);
64+
}
65+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/***************************************************************************************
2+
* Title: cake-build/cake
3+
* Author: Mattias Karlsson, Patrik Svensson, Gary Ewan Park
4+
* Date: 01.05.2020
5+
* Code version: commit: 0c46856abe976f6c122f05a5e82ccb854fd923c1
6+
* Availability: https://github.com/cake-build/cake/blob/develop/src/Cake.Core/IO/IFileSystem.cs
7+
*
8+
***************************************************************************************/
9+
10+
using LocalStack.AwsLocal.ProcessCore.IO;
11+
12+
namespace LocalStack.AwsLocal.Contracts
13+
{
14+
/// <summary>
15+
/// Represents a file system.
16+
/// </summary>
17+
public interface IFileSystem
18+
{
19+
/// <summary>
20+
/// Gets a <see cref="IFile"/> instance representing the specified path.
21+
/// </summary>
22+
/// <param name="path">The path.</param>
23+
/// <returns>A <see cref="IFile"/> instance representing the specified path.</returns>
24+
IFile GetFile(FilePath path);
25+
26+
/// <summary>
27+
/// Gets a <see cref="IDirectory"/> instance representing the specified path.
28+
/// </summary>
29+
/// <param name="path">The path.</param>
30+
/// <returns>A <see cref="IDirectory"/> instance representing the specified path.</returns>
31+
IDirectory GetDirectory(DirectoryPath path);
32+
}
33+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/***************************************************************************************
2+
* Title: cake-build/cake
3+
* Author: Mattias Karlsson, Gary Ewan Park
4+
* Date: 01.05.2020
5+
* Code version: commit: 0c46856abe976f6c122f05a5e82ccb854fd923c1
6+
* Availability: https://github.com/cake-build/cake/blob/develop/src/Cake.Core/IO/IFileSystemInfo.cs
7+
*
8+
***************************************************************************************/
9+
10+
using LocalStack.AwsLocal.ProcessCore.IO;
11+
12+
namespace LocalStack.AwsLocal.Contracts
13+
{
14+
/// <summary>
15+
/// Represents an entry in the file system
16+
/// </summary>
17+
public interface IFileSystemInfo
18+
{
19+
/// <summary>
20+
/// Gets the path to the entry.
21+
/// </summary>
22+
/// <value>The path.</value>
23+
Path? Path { get; }
24+
25+
/// <summary>
26+
/// Gets a value indicating whether this <see cref="IFileSystemInfo"/> exists.
27+
/// </summary>
28+
/// <value>
29+
/// <c>true</c> if the entry exists; otherwise, <c>false</c>.
30+
/// </value>
31+
bool Exists { get; }
32+
33+
/// <summary>
34+
/// Gets a value indicating whether this <see cref="IFileSystemInfo"/> is hidden.
35+
/// </summary>
36+
/// <value>
37+
/// <c>true</c> if the entry is hidden; otherwise, <c>false</c>.
38+
/// </value>
39+
bool Hidden { get; }
40+
}
41+
}

0 commit comments

Comments
 (0)