WorkflowBase.h (17122B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #pragma once 4 #include "ExecutionArgs.h" 5 #include "ExecutionReporter.h" 6 #include <winget/ExperimentalFeature.h> 7 #include <winget/RepositorySearch.h> 8 #include <winget/RepositorySource.h> 9 #include <winget/Authentication.h> 10 #include <winget/ManifestComparator.h> 11 12 #include <string> 13 #include <string_view> 14 15 namespace AppInstaller::CLI::Execution 16 { 17 struct Context; 18 } 19 20 namespace AppInstaller::CLI::Workflow 21 { 22 // Values are ordered in a typical workflow stages 23 enum class ExecutionStage : uint32_t 24 { 25 Initial = 0, 26 ParseArgs = 1000, 27 Discovery = 2000, 28 Download = 3000, 29 PreExecution = 3500, 30 Execution = 4000, 31 PostExecution = 5000, 32 }; 33 34 enum class OperationType 35 { 36 Completion, 37 Export, 38 Install, 39 List, 40 Pin, 41 Search, 42 Show, 43 Uninstall, 44 Upgrade, 45 Download, 46 Repair, 47 }; 48 49 // A task in the workflow. 50 struct WorkflowTask 51 { 52 using Func = void (*)(Execution::Context&); 53 54 WorkflowTask(Func f) : m_isFunc(true), m_func(f) {} 55 WorkflowTask(std::string_view name, bool executeAlways = false) : m_name(name), m_executeAlways(executeAlways) {} 56 57 virtual ~WorkflowTask() = default; 58 59 WorkflowTask(const WorkflowTask&) = default; 60 WorkflowTask& operator=(const WorkflowTask&) = default; 61 62 WorkflowTask(WorkflowTask&&) = default; 63 WorkflowTask& operator=(WorkflowTask&&) = default; 64 65 bool operator==(const WorkflowTask& other) const; 66 67 virtual void operator()(Execution::Context& context) const; 68 69 const std::string& GetName() const { return m_name; } 70 bool IsFunction() const { return m_isFunc; } 71 Func Function() const { return m_func; } 72 bool ExecuteAlways() const { return m_executeAlways; } 73 void Log() const; 74 75 private: 76 bool m_isFunc = false; 77 Func m_func = nullptr; 78 std::string m_name; 79 bool m_executeAlways = false; 80 }; 81 82 // Helper to determine installed source to use based on context input. 83 Repository::PredefinedSource DetermineInstalledSource(const Execution::Context& context); 84 85 // Helper to create authentication arguments from context input. 86 Authentication::AuthenticationArguments GetAuthenticationArguments(const Execution::Context& context); 87 88 // Helper to report exceptions and return the HRESULT. 89 // If context is null, no output will be attempted. 90 HRESULT HandleException(Execution::Context* context, std::exception_ptr exception); 91 92 // Helper to report exceptions and return the HRESULT. 93 HRESULT HandleException(Execution::Context& context, std::exception_ptr exception); 94 95 // Fills the options from the given context and metadata. 96 AppInstaller::Manifest::ManifestComparator::Options GetManifestComparatorOptions(const Execution::Context& context, const Repository::IPackageVersion::Metadata& metadata); 97 98 // Creates the source object. 99 // Required Args: None 100 // Inputs: None 101 // Outputs: Source 102 struct OpenSource : public WorkflowTask 103 { 104 OpenSource(bool forDependencies = false) : WorkflowTask("OpenSource"), m_forDependencies(forDependencies) {} 105 106 void operator()(Execution::Context& context) const override; 107 108 private: 109 bool m_forDependencies; 110 }; 111 112 // Creates a source object for a source specified by name, and adds it to the list of open sources. 113 // Required Args: None 114 // Inputs: Sources? 115 // Outputs: Sources 116 struct OpenNamedSourceForSources : public WorkflowTask 117 { 118 OpenNamedSourceForSources(std::string_view sourceName) : WorkflowTask("OpenNamedSourceForSources"), m_sourceName(sourceName) {} 119 120 void operator()(Execution::Context& context) const override; 121 122 private: 123 Utility::LocIndView m_sourceName; 124 }; 125 126 // Creates a source object for a predefined source. 127 // Required Args: None 128 // Inputs: None 129 // Outputs: Source 130 struct OpenPredefinedSource : public WorkflowTask 131 { 132 OpenPredefinedSource(Repository::PredefinedSource source, bool forDependencies = false) : WorkflowTask("OpenPredefinedSource"), m_predefinedSource(source), m_forDependencies(forDependencies) {} 133 134 void operator()(Execution::Context& context) const override; 135 136 private: 137 Repository::PredefinedSource m_predefinedSource; 138 bool m_forDependencies; 139 }; 140 141 // Creates a composite source from the given predefined source and the existing source. 142 // Required Args: None 143 // Inputs: Source 144 // Outputs: Source 145 struct OpenCompositeSource : public WorkflowTask 146 { 147 OpenCompositeSource( 148 Repository::PredefinedSource source, 149 bool forDependencies = false, 150 Repository::CompositeSearchBehavior searchBehavior = Repository::CompositeSearchBehavior::Installed) : 151 WorkflowTask("OpenCompositeSource"), m_predefinedSource(source), m_forDependencies(forDependencies), m_searchBehavior(searchBehavior) {} 152 153 void operator()(Execution::Context& context) const override; 154 155 private: 156 Repository::PredefinedSource m_predefinedSource; 157 bool m_forDependencies; 158 Repository::CompositeSearchBehavior m_searchBehavior; 159 }; 160 161 // Performs a search on the source. 162 // Required Args: None 163 // Inputs: Source 164 // Outputs: SearchResult 165 void SearchSourceForMany(Execution::Context& context); 166 167 // Creates a search request object with the semantics of targeting a single package. 168 // Required Args: None 169 // Inputs: Query, search filters (Id, Name, etc.) 170 // Outputs: SearchRequest 171 void GetSearchRequestForSingle(Execution::Context& context); 172 173 // Performs a search on the source with the semantics of targeting a single package. 174 // Required Args: None 175 // Inputs: Source, SearchRequest 176 // Outputs: SearchResult 177 void SearchSourceForSingle(Execution::Context& context); 178 179 // Performs a search on the source with the semantics of targeting many packages, 180 // but for completion purposes. 181 // Required Args: None 182 // Inputs: Source, CompletionData 183 // Outputs: SearchResult 184 void SearchSourceForManyCompletion(Execution::Context& context); 185 186 // Performs a search on the source with the semantics of targeting a single package, 187 // but for completion purposes. 188 // Required Args: None 189 // Inputs: Source, CompletionData 190 // Outputs: SearchResult 191 void SearchSourceForSingleCompletion(Execution::Context& context); 192 193 // Searches the source for the specific field as a completion. 194 // Required Args: None 195 // Inputs: CompletionData, Source 196 // Outputs: None 197 struct SearchSourceForCompletionField : public WorkflowTask 198 { 199 SearchSourceForCompletionField(Repository::PackageMatchField field) : WorkflowTask("SearchSourceForCompletionField"), m_field(field) {} 200 201 void operator()(Execution::Context& context) const override; 202 203 private: 204 Repository::PackageMatchField m_field; 205 }; 206 207 // Outputs the search results. 208 // Required Args: None 209 // Inputs: SearchResult 210 // Outputs: None 211 void ReportSearchResult(Execution::Context& context); 212 213 // Outputs the search results as the list command would show. 214 // Required Args: None 215 // Inputs: SearchResult 216 // Outputs: None 217 struct ReportListResult : public WorkflowTask 218 { 219 ReportListResult(bool onlyShowUpgrades = false) : WorkflowTask("ReportListResult"), m_onlyShowUpgrades(onlyShowUpgrades) {} 220 221 void operator()(Execution::Context& context) const override; 222 223 private: 224 bool m_onlyShowUpgrades; 225 }; 226 227 // Handles failures in the SearchResult either by warning or failing. 228 // Required Args: None 229 // Inputs: SearchResult 230 // Outputs: None 231 void HandleSearchResultFailures(Execution::Context& context); 232 233 // Outputs the search results when multiple packages found but only one expected. 234 // Required Args: None 235 // Inputs: SearchResult 236 // Outputs: None 237 void ReportMultiplePackageFoundResult(Execution::Context& context); 238 239 // Outputs the search results when multiple packages found but only one expected. 240 // Required Args: None 241 // Inputs: SearchResult 242 // Outputs: None 243 void ReportMultiplePackageFoundResultWithSource(Execution::Context& context); 244 245 // Ensures that there is at least one result in the search. 246 // Required Args: bool indicating if the search result is from installed source 247 // Inputs: SearchResult 248 // Outputs: None 249 struct EnsureMatchesFromSearchResult : public WorkflowTask 250 { 251 EnsureMatchesFromSearchResult(OperationType operation) : 252 WorkflowTask("EnsureMatchesFromSearchResult"), m_operationType(operation) {} 253 254 void operator()(Execution::Context& context) const override; 255 256 private: 257 OperationType m_operationType; 258 }; 259 260 // Ensures that there is only one result in the search. 261 // Required Args: bool indicating if the search result is from installed source 262 // Inputs: SearchResult 263 // Outputs: Package 264 struct EnsureOneMatchFromSearchResult : public WorkflowTask 265 { 266 EnsureOneMatchFromSearchResult(OperationType operation) : 267 WorkflowTask("EnsureOneMatchFromSearchResult"), m_operationType(operation) {} 268 269 void operator()(Execution::Context& context) const override; 270 271 private: 272 OperationType m_operationType; 273 }; 274 275 // Gets the manifest from package. 276 // Required Args: Version and channel; can be empty. A flag indicating whether to consider package pins 277 // Inputs: Package 278 // Outputs: Manifest, PackageVersion 279 struct GetManifestWithVersionFromPackage : public WorkflowTask 280 { 281 GetManifestWithVersionFromPackage(std::string_view version, std::string_view channel, bool considerPins) : 282 WorkflowTask("GetManifestWithVersionFromPackage"), m_version(version), m_channel(channel), m_considerPins(considerPins) {} 283 284 GetManifestWithVersionFromPackage(const Utility::VersionAndChannel& versionAndChannel, bool considerPins) : 285 GetManifestWithVersionFromPackage(versionAndChannel.GetVersion().ToString(), versionAndChannel.GetChannel().ToString(), considerPins) {} 286 287 void operator()(Execution::Context& context) const override; 288 289 private: 290 std::string_view m_version; 291 std::string_view m_channel; 292 bool m_considerPins; 293 }; 294 295 // Gets the manifest from package. 296 // Required Args: A value indicating whether to consider pins 297 // Inputs: Package. Optionally Version and Channel 298 // Outputs: Manifest, PackageVersion 299 struct GetManifestFromPackage : public WorkflowTask 300 { 301 GetManifestFromPackage(bool considerPins) : WorkflowTask("GetManifestFromPackage"), m_considerPins(considerPins) {} 302 303 void operator()(Execution::Context& context) const override; 304 305 private: 306 bool m_considerPins; 307 }; 308 309 // Ensures the file exists and is not a directory. 310 // Required Args: the one given 311 // Inputs: None 312 // Outputs: None 313 struct VerifyFile : public WorkflowTask 314 { 315 VerifyFile(Execution::Args::Type arg) : WorkflowTask("VerifyFile"), m_arg(arg) {} 316 317 void operator()(Execution::Context& context) const override; 318 319 private: 320 Execution::Args::Type m_arg; 321 }; 322 323 // Ensures the path exists. 324 // Required Args: the one given 325 // Inputs: None 326 // Outputs: None 327 struct VerifyPath : public WorkflowTask 328 { 329 VerifyPath(Execution::Args::Type arg) : WorkflowTask("VerifyPath"), m_arg(arg) {} 330 331 void operator()(Execution::Context& context) const override; 332 333 private: 334 Execution::Args::Type m_arg; 335 }; 336 337 // Ensures the local file exists and is not a directory. Or it's a Uri. Default only https is supported at the moment. 338 // Required Args: the one given 339 // Inputs: None 340 // Outputs: None 341 struct VerifyFileOrUri : public WorkflowTask 342 { 343 VerifyFileOrUri(Execution::Args::Type arg, std::vector<std::wstring> supportedSchemes = { L"https" }) : 344 WorkflowTask("VerifyFileOrUri"), m_arg(arg), m_supportedSchemes(std::move(supportedSchemes)) {} 345 346 void operator()(Execution::Context& context) const override; 347 348 private: 349 Execution::Args::Type m_arg; 350 std::vector<std::wstring> m_supportedSchemes; 351 }; 352 353 // Opens the manifest file provided on the command line. 354 // Required Args: Manifest 355 // Inputs: None 356 // Outputs: Manifest 357 void GetManifestFromArg(Execution::Context& context); 358 359 // Reports the search result's package identity. 360 // Required Args: None 361 // Inputs: Package 362 // Outputs: None 363 void ReportPackageIdentity(Execution::Context& context); 364 365 // Reports the installed package version identity. 366 // Required Args: None 367 // Inputs: InstalledPackageVersion 368 // Outputs: None 369 void ReportInstalledPackageVersionIdentity(Execution::Context& context); 370 371 // Reports the manifest's identity. 372 // Required Args: None 373 // Inputs: Manifest 374 // Outputs: None 375 void ReportManifestIdentity(Execution::Context& context); 376 377 // Reports the manifest's identity with version. 378 // Required Args: None 379 // Inputs: Manifest 380 // Outputs: None 381 struct ReportManifestIdentityWithVersion : public WorkflowTask 382 { 383 ReportManifestIdentityWithVersion(Utility::LocIndView prefix, Execution::Reporter::Level level = Execution::Reporter::Level::Info) : 384 WorkflowTask("ReportManifestIdentityWithVersion"), m_prefix(prefix), m_level(level) {} 385 ReportManifestIdentityWithVersion(Resource::StringId label = Resource::String::ReportIdentityFound, Execution::Reporter::Level level = Execution::Reporter::Level::Info) : 386 WorkflowTask("ReportManifestIdentityWithVersion"), m_label(label), m_level(level) {} 387 388 void operator()(Execution::Context& context) const override; 389 390 private: 391 Utility::LocIndView m_prefix; 392 std::optional<Resource::StringId> m_label; 393 Execution::Reporter::Level m_level; 394 }; 395 396 // Selects the installer from the manifest, if one is applicable. 397 // Required Args: None 398 // Inputs: Manifest 399 // Outputs: Installer 400 void SelectInstaller(Execution::Context& context); 401 402 // Ensures that the process is running as admin. 403 // Required Args: None 404 // Inputs: None 405 // Outputs: None 406 void EnsureRunningAsAdmin(Execution::Context& context); 407 408 // Ensures that the feature is enabled. 409 // Required Args: the desired feature 410 // Inputs: None 411 // Outputs: None 412 struct EnsureFeatureEnabled : public WorkflowTask 413 { 414 EnsureFeatureEnabled(Settings::ExperimentalFeature::Feature feature) : WorkflowTask("EnsureFeatureEnabled"), m_feature(feature) {} 415 416 void operator()(Execution::Context& context) const override; 417 418 private: 419 Settings::ExperimentalFeature::Feature m_feature; 420 }; 421 422 // Performs a search on the source with the semantics of targeting packages matching input manifest 423 // Required Args: None 424 // Inputs: Source, Manifest 425 // Outputs: SearchResult 426 void SearchSourceUsingManifest(Execution::Context& context); 427 428 // Gets the installed package version 429 // Required Args: None 430 // Inputs: Package 431 // Outputs: InstalledPackageVersion 432 void GetInstalledPackageVersion(Execution::Context& context); 433 434 // Shows all versions for an application. 435 // Required Args: None 436 // Inputs: SearchResult [only operates on first match] 437 // Outputs: None 438 void ShowAppVersions(Execution::Context& context); 439 440 // Reports execution stage in a workflow 441 // Required Args: ExecutionStage 442 // Inputs: ExecutionStage? 443 // Outputs: ExecutionStage 444 struct ReportExecutionStage : public WorkflowTask 445 { 446 ReportExecutionStage(ExecutionStage stage) : WorkflowTask("ReportExecutionStage"), m_stage(stage) {} 447 448 void operator()(Execution::Context& context) const override; 449 450 private: 451 ExecutionStage m_stage; 452 }; 453 } 454 455 // Passes the context to the function if it has not been terminated; returns the context. 456 AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, AppInstaller::CLI::Workflow::WorkflowTask::Func f); 457 458 // Passes the context to the task if it has not been terminated; returns the context. 459 AppInstaller::CLI::Execution::Context& operator<<(AppInstaller::CLI::Execution::Context& context, const AppInstaller::CLI::Workflow::WorkflowTask& task);