chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using OpenTap.Cli;
 
namespace OpenTap.Package
{
    /// <summary>
    /// Checks packages for updates
    /// </summary>
    [Browsable(false)]
    [Display("check-updates", "Checks for update for installed packages.", "package")]
    internal class UpdateCheck : ICliAction
    {
        /// <summary>
        /// Used to specify if PackageManagerSettings.CheckForUpdates should be checked before executing.
        /// </summary>
        [Browsable(false)]
        [CommandLineArgument("startup")]
        public bool Startup { get; set; }
 
        static readonly TraceSource Log = OpenTap.Log.CreateSource("UpdateCheck");
 
        private void CheckForUpdatesAsync(CancellationToken cancellationToken)
        {
            string noUpdateCheckEnv = Environment.GetEnvironmentVariable("OPENTAP_NO_UPDATE_CHECK");
            bool noUpdateCheck = noUpdateCheckEnv == "true" || noUpdateCheckEnv == "1";
 
            if (Startup && (PackageManagerSettings.Current.CheckForUpdates == false || noUpdateCheck))
                return;
 
            // Since we are deciding to do the update check for the parent process there is no reason to also
            // do it for the child processes.
            Environment.SetEnvironmentVariable("OPENTAP_NO_UPDATE_CHECK", "true");
            string noUpdateMessageEnv = Environment.GetEnvironmentVariable("OPENTAP_NO_UPDATE_MESSAGE");
            bool noUpdateMessage = noUpdateMessageEnv == "true" || noUpdateMessageEnv == "1";
 
            var timer = Stopwatch.StartNew();
            List<PackageDef> updates = new List<PackageDef>();
 
            var installation = new Installation(ExecutorClient.ExeDir);
            IPackageIdentifier[] installedPackages = installation.GetPackages().ToArray();
            Parallel.ForEach(PackageManagerSettings.Current.GetEnabledRepositories(null), repo =>
            {
                try
                {
                    updates.AddRange(repo.CheckForUpdates(installedPackages, cancellationToken));
                }
                catch (Exception ex)
                {
                    if (noUpdateMessage)
                        Log.Debug("Update check against {0} failed. See debug messages for details.", repo.Url);
                    else
                        Log.Warning("Update check against {0} failed. See debug messages for details.", repo.Url);
                    Log.Debug(ex);
                }
            });
 
            if (noUpdateMessage)
                return;
 
 
            using var _ = CliUserInputInterface.AcquireUserInputLock();
            if (updates.Any())
            {
                Log.Info("Updates available for:");
                foreach (var update in updates.GroupBy(p => p.Name))
                {
                    var currentPackage = installedPackages.FirstOrDefault(p => p.Name == update.Key);
                    var updatedPackage = update.OrderByDescending(p => p.Version).FirstOrDefault();
                    Log.Info($" - {update.Key}: {currentPackage?.Version} -> {updatedPackage?.Version}");
                }
            }
            else
            {
                Log.Debug(timer, "Update check completed.");
                if (!Startup)
                    Log.Info("All installed packages are up to date.");
            }
        }
 
        /// <summary>
        /// Runs the check for updates action.
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public int Execute(CancellationToken cancellationToken)
        {
            CheckForUpdatesAsync(cancellationToken);
            return (int) ExitCodes.Success;
        }
    }
}