Skip to content

Commit

Permalink
Merge from master
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnMcPMS committed Oct 12, 2023
2 parents 2082bd9 + 85951ae commit 4faaf0c
Show file tree
Hide file tree
Showing 71 changed files with 2,159 additions and 699 deletions.
2 changes: 2 additions & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ deserializing
dest
devblogs
differentpath
DISMAPI
DIRECTONLY
distro
dll
Expand Down Expand Up @@ -232,6 +233,7 @@ https
HWND
Hyperlink
IAppx
ICheckpoint
IConfiguration
icu
IDX
Expand Down
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ seof
servercert
servercertificate
setmetadatabymanifestid
SETTINGCHANGE
SETTINGMAPPING
SHCONTF
SHGDN
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@
<ItemGroup>
<ClInclude Include="Argument.h" />
<ClInclude Include="ChannelStreams.h" />
<ClInclude Include="CheckpointManager.h" />
<ClInclude Include="Command.h" />
<ClInclude Include="Commands\COMCommand.h" />
<ClInclude Include="Commands\CompleteCommand.h" />
Expand All @@ -371,6 +372,7 @@
<ClInclude Include="Commands\ShowCommand.h" />
<ClInclude Include="Commands\InstallCommand.h" />
<ClInclude Include="Commands\RootCommand.h" />
<ClInclude Include="Commands\ResumeCommand.h" />
<ClInclude Include="Commands\SourceCommand.h" />
<ClInclude Include="Commands\TestCommand.h" />
<ClInclude Include="Commands\UninstallCommand.h" />
Expand Down Expand Up @@ -414,6 +416,7 @@
<ClInclude Include="Workflows\ShellExecuteInstallerHandler.h" />
<ClInclude Include="Workflows\InstallFlow.h" />
<ClInclude Include="Workflows\ManifestComparator.h" />
<ClInclude Include="Workflows\ResumeFlow.h" />
<ClInclude Include="Workflows\ShowFlow.h" />
<ClInclude Include="Workflows\SourceFlow.h" />
<ClInclude Include="Workflows\UninstallFlow.h" />
Expand All @@ -422,6 +425,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="COMContext.cpp" />
<ClCompile Include="CheckpointManager.cpp" />
<ClCompile Include="Commands\COMCommand.cpp" />
<ClCompile Include="Commands\ConfigureCommand.cpp" />
<ClCompile Include="Commands\ConfigureShowCommand.cpp" />
Expand Down Expand Up @@ -453,6 +457,7 @@
<ClCompile Include="Commands\ShowCommand.cpp" />
<ClCompile Include="Commands\InstallCommand.cpp" />
<ClCompile Include="Commands\RootCommand.cpp" />
<ClCompile Include="Commands\ResumeCommand.cpp" />
<ClCompile Include="Commands\SourceCommand.cpp" />
<ClCompile Include="Commands\UninstallCommand.cpp" />
<ClCompile Include="Commands\UpgradeCommand.cpp" />
Expand Down Expand Up @@ -484,6 +489,7 @@
<ClCompile Include="Workflows\ShellExecuteInstallerHandler.cpp" />
<ClCompile Include="Workflows\InstallFlow.cpp" />
<ClCompile Include="Workflows\ManifestComparator.cpp" />
<ClCompile Include="Workflows\ResumeFlow.cpp" />
<ClCompile Include="Workflows\ShowFlow.cpp" />
<ClCompile Include="Workflows\SourceFlow.cpp" />
<ClCompile Include="Workflows\UninstallFlow.cpp" />
Expand Down
22 changes: 20 additions & 2 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@
<ClInclude Include="Commands\ErrorCommand.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="Commands\ResumeCommand.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="Workflows\ResumeFlow.h">
<Filter>Workflows</Filter>
</ClInclude>
<ClInclude Include="CheckpointManager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -424,10 +433,19 @@
<ClCompile Include="Commands\TestCommand.cpp">
<Filter>Commands</Filter>
</ClCompile>
<ClCompile Include="Commands\DownloadCommand.cpp">
<ClCompile Include="ConfigurationCommon.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ConfigurationCommon.cpp">
<ClInclude Include="Commands\DownloadCommand.cpp">
<Filter>Commands</Filter>
</ClInclude>
<ClCompile Include="Commands\ResumeCommand.cpp">
<Filter>Commands</Filter>
</ClCompile>
<ClCompile Include="Workflows\ResumeFlow.cpp">
<Filter>Workflows</Filter>
</ClCompile>
<ClCompile Include="CheckpointManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Commands\ErrorCommand.cpp">
Expand Down
16 changes: 11 additions & 5 deletions src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace AppInstaller::CLI
case Execution::Args::Type::Command:
return { type, "command"_liv, "cmd"_liv, ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery };
case Execution::Args::Type::Source:
return { type, "source"_liv, 's', ArgTypeCategory::Source };
return { type, "source"_liv, 's', ArgTypeCategory::QuerySource };
case Execution::Args::Type::Count:
return { type, "count"_liv, 'n', ArgTypeCategory::PackageQuery | ArgTypeCategory::SinglePackageQuery };
case Execution::Args::Type::Exact:
Expand Down Expand Up @@ -180,6 +180,10 @@ namespace AppInstaller::CLI
case Execution::Args::Type::ErrorInput:
return { type, "input"_liv, ArgTypeCategory::None };

// Resume command
case Execution::Args::Type::ResumeId:
return { type, "resume-id"_liv, 'g', ArgTypeCategory::None };

// Configuration commands
case Execution::Args::Type::ConfigurationFile:
return { type, "file"_liv, 'f' };
Expand Down Expand Up @@ -219,11 +223,11 @@ namespace AppInstaller::CLI
return { type, "force"_liv, ArgTypeCategory::CopyFlagToSubContext };

case Execution::Args::Type::DependencySource:
return { type, "dependency-source"_liv, ArgTypeCategory::Source };
return { type, "dependency-source"_liv, ArgTypeCategory::ExtendedSource };
case Execution::Args::Type::CustomHeader:
return { type, "header"_liv, ArgTypeCategory::Source };
return { type, "header"_liv, ArgTypeCategory::QuerySource };
case Execution::Args::Type::AcceptSourceAgreements:
return { type, "accept-source-agreements"_liv, ArgTypeCategory::Source };
return { type, "accept-source-agreements"_liv, ArgTypeCategory::ExtendedSource };

case Execution::Args::Type::ToolVersion:
return { type, "version"_liv, 'v' };
Expand Down Expand Up @@ -355,6 +359,8 @@ namespace AppInstaller::CLI
return Argument{ type, Resource::String::DownloadDirectoryArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false };
case Args::Type::InstallerType:
return Argument{ type, Resource::String::InstallerTypeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false };
case Args::Type::ResumeId:
return Argument{ type, Resource::String::ResumeIdArgumentDescription, ArgumentType::Standard, true };
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down Expand Up @@ -459,7 +465,7 @@ namespace AppInstaller::CLI

// If a manifest is specified, we cannot also have arguments for searching
if (WI_IsFlagSet(categories, ArgTypeCategory::Manifest) &&
WI_IsAnyFlagSet(categories, ArgTypeCategory::PackageQuery | ArgTypeCategory::Source))
WI_IsAnyFlagSet(categories, ArgTypeCategory::PackageQuery | ArgTypeCategory::QuerySource))
{
throw CommandException(Resource::String::BothManifestAndSearchQueryProvided);
}
Expand Down
10 changes: 7 additions & 3 deletions src/AppInstallerCLICore/Argument.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,19 @@ namespace AppInstaller::CLI
// Arguments for installer or uninstaller behavior, which do not work for multiple packages.
// E.g.: --override
SingleInstallerBehavior = 0x20,
// Arguments for selecting or interacting with the source.
// E.g.: --accept-source-agreements
Source = 0x40,
// Arguments for selecting or interacting with the source used for initial querying
// E.g.: --header
QuerySource = 0x40,
// Arguments that only make sense when talking about multiple packages
MultiplePackages = 0x80,
// Flag arguments that should be copied over when creating a sub-context
CopyFlagToSubContext = 0x100,
// Arguments with associated values that should be copied over when creating a sub-context
CopyValueToSubContext = 0x200,
// Arguments for selecting or interacting with dependencies or setting specific source behaviors
// E.g.: --dependency-source
// E.g.: --accept-source-agreements
ExtendedSource = 0x400,
};

DEFINE_ENUM_FLAG_OPERATORS(ArgTypeCategory);
Expand Down
166 changes: 166 additions & 0 deletions src/AppInstallerCLICore/CheckpointManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "CheckpointManager.h"
#include "Command.h"
#include "ExecutionContextData.h"
#include <AppInstallerRuntime.h>

namespace AppInstaller::Checkpoints
{
using namespace AppInstaller::CLI;
using namespace AppInstaller::Repository::Microsoft;
using namespace AppInstaller::Repository::SQLite;

// This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata.
constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv;
constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv;

std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory)
{
const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / resumeId;

if (createCheckpointDirectory)
{
if (!std::filesystem::exists(checkpointsDirectory))
{
AICLI_LOG(Repo, Info, << "Creating checkpoint database directory: " << checkpointsDirectory);
std::filesystem::create_directories(checkpointsDirectory);
}
else
{
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(checkpointsDirectory));
}
}

auto recordPath = checkpointsDirectory / s_CheckpointsFileName;
return recordPath;
}

CheckpointManager::CheckpointManager()
{
GUID resumeId;
std::ignore = CoCreateGuid(&resumeId);
m_resumeId = Utility::ConvertGuidToString(resumeId);
const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId, true);
m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string());
}

CheckpointManager::CheckpointManager(const std::string& resumeId)
{
m_resumeId = resumeId;
const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId);
m_checkpointDatabase = CheckpointDatabase::Open(checkpointDatabasePath.u8string());
}

void CheckpointManager::CreateAutomaticCheckpoint(CLI::Execution::Context& context)
{
CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(s_AutomaticCheckpoint);
Checkpoint<AutomaticCheckpointData> automaticCheckpoint{ m_checkpointDatabase, startCheckpointId };

automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, {}, AppInstaller::Runtime::GetClientVersion());

const auto& executingCommand = context.GetExecutingCommand();
if (executingCommand != nullptr)
{
automaticCheckpoint.Set(AutomaticCheckpointData::Command, {}, std::string{ executingCommand->FullName() });
}

const auto& argTypes = context.Args.GetTypes();
for (auto type : argTypes)
{
const auto& argument = std::to_string(static_cast<int>(type));
auto argumentType = Argument::ForType(type).Type();

if (argumentType == ArgumentType::Flag)
{
automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {});
}
else
{
const auto& values = *context.Args.GetArgs(type);
automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values);
}
}
}

void LoadCommandArgsFromAutomaticCheckpoint(CLI::Execution::Context& context, Checkpoint<AutomaticCheckpointData>& automaticCheckpoint)
{
for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments))
{
// Command arguments are represented as integer strings in the checkpoint record.
Execution::Args::Type type = static_cast<Execution::Args::Type>(std::stoi(fieldName));
auto argumentType = Argument::ForType(type).Type();
if (argumentType == ArgumentType::Flag)
{
context.Args.AddArg(type);
}
else
{
const auto& values = automaticCheckpoint.GetMany(AutomaticCheckpointData::Arguments, fieldName);
for (const auto& value : values)
{
context.Args.AddArg(type, value);
}
}
}
}

std::optional<Checkpoint<AutomaticCheckpointData>> CheckpointManager::GetAutomaticCheckpoint()
{
const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds();
if (checkpointIds.empty())
{
return {};
}

CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back();
return Checkpoint<AutomaticCheckpointData>{ m_checkpointDatabase, automaticCheckpointId };
}

Checkpoint<CLI::Execution::Data> CheckpointManager::CreateCheckpoint(std::string_view checkpointName)
{
CheckpointDatabase::IdType checkpointId = m_checkpointDatabase->AddCheckpoint(checkpointName);
Checkpoint<CLI::Execution::Data> checkpoint{ m_checkpointDatabase, checkpointId };
return checkpoint;
}

std::vector<Checkpoint<CLI::Execution::Data>> CheckpointManager::GetCheckpoints()
{
auto checkpointIds = m_checkpointDatabase->GetCheckpointIds();
if (checkpointIds.empty())
{
return {};
}

// Remove the last checkpoint (automatic)
checkpointIds.pop_back();

std::vector<Checkpoint<CLI::Execution::Data>> checkpoints;
for (const auto& checkpointId : checkpointIds)
{
checkpoints.emplace_back(Checkpoint<CLI::Execution::Data>{ m_checkpointDatabase, checkpointId });
}

return checkpoints;
}

void CheckpointManager::CleanUpDatabase()
{
if (m_checkpointDatabase)
{
m_checkpointDatabase.reset();
}

if (!m_resumeId.empty())
{
const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId);
if (std::filesystem::exists(checkpointDatabasePath))
{
const auto& checkpointDatabaseParentDirectory = checkpointDatabasePath.parent_path();
AICLI_LOG(CLI, Info, << "Deleting Checkpoint database directory: " << checkpointDatabaseParentDirectory);
std::filesystem::remove_all(checkpointDatabaseParentDirectory);
}
}
}
}
Loading

0 comments on commit 4faaf0c

Please sign in to comment.