Skip to content

Commit

Permalink
Configuration history support (#4552)
Browse files Browse the repository at this point in the history
## Change
This change adds basic configuration history support. This is stored in
an SQLite database that is shared by all of the configuration code (so
both `winget.exe` and PowerShell modules will use the same database).

The database currently holds a representation of every configuration set
that has been applied (or at least attempted to be applied). Basic
fields are stored directly, while more complex data is stored by
serializing to YAML. While the database currently has only basic data,
it will eventually contain status information for configuration units
and other information used during synchronization of multiple
configuration users.

>[!Note]
>The dev build (based on `AICLI_DISABLE_TEST_HOOKS`) uses a different
location for history to prevent local tests runs from adversely
affecting the configuration usage experience for us.

### winget.exe interface changes
A new command is added under the `configure` top level command, `list`.
This shows details about items in the history.
```PowerShell
> wingetdev configure list
Identifier                             Name      First Applied           Origin
----------------------------------------------------------------------------------
{9C8386B3-6C06-46D8-A0B1-83F3C73D86CE} Test Name 2024-06-13 11:43:20.000 Test Path
{F9DB9D25-92F3-4FBC-AD34-BEEEE53F08A0} Test Name 2024-06-13 11:43:21.000 Test Path
{49B3FDDA-9ABA-475C-A9FE-296CE3D7ED48} Test Name 2024-06-13 11:43:21.000 Test Path
{436B929A-E717-4F3B-B16E-BD268D5916D6} Test Name 2024-06-13 11:43:21.000 Test Path
> wingetdev configure list -h "{9C8386B3-6C06-46D8-A0B1-83F3C73D86CE}"
Field         Value
----------------------------------------------------
Identifier    {9C8386B3-6C06-46D8-A0B1-83F3C73D86CE}
Name          Test Name
First Applied 2024-06-13 11:43:20.000
Origin        Test Origin
Path          Test Path
```
In addition to listing everything, one can provide the listed name of
the configuration or any unique starting sequence of the identifier to
select a single item to view. The selection options apply to all other
commands that take in the history item parameter, and completion has
been added for the parameter so that substrings of either identifier or
name can be expanded.

The `configure`, `configure show`, and `configure test` commands have
all had the history parameter added so that one can operate directly
against a historical set.

In addition to displaying information about history, the `configure
list` command also allows for removing items from history with
`--remove` and creating a YAML file for the set with `--output`.

### PowerShell interface changes
The existing cmdlet `Get-WinGetConfiguration` was updated to include
parameter set options `-All` to get all set from history and
`-InstanceIdentifier` to get a single one (these are exclusive with
`-File` and each other). `Remove-WinGetConfigurationHistory` was added
to allow removing sets from history, and
`ConvertTo-WinGetConfigurationYaml` was added to enable serializing a
set to a string.
  • Loading branch information
JohnMcPMS authored Jun 21, 2024
1 parent b433fa4 commit c074cbe
Show file tree
Hide file tree
Showing 102 changed files with 3,198 additions and 170 deletions.
3 changes: 2 additions & 1 deletion .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ CODEOWNERS
COINIT
COMGLB
commandline
compressapi
compressapi

Check warning on line 77 in .github/actions/spelling/expect.txt

View workflow job for this annotation

GitHub Actions / Check Spelling

entry has inconsistent line ending (unexpected-line-ending)
concurrencysal
contactsupport
contentfiles
contoso
Expand Down
4 changes: 3 additions & 1 deletion src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@
<ClInclude Include="Commands\COMCommand.h" />
<ClInclude Include="Commands\CompleteCommand.h" />
<ClInclude Include="Commands\ConfigureCommand.h" />
<ClInclude Include="Commands\ConfigureListCommand.h" />
<ClInclude Include="Commands\ConfigureShowCommand.h" />
<ClInclude Include="Commands\ConfigureTestCommand.h" />
<ClInclude Include="Commands\ConfigureValidateCommand.h" />
Expand Down Expand Up @@ -432,6 +433,7 @@
<ClCompile Include="CheckpointManager.cpp" />
<ClCompile Include="Commands\COMCommand.cpp" />
<ClCompile Include="Commands\ConfigureCommand.cpp" />
<ClCompile Include="Commands\ConfigureListCommand.cpp" />
<ClCompile Include="Commands\ConfigureShowCommand.cpp" />
<ClCompile Include="Commands\ConfigureTestCommand.cpp" />
<ClCompile Include="Commands\ConfigureValidateCommand.cpp" />
Expand Down Expand Up @@ -551,4 +553,4 @@
</Reference>
</ItemGroup>
</Target>
</Project>
</Project>
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@
<ClInclude Include="ConfigureExportCommand.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="Commands\ConfigureListCommand.h">
<Filter>Commands</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -478,6 +481,9 @@
<ClCompile Include="ConfigureExportCommand.cpp">
<Filter>Commands</Filter>
</ClCompile>
<ClCompile Include="Commands\ConfigureListCommand.cpp">
<Filter>Commands</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
Expand Down
6 changes: 5 additions & 1 deletion src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ namespace AppInstaller::CLI

// Configuration commands
case Execution::Args::Type::ConfigurationFile:
return { type, "file"_liv, 'f' };
return { type, "file"_liv, 'f', ArgTypeCategory::ConfigurationSetChoice, ArgTypeExclusiveSet::ConfigurationSetChoice };
case Execution::Args::Type::ConfigurationAcceptWarning:
return { type, "accept-configuration-agreements"_liv };
case Execution::Args::Type::ConfigurationEnable:
Expand All @@ -215,6 +215,10 @@ namespace AppInstaller::CLI
return { type, "module"_liv };
case Execution::Args::Type::ConfigurationExportResource:
return { type, "resource"_liv };
case Execution::Args::Type::ConfigurationHistoryItem:
return { type, "history"_liv, 'h', ArgTypeCategory::ConfigurationSetChoice, ArgTypeExclusiveSet::ConfigurationSetChoice };
case Execution::Args::Type::ConfigurationHistoryRemove:
return { type, "remove"_liv };

// Download command
case Execution::Args::Type::DownloadDirectory:
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCLICore/Argument.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ namespace AppInstaller::CLI
// E.g.: --dependency-source
// E.g.: --accept-source-agreements
ExtendedSource = 0x400,
// Arguments for selecting a configuration set (file or history).
ConfigurationSetChoice = 0x800,
};

DEFINE_ENUM_FLAG_OPERATORS(ArgTypeCategory);
Expand All @@ -87,6 +89,7 @@ namespace AppInstaller::CLI
StubType = 0x10,
Proxy = 0x20,
AllAndTargetVersion = 0x40,
ConfigurationSetChoice = 0x80,

// This must always be at the end
Max
Expand Down
16 changes: 11 additions & 5 deletions src/AppInstallerCLICore/Commands/ConfigureCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
#include "pch.h"
#include "ConfigureCommand.h"
#include "ConfigureListCommand.h"
#include "ConfigureShowCommand.h"
#include "ConfigureTestCommand.h"
#include "ConfigureValidateCommand.h"
Expand All @@ -24,6 +25,7 @@ namespace AppInstaller::CLI
{
return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>({
std::make_unique<ConfigureShowCommand>(FullName()),
std::make_unique<ConfigureListCommand>(FullName()),
std::make_unique<ConfigureTestCommand>(FullName()),
std::make_unique<ConfigureValidateCommand>(FullName()),
std::make_unique<ConfigureExportCommand>(FullName()),
Expand All @@ -35,6 +37,7 @@ namespace AppInstaller::CLI
return {
Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help },
Argument{ Execution::Args::Type::ConfigurationAcceptWarning, Resource::String::ConfigurationAcceptWarningArgumentDescription, ArgumentType::Flag },
Argument{ Execution::Args::Type::ConfigurationEnable, Resource::String::ConfigurationEnableMessage, ArgumentType::Flag, Argument::Visibility::Help },
Argument{ Execution::Args::Type::ConfigurationDisable, Resource::String::ConfigurationDisableMessage, ArgumentType::Flag, Argument::Visibility::Help },
Expand Down Expand Up @@ -94,12 +97,15 @@ namespace AppInstaller::CLI
}
else
{
if (!execArgs.Contains(Execution::Args::Type::ConfigurationFile))
{
throw CommandException(Resource::String::RequiredArgError("file"_liv));
}
Configuration::ValidateCommonArguments(execArgs, true);
}
}

Configuration::ValidateCommonArguments(execArgs);
void ConfigureCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const
{
if (argType == Execution::Args::Type::ConfigurationHistoryItem)
{
context << CompleteConfigurationHistoryItem;
}
}
}
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/ConfigureCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ namespace AppInstaller::CLI
protected:
void ExecuteInternal(Execution::Context& context) const override;
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
void Complete(Execution::Context& context, Execution::Args::Type argType) const override;
};
}
84 changes: 84 additions & 0 deletions src/AppInstallerCLICore/Commands/ConfigureListCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "ConfigureListCommand.h"
#include "Workflows/ConfigurationFlow.h"
#include "ConfigurationCommon.h"

using namespace AppInstaller::CLI::Workflow;

namespace AppInstaller::CLI
{
std::vector<Argument> ConfigureListCommand::GetArguments() const
{
return {
Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard },
Argument{ Execution::Args::Type::OutputFile, Resource::String::OutputFileArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help },
Argument{ Execution::Args::Type::ConfigurationHistoryRemove, Resource::String::ConfigurationHistoryRemoveArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help },
};
}

Resource::LocString ConfigureListCommand::ShortDescription() const
{
return { Resource::String::ConfigureListCommandShortDescription };
}

Resource::LocString ConfigureListCommand::LongDescription() const
{
return { Resource::String::ConfigureListCommandLongDescription };
}

Utility::LocIndView ConfigureListCommand::HelpLink() const
{
return "https://aka.ms/winget-command-configure#list"_liv;
}

void ConfigureListCommand::ExecuteInternal(Execution::Context& context) const
{
context <<
VerifyIsFullPackage <<
CreateConfigurationProcessorWithoutFactory <<
GetConfigurationSetHistory;

if (context.Args.Contains(Execution::Args::Type::ConfigurationHistoryItem))
{
context << SelectSetFromHistory;

if (context.Args.Contains(Execution::Args::Type::OutputFile))
{
context << SerializeConfigurationSetHistory;
}

if (context.Args.Contains(Execution::Args::Type::ConfigurationHistoryRemove))
{
context << RemoveConfigurationSetHistory;
}
else
{
context << ShowSingleConfigurationSetHistory;
}
}
else
{
context << ShowConfigurationSetHistory;
}
}

void ConfigureListCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
{
if ((execArgs.Contains(Execution::Args::Type::ConfigurationHistoryRemove) ||
execArgs.Contains(Execution::Args::Type::OutputFile)) &&
!execArgs.Contains(Execution::Args::Type::ConfigurationHistoryItem))
{
throw CommandException(Resource::String::RequiredArgError(ArgumentCommon::ForType(Execution::Args::Type::ConfigurationHistoryItem).Name));
}
}

void ConfigureListCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const
{
if (argType == Execution::Args::Type::ConfigurationHistoryItem)
{
context << CompleteConfigurationHistoryItem;
}
}
}
24 changes: 24 additions & 0 deletions src/AppInstallerCLICore/Commands/ConfigureListCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "Command.h"

namespace AppInstaller::CLI
{
struct ConfigureListCommand final : public Command
{
ConfigureListCommand(std::string_view parent) : Command("list", { "ls" }, parent) {}

std::vector<Argument> GetArguments() const override;

Resource::LocString ShortDescription() const override;
Resource::LocString LongDescription() const override;

Utility::LocIndView HelpLink() const override;

protected:
void ExecuteInternal(Execution::Context& context) const override;
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
void Complete(Execution::Context& context, Execution::Args::Type argType) const override;
};
}
13 changes: 11 additions & 2 deletions src/AppInstallerCLICore/Commands/ConfigureShowCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ namespace AppInstaller::CLI
{
return {
// Required for now, make exclusive when history implemented
Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional, true },
Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help },
};
}

Expand Down Expand Up @@ -45,6 +46,14 @@ namespace AppInstaller::CLI

void ConfigureShowCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
{
Configuration::ValidateCommonArguments(execArgs);
Configuration::ValidateCommonArguments(execArgs, true);
}

void ConfigureShowCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const
{
if (argType == Execution::Args::Type::ConfigurationHistoryItem)
{
context << CompleteConfigurationHistoryItem;
}
}
}
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/ConfigureShowCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ namespace AppInstaller::CLI
protected:
void ExecuteInternal(Execution::Context& context) const override;
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
void Complete(Execution::Context& context, Execution::Args::Type argType) const override;
};
}
13 changes: 11 additions & 2 deletions src/AppInstallerCLICore/Commands/ConfigureTestCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ namespace AppInstaller::CLI
std::vector<Argument> ConfigureTestCommand::GetArguments() const
{
return {
Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional, true },
Argument{ Execution::Args::Type::ConfigurationFile, Resource::String::ConfigurationFileArgumentDescription, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationModulePath, Resource::String::ConfigurationModulePath, ArgumentType::Positional },
Argument{ Execution::Args::Type::ConfigurationHistoryItem, Resource::String::ConfigurationHistoryItemArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help },
Argument{ Execution::Args::Type::ConfigurationAcceptWarning, Resource::String::ConfigurationAcceptWarningArgumentDescription, ArgumentType::Flag },
};
}
Expand Down Expand Up @@ -48,6 +49,14 @@ namespace AppInstaller::CLI

void ConfigureTestCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
{
Configuration::ValidateCommonArguments(execArgs);
Configuration::ValidateCommonArguments(execArgs, true);
}

void ConfigureTestCommand::Complete(Execution::Context& context, Execution::Args::Type argType) const
{
if (argType == Execution::Args::Type::ConfigurationHistoryItem)
{
context << CompleteConfigurationHistoryItem;
}
}
}
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/ConfigureTestCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ namespace AppInstaller::CLI
protected:
void ExecuteInternal(Execution::Context& context) const override;
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
void Complete(Execution::Context& context, Execution::Args::Type argType) const override;
};
}
8 changes: 7 additions & 1 deletion src/AppInstallerCLICore/ConfigurationCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace AppInstaller::CLI

namespace Configuration
{
void ValidateCommonArguments(Execution::Args& execArgs)
void ValidateCommonArguments(Execution::Args& execArgs, bool requireConfigurationSetChoice)
{
auto modulePath = GetModulePathInfo(execArgs);

Expand All @@ -71,6 +71,12 @@ namespace AppInstaller::CLI
throw CommandException(Resource::String::ConfigurationModulePathArgError);
}
}

if (requireConfigurationSetChoice &&
!WI_IsFlagSet(Argument::GetCategoriesPresent(execArgs), ArgTypeCategory::ConfigurationSetChoice))
{
throw CommandException(Resource::String::RequiredArgError("file"_liv));
}
}

void SetModulePath(Execution::Context& context, IConfigurationSetProcessorFactory const& factory)
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/ConfigurationCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace AppInstaller::CLI
namespace Configuration
{
// Validates common arguments between configuration commands.
void ValidateCommonArguments(Execution::Args& execArgs);
void ValidateCommonArguments(Execution::Args& execArgs, bool requireConfigurationSetChoice = false);

// Sets the module path to install modules in the set processor.
void SetModulePath(Execution::Context& context, winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory const& factory);
Expand Down
18 changes: 18 additions & 0 deletions src/AppInstallerCLICore/ConfigurationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace AppInstaller::CLI::Execution
{
ConfigurationProcessor Processor = nullptr;
ConfigurationSet Set = nullptr;
std::vector<ConfigurationSet> History;
};
}

Expand Down Expand Up @@ -65,4 +66,21 @@ namespace AppInstaller::CLI::Execution
{
m_data->Set = std::move(value);
}

std::vector<ConfigurationSet>& ConfigurationContext::History()
{
return m_data->History;
}

const std::vector<ConfigurationSet>& ConfigurationContext::History() const
{
return m_data->History;
}

void ConfigurationContext::History(const winrt::Windows::Foundation::Collections::IVector<ConfigurationSet>& value)
{
std::vector<ConfigurationSet> history{ value.Size() };
value.GetMany(0, history);
m_data->History = std::move(history);
}
}
Loading

0 comments on commit c074cbe

Please sign in to comment.