winget-cli

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

ResumeCommand.cpp (5507B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "AppInstallerRuntime.h"
      5 #include "Resources.h"
      6 #include "ResumeCommand.h"
      7 #include "RootCommand.h"
      8 #include "CheckpointManager.h"
      9 #include "Workflows/ResumeFlow.h"
     10 
     11 using namespace AppInstaller::Checkpoints;
     12 
     13 namespace AppInstaller::CLI
     14 {
     15     namespace
     16     {
     17         std::unique_ptr<Command> FindCommandToResume(const std::string& commandFullName)
     18         {
     19             std::unique_ptr<Command> commandToResume = std::make_unique<RootCommand>();
     20 
     21             for (const auto& commandPart : Utility::Split(commandFullName, ':'))
     22             {
     23                 bool commandFound = false;
     24                 if (Utility::CaseInsensitiveEquals(commandPart, commandToResume->Name()))
     25                 {
     26                     // Since we always expect to start at the 'root' command, skip and check the next command part. 
     27                     continue;
     28                 }
     29 
     30                 for (auto& command : commandToResume->GetCommands())
     31                 {
     32                     if (Utility::CaseInsensitiveEquals(commandPart, command->Name()))
     33                     {
     34                         commandFound = true;
     35                         commandToResume = std::move(command);
     36                         break;
     37                     }
     38                 }
     39 
     40                 if (!commandFound)
     41                 {
     42                     THROW_HR_MSG(E_UNEXPECTED, "Command to resume not found.");
     43                 }
     44             }
     45 
     46             return std::move(commandToResume);
     47         }
     48     }
     49 
     50     using namespace std::string_view_literals;
     51     using namespace Execution;
     52 
     53     std::vector<Argument> ResumeCommand::GetArguments() const
     54     {
     55         return {
     56             Argument::ForType(Execution::Args::Type::ResumeId),
     57             Argument::ForType(Execution::Args::Type::IgnoreResumeLimit),
     58         };
     59     }
     60 
     61     Resource::LocString ResumeCommand::ShortDescription() const
     62     {
     63         return { Resource::String::ResumeCommandShortDescription };
     64     }
     65 
     66     Resource::LocString ResumeCommand::LongDescription() const
     67     {
     68         return { Resource::String::ResumeCommandLongDescription };
     69     }
     70 
     71     Utility::LocIndView ResumeCommand::HelpLink() const
     72     {
     73         return "https://aka.ms/winget-command-resume"_liv;
     74     }
     75 
     76     void ResumeCommand::ExecuteInternal(Execution::Context& context) const
     77     {
     78         const auto& resumeId = context.Args.GetArg(Execution::Args::Type::ResumeId);
     79 
     80         if (!std::filesystem::exists(Checkpoints::CheckpointManager::GetCheckpointDatabasePath(resumeId)))
     81         {
     82             context.Reporter.Error() << Resource::String::ResumeIdNotFoundError(Utility::LocIndView{ resumeId }) << std::endl;
     83             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_ID_NOT_FOUND);
     84         }
     85 
     86         Execution::Context resumeContext = context.CreateEmptyContext();
     87         std::optional<Checkpoint<AutomaticCheckpointData>> foundAutomaticCheckpoint = resumeContext.LoadCheckpoint(std::string{ resumeId });
     88         if (!foundAutomaticCheckpoint.has_value())
     89         {
     90             context.Reporter.Error() << Resource::String::ResumeStateDataNotFoundError << std::endl;
     91             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_RESUME_STATE);
     92         }
     93 
     94         Checkpoint<AutomaticCheckpointData> automaticCheckpoint = foundAutomaticCheckpoint.value();
     95 
     96         const auto& checkpointClientVersion = automaticCheckpoint.Get(AutomaticCheckpointData::ClientVersion, {});
     97         if (checkpointClientVersion != AppInstaller::Runtime::GetClientVersion().get())
     98         {
     99             context.Reporter.Error() << Resource::String::ClientVersionMismatchError(Utility::LocIndView{ checkpointClientVersion }) << std::endl;
    100             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_CLIENT_VERSION_MISMATCH);
    101         }
    102 
    103         const auto& resumeCountString = automaticCheckpoint.Get(AutomaticCheckpointData::ResumeCount, {});
    104         int resumeCount = std::stoi(resumeCountString);
    105         if (!context.Args.Contains(Execution::Args::Type::IgnoreResumeLimit) && resumeCount >= Settings::User().Get<Settings::Setting::MaxResumes>())
    106         {
    107             std::string manualResumeString = "winget resume -g " + std::string{ resumeId } + " --ignore-resume-limit";
    108             context.Reporter.Error() << Resource::String::ResumeLimitExceeded(Utility::LocIndView{ resumeCountString }) << Utility::LocIndView{ manualResumeString } << std::endl;
    109             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_RESUME_LIMIT_EXCEEDED);
    110         }
    111         else
    112         {
    113             automaticCheckpoint.Update(AutomaticCheckpointData::ResumeCount, {}, std::to_string(resumeCount + 1));
    114         }
    115 
    116         const auto& checkpointCommand = automaticCheckpoint.Get(AutomaticCheckpointData::Command, {});
    117         AICLI_LOG(CLI, Info, << "Resuming command: " << checkpointCommand);
    118         std::unique_ptr<Command> commandToResume = FindCommandToResume(checkpointCommand);
    119 
    120         LoadCommandArgsFromAutomaticCheckpoint(resumeContext, automaticCheckpoint);
    121 
    122         resumeContext.SetExecutingCommand(commandToResume.get());
    123 
    124         // TODO: Ensure telemetry is properly handled for resume context.
    125         resumeContext.SetFlags(Execution::ContextFlag::Resume);
    126 
    127         auto previousThreadGlobals = resumeContext.SetForCurrentThread();
    128         resumeContext.EnableSignalTerminationHandler();
    129         commandToResume->Resume(resumeContext);
    130         context.SetTerminationHR(resumeContext.GetTerminationHR());
    131     }
    132 }