Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete command, install only database #8

Merged
merged 12 commits into from
Sep 30, 2024
Merged
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"args": ["i"],
"cwd": "${workspaceFolder}",
"console": "externalTerminal",
"stopAtEntry": false
"stopAtEntry": false,
"brokeredServicePipeName": "undefined",
}
]
}
20 changes: 11 additions & 9 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<Project>
<PropertyGroup>
<Company>Kentico</Company>
<Authors>Eric Dugre</Authors>
<VersionPrefix>4.0.0</VersionPrefix>
<Product>Xperience by Kentico</Product>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<Company>Kentico</Company>
<Authors>Eric Dugre</Authors>
<VersionPrefix>5.0.0</VersionPrefix>
<Product>Xperience by Kentico</Product>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RepositoryUrl>https://github.com/Kentico/xperience-by-kentico-manager</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>

<ItemGroup Label="StaticCodeAnalysis">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.41.0.50478">
Expand All @@ -19,4 +21,4 @@
<PropertyGroup>
<TimestampServerUrl>http://timestamp.digicert.com</TimestampServerUrl>
</PropertyGroup>
</Project>
</Project>
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ The following commands can be executed using the `xman` tool name:
- `?`, `help`
- [`i`, `install`](#installing-a-new-project)
- [`u`, `update`](#updating-a-project-version)
- [`d`, `delete`](#deleting-a-project)
- [`m`, `macros`](#re-signing-macros)
- [`b`, `build`](#building-projects)
- [`g`, `generate`](#generating-code-for-object-types)
Expand Down Expand Up @@ -103,6 +104,8 @@ The installation wizard will automatically generate an administrator password fo
xman install
```

Installing a new project automatically includes a database as well. If you want to _only_ install a database and not the project files, use the __db__ parameter: `xman install db`.

### Updating a project version

1. (optional) Select a profile with the [`profile`](#managing-profiles) command
Expand All @@ -112,6 +115,17 @@ The installation wizard will automatically generate an administrator password fo
xman update
```

### Deleting a project

> :warning: The `delete` command will drop the database and delete the files. Use with caution!

1. (optional) Select a profile with the [`profile`](#managing-profiles) command
1. Run the `delete` command from the directory containing the [configuration file](#configuration-file):

```bash
xman delete
```

### Modifying appsettings.json

This tool can assist with changing the _CMSConnectionString_, supported [configuration keys](https://docs.xperience.io/xp/developers-and-admins/configuration/reference-configuration-keys), and the [headless API](https://docs.xperience.io/xp/developers-and-admins/configuration/headless-channel-management#Headlesschannelmanagement-ConfiguretheheadlessAPI).
Expand Down
Binary file added img/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/Commands/Base/AbstractCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public virtual Task PreExecute(ToolProfile? profile, string? action)


/// <summary>
/// A handler which can be assigned to <see cref="Process.ErrorDataReceived"/> to handler errors.
/// A handler which can be assigned to <see cref="Process.ErrorDataReceived"/> to handle errors.
/// </summary>
protected void ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Expand All @@ -80,7 +80,7 @@ protected void LogError(string message, Process? process = null)
}


protected void PrintCurrentProfile(ToolProfile? profile)
protected static void PrintCurrentProfile(ToolProfile? profile)
{
AnsiConsole.Write(new Rule("Current profile:") { Justification = Justify.Left });
AnsiConsole.MarkupLineInterpolated($"Name: [{Constants.EMPHASIS_COLOR}]{profile?.ProjectName ?? "None"}[/]");
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class BuildCommand : AbstractCommand
public override IEnumerable<string> Keywords => ["b", "build"];


public override IEnumerable<string> Parameters => Enumerable.Empty<string>();
public override IEnumerable<string> Parameters => [];


public override string Description => "Builds a project";
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/CodeGenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class CodeGenerateCommand : AbstractCommand
public override IEnumerable<string> Keywords => ["g", "generate"];


public override IEnumerable<string> Parameters => Enumerable.Empty<string>();
public override IEnumerable<string> Parameters => [];


public override string Description => "Generates code files for Xperience objects";
Expand Down
145 changes: 145 additions & 0 deletions src/Commands/DeleteCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using Spectre.Console;

using Xperience.Manager.Configuration;
using Xperience.Manager.Options;
using Xperience.Manager.Services;

namespace Xperience.Manager.Commands
{
/// <summary>
/// A command which deletes an Xperience by Kentico project.
/// </summary>
public class DeleteCommand : AbstractCommand
{
private bool deleteConfirmed;
private readonly IShellRunner shellRunner;
private readonly IScriptBuilder scriptBuilder;
private readonly IConfigManager configManager;
private readonly IAppSettingsManager appSettingsManager;


public override IEnumerable<string> Keywords => ["d", "delete"];


public override IEnumerable<string> Parameters => [];


public override string Description => "Deletes a project and its database";


public override bool RequiresProfile => true;


/// <summary>
/// Do not use. Workaround for circular dependency in <see cref="HelpCommand"/> when commands are injected
/// into the constuctor.
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
internal DeleteCommand()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
}


public DeleteCommand(IShellRunner shellRunner, IScriptBuilder scriptBuilder, IConfigManager configManager, IAppSettingsManager appSettingsManager)
{
this.shellRunner = shellRunner;
this.scriptBuilder = scriptBuilder;
this.configManager = configManager;
this.appSettingsManager = appSettingsManager;
}


public override async Task Execute(ToolProfile? profile, string? action)
{
deleteConfirmed = AnsiConsole.Confirm($"This will [{Constants.ERROR_COLOR}]delete[/] the current profile's physical folder and database!\nDo you want to continue?", false);
if (!deleteConfirmed)
{
return;
}

AnsiConsole.WriteLine();
await DropDatabase(profile);
await UninstallFiles(profile);
if (!StopProcessing)
{
await configManager.RemoveProfile(profile);
}
}


public override async Task PostExecute(ToolProfile? profile, string? action)
{
if (!deleteConfirmed)
{
AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Delete cancelled[/]\n");
}
else if (!Errors.Any())
{
AnsiConsole.MarkupLineInterpolated($"[{Constants.SUCCESS_COLOR}]Delete complete![/]\n");
}

await base.PostExecute(profile, action);
}


private async Task DropDatabase(ToolProfile? profile)
{
if (StopProcessing)
{
return;
}

AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Deleting database...[/]");

string? connString = await appSettingsManager.GetConnectionString(profile, "CMSConnectionString");
if (connString is null)
{
LogError("Couldn't load connection string.");
return;
}

// Find "Initial Catalog" in connection string
IEnumerable<string> parts = connString.Split(';').ToList();
string? initialCatalogPart = parts.FirstOrDefault(p => p.ToLower().StartsWith("initial catalog"));
if (initialCatalogPart is null)
{
LogError("Couldn't find database name.");
return;
}

// Remove "Initial Catalog" from connection string, or trying to delete will throw "in use" error
parts = parts.Where(p => !p.Equals(initialCatalogPart, StringComparison.OrdinalIgnoreCase));
connString = string.Join(';', parts);
string databaseName = initialCatalogPart.Split('=')[1].Trim();

var options = new RunSqlOptions()
{
SqlQuery = $"DROP DATABASE {databaseName}",
ConnString = connString
};
string dbScript = scriptBuilder.SetScript(ScriptType.ExecuteSql).WithPlaceholders(options).Build();
await shellRunner.Execute(new(dbScript)
{
ErrorHandler = ErrorDataReceived
}).WaitForExitAsync();
}


private async Task UninstallFiles(ToolProfile? profile)
{
if (StopProcessing)
{
return;
}

AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Deleting local files...[/]");

string uninstallScript = scriptBuilder.SetScript(ScriptType.DeleteDirectory).WithPlaceholders(profile).Build();
await shellRunner.Execute(new(uninstallScript)
{
ErrorHandler = ErrorDataReceived
}).WaitForExitAsync();
}
}
}
5 changes: 3 additions & 2 deletions src/Commands/HelpCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class HelpCommand : AbstractCommand
[
new ProfileCommand(),
new InstallCommand(),
new DeleteCommand(),
new UpdateCommand(),
new ContinuousIntegrationCommand(),
new ContinuousDeploymentCommand(),
Expand All @@ -32,7 +33,7 @@ public class HelpCommand : AbstractCommand
public override IEnumerable<string> Keywords => ["?", "help"];


public override IEnumerable<string> Parameters => Enumerable.Empty<string>();
public override IEnumerable<string> Parameters => [];


public override string Description => "Displays the help menu (this screen)";
Expand All @@ -51,7 +52,7 @@ public override async Task Execute(ToolProfile? profile, string? action)
AnsiConsole.WriteLine($" v{v.Major}.{v.Minor}.{v.Build}");
}

AnsiConsole.MarkupInterpolated($" [{Constants.EMPHASIS_COLOR}]https://github.com/kentico/xperience-manager[/]\n");
AnsiConsole.MarkupInterpolated($" [{Constants.EMPHASIS_COLOR}]https://github.com/Kentico/xperience-by-kentico-manager[/]\n");

var table = new Table()
.AddColumn("Command")
Expand Down
Loading
Loading