Skip to content

Commit 9324cbf

Browse files
committed
cloud package create
1 parent 60b15ec commit 9324cbf

File tree

9 files changed

+361
-137
lines changed

9 files changed

+361
-137
lines changed

Source/v2/Meadow.Cli/Commands/Current/Cloud/Collection/CloudCollectionListCommand.cs

-87
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,10 @@
11
using CliFx.Attributes;
2-
using CliFx.Exceptions;
32
using Meadow.Cloud;
43
using Meadow.Cloud.Identity;
54
using Microsoft.Extensions.Logging;
6-
using System.Text.Json;
75

86
namespace Meadow.CLI.Commands.DeviceManagement;
97

10-
[Command("cloud command publish", Description = "Publish a command to Meadow devices via the Meadow Service")]
11-
public class CloudCommandPublishCommand : BaseCloudCommand<CloudCommandPublishCommand>
12-
{
13-
public const string DefaultHost = "https://www.meadowcloud.co";
14-
15-
[CommandParameter(0, Description = "The name of the command", IsRequired = true, Name = "COMMAND_NAME")]
16-
public string CommandName { get; set; }
17-
18-
[CommandOption("collectionId", 'c', Description = "The target collection for publishing the command")]
19-
public string? CollectionId { get; set; }
20-
21-
[CommandOption("deviceIds", 'd', Description = "The target devices for publishing the command")]
22-
public string[]? DeviceIds { get; set; }
23-
24-
[CommandOption("args", 'a', Description = "The arguments for the command as a JSON string", Converter = typeof(JsonDocumentBindingConverter))]
25-
public JsonDocument? Arguments { get; set; }
26-
27-
[CommandOption("qos", 'q', Description = "The MQTT-defined quality of service for the command")]
28-
public QualityOfService QualityOfService { get; set; } = QualityOfService.AtLeastOnce;
29-
30-
[CommandOption("host", Description = "Optionally set a host (default is https://www.meadowcloud.co)")]
31-
public string? Host { get; set; }
32-
33-
private CommandService CommandService { get; }
34-
35-
public CloudCommandPublishCommand(
36-
IdentityManager identityManager,
37-
UserService userService,
38-
DeviceService deviceService,
39-
CollectionService collectionService,
40-
CommandService commandService,
41-
ILoggerFactory? loggerFactory)
42-
: base(identityManager, userService, deviceService, collectionService, loggerFactory)
43-
{
44-
CommandService = commandService;
45-
}
46-
47-
protected override async ValueTask ExecuteCommand(CancellationToken? cancellationToken)
48-
{
49-
if (string.IsNullOrWhiteSpace(CollectionId) && (DeviceIds == null || DeviceIds.Length == 0))
50-
{
51-
throw new CommandException("Either a collection ID (-c|--collectionId) or a list of device IDs (-d|--deviceIds) must be specified.", showHelp: true);
52-
}
53-
54-
if (!string.IsNullOrWhiteSpace(CollectionId) && (DeviceIds != null && DeviceIds.Length > 0))
55-
{
56-
throw new CommandException("Cannot specify both a collection ID (-c|--collectionId) and list of device IDs (-d|--deviceIds). Only one is allowed.", showHelp: true);
57-
}
58-
59-
if (Host == null) Host = DefaultHost;
60-
61-
var token = await IdentityManager.GetAccessToken(cancellationToken);
62-
if (string.IsNullOrWhiteSpace(token))
63-
{
64-
throw new CommandException("You must be signed into Meadow.Cloud to execute this command. Run 'meadow cloud login' to do so.");
65-
}
66-
67-
try
68-
{
69-
Logger?.LogInformation($"Publishing '{CommandName}' command to Meadow.Cloud. Please wait...");
70-
if (!string.IsNullOrWhiteSpace(CollectionId))
71-
{
72-
await CommandService.PublishCommandForCollection(CollectionId, CommandName, Arguments, (int)QualityOfService, Host, cancellationToken);
73-
}
74-
else if (DeviceIds.Any())
75-
{
76-
await CommandService.PublishCommandForDevices(DeviceIds, CommandName, Arguments, (int)QualityOfService, Host, cancellationToken);
77-
}
78-
else
79-
{
80-
throw new CommandException("Cannot specify both a collection ID (-c|--collectionId) and list of device IDs (-d|--deviceIds). Only one is allowed.");
81-
}
82-
Logger?.LogInformation("Publish command successful.");
83-
}
84-
catch (MeadowCloudAuthException ex)
85-
{
86-
throw new CommandException("You must be signed in to execute this command.", innerException: ex);
87-
}
88-
catch (MeadowCloudException ex)
89-
{
90-
throw new CommandException($"Publish command failed: {ex.Message}", innerException: ex);
91-
}
92-
}
93-
}
94-
958
[Command("cloud collection list", Description = "List Meadow Collections")]
969
public class CloudCollectionListCommand : BaseCloudCommand<CloudCollectionListCommand>
9710
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using CliFx.Attributes;
2+
using CliFx.Exceptions;
3+
using Meadow.Cloud;
4+
using Meadow.Cloud.Identity;
5+
using Microsoft.Extensions.Logging;
6+
using System.Text.Json;
7+
8+
namespace Meadow.CLI.Commands.DeviceManagement;
9+
10+
[Command("cloud command publish", Description = "Publish a command to Meadow devices via the Meadow Service")]
11+
public class CloudCommandPublishCommand : BaseCloudCommand<CloudCommandPublishCommand>
12+
{
13+
public const string DefaultHost = "https://www.meadowcloud.co";
14+
15+
[CommandParameter(0, Description = "The name of the command", IsRequired = true, Name = "COMMAND_NAME")]
16+
public string CommandName { get; set; }
17+
18+
[CommandOption("collectionId", 'c', Description = "The target collection for publishing the command")]
19+
public string? CollectionId { get; set; }
20+
21+
[CommandOption("deviceIds", 'd', Description = "The target devices for publishing the command")]
22+
public string[]? DeviceIds { get; set; }
23+
24+
[CommandOption("args", 'a', Description = "The arguments for the command as a JSON string", Converter = typeof(JsonDocumentBindingConverter))]
25+
public JsonDocument? Arguments { get; set; }
26+
27+
[CommandOption("qos", 'q', Description = "The MQTT-defined quality of service for the command")]
28+
public QualityOfService QualityOfService { get; set; } = QualityOfService.AtLeastOnce;
29+
30+
[CommandOption("host", Description = "Optionally set a host (default is https://www.meadowcloud.co)")]
31+
public string? Host { get; set; }
32+
33+
private CommandService CommandService { get; }
34+
35+
public CloudCommandPublishCommand(
36+
IdentityManager identityManager,
37+
UserService userService,
38+
DeviceService deviceService,
39+
CollectionService collectionService,
40+
CommandService commandService,
41+
ILoggerFactory? loggerFactory)
42+
: base(identityManager, userService, deviceService, collectionService, loggerFactory)
43+
{
44+
CommandService = commandService;
45+
}
46+
47+
protected override async ValueTask ExecuteCommand(CancellationToken? cancellationToken)
48+
{
49+
if (string.IsNullOrWhiteSpace(CollectionId) && (DeviceIds == null || DeviceIds.Length == 0))
50+
{
51+
throw new CommandException("Either a collection ID (-c|--collectionId) or a list of device IDs (-d|--deviceIds) must be specified.", showHelp: true);
52+
}
53+
54+
if (!string.IsNullOrWhiteSpace(CollectionId) && (DeviceIds != null && DeviceIds.Length > 0))
55+
{
56+
throw new CommandException("Cannot specify both a collection ID (-c|--collectionId) and list of device IDs (-d|--deviceIds). Only one is allowed.", showHelp: true);
57+
}
58+
59+
if (Host == null) Host = DefaultHost;
60+
61+
var token = await IdentityManager.GetAccessToken(cancellationToken);
62+
if (string.IsNullOrWhiteSpace(token))
63+
{
64+
throw new CommandException("You must be signed into Meadow.Cloud to execute this command. Run 'meadow cloud login' to do so.");
65+
}
66+
67+
try
68+
{
69+
Logger?.LogInformation($"Publishing '{CommandName}' command to Meadow.Cloud. Please wait...");
70+
if (!string.IsNullOrWhiteSpace(CollectionId))
71+
{
72+
await CommandService.PublishCommandForCollection(CollectionId, CommandName, Arguments, (int)QualityOfService, Host, cancellationToken);
73+
}
74+
else if (DeviceIds.Any())
75+
{
76+
await CommandService.PublishCommandForDevices(DeviceIds, CommandName, Arguments, (int)QualityOfService, Host, cancellationToken);
77+
}
78+
else
79+
{
80+
throw new CommandException("Cannot specify both a collection ID (-c|--collectionId) and list of device IDs (-d|--deviceIds). Only one is allowed.");
81+
}
82+
Logger?.LogInformation("Publish command successful.");
83+
}
84+
catch (MeadowCloudAuthException ex)
85+
{
86+
throw new CommandException("You must be signed in to execute this command.", innerException: ex);
87+
}
88+
catch (MeadowCloudException ex)
89+
{
90+
throw new CommandException($"Publish command failed: {ex.Message}", innerException: ex);
91+
}
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using CliFx.Attributes;
2+
using Meadow.Cli;
3+
using Meadow.Cloud;
4+
using Meadow.Cloud.Identity;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Meadow.CLI.Commands.DeviceManagement;
8+
9+
[Command("cloud package create", Description = "Create a Meadow Package (MPAK)")]
10+
public class CloudPackageCreateCommand : BaseCloudCommand<CloudPackageCreateCommand>
11+
{
12+
[CommandParameter(0, Name = "Path to project file", IsRequired = false)]
13+
public string? ProjectPath { get; set; } = default!;
14+
15+
[CommandOption('c', Description = "The build configuration to compile", IsRequired = false)]
16+
public string Configuration { get; set; } = "Release";
17+
18+
[CommandOption("name", 'n', Description = "Name of the mpak file to be created", IsRequired = false)]
19+
public string? MpakName { get; init; } = default!;
20+
21+
[CommandOption("filter", 'f', Description = "Glob pattern to filter files. ex ('app.dll', 'app*','{app.dll,meadow.dll}')",
22+
IsRequired = false)]
23+
public string Filter { get; init; } = "*";
24+
25+
private IPackageManager _packageManager;
26+
27+
public CloudPackageCreateCommand(
28+
IdentityManager identityManager,
29+
UserService userService,
30+
DeviceService deviceService,
31+
CollectionService collectionService,
32+
IPackageManager packageManager,
33+
ILoggerFactory? loggerFactory)
34+
: base(identityManager, userService, deviceService, collectionService, loggerFactory)
35+
{
36+
_packageManager = packageManager;
37+
}
38+
39+
protected override async ValueTask ExecuteCommand(CancellationToken? cancellationToken)
40+
{
41+
if (ProjectPath == null)
42+
{
43+
ProjectPath = AppDomain.CurrentDomain.BaseDirectory;
44+
}
45+
46+
// build
47+
Logger?.LogInformation($"Building {Configuration} version of application...");
48+
if (!_packageManager.BuildApplication(ProjectPath, Configuration, true, cancellationToken))
49+
{
50+
return;
51+
}
52+
53+
var candidates = PackageManager.GetAvailableBuiltConfigurations(ProjectPath, "App.dll");
54+
55+
if (candidates.Length == 0)
56+
{
57+
Logger?.LogError($"Cannot find a compiled application at '{ProjectPath}'");
58+
return;
59+
}
60+
61+
var file = candidates.OrderByDescending(c => c.LastWriteTime).First(); // trim
62+
Logger?.LogInformation($"Trimming application...");
63+
await _packageManager.TrimApplication(file, cancellationToken: cancellationToken);
64+
65+
// package
66+
var packageDir = Path.Combine(file.Directory?.FullName ?? string.Empty, PackageManager.PackageOutputDirectoryName);
67+
var postlinkDir = Path.Combine(file.Directory?.FullName ?? string.Empty, PackageManager.PostLinkDirectoryName);
68+
69+
Logger?.LogInformation($"Assembling the MPAK...");
70+
var packagePath = await _packageManager.AssemblePackage(postlinkDir, packageDir, Filter, true, cancellationToken);
71+
72+
if (packagePath != null)
73+
{
74+
Logger?.LogInformation($"Done. Package is available at {packagePath}");
75+
}
76+
else
77+
{
78+
Logger?.LogError($"Package assembly failed.");
79+
}
80+
81+
}
82+
}

Source/v2/Meadow.Cli/Commands/commands.md

+40-40
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
11
# Commands
22

3-
| v1 | v2 | Legacy |
3+
| v1 | v2 | Legacy Implemented |
44
|---|---|---|---|
5-
| list ports | port list | n |
5+
| list ports | port list | n |
66
| n/a | port select | - |
7-
| listen | listen | |
8-
| device info | device info | |
9-
| n/a | device reset | |
10-
| n/a | device clock | |
11-
| use port | config route |
12-
| mono enable | runtime enable | |
13-
| mono disable | runtime disable | |
14-
| mono state | runtime state | |
15-
| file list | file list |
16-
| file delete | file delete |
17-
| file read | file read |
18-
| file write | file write |
19-
| file initial | file initial |
20-
| n/a | firmware list |
21-
| download os | firmware download |
22-
| n/a | firmware default |
23-
| n/a | firmware delete |
24-
| flash esp | firmware write esp |
25-
| flash os | firmware write os |
26-
| mono flash | flash write runtime |
27-
| mono update rt | flash write runtime |
28-
| trace enable | trace enable |
29-
| trace disable | trace disable |
30-
| trace level | trace level |
7+
| listen | listen | - |
8+
| device info | device info | - |
9+
| n/a | device reset | - |
10+
| n/a | device clock | - |
11+
| use port | config route | n |
12+
| mono enable | runtime enable | y |
13+
| mono disable | runtime disable | y |
14+
| mono state | runtime state | n |
15+
| file list | file list | - |
16+
| file delete | file delete | - |
17+
| n/a | file read | - |
18+
| file write | file write | - |
19+
| file initial | file initial | - |
20+
| n/a | firmware list | - |
21+
| download os | firmware download | n |
22+
| n/a | firmware default | - |
23+
| n/a | firmware delete | - |
24+
| flash esp | firmware write esp | n |
25+
| flash os | firmware write os | n |
26+
| mono flash | flash write runtime | n |
27+
| mono update rt | flash write runtime | n |
28+
| trace enable | trace enable | - |
29+
| trace disable | trace disable | - |
30+
| trace level | trace level | - |
3131
| set developer | developer |
32-
| uart trace | uart trace |
33-
| n/a | app build |
34-
| n/a | app trim |
35-
| install dfu-util | dfu install |
36-
| app deploy | app deply |
37-
| n/a | app run |
38-
| flash erase | flash erase |
39-
| debug | TODO|
40-
| device provision | device provision |
41-
| package create | TODO |
32+
| uart trace | uart trace | - |
33+
| n/a | app build | - |
34+
| n/a | app trim | - |
35+
| install dfu-util | dfu install | n |
36+
| app deploy | app deploy | - |
37+
| n/a | app run | - |
38+
| flash erase | flash erase | - |
39+
| debug | TODO |
40+
| device provision | device provision | - |
41+
| package create | cloud package create | n |
4242
| package list | TODO |
4343
| package publish | TODO |
4444
| package upload | TODO |
45-
| collection list | cloud collection list |
46-
| cloud login | cloud login |
47-
| cloud logout | cloud logout |
48-
| cloud command publish | TODO |
45+
| collection list | cloud collection list | - |
46+
| cloud login | cloud login | - |
47+
| cloud logout | cloud logout | - |
48+
| cloud command publish | cloud command publish | - |
4949

5050

5151

Source/v2/Meadow.Cli/IPackageManager.cs

+16-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,24 @@
33
public interface IPackageManager
44
{
55
List<string> GetDependencies(FileInfo file);
6-
bool BuildApplication(string projectFilePath, string configuration = "Release");
6+
7+
bool BuildApplication(
8+
string projectFilePath,
9+
string configuration = "Release",
10+
bool clean = true,
11+
CancellationToken? cancellationToken = null);
12+
713
Task TrimApplication(
814
FileInfo applicationFilePath,
915
bool includePdbs = false,
1016
IList<string>? noLink = null,
11-
CancellationToken cancellationToken = default);
17+
CancellationToken? cancellationToken = null);
18+
19+
Task<string> AssemblePackage(
20+
string contentSourceFolder,
21+
string outputFolder,
22+
string filter = "*",
23+
bool overwrite = false,
24+
CancellationToken? cancellationToken = null);
25+
1226
}

0 commit comments

Comments
 (0)