winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

CheckpointManager.cpp (6644B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "CheckpointManager.h"
      5 #include "Command.h"
      6 #include "ExecutionContextData.h"
      7 #include <AppInstallerRuntime.h>
      8 
      9 using namespace AppInstaller::CLI;
     10 using namespace AppInstaller::Repository::Microsoft;
     11 using namespace AppInstaller::SQLite;
     12 
     13 namespace AppInstaller::Checkpoints
     14 {
     15 
     16     // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata.
     17     constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv;
     18     constexpr std::string_view s_CheckpointsFileName = "checkpoints.db"sv;
     19 
     20     std::filesystem::path CheckpointManager::GetCheckpointDatabasePath(const std::string_view& resumeId, bool createCheckpointDirectory)
     21     {
     22         const auto checkpointsDirectory = Runtime::GetPathTo(Runtime::PathName::CheckpointsLocation) / resumeId;
     23 
     24         if (createCheckpointDirectory)
     25         {
     26             if (!std::filesystem::exists(checkpointsDirectory))
     27             {
     28                 AICLI_LOG(Repo, Info, << "Creating checkpoint database directory: " << checkpointsDirectory);
     29                 std::filesystem::create_directories(checkpointsDirectory);
     30             }
     31             else
     32             {
     33                 THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(checkpointsDirectory));
     34             }
     35         }
     36 
     37         auto recordPath = checkpointsDirectory / s_CheckpointsFileName;
     38         return recordPath;
     39     }
     40 
     41     CheckpointManager::CheckpointManager()
     42     {
     43         GUID resumeId;
     44         std::ignore = CoCreateGuid(&resumeId);
     45         m_resumeId = Utility::ConvertGuidToString(resumeId);
     46         const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId, true);
     47         m_checkpointDatabase = CheckpointDatabase::CreateNew(checkpointDatabasePath.u8string());
     48     }
     49 
     50     CheckpointManager::CheckpointManager(const std::string& resumeId)
     51     {
     52         m_resumeId = resumeId;
     53         const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId);
     54         m_checkpointDatabase = CheckpointDatabase::Open(checkpointDatabasePath.u8string());
     55     }
     56 
     57     void CheckpointManager::CreateAutomaticCheckpoint(CLI::Execution::Context& context)
     58     {
     59         CheckpointDatabase::IdType startCheckpointId = m_checkpointDatabase->AddCheckpoint(s_AutomaticCheckpoint);
     60         Checkpoint<AutomaticCheckpointData> automaticCheckpoint{ m_checkpointDatabase, startCheckpointId };
     61 
     62         automaticCheckpoint.Set(AutomaticCheckpointData::ClientVersion, {}, AppInstaller::Runtime::GetClientVersion());
     63 
     64         const auto& executingCommand = context.GetExecutingCommand();
     65         if (executingCommand != nullptr)
     66         {
     67             automaticCheckpoint.Set(AutomaticCheckpointData::Command, {}, std::string{ executingCommand->FullName() });
     68         }
     69 
     70         const auto& argTypes = context.Args.GetTypes();
     71         for (auto type : argTypes)
     72         {
     73             const auto& argument = std::to_string(static_cast<int>(type));
     74             auto argumentType = Argument::ForType(type).Type();
     75 
     76             if (argumentType == ArgumentType::Flag)
     77             {
     78                 automaticCheckpoint.Set(AutomaticCheckpointData::Arguments, argument, {});
     79             }
     80             else
     81             {
     82                 const auto& values = *context.Args.GetArgs(type);
     83                 automaticCheckpoint.SetMany(AutomaticCheckpointData::Arguments, argument, values);
     84             }
     85         }
     86 
     87         automaticCheckpoint.Set(AutomaticCheckpointData::ResumeCount, {}, std::to_string(0));
     88     }
     89 
     90     void LoadCommandArgsFromAutomaticCheckpoint(CLI::Execution::Context& context, Checkpoint<AutomaticCheckpointData>& automaticCheckpoint)
     91     {
     92         for (const auto& fieldName : automaticCheckpoint.GetFieldNames(AutomaticCheckpointData::Arguments))
     93         {
     94             // Command arguments are represented as integer strings in the checkpoint record.
     95             Execution::Args::Type type = static_cast<Execution::Args::Type>(std::stoi(fieldName));
     96             auto argumentType = Argument::ForType(type).Type();
     97             if (argumentType == ArgumentType::Flag)
     98             {
     99                 context.Args.AddArg(type);
    100             }
    101             else
    102             {
    103                 const auto& values = automaticCheckpoint.GetMany(AutomaticCheckpointData::Arguments, fieldName);
    104                 for (const auto& value : values)
    105                 {
    106                     context.Args.AddArg(type, value);
    107                 }
    108             }
    109         }
    110     }
    111 
    112     std::optional<Checkpoint<AutomaticCheckpointData>> CheckpointManager::GetAutomaticCheckpoint()
    113     {
    114         const auto& checkpointIds = m_checkpointDatabase->GetCheckpointIds();
    115         if (checkpointIds.empty())
    116         {
    117             return {};
    118         }
    119 
    120         CheckpointDatabase::IdType automaticCheckpointId = checkpointIds.back();
    121         return Checkpoint<AutomaticCheckpointData>{ m_checkpointDatabase, automaticCheckpointId };
    122     }
    123 
    124     Checkpoint<CLI::Execution::Data> CheckpointManager::CreateCheckpoint(std::string_view checkpointName)
    125     {
    126         CheckpointDatabase::IdType checkpointId = m_checkpointDatabase->AddCheckpoint(checkpointName);
    127         Checkpoint<CLI::Execution::Data> checkpoint{ m_checkpointDatabase, checkpointId };
    128         return checkpoint;
    129     }
    130 
    131     std::vector<Checkpoint<CLI::Execution::Data>> CheckpointManager::GetCheckpoints()
    132     {
    133         auto checkpointIds = m_checkpointDatabase->GetCheckpointIds();
    134         if (checkpointIds.empty())
    135         {
    136             return {};
    137         }
    138 
    139         // Remove the last checkpoint (automatic)
    140         checkpointIds.pop_back();
    141 
    142         std::vector<Checkpoint<CLI::Execution::Data>> checkpoints;
    143         for (const auto& checkpointId : checkpointIds)
    144         {
    145             checkpoints.emplace_back(Checkpoint<CLI::Execution::Data>{ m_checkpointDatabase, checkpointId });
    146         }
    147 
    148         return checkpoints;
    149     }
    150 
    151     void CheckpointManager::CleanUpDatabase()
    152     {
    153         if (m_checkpointDatabase)
    154         {
    155             m_checkpointDatabase.reset();
    156         }
    157 
    158         if (!m_resumeId.empty())
    159         {
    160             const auto& checkpointDatabasePath = GetCheckpointDatabasePath(m_resumeId);
    161             if (std::filesystem::exists(checkpointDatabasePath))
    162             {
    163                 const auto& checkpointDatabaseParentDirectory = checkpointDatabasePath.parent_path();
    164                 AICLI_LOG(CLI, Info, << "Deleting Checkpoint database directory: " << checkpointDatabaseParentDirectory);
    165                 std::filesystem::remove_all(checkpointDatabaseParentDirectory);
    166             }
    167         }
    168     }
    169 }