Skip to content

Commit ad9fa9c

Browse files
committed
Add meadow update command.
1 parent b1f21c6 commit ad9fa9c

File tree

10 files changed

+296
-9
lines changed

10 files changed

+296
-9
lines changed

.github/workflows/dotnet.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
run: dotnet build main/MeadowCLI.Classic.sln /p:Configuration=Release
8989

9090
- name: Upload nuget Artifacts for internal testing
91-
uses: actions/upload-artifact@v2
91+
uses: actions/upload-artifact@v4
9292
with:
9393
name: Meadow.CLI.Classic.nuget.${{ ENV.CLI_RELEASE_VERSION_1 }}
9494
path: 'main\Meadow.CLI.Classic\bin\Release\*.nupkg'
@@ -100,7 +100,7 @@ jobs:
100100
run: dotnet build main/MeadowCLI.sln /p:Configuration=Release
101101

102102
- name: Upload nuget Artifacts for internal testing
103-
uses: actions/upload-artifact@v2
103+
uses: actions/upload-artifact@v4
104104
with:
105105
name: Meadow.CLI.nuget.${{ ENV.CLI_RELEASE_VERSION_1 }}
106106
path: 'main\Meadow.CLI\bin\Release\*.nupkg'
@@ -117,7 +117,7 @@ jobs:
117117
run: dotnet build main/Source/v2/Meadow.CLI.v2.sln /p:Configuration=Release
118118

119119
- name: Upload nuget Artifacts for internal testing
120-
uses: actions/upload-artifact@v2
120+
uses: actions/upload-artifact@v4
121121
with:
122122
name: Meadow.CLI.nuget.${{ ENV.CLI_RELEASE_VERSION_2 }}
123123
path: 'main\Source\v2\Meadow.CLI\bin\Release\*.nupkg'
@@ -185,7 +185,7 @@ jobs:
185185
# DevEnvDir: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE'
186186

187187
# - name: Upload VS2019 VSIX Artifacts
188-
# uses: actions/upload-artifact@v2
188+
# uses: actions/upload-artifact@v4
189189
# with:
190190
# name: Meadow.Win.VS2019.vsix.${{ ENV.IDE_TOOLS_RELEASE_VERSION }}
191191
# path: 'vs-win\VS_Meadow_Extension\VS_Meadow_Extension.2019\bin\Release\*.vsix'
@@ -326,7 +326,7 @@ jobs:
326326
# msbuild vs-mac/VS4Mac_Meadow_Extension.sln /t:Build /p:Configuration=Release /p:CreatePackage=true
327327

328328
# - name: Upload Mac VS2019 mpack Artifacts
329-
# uses: actions/upload-artifact@v2
329+
# uses: actions/upload-artifact@v4
330330
# with:
331331
# name: Meadow.Mac.2019.mpack.${{ ENV.IDE_TOOLS_RELEASE_VERSION }}
332332
# path: 'vs-mac/VS4Mac_Meadow_Extension/bin/Release/net472/*.mpack'
@@ -435,7 +435,7 @@ jobs:
435435
dotnet msbuild vs-mac/VS4Mac_Meadow_Extension/Meadow.Sdks.IdeExtensions.Vs4Mac.2022.csproj /t:Build /p:Configuration=Release /p:CreatePackage=true
436436
437437
- name: Upload VS2022 mpack Artifacts
438-
uses: actions/upload-artifact@v2
438+
uses: actions/upload-artifact@v4
439439
with:
440440
name: Meadow.Mac.2022.mpack.${{ ENV.IDE_TOOLS_RELEASE_VERSION }}
441441
path: 'vs-mac/VS4Mac_Meadow_Extension/bin/Release/net7.0/*.mpack'
@@ -584,7 +584,7 @@ jobs:
584584
vsce package
585585
586586
- name: Upload VSIX Artifacts
587-
uses: actions/upload-artifact@v2
587+
uses: actions/upload-artifact@v4
588588
with:
589589
name: Meadow.VSCode.vsix.${{ ENV.IDE_TOOLS_RELEASE_VERSION }}
590590
path: 'vs-code/*.vsix'

Source/v2/Meadow.CLI.v2.sln

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Dfu", "Meadow.Dfu\Me
4040
EndProject
4141
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Firmware", "Meadow.Firmware\Meadow.Firmware.csproj", "{D2274F30-A001-482A-99E3-0AB1970CF695}"
4242
EndProject
43-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meadow.Tooling.Core", "Meadow.Tooling.Core\Meadow.Tooling.Core.csproj", "{A22DBF4A-E472-445E-96C0-930C126039C2}"
43+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Tooling.Core", "Meadow.Tooling.Core\Meadow.Tooling.Core.csproj", "{A22DBF4A-E472-445E-96C0-930C126039C2}"
44+
EndProject
45+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meadow.Updater", "Meadow.Updater\Meadow.Updater.csproj", "{78D924D6-9D20-46DE-A178-D3AA830B9A55}"
4446
EndProject
4547
Global
4648
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -124,6 +126,10 @@ Global
124126
{A22DBF4A-E472-445E-96C0-930C126039C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
125127
{A22DBF4A-E472-445E-96C0-930C126039C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
126128
{A22DBF4A-E472-445E-96C0-930C126039C2}.Release|Any CPU.Build.0 = Release|Any CPU
129+
{78D924D6-9D20-46DE-A178-D3AA830B9A55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
130+
{78D924D6-9D20-46DE-A178-D3AA830B9A55}.Debug|Any CPU.Build.0 = Debug|Any CPU
131+
{78D924D6-9D20-46DE-A178-D3AA830B9A55}.Release|Any CPU.ActiveCfg = Release|Any CPU
132+
{78D924D6-9D20-46DE-A178-D3AA830B9A55}.Release|Any CPU.Build.0 = Release|Any CPU
127133
EndGlobalSection
128134
GlobalSection(SolutionProperties) = preSolution
129135
HideSolutionNode = FALSE

Source/v2/Meadow.CLI/Commands/Current/App/AppTools.cs

+39
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Meadow.Hcom;
33
using Meadow.Package;
44
using Microsoft.Extensions.Logging;
5+
using System.Diagnostics;
56

67
namespace Meadow.CLI.Commands.DeviceManagement;
78

@@ -136,4 +137,42 @@ internal static string SanitizeMeadowFilename(string fileName)
136137

137138
return meadowFileName!.Replace(Path.DirectorySeparatorChar, '/');
138139
}
140+
141+
internal static async Task<int> RunProcessCommand(string command, string args, Action<string>? handleOutput = null, Action<string>? handleError = null, CancellationToken cancellationToken = default)
142+
{
143+
var processStartInfo = new ProcessStartInfo
144+
{
145+
FileName = command,
146+
Arguments = args,
147+
RedirectStandardOutput = true,
148+
RedirectStandardError = true,
149+
UseShellExecute = false,
150+
CreateNoWindow = true
151+
};
152+
153+
using (var process = new Process { StartInfo = processStartInfo })
154+
{
155+
process.Start();
156+
157+
var outputCompletion = ReadLinesAsync(process.StandardOutput, handleOutput, cancellationToken);
158+
var errorCompletion = ReadLinesAsync(process.StandardError, handleError, cancellationToken);
159+
160+
await Task.WhenAll(outputCompletion, errorCompletion, process.WaitForExitAsync());
161+
162+
return process.ExitCode;
163+
}
164+
}
165+
166+
private static async Task ReadLinesAsync(StreamReader reader, Action<string>? handleLine, CancellationToken cancellationToken)
167+
{
168+
while (!reader.EndOfStream)
169+
{
170+
var line = await reader.ReadLineAsync(cancellationToken);
171+
if (!string.IsNullOrWhiteSpace(line)
172+
&& handleLine != null)
173+
{
174+
handleLine(line);
175+
}
176+
}
177+
}
139178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Reflection;
2+
using CliFx.Attributes;
3+
using Microsoft.Extensions.Logging;
4+
5+
namespace Meadow.CLI.Commands.DeviceManagement;
6+
7+
[Command("update", Description = Strings.Update.Description)]
8+
public class UpdateCommand : BaseCommand<UpdateCommand>
9+
{
10+
const string MEADOW_CLI = "WildernessLabs.Meadow.CLI";
11+
const string MEADOW_UPDATER = "Meadow.Updater";
12+
const string CLIFX = "CliFx";
13+
14+
[CommandOption("version", 'v', IsRequired = false)]
15+
public string? Version { get; set; }
16+
17+
public UpdateCommand(ILoggerFactory loggerFactory)
18+
: base(loggerFactory)
19+
{
20+
}
21+
22+
protected override async ValueTask ExecuteCommand()
23+
{
24+
Logger.LogInformation(Strings.Update.Updating, MEADOW_CLI);
25+
26+
string toVersion;
27+
if (!string.IsNullOrWhiteSpace(Version))
28+
{
29+
toVersion = $"v{Version}";
30+
}
31+
else
32+
{
33+
toVersion = "vLatest";
34+
}
35+
;
36+
Logger.LogInformation(Strings.Update.Instruction1, MEADOW_CLI, toVersion);
37+
Logger.LogInformation(Strings.Update.Instruction2);
38+
39+
// Path to the updater executable within the tool's output directory
40+
string meadowUpdaterPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, $"{MEADOW_UPDATER}.dll");
41+
42+
// Ensure the updater executable exists
43+
if (!File.Exists(meadowUpdaterPath))
44+
{
45+
Logger.LogError(Strings.Update.UpdaterNotFound);
46+
return;
47+
}
48+
49+
// Copy all necessary files to a temporary location, so there aren't any access issues
50+
string tempUpdaterDir = Path.Combine(Path.GetTempPath(), MEADOW_UPDATER);
51+
if (!Directory.Exists(tempUpdaterDir))
52+
{
53+
Directory.CreateDirectory(tempUpdaterDir);
54+
}
55+
CopyMeadowUpdaterFiles(Path.GetDirectoryName(meadowUpdaterPath), tempUpdaterDir, MEADOW_UPDATER);
56+
57+
// Supporting files required in the temp directory
58+
CopyMeadowUpdaterFiles(Path.GetDirectoryName(meadowUpdaterPath), tempUpdaterDir, CLIFX);
59+
60+
string commandArguments = $"update -t {MEADOW_CLI}";
61+
if (!string.IsNullOrWhiteSpace(Version))
62+
{
63+
commandArguments += $" -v {Version}";
64+
}
65+
66+
await AppTools.RunProcessCommand("dotnet", $"{Path.Combine(tempUpdaterDir, $"{MEADOW_UPDATER}.dll")} {commandArguments}", cancellationToken: CancellationToken);
67+
}
68+
69+
internal static void CopyMeadowUpdaterFiles(string? sourceDirectory, string targetDirectory, string filesToCopy)
70+
{
71+
if (sourceDirectory == null)
72+
{
73+
return;
74+
}
75+
76+
var toolUpdaterFiles = Directory.GetFiles(sourceDirectory, $"{filesToCopy}*");
77+
foreach (var file in toolUpdaterFiles)
78+
{
79+
string fileName = Path.GetFileName(file);
80+
string destFile = Path.Combine(targetDirectory, fileName);
81+
File.Copy(file, destFile, true);
82+
}
83+
}
84+
}

Source/v2/Meadow.Cli/Meadow.CLI.csproj

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<ProjectReference Include="..\Meadow.Dfu\Meadow.Dfu.csproj" />
4747
<ProjectReference Include="..\Meadow.Linker\Meadow.Linker.csproj" />
4848
<ProjectReference Include="..\Meadow.Tooling.Core\Meadow.Tooling.Core.csproj" />
49+
<ProjectReference Include="..\Meadow.Updater\Meadow.Updater.csproj" />
4950
<ProjectReference Include="..\Meadow.UsbLib\Meadow.UsbLib.csproj" />
5051
</ItemGroup>
5152

@@ -62,4 +63,13 @@
6263
<ItemGroup>
6364
<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
6465
</ItemGroup>
66+
67+
<ItemGroup>
68+
<None Update="..\Meadow.Updater\bin\$(Configuration)\net8.0\Meadow.Updater.exe">
69+
<Pack>true</Pack>
70+
<PackagePath>tools\net8.0\any\</PackagePath>
71+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
72+
</None>
73+
</ItemGroup>
74+
6575
</Project>

Source/v2/Meadow.Cli/Strings.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Meadow.Telemetry;
1+
using System.Numerics;
2+
using Meadow.Telemetry;
23

34
namespace Meadow.CLI;
45

@@ -86,4 +87,13 @@ public static class Telemetry
8687

8788
public const string AskToParticipate = "Would you like to participate?";
8889
}
90+
91+
public static class Update
92+
{
93+
public const string Description = "Update WildernessLabs.Meadow.CLI";
94+
public const string Updating = "Updating {0}";
95+
public const string Instruction1 = "This will initially uninstall {0} and will then install version {1}";
96+
public const string Instruction2 = "Please wait about 10s to allow the above steps to complete.";
97+
public const string UpdaterNotFound = "Meadow.Updater not found.";
98+
}
8999
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<LangVersion>11</LangVersion>
9+
<Platforms>AnyCPU</Platforms>
10+
</PropertyGroup>
11+
12+
<PropertyGroup>
13+
<SignAssembly>true</SignAssembly>
14+
<AssemblyOriginatorKeyFile>..\MeadowCLIKey.snk</AssemblyOriginatorKeyFile>
15+
</PropertyGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="CliFx" Version="*" />
19+
</ItemGroup>
20+
</Project>

Source/v2/Meadow.Updater/Program.cs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using CliFx;
3+
using CliFx.Exceptions;
4+
using System.Diagnostics;
5+
6+
namespace Meadow.Updater;
7+
8+
public class Program
9+
{
10+
public static async Task<int> Main(string[] args)
11+
{
12+
int returnCode;
13+
14+
try
15+
{
16+
returnCode = await new CliApplicationBuilder()
17+
.AddCommandsFromThisAssembly()
18+
//.UseTypeActivator(serviceProvider.GetService!)
19+
.SetExecutableName("Meadow.Updater")
20+
.Build()
21+
.RunAsync();
22+
}
23+
catch (CommandException ce)
24+
{
25+
returnCode = ce.ExitCode;
26+
}
27+
catch (Exception ex)
28+
{
29+
Console.WriteLine($"Operation failed: {ex.Message}");
30+
returnCode = 1;
31+
}
32+
33+
return returnCode;
34+
}
35+
}

Source/v2/Meadow.Updater/Strings.cs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
namespace Meadow.Updater;
3+
4+
public static class Strings
5+
{
6+
public static class Update
7+
{
8+
public const string Description = "Update the specified tool";
9+
public const string NoToolSpecified = "No tool specified. Exiting.";
10+
}
11+
}

0 commit comments

Comments
 (0)