Skip to content

Commit

Permalink
feat(pack): support app packing
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Maia authored and Jonas Maia committed Apr 30, 2024
1 parent 8b7361b commit 6441819
Show file tree
Hide file tree
Showing 12 changed files with 464 additions and 2 deletions.
7 changes: 6 additions & 1 deletion cmf-cli/Commands/init/InitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ internal void Execute(InitArguments x)
}

args.AddRange(new[]
{
{
"--appName", x.appName,
"--appId", x.appId,
"--appIcon", x.appIcon ?? string.Empty,
Expand Down Expand Up @@ -417,6 +417,11 @@ internal void Execute(InitArguments x)
x.config.CopyTo(this.fileSystem.Path.Join(envConfigPath, x.config.Name));
this.fileSystem.FileInfo.New(this.fileSystem.Path.Join(envConfigPath, ".gitkeep")).Delete();
}

if (x.repositoryType == RepositoryType.App)
{
Log.Information($"Apps need to have an ApplicationVersion package. Make sure you create at least one business package for your application using the addApplicationVersionAssembly flag");
}
}

bool isToIgnoreOptionToken = false;
Expand Down
87 changes: 87 additions & 0 deletions cmf-cli/Handlers/PackageType/RootPackageTypeHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
using Cmf.CLI.Constants;
using Cmf.CLI.Core;
using Cmf.CLI.Core.Enums;
using Cmf.CLI.Core.Objects;
using Cmf.CLI.Core.Objects.CmfApp;
using Cmf.CLI.Core.Utilities;
using Cmf.CLI.Utilities;
using System.IO;
using System.IO.Abstractions;
using static NuGet.Packaging.PackagingConstants;


namespace Cmf.CLI.Handlers
Expand All @@ -11,6 +18,15 @@ namespace Cmf.CLI.Handlers
/// <seealso cref="PackageTypeHandler" />
public class RootPackageTypeHandler : PackageTypeHandler
{
#region Private Properties

/// <summary>
/// The CMF app data object
/// </summary>
private CmfApp CmfApp;

#endregion

/// <summary>
/// Initializes a new instance of the <see cref="RootPackageTypeHandler" /> class.
/// </summary>
Expand All @@ -27,5 +43,76 @@ public RootPackageTypeHandler(CmfPackage cmfPackage) : base(cmfPackage)

cmfPackage.DFPackageType = PackageType.Generic;
}

/// <summary>
/// Packs the specified package output dir. If root package is an app, packs app files
/// </summary>
/// <param name="packageOutputDir"></param>
/// <param name="outputDir"></param>
public override void Pack(IDirectoryInfo packageOutputDir, IDirectoryInfo outputDir)
{
if (ExecutionContext.Instance.ProjectConfig?.RepositoryType == RepositoryType.App)
{
GenerateAppFiles(packageOutputDir, outputDir);
}

base.Pack(packageOutputDir, outputDir);
}

/// <summary>
/// Generates the deployment framework app manifest and the app icon image.
/// </summary>
/// <param name="packageOutputDir">The package output dir.</param>
/// <exception cref="CliException"></exception>
internal virtual void GenerateAppFiles(IDirectoryInfo packageOutputDir, IDirectoryInfo outputDir)
{
Log.Debug("Generating App manifest");

IFileInfo cmfAppFile = fileSystem.FileInfo.New(CliConstants.CmfAppFileName);

CmfApp = CmfApp.Load(cmfAppFile, fileSystem);

if (string.IsNullOrWhiteSpace(CmfApp.Content.App.Image.File))
{
string defaultIconPath = fileSystem.Path.Join(FileSystemUtilities.GetProjectRoot(fileSystem, throwException: true).FullName,
CliConstants.AssetsFolder,
CliConstants.DefaultAppIcon);

CmfApp.Content.App.Image.File = defaultIconPath;
}
else if (!AppIconUtilities.IsIconValid(CmfApp.Content.App.Image.File))
{
throw new CliException(string.Format(CoreMessages.InvalidValue, cmfAppFile.FullName));
}

fileSystem.Directory.CreateDirectory(packageOutputDir.FullName);

string iconPath = fileSystem.Path.Join(packageOutputDir.FullName, CliConstants.AppIcon);
CmfApp.SaveIcon(iconPath);
CmfApp.Content.App.Image.File = CliConstants.AppIcon;

string manifestPath = fileSystem.Path.Join(packageOutputDir.FullName, CliConstants.DeploymentFrameworkAppManifestFileName);
CmfApp.Save(manifestPath);

string appPackage = $"{CmfApp.Content.App.Name}@{CmfPackage.Version}.zip";

string tempzipPath = fileSystem.Path.Join(packageOutputDir.FullName, appPackage);
if (fileSystem.File.Exists(tempzipPath))
{
fileSystem.File.Delete(tempzipPath);
}

FileSystemUtilities.ZipDirectory(fileSystem, tempzipPath, packageOutputDir);

// move to final destination

string destZipPath = fileSystem.Path.Join(outputDir.FullName, appPackage);
fileSystem.File.Move(tempzipPath, destZipPath, true);

// clean up folder files
fileSystem.File.Delete(iconPath);
fileSystem.File.Delete(manifestPath);
}

}
}
11 changes: 11 additions & 0 deletions cmf-cli/resources/templateFiles/app_manifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<App id="$(id)"
version="$(version)"
author="$(author)"
name="$(name)"
description="$(description)"
xmlns:metadata="$(metadata)">
<metadata:Framework version="$(frameworkVersion)"/>
<metadata:LicensedApplication name="$(licensedApplication)"/>
<metadata:Image file="$(image)"/>
</App>
1 change: 0 additions & 1 deletion core/Commands/TemplateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Cmf.CLI.Core.Constants;
using Cmf.CLI.Core.Objects;
using Cmf.CLI.Utilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Abstractions.TemplatePackage;
using Microsoft.TemplateEngine.Edge;
Expand Down
5 changes: 5 additions & 0 deletions core/Constants/CoreConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public static class CoreConstants
/// </summary>
public const string PackageJson = "package.json";

/// <summary>
/// The CMF app file name
/// </summary>
public const string CmfAppFileName = "cmfapp.json";

/// <summary>
/// The project config file name, located in the project root
/// </summary>
Expand Down
162 changes: 162 additions & 0 deletions core/Objects/CmfApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Xml.Serialization;
using Cmf.CLI.Core.Enums;
using Cmf.CLI.Utilities;
using Newtonsoft.Json;

namespace Cmf.CLI.Core.Objects.CmfApp;

/// <summary>
/// CMF App data
/// </summary>
public record AppData
{
public string id { get; set; }
public string name { get; set; }
public string author { get; set; }
public string description { get; set; }
public string targetFramework { get; set; }
public string licensedApplication { get; set; }
public string icon { get; set; }
}

/// <summary>
/// Target framework of the app
/// </summary>
public record Framework
{
[XmlAttribute("version")]
public string Version { get; set; }
}

/// <summary>
/// App license related data
/// </summary>
public record LicensedApplication
{
[XmlAttribute("name")]
public string Name { get; set; }
}

/// <summary>
/// App icon file information
/// </summary>
public record Image
{
[XmlAttribute("file")]
public string File { get; set; }
}

/// <summary>
/// CMF App data object for serialization Version 1
/// </summary>
[XmlRoot("App")]
public record CmfAppV1
{
[XmlAttribute("id")] public string Id { get; set; }
[XmlAttribute("author")] public string Author { get; set; }
[XmlAttribute("name")] public string Name { get; set; }
[XmlAttribute("description")] public string Description { get; set; }
[XmlElement(Namespace = "urn:cmf:dp:xml:ns:app-metadata-v1")] public Framework Framework { get; set; }
[XmlElement(Namespace = "urn:cmf:dp:xml:ns:app-metadata-v1")] public LicensedApplication LicensedApplication { get; set; }
[XmlElement(Namespace = "urn:cmf:dp:xml:ns:app-metadata-v1")] public Image Image { get; set; }
}

/// <summary>
/// CMF App container for data and metadata
/// </summary>
public record AppContainer
{
public record HeaderRec
{
public string Version => "1.0";
public string Encoding => "utf-8";
}

[JsonProperty("?xml")] public HeaderRec Header => new HeaderRec();

[XmlElement("App")]
[JsonProperty("App")] public CmfAppV1 App { get; set; }
}

/// <summary>
/// CMF App content data object handler class
/// </summary>
public class CmfApp
{
public IFileInfo FileInfo { get; private set; }
public PackageLocation Location { get; private set; }
public IFileSystem FileSystem { get; private set; }
public AppContainer Content { get; private set; }
public string PackageName => Content.App.Id;

/// <summary>
/// Loads CmfApp data object from a specified file using file system from the execution context.
/// </summary>
/// <param name="file">The file to load the CmfApp from.</param>
/// <param name="fileSystem">Optional parameter specifying the file system to use for file operations.</param>
/// <returns>The loaded CmfApp instance.</returns>
public static CmfApp Load(IFileInfo file, IFileSystem fileSystem = null)
{
fileSystem ??= ExecutionContext.Instance.FileSystem;
if (!file.Exists)
{
throw new CliException(string.Format(CoreMessages.NotFound, file.FullName));
}

string fileContent = file.ReadToString();

var appData = JsonConvert.DeserializeObject<AppData>(fileContent);

CmfAppV1 cmfAppData = new()
{
Id = appData.id,
Author = appData.author,
Name = appData.name,
Description = appData.description,
Framework = new Framework { Version = appData.targetFramework },
LicensedApplication = new LicensedApplication { Name = appData.licensedApplication },
Image = new Image { File = appData.icon }
};

CmfApp cmfApp = new()
{
Content = new AppContainer { App = cmfAppData },
FileInfo = file,
Location = PackageLocation.Local,
FileSystem = fileSystem,
};

return cmfApp;
}

/// <summary>
/// Serializes the content of the CmfApp instance and saves it to the specified path as an XML file.
/// </summary>
/// <param name="path">The path where the XML file will be saved.</param>
public void Save(string path)
{
XmlSerializer serializer = new(typeof(AppContainer));
using StringWriter writer = new();

serializer.Serialize(writer, Content);
string xmlString = writer.ToString();
FileSystem.File.WriteAllText(path, xmlString);
}

/// <summary>
/// Saves the icon associated with the CmfApp instance to the specified destination.
/// </summary>
/// <param name="iconDestination">The destination path where the icon will be saved.</param>
public void SaveIcon(string path)
{
string iconSource = Content.App.Image.File;

byte[] iconBytes = FileSystem.File.ReadAllBytes(iconSource);

FileSystem.File.WriteAllBytes(path, iconBytes);
}
}
30 changes: 30 additions & 0 deletions tests/Fixtures/pack/app/.project-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"ProjectName": "CF58AABF",
"NPMRegistry": "http://npm_registry/",
"RepositoryType": "App",
"BaseLayer": "Core",
"NuGetRegistry": "htt://nuget_registry/",
"RepositoryURL": "https://repo_url/collection/project/_git/repo",
"EnvironmentName": "system_name",
"DefaultDomain": "DOMAIN",
"RESTPort": "1234",
"Tenant": "tenant",
"MESVersion": "10.2.1",
"NugetVersion": "10.0.0",
"TestScenariosNugetVersion": "10.0.0",
"IsSslEnabled": "True",
"vmHostname": "app_server_address",
"DBReplica1": "server1\\instance",
"DBReplica2": "server2\\instance",
"DBServerOnline": "server1\\instance",
"DBServerODS": "server2\\instance",
"DBServerDWH": "server3\\instance",
"ReportServerURI": "http://reporting_services/Reports",
"AlwaysOn": "False",
"InstallationPath": "install_path",
"DBBackupPath": "backup_share",
"TemporaryPath": "temp_folder",
"HTMLPort": "443",
"GatewayPort": "5678",
"ReleaseEnvironmentConfig": "config.json"
}
Binary file added tests/Fixtures/pack/app/assets/default_app_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions tests/Fixtures/pack/app/cmfapp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "MockId",
"name": "MockName",
"author": "MockAuthor",
"description": "MockDescription",
"targetFramework": "10.0.0",
"licensedApplication": "MockId",
"icon": ""
}
15 changes: 15 additions & 0 deletions tests/Fixtures/pack/app/cmfpackage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"packageId": "Cmf.Custom.Package",
"version": "1.0.0",
"description": "This package deploys Critical Manufacturing Customization",
"packageType": "Root",
"isInstallable": true,
"isUniqueInstall": false,
"dependencies": [
{
"id": "Cmf.Environment",
"version": "10.2.1",
"mandatory": false
}
]
}
Loading

0 comments on commit 6441819

Please sign in to comment.