Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,9 @@ If not specified the file will be generated inside the default 'TestResults' dir
<data name="ProjectConvertDryRun" xml:space="preserve">
<value>Determines changes without actually modifying the file system</value>
</data>
<data name="ProjectConvertDeleteSource" xml:space="preserve">
<value>Delete the source file after conversion</value>
Comment thread
jjonescz marked this conversation as resolved.
Outdated
</data>
<data name="ProjectManifest" xml:space="preserve">
<value>PROJECT_MANIFEST</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ internal sealed class ProjectConvertCommandDefinition : Command
Arity = ArgumentArity.Zero,
};

public readonly Option<bool> DeleteSourceOption = new("--delete-source")
{
Description = CommandDefinitionStrings.ProjectConvertDeleteSource,
Arity = ArgumentArity.Zero,
};

public ProjectConvertCommandDefinition()
: base("convert", CommandDefinitionStrings.ProjectConvertAppFullName)
{
Expand All @@ -42,5 +48,6 @@ public ProjectConvertCommandDefinition()
Options.Add(ForceOption);
Options.Add(InteractiveOption);
Options.Add(DryRunOption);
Options.Add(DeleteSourceOption);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions src/Cli/dotnet/Commands/CliCommandStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,24 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
<value>Dry run: would create file: {0}</value>
<comment>{0} is the file full path.</comment>
</data>
<data name="ProjectConvertAskDeleteSource" xml:space="preserve">
<value>Delete the source file '{0}' after conversion?</value>
<comment>{0} is the source file path.</comment>
</data>
<data name="ProjectConvertDeletedSourceFile" xml:space="preserve">
<value>Deleted source file: {0}</value>
<comment>{0} is the source file path.</comment>
</data>
<data name="ProjectConvertWouldDeleteSourceFile" xml:space="preserve">
<value>Dry run: would delete source file: {0}</value>
<comment>{0} is the source file path.</comment>
</data>
<data name="ProjectConvertDeleteSourceChoiceYes" xml:space="preserve">
<value>Yes - delete the source file</value>
</data>
<data name="ProjectConvertDeleteSourceChoiceNo" xml:space="preserve">
<value>No - keep the source file</value>
</data>
<data name="ProjectsHeader" xml:space="preserve">
<value>Project(s)</value>
</data>
Expand Down
115 changes: 93 additions & 22 deletions src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
using Microsoft.DotNet.ProjectTools;
using Spectre.Console;
Comment thread
jjonescz marked this conversation as resolved.

namespace Microsoft.DotNet.Cli.Commands.Project.Convert;

Expand All @@ -19,6 +20,7 @@ internal sealed class ProjectConvertCommand : CommandBase<ProjectConvertCommandD
private readonly string? _outputDirectory;
private readonly bool _force;
private readonly bool _dryRun;
private readonly bool _deleteSource;

public ProjectConvertCommand(ParseResult parseResult)
: base(parseResult)
Expand All @@ -27,6 +29,7 @@ public ProjectConvertCommand(ParseResult parseResult)
_outputDirectory = parseResult.GetValue(Definition.OutputOption)?.FullName;
_force = parseResult.GetValue(Definition.ForceOption);
_dryRun = parseResult.GetValue(Definition.DryRunOption);
_deleteSource = parseResult.GetValue(Definition.DeleteSourceOption);
}

public override int Execute()
Expand Down Expand Up @@ -102,6 +105,13 @@ public override int Execute()
CopyFile(item.FullPath, targetItemFullPath);
}

// Handle deletion of source file if requested.
bool shouldDelete = _deleteSource || TryAskForDeleteSource(file);
Comment thread
jjonescz marked this conversation as resolved.
if (shouldDelete)
{
DeleteFile(file);
}

return 0;

void CreateDirectory(string path)
Expand Down Expand Up @@ -131,6 +141,19 @@ void CopyFile(string source, string target)
}
}

void DeleteFile(string path)
{
if (_dryRun)
{
Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertWouldDeleteSourceFile, path);
}
else
{
File.Delete(path);
Reporter.Output.WriteLine(CliCommandStrings.ProjectConvertDeletedSourceFile, path);
}
}

IEnumerable<(string FullPath, string RelativePath)> FindIncludedItems()
{
string entryPointFileDirectory = PathUtilities.EnsureTrailingSlash(Path.GetDirectoryName(file)!);
Expand Down Expand Up @@ -249,37 +272,85 @@ private string DetermineOutputDirectory(string file)
{
string defaultValue = Path.ChangeExtension(file, null);
string defaultValueRelative = Path.GetRelativePath(relativeTo: Environment.CurrentDirectory, defaultValue);
string targetDirectory = _outputDirectory
?? TryAskForOutputDirectory(defaultValueRelative)
?? defaultValue;
if (Directory.Exists(targetDirectory))
Comment thread
jjonescz marked this conversation as resolved.

Comment thread
jjonescz marked this conversation as resolved.
Outdated
// If output directory is provided via CLI, use it directly
if (_outputDirectory != null)
{
if (Directory.Exists(_outputDirectory))
{
throw new GracefulException(CliCommandStrings.DirectoryAlreadyExists, _outputDirectory);
}
return _outputDirectory;
}

// Try to ask for output directory in interactive mode
string? targetDirectory = TryAskForOutputDirectory(defaultValueRelative, defaultValue);

// Use the result from interactive prompt or fall back to default
targetDirectory ??= defaultValue;

// Final validation (only needed if not in interactive mode, as interactive mode validates already)
if (!_parseResult.GetValue<bool>(CommonOptions.InteractiveOptionName) && Directory.Exists(targetDirectory))
{
throw new GracefulException(CliCommandStrings.DirectoryAlreadyExists, targetDirectory);
}

return targetDirectory;
}

private string? TryAskForOutputDirectory(string defaultValueRelative)
private string? TryAskForOutputDirectory(string defaultValueRelative, string defaultValue)
Comment thread
jjonescz marked this conversation as resolved.
Outdated
{
return InteractiveConsole.Ask<string?>(
Comment thread
jjonescz marked this conversation as resolved.
string.Format(CliCommandStrings.ProjectConvertAskForOutputDirectory, defaultValueRelative),
_parseResult,
(path, out result, [NotNullWhen(returnValue: false)] out error) =>
{
if (Directory.Exists(path))
if (!_parseResult.GetValue<bool>(CommonOptions.InteractiveOptionName))
Comment thread
jjonescz marked this conversation as resolved.
Outdated
{
return null;
}

try
{
var prompt = new TextPrompt<string>(string.Format(CliCommandStrings.ProjectConvertAskForOutputDirectory, defaultValueRelative))
.AllowEmpty()
.Validate(path =>
{
result = null;
error = string.Format(CliCommandStrings.DirectoryAlreadyExists, Path.GetFullPath(path));
return false;
}
// Determine the actual path to validate
string pathToValidate = string.IsNullOrWhiteSpace(path) ? defaultValue : Path.GetFullPath(path);

if (Directory.Exists(pathToValidate))
{
return ValidationResult.Error(string.Format(CliCommandStrings.DirectoryAlreadyExists, pathToValidate));
}

return ValidationResult.Success();
});

var answer = Spectre.Console.AnsiConsole.Prompt(prompt);
return string.IsNullOrWhiteSpace(answer) ? null : Path.GetFullPath(answer);
}
catch (Exception)
{
return null;
}
}

result = path is null ? null : Path.GetFullPath(path);
error = null;
return true;
},
out var result)
? result
: null;
private bool TryAskForDeleteSource(string sourceFile)
Comment thread
jjonescz marked this conversation as resolved.
{
if (!_parseResult.GetValue<bool>(CommonOptions.InteractiveOptionName))
{
return false;
}

try
{
var choice = Spectre.Console.AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title($"[cyan]{Markup.Escape(string.Format(CliCommandStrings.ProjectConvertAskDeleteSource, Path.GetFileName(sourceFile)))}[/]")
.AddChoices([CliCommandStrings.ProjectConvertDeleteSourceChoiceYes, CliCommandStrings.ProjectConvertDeleteSourceChoiceNo])
);

return choice == CliCommandStrings.ProjectConvertDeleteSourceChoiceYes;
}
catch (Exception)
{
return false;
}
}
}
Loading
Loading