Skip to content

Commit e7ea902

Browse files
committed
Provide a prev command to quickly swap between versions
1 parent 5170b95 commit e7ea902

File tree

7 files changed

+114
-44
lines changed

7 files changed

+114
-44
lines changed

NodeSwap/Commands/PrevCommand.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using DotMake.CommandLine;
3+
using NodeSwap.Utils;
4+
5+
namespace NodeSwap.Commands;
6+
7+
[CliCommand(
8+
Description = "Switch to the previously installed version of Node.js.",
9+
Parent = typeof(RootCommand)
10+
)]
11+
public class PrevCommand(GlobalContext globalContext, NodeJs nodeLocal)
12+
{
13+
public int Run()
14+
{
15+
var prevVersion = nodeLocal.GetPreviousVersion();
16+
if (prevVersion == null)
17+
{
18+
Console.Error.WriteLine("No previous version found");
19+
return 1;
20+
}
21+
22+
if (!ProcessElevation.IsAdministrator())
23+
{
24+
// Restart the application with elevated privileges
25+
return ProcessElevation.ElevateApplication();
26+
}
27+
28+
var useCommand = new UseCommand(globalContext, nodeLocal)
29+
{
30+
Version = prevVersion.ToString(),
31+
};
32+
33+
return useCommand.Run();
34+
}
35+
}

NodeSwap/Commands/UseCommand.cs

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System;
2-
using System.Diagnostics;
32
using System.IO;
4-
using System.Linq;
53
using System.Runtime.InteropServices;
6-
using System.Security.Principal;
74
using DotMake.CommandLine;
5+
using NodeSwap.Utils;
86

97
namespace NodeSwap.Commands;
108

@@ -61,10 +59,20 @@ public int Run()
6159
}
6260
}
6361

64-
if (!IsAdministrator())
62+
if (!ProcessElevation.IsAdministrator())
6563
{
6664
// Restart the application with elevated privileges
67-
return ElevateApplication();
65+
return ProcessElevation.ElevateApplication();
66+
}
67+
68+
//
69+
// Track the previous NodeJS version used
70+
//
71+
72+
var activeVersion = nodeLocal.GetActiveVersion();
73+
if (activeVersion != null)
74+
{
75+
File.WriteAllText(globalContext.PreviousVersionTrackerFilePath, activeVersion.ToString());
6876
}
6977

7078
//
@@ -103,42 +111,6 @@ public int Run()
103111
return 0;
104112
}
105113

106-
private static bool IsAdministrator()
107-
{
108-
using var identity = WindowsIdentity.GetCurrent();
109-
var principal = new WindowsPrincipal(identity);
110-
return principal.IsInRole(WindowsBuiltInRole.Administrator);
111-
}
112-
113-
private static int ElevateApplication()
114-
{
115-
var currentProcessModule = Process.GetCurrentProcess().MainModule;
116-
if (currentProcessModule == null) throw new Exception("Unable to get the current process module");
117-
118-
var process = new Process
119-
{
120-
StartInfo = new ProcessStartInfo
121-
{
122-
FileName = currentProcessModule.FileName,
123-
UseShellExecute = true,
124-
Verb = "runas", // Forces the application to run with elevated permissions
125-
Arguments = string.Join(" ", Environment.GetCommandLineArgs().Skip(1)),
126-
},
127-
};
128-
129-
try
130-
{
131-
process.Start();
132-
process.WaitForExit();
133-
return process.ExitCode;
134-
}
135-
catch (Exception ex)
136-
{
137-
Console.Error.WriteLine("Could not restart as Administrator: " + ex.Message);
138-
return 1;
139-
}
140-
}
141-
142114
[DllImport("kernel32.dll")]
143115
private static extern bool CreateSymbolicLink(
144116
string lpSymlinkFileName,

NodeSwap/GlobalContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ public class GlobalContext
55
public string StoragePath;
66
public string SymlinkPath;
77
public string ActiveVersionTrackerFilePath;
8+
public string PreviousVersionTrackerFilePath;
89
public bool Is64Bit;
910
}

NodeSwap/NodeJs.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,35 @@ public List<NodeJsVersion> GetInstalledVersions()
2929
{
3030
Path = dir,
3131
Version = version,
32-
IsActive = version.Equals(activeVersion),
32+
IsActive = activeVersion != null && version.Equals(activeVersion),
3333
};
3434
})
3535
.OrderByDescending(v => v.Version)
3636
.ToList();
3737
}
3838

39-
private Version GetActiveVersion()
39+
public Version? GetActiveVersion()
4040
{
41+
if (!File.Exists(globalContext.ActiveVersionTrackerFilePath))
42+
{
43+
return null;
44+
}
45+
4146
var str = File.ReadAllText(globalContext.ActiveVersionTrackerFilePath);
4247
return VersionParser.Parse(str);
4348
}
4449

50+
public Version? GetPreviousVersion()
51+
{
52+
if (!File.Exists(globalContext.PreviousVersionTrackerFilePath))
53+
{
54+
return null;
55+
}
56+
57+
var str = File.ReadAllText(globalContext.PreviousVersionTrackerFilePath);
58+
return VersionParser.Parse(str);
59+
}
60+
4561
[GeneratedRegex(@"node-v(\d+\.\d+\.\d+)")]
4662
private static partial Regex NodeVersionRegex();
4763
}

NodeSwap/NodeSwap.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net8.0-windows</TargetFramework>
66
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
7-
<Version>1.4.2</Version>
7+
<Version>1.5.0</Version>
88
<!-- Single file app – https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file -->
99
<PublishSingleFile>true</PublishSingleFile>
1010
<RuntimeIdentifier>win-x64</RuntimeIdentifier>

NodeSwap/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static Program()
2323
};
2424

2525
globalContext.ActiveVersionTrackerFilePath = Path.Combine(globalContext.StoragePath, "last-used");
26+
globalContext.PreviousVersionTrackerFilePath = Path.Combine(globalContext.StoragePath, "previous-used");
2627

2728
var services = new ServiceCollection();
2829
services.AddSingleton(globalContext);

NodeSwap/Utils/ProcessElevation.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Linq;
4+
using System.Security.Principal;
5+
6+
namespace NodeSwap.Utils;
7+
8+
public class ProcessElevation
9+
{
10+
public static bool IsAdministrator()
11+
{
12+
using var identity = WindowsIdentity.GetCurrent();
13+
var principal = new WindowsPrincipal(identity);
14+
return principal.IsInRole(WindowsBuiltInRole.Administrator);
15+
}
16+
17+
public static int ElevateApplication()
18+
{
19+
var currentProcessModule = Process.GetCurrentProcess().MainModule;
20+
if (currentProcessModule == null) throw new Exception("Unable to get the current process module");
21+
22+
var process = new Process
23+
{
24+
StartInfo = new ProcessStartInfo
25+
{
26+
FileName = currentProcessModule.FileName,
27+
UseShellExecute = true,
28+
Verb = "runas", // Forces the application to run with elevated permissions
29+
Arguments = string.Join(" ", Environment.GetCommandLineArgs().Skip(1)),
30+
},
31+
};
32+
33+
try
34+
{
35+
process.Start();
36+
process.WaitForExit();
37+
return process.ExitCode;
38+
}
39+
catch (Exception ex)
40+
{
41+
Console.Error.WriteLine("Could not restart as Administrator: " + ex.Message);
42+
return 1;
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)