diff --git a/Sdk/Sdk.csproj b/Sdk/Sdk.csproj
index 8ac1659..dbf8ff6 100644
--- a/Sdk/Sdk.csproj
+++ b/Sdk/Sdk.csproj
@@ -43,8 +43,8 @@ By integrating this SDK into your build process, you can easily generate install
-
-
+
+
diff --git a/Sdk/Tasks/CatalogInformation.cs b/Sdk/Tasks/CatalogInformation.cs
index 7e2fcf9..d57f373 100644
--- a/Sdk/Tasks/CatalogInformation.cs
+++ b/Sdk/Tasks/CatalogInformation.cs
@@ -5,14 +5,22 @@
namespace Skyline.DataMiner.Sdk.Tasks
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Compression;
+ using System.Linq;
+ using System.Threading;
using Microsoft.Build.Framework;
+ using Microsoft.CodeAnalysis;
+
+ using NuGet.Packaging;
using Skyline.DataMiner.CICD.FileSystem;
using Skyline.DataMiner.Sdk.Helpers;
+ using static NuGet.Packaging.PackagingConstants;
+
using Task = Microsoft.Build.Utilities.Task;
public class CatalogInformation : Task, ICancelableTask
@@ -61,20 +69,33 @@ public override bool Execute()
return true;
}
- string outputDirectory = BuildOutputHandler.GetOutputPath(Output, ProjectDirectory);
- string destinationFilePath = fs.Path.Combine(outputDirectory, $"{PackageId}.{PackageVersion}.CatalogInformation.zip");
+ // make a temporary directory to work in and make changes
+ string tempDirectory = FileSystem.Instance.Directory.CreateTemporaryDirectory();
+ try
+ {
+ FileSystem.Instance.Directory.CopyRecursive(catalogInformationFolder, tempDirectory);
- fs.File.DeleteFile(destinationFilePath);
- ZipFile.CreateFromDirectory(catalogInformationFolder, destinationFilePath, CompressionLevel.Optimal, includeBaseDirectory: false);
+ AddOfficialNotices(fs, tempDirectory);
- Log.LogMessage(MessageImportance.High, $"Successfully created zip '{destinationFilePath}'.");
+ string outputDirectory = BuildOutputHandler.GetOutputPath(Output, ProjectDirectory);
+ string destinationFilePath = fs.Path.Combine(outputDirectory, $"{PackageId}.{PackageVersion}.CatalogInformation.zip");
- if (cancel)
+ fs.File.DeleteFile(destinationFilePath);
+ ZipFile.CreateFromDirectory(tempDirectory, destinationFilePath, CompressionLevel.Optimal, includeBaseDirectory: false);
+
+ Log.LogMessage(MessageImportance.High, $"Successfully created zip '{destinationFilePath}'.");
+
+ if (cancel)
+ {
+ return false;
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+ finally
{
- return false;
+ FileSystem.Instance.Directory.DeleteDirectory(tempDirectory);
}
-
- return !Log.HasLoggedErrors;
}
catch (Exception e)
{
@@ -87,5 +108,51 @@ public override bool Execute()
Log.LogMessage(MessageImportance.High, $"Catalog information creation for '{PackageId}' took {timer.ElapsedMilliseconds} ms.");
}
}
+
+ private void AddOfficialNotices(IFileSystem fs, string catalogInformationFolder)
+ {
+ // example D:\GITHUB\Skyline-QAOps\Skyline-QAOps-Package\PackageContent\CompanionFiles
+ var pathToPublicDirectoryOnSystem = fs.Path.Combine(@"C:\", "Skyline DataMiner", "Webpages", "Public");
+ var webpagesPublicDirectory = fs.Path.Combine(ProjectDirectory, "PackageContent", "CompanionFiles", "Skyline DataMiner", "Webpages", "Public");
+ if (fs.Directory.Exists(webpagesPublicDirectory))
+ {
+ // Get all files and folders directly under webpagesPublicDirectory
+ var entries = fs.Directory
+ .EnumerateDirectories(webpagesPublicDirectory)
+ .Select(path => fs.Path.Combine(pathToPublicDirectoryOnSystem, fs.Path.GetFileName(path)))
+ .OrderBy(name => name)
+ .ToList();
+
+ entries.AddRange(fs.Directory
+ .EnumerateFiles(webpagesPublicDirectory)
+ .Select(path => fs.Path.Combine(pathToPublicDirectoryOnSystem, fs.Path.GetFileName(path)))
+ .OrderBy(name => name)
+ .ToList());
+
+ if (entries.Count > 0)
+ {
+ var readmeFilePath = fs.Path.Combine(catalogInformationFolder, "README.md");
+ if (fs.File.Exists(readmeFilePath))
+ {
+ var noticeLines = new List
+ {
+ "",
+ "",
+ "> [!IMPORTANT]",
+ ">",
+ $"> - For DataMiner versions prior to 10.5.10, this package includes files located in `{pathToPublicDirectoryOnSystem}` that are **not automatically deployed** to all Agents in a DataMiner System.",
+ "> - To ensure proper functionality across the entire cluster, manually copy the following files and folders to the corresponding location on each Agent after installation:",
+ ">",
+ };
+
+ noticeLines.AddRange(entries.Select(e => $"> - `{e}`"));
+ noticeLines.Add(""); // final newline for clean formatting
+
+ var noticeText = string.Join(Environment.NewLine, noticeLines);
+ fs.File.AppendAllText(readmeFilePath, noticeText);
+ }
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/SdkTests/SdkTests.csproj b/SdkTests/SdkTests.csproj
index 8790916..f4ad545 100644
--- a/SdkTests/SdkTests.csproj
+++ b/SdkTests/SdkTests.csproj
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/SdkTests/Tasks/DmappCreationTests.cs b/SdkTests/Tasks/DmappCreationTests.cs
index 07b5067..883a87a 100644
--- a/SdkTests/Tasks/DmappCreationTests.cs
+++ b/SdkTests/Tasks/DmappCreationTests.cs
@@ -1,5 +1,7 @@
namespace SdkTests.Tasks
{
+ using System.IO.Compression;
+
using FluentAssertions;
using Microsoft.Build.Framework;
@@ -95,5 +97,126 @@ public void ExecuteTest_Package6()
FileSystem.Instance.Directory.DeleteDirectory(tempDirectory);
}
}
+
+ [TestMethod]
+ public void ExecuteCatalogInformation_NoNotice()
+ {
+ string tempDirectory = FileSystem.Instance.Directory.CreateTemporaryDirectory();
+ try
+ {
+ string projectDir = FileSystem.Instance.Path.Combine(TestHelper.GetTestFilesDirectory(), "Package 6", "My Package");
+
+ CatalogInformation info = new CatalogInformation()
+ {
+ ProjectDirectory = projectDir,
+ Output = tempDirectory,
+ PackageId = "My Package",
+ PackageVersion = "1.0.0",
+ BuildEngine = buildEngine.Object
+ };
+
+ string expectedDestinationFilePath = FileSystem.Instance.Path.Combine(
+ tempDirectory,
+ BuildOutputHandler.BuildDirectoryName,
+ $"{info.PackageId}.{info.PackageVersion}.CatalogInformation.zip");
+
+ // LOAD original README.md
+ string expectedReadmePath = FileSystem.Instance.Path.Combine(projectDir, "CatalogInformation", "README.md");
+ FileSystem.Instance.File.Exists(expectedReadmePath).Should().BeTrue("expected README.md must exist");
+ string expectedReadmeContent = FileSystem.Instance.File.ReadAllText(expectedReadmePath);
+
+ // Act
+ bool result = info.Execute();
+ errors.Should().BeEmpty();
+ result.Should().BeTrue();
+ FileSystem.Instance.File.Exists(expectedDestinationFilePath).Should().BeTrue();
+
+ // UNZIP to a temporary folder
+ string unzipDir = FileSystem.Instance.Path.Combine(tempDirectory, "unzipped");
+ ZipFile.ExtractToDirectory(expectedDestinationFilePath, unzipDir);
+
+ // FIND README.md inside the unzipped content
+ string[] readmeFiles = Directory.GetFiles(unzipDir, "README.md", SearchOption.AllDirectories);
+ readmeFiles.Length.Should().Be(1, "there should be exactly one README.md in the zipped output");
+ string actualReadmeContent = File.ReadAllText(readmeFiles[0]);
+
+ // COMPARE
+ actualReadmeContent.Should().Be(expectedReadmeContent, "README.md content in zip should match source");
+ }
+ finally
+ {
+ FileSystem.Instance.Directory.DeleteDirectory(tempDirectory);
+ }
+ }
+
+
+ [TestMethod]
+ public void ExecuteCatalogInformation_WithNotice()
+ {
+ string tempDirectory = FileSystem.Instance.Directory.CreateTemporaryDirectory();
+ try
+ {
+ // Arrange
+ string projectDir = FileSystem.Instance.Path.Combine(TestHelper.GetTestFilesDirectory(), "Package 7", "My Package");
+
+ CatalogInformation info = new CatalogInformation()
+ {
+ ProjectDirectory = projectDir,
+ Output = tempDirectory,
+ PackageId = "My Package",
+ PackageVersion = "1.0.0",
+ BuildEngine = buildEngine.Object
+ };
+
+ string expectedDestinationFilePath = FileSystem.Instance.Path.Combine(
+ tempDirectory,
+ BuildOutputHandler.BuildDirectoryName,
+ $"{info.PackageId}.{info.PackageVersion}.CatalogInformation.zip");
+
+ // LOAD original README.md
+ string expectedReadmePath = FileSystem.Instance.Path.Combine(projectDir, "CatalogInformation", "README.md");
+ FileSystem.Instance.File.Exists(expectedReadmePath).Should().BeTrue("expected README.md must exist");
+ string expectedReadmeContent = FileSystem.Instance.File.ReadAllText(expectedReadmePath);
+
+ // Act
+ bool result = info.Execute();
+ errors.Should().BeEmpty();
+ result.Should().BeTrue();
+ FileSystem.Instance.File.Exists(expectedDestinationFilePath).Should().BeTrue();
+
+ // UNZIP to a temporary folder
+ string unzipDir = FileSystem.Instance.Path.Combine(tempDirectory, "unzipped");
+ ZipFile.ExtractToDirectory(expectedDestinationFilePath, unzipDir);
+
+ // FIND README.md inside the unzipped content
+ string[] readmeFiles = Directory.GetFiles(unzipDir, "README.md", SearchOption.AllDirectories);
+ readmeFiles.Length.Should().Be(1, "there should be exactly one README.md in the zipped output");
+ string actualReadmeContent = File.ReadAllText(readmeFiles[0]);
+ string expectedOriginalReadmeContent = FileSystem.Instance.File.ReadAllText(expectedReadmePath);
+
+ // Normalize line endings to \n and trim
+ string Normalize(string input) =>
+ input.Replace("\r\n", "\n").Replace("\r", "\n").TrimEnd();
+
+ string expectedWithNotice = expectedOriginalReadmeContent +
+ "\n\n" +
+ "> [!IMPORTANT]\n" +
+ ">\n" +
+ "> - For DataMiner versions prior to 10.5.10, this package includes files located in `C:\\Skyline DataMiner\\Webpages\\Public` that are **not automatically deployed** to all Agents in a DataMiner System.\n" +
+ "> - To ensure proper functionality across the entire cluster, manually copy the following files and folders to the corresponding location on each Agent after installation:\n" +
+ ">\n" +
+ "> - `C:\\Skyline DataMiner\\Webpages\\Public\\MyDirectory`\n" +
+ "> - `C:\\Skyline DataMiner\\Webpages\\Public\\MyFile1.txt`\n" +
+ "> - `C:\\Skyline DataMiner\\Webpages\\Public\\XMLFile1.xml`";
+
+
+ Normalize(actualReadmeContent)
+ .Should().Be(Normalize(expectedWithNotice), "README.md content in zip should match source with a notice appended to it.");
+ }
+ finally
+ {
+ FileSystem.Instance.Directory.DeleteDirectory(tempDirectory);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/.editorconfig b/SdkTests/Test Files/Package 7/.editorconfig
new file mode 100644
index 0000000..a612a7a
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = 4
+tab_width = 4
+end_of_line = crlf
+trim_trailing_whitespace = true
+
+[*.cs]
+dotnet_sort_system_directives_first = true
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/Directory.Build.props b/SdkTests/Test Files/Package 7/Directory.Build.props
new file mode 100644
index 0000000..ef01745
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/Directory.Build.props
@@ -0,0 +1,24 @@
+
+
+ full
+ ..\Internal\Code Analysis\qaction-debug.ruleset
+
+
+ pdbonly
+ ..\Internal\Code Analysis\qaction-release.ruleset
+
+
+
+ Properties\stylecop.json
+
+
+ Properties\.editorconfig
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-debug.ruleset b/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-debug.ruleset
new file mode 100644
index 0000000..26e7d46
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-debug.ruleset
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-release.ruleset b/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-release.ruleset
new file mode 100644
index 0000000..de0890a
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/Internal/Code Analysis/qaction-release.ruleset
@@ -0,0 +1,484 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/Internal/Code Analysis/stylecop.json b/SdkTests/Test Files/Package 7/Internal/Code Analysis/stylecop.json
new file mode 100644
index 0000000..b2d519d
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/Internal/Code Analysis/stylecop.json
@@ -0,0 +1,74 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "indentation": {
+ "useTabs": true,
+ "tabSize": 4
+ },
+ "namingRules": {
+ "allowCommonHungarianPrefixes": false,
+ "allowedHungarianPrefixes": [
+ "ab",
+ "ac",
+ "ad",
+ "af",
+ "ai",
+ "al",
+ "ao",
+ "as",
+ "ax",
+ "b",
+ "by",
+ "c",
+ "d",
+ "dt",
+ "e",
+ "f",
+ "hs",
+ "i",
+ "ip",
+ "is",
+ "l",
+ "lb",
+ "lc",
+ "ld",
+ "lf",
+ "li",
+ "ll",
+ "lo",
+ "ls",
+ "lx",
+ "my",
+ "no",
+ "o",
+ "qb",
+ "qc",
+ "qd",
+ "qf",
+ "qi",
+ "ql",
+ "qo",
+ "qs",
+ "qx",
+ "rx",
+ "s",
+ "sb",
+ "sc",
+ "sd",
+ "sf",
+ "sh",
+ "si",
+ "sl",
+ "so",
+ "ss",
+ "sx",
+ "to",
+ "ts",
+ "tx",
+ "ui",
+ "ul",
+ "x"
+ ]
+ }
+ }
+}
diff --git a/SdkTests/Test Files/Package 7/My Package/CatalogInformation/Images/wip.png b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/Images/wip.png
new file mode 100644
index 0000000..2413c2d
Binary files /dev/null and b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/Images/wip.png differ
diff --git a/SdkTests/Test Files/Package 7/My Package/CatalogInformation/README.md b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/README.md
new file mode 100644
index 0000000..0ef542d
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/README.md
@@ -0,0 +1,3 @@
+# My Package
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/CatalogInformation/manifest.yml b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/manifest.yml
new file mode 100644
index 0000000..1bd7ec5
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/CatalogInformation/manifest.yml
@@ -0,0 +1,67 @@
+# [Required]
+# Possible values for the Catalog item that can be deployed on a DataMiner System:
+# - Automation: If the Catalog item is a general-purpose DataMiner Automation script.
+# - Ad Hoc Data Source: If the Catalog item is a DataMiner Automation script designed for an ad hoc data source integration.
+# - ChatOps Extension: If the Catalog item is a DataMiner Automation script designed as a ChatOps extension.
+# - Connector: If the Catalog item is a DataMiner XML connector.
+# - Custom Solution: If the Catalog item is a DataMiner Solution.
+# - Data Query: If the Catalog item is a GQI data query.
+# - Data Transformer: Includes a data transformer that enables you to modify data using a GQI data query before making it available to users in low-code apps or dashboards.
+# - Dashboard: If the Catalog item is a DataMiner dashboard.
+# - DevTool: If the Catalog item is a DevTool.
+# - Learning & Sample: If the Catalog item is a sample.
+# - Product Solution: If the Catalog item is a DataMiner Solution that is an out-of-the-box solution for a specific product.
+# - Scripted Connector: If the Catalog item is a DataMiner scripted connector.
+# - Standard Solution: If the Catalog item is a DataMiner Solution that is an out-of-the-box solution for a specific use case or application.
+# - System Health: If the Catalog item is intended to monitor the health of a system.
+# - User-Defined API: If the Catalog item is a DataMiner Automation script designed as a user-defined API.
+# - Visual Overview: If the Catalog item is a Microsoft Visio design.
+#
+type: Custom Solution
+# [Required]
+# The ID of the Catalog item.
+# All registered versions for the same ID are shown together in the Catalog.
+# This ID cannot be changed.
+# If the ID is not filled in, the registration will fail with HTTP status code 500.
+# If the ID is filled in but does not exist yet, a new Catalog item will be registered with this ID.
+# If the ID is filled in but does exist, properties of the item will be overwritten.
+# Must be a valid GUID.
+id: e9ef5f60-fa3f-4081-b879-39ffb031255a
+# [Required]
+# The human-friendly name of the Catalog item.
+# Can be changed at any time.
+# Max length: 100 characters.
+# Cannot contain newlines.
+# Cannot contain leading or trailing whitespace characters.
+title: My Package
+# [Optional]
+# General information about the Catalog item.
+# Max length: 100,000 characters
+short_description: This is a custom solution for DataMiner.
+# [Optional]
+# A valid URL that points to the source code.
+# A valid URL
+# Max length: 2048 characters
+# Note: When Skyline Communications Reusable GitHub workflows are used, this will be automatically filled in.
+source_code_url:
+# [Optional]
+# A valid URL that points to documentation.
+# A valid URL
+# Max length: 2048 characters
+# Note: When Skyline Communications Reusable GitHub workflows are used, this will be automatically filled in.
+documentation_url:
+# [Optional]
+# People who are responsible for this Catalog item. Might be developers, but this is not required.
+# Format: 'name (URL)'
+# The name is required; max 256 characters.
+# The email and URL are optional, and should be in valid email/URL formats.
+owners:
+ - name: 'MOD'
+# [Optional]
+# Tags that allow you to categorize your Catalog items.
+# Max number of tags: 5
+# Max length: 50 characters.
+# Cannot contain newlines.
+# Cannot contain leading or trailing whitespace characters.
+tags:
+ - dataminer
diff --git a/SdkTests/Test Files/Package 7/My Package/GettingStarted.md b/SdkTests/Test Files/Package 7/My Package/GettingStarted.md
new file mode 100644
index 0000000..ab44a75
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/GettingStarted.md
@@ -0,0 +1,110 @@
+# Getting Started with Skyline DataMiner DevOps
+
+Welcome to the Skyline DataMiner DevOps environment!
+This quick-start guide will help you get up and running.
+For more details and comprehensive instructions, please visit [DataMiner Docs](https://docs.dataminer.services/).
+
+## Creating a DataMiner Application Package
+
+This project is configured to create a `.dmapp` file every time you build the project.
+When you compile or build the project, you will find the generated `.dmapp` in the standard output folder, typically the `bin` folder of your project.
+
+When you publish the project, a corresponding item will be created in the online DataMiner Catalog.
+
+## The DataMiner Package Project
+
+This project is designed to create multi-artifact packages in a straightforward manner.
+
+### Adding Extra Artifacts in the Same Solution
+
+You can right-click the solution and select **Add** and then **New Project**. This will allow you to select DataMiner project templates (e.g. adding additional Automation scripts).
+
+> [!NOTE]
+> Connectors are currently not supported.
+
+Every **Skyline.DataMiner.SDK** project, except other DataMiner package projects, will by default be included within the `.dmapp` created by this project.
+You can customize this behavior using the **PackageContent/ProjectReferences.xml** file. This allows you to add filters to include or exclude projects as needed.
+
+
+
+### Importing from DataMiner
+
+You can import specific items directly from a DataMiner Agent:
+
+1. Connect to an Agent via **Extensions > DIS > DMA > Connect**.
+
+1. If your Agent is not listed, add it by going to **Extensions > DIS > Settings** and clicking **Add** on the DMA tab.
+
+1. Once connected, you can import specific DataMiner artifacts: in your **Solution Explorer**, navigate to folders such as **PackageContent/Dashboards** or **PackageContent/LowCodeApps**, right-click, select **Add**, and select **Import DataMiner Dashboard/Low Code App** or the equivalent.
+
+## Executing Additional Code on Installation
+
+Open the **My Package.cs** file to write custom installation code. Common actions include creating elements, services, or views.
+
+**Quick tip:** Type `clGetDms` in the `.cs` file and press **Tab** twice to insert a snippet that gives you access to the **IDms** classes, making DataMiner manipulation easier.
+
+## Does Your Installation Code Need Configuration Files?
+
+You can add configuration files (e.g. `.json`, `.xml`) to the **SetupContent** folder, which can be accessed during installation.
+
+Access them in your code using:
+
+```csharp
+string setupContentPath = installer.GetSetupContentDirectory();
+```
+
+
+## Publishing to the Catalog
+
+This project was created with support for publishing to the DataMiner Catalog.
+You can publish your artifact manually through Visual Studio or by setting up a CI/CD workflow.
+
+### Manual Publishing
+
+1. Obtain an **Organization Key** from [admin.dataminer.services](https://admin.dataminer.services/) with the following scopes:
+ - **Register Catalog items**
+ - **Read Catalog items**
+
+1. Securely store the key using Visual Studio User Secrets:
+
+ 1. Right-click the project and select **Manage User Secrets**.
+
+ 1. Add the key in the following format:
+
+ ```json
+ {
+ "skyline": {
+ "sdk": {
+ "catalogpublishtoken": "MyKeyHere"
+ }
+ }
+ }
+ ```
+
+1. Publish the package by right-clicking your project in Visual Studio and then selecting the **Publish** option.
+
+ This will open a new window, where you will find a Publish button and a link where your item will eventually be registered.
+
+**Recommendation:** To safeguard the quality of your product, consider using a CI/CD setup to run **dotnet publish** only after passing quality checks.
+
+### Changing the Version
+
+1. Navigate to your project in Visual Studio, right-click, and select Properties.
+
+1. Search for Package Version.
+
+1. Adjust the value as needed.
+
+### Changing the Version - Alternative
+
+1. Navigate to your project in Visual Studio and double-click it.
+
+1. Adjust the "Version" XML tag to the version you want to register.
+
+ ```xml
+ 1.0.1
+ ```
diff --git a/SdkTests/Test Files/Package 7/My Package/My Package.cs b/SdkTests/Test Files/Package 7/My Package/My Package.cs
new file mode 100644
index 0000000..f0b440a
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/My Package.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Skyline.AppInstaller;
+using Skyline.DataMiner.Automation;
+using Skyline.DataMiner.Net.AppPackages;
+
+///
+/// DataMiner Script Class.
+///
+internal class Script
+{
+ ///
+ /// The script entry point.
+ ///
+ /// Provides access to the Automation engine.
+ /// Provides access to the installation context.
+ [AutomationEntryPoint(AutomationEntryPointType.Types.InstallAppPackage)]
+ public void Install(IEngine engine, AppInstallContext context)
+ {
+ try
+ {
+ engine.Timeout = new TimeSpan(0, 10, 0);
+ engine.GenerateInformation("Starting installation");
+ var installer = new AppInstaller(Engine.SLNetRaw, context);
+ installer.InstallDefaultContent();
+
+ string setupContentPath = installer.GetSetupContentDirectory();
+
+ // Custom installation logic can be added here for each individual install package.
+ }
+ catch (Exception e)
+ {
+ engine.ExitFail($"Exception encountered during installation: {e}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/My Package.csproj b/SdkTests/Test Files/Package 7/My Package/My Package.csproj
new file mode 100644
index 0000000..47f95d0
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/My Package.csproj
@@ -0,0 +1,23 @@
+
+
+ net48
+ true
+
+
+ Package
+ True
+ 1.0.0
+ Initial Version
+
+ skyline:sdk:catalogpublishtoken
+ skyline:sdk:catalogdownloadtoken
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/My Package.xml b/SdkTests/Test Files/Package 7/My Package/My Package.xml
new file mode 100644
index 0000000..c890fab
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/My Package.xml
@@ -0,0 +1,26 @@
+
+
+ My Package
+
+ Automation
+ MOD
+ FALSE
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/CatalogReferences.xml b/SdkTests/Test Files/Package 7/My Package/PackageContent/CatalogReferences.xml
new file mode 100644
index 0000000..4b6f754
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/CatalogReferences.xml
@@ -0,0 +1,20 @@
+
+
+ Microsoft Platform
+
+ 1.1.3.25
+
+
+
+ Microsoft Platform
+
+ 5.0.0
+
+
+
+ Microsoft Platform
+
+ 6.0.0
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/AboutThisFolder.md b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/AboutThisFolder.md
new file mode 100644
index 0000000..fdf2173
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/AboutThisFolder.md
@@ -0,0 +1 @@
+This folder can contain any file you would like to add under the Skyline DataMiner folder. Warning: it will overwrite any file with the same name.
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/MyDirectory/MyStuff.txt b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/MyDirectory/MyStuff.txt
new file mode 100644
index 0000000..e69de29
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/MyFile1.txt b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/MyFile1.txt
new file mode 100644
index 0000000..05eac02
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/MyFile1.txt
@@ -0,0 +1 @@
+Hello World
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/XMLFile1.xml b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/XMLFile1.xml
new file mode 100644
index 0000000..7dde50e
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/CompanionFiles/Skyline DataMiner/Webpages/Public/XMLFile1.xml
@@ -0,0 +1 @@
+
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/Dashboards/AboutThisFolder.md b/SdkTests/Test Files/Package 7/My Package/PackageContent/Dashboards/AboutThisFolder.md
new file mode 100644
index 0000000..5ad682f
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/Dashboards/AboutThisFolder.md
@@ -0,0 +1 @@
+This folder can contain .zip files containing dashboards exported from DataMiner. They will be imported during installation of a .dmapp.
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/LowCodeApps/AboutThisFolder.md b/SdkTests/Test Files/Package 7/My Package/PackageContent/LowCodeApps/AboutThisFolder.md
new file mode 100644
index 0000000..0cf0cc3
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/LowCodeApps/AboutThisFolder.md
@@ -0,0 +1 @@
+This folder can contain .zip files containing low-code apps exported from DataMiner. They will be imported during installation of a .dmapp.
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/PackageContent/ProjectReferences.xml b/SdkTests/Test Files/Package 7/My Package/PackageContent/ProjectReferences.xml
new file mode 100644
index 0000000..da65000
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/PackageContent/ProjectReferences.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/README.md b/SdkTests/Test Files/Package 7/My Package/README.md
new file mode 100644
index 0000000..98876b1
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/README.md
@@ -0,0 +1 @@
+# Technical Documentation for My Package
\ No newline at end of file
diff --git a/SdkTests/Test Files/Package 7/My Package/SetupContent/AboutThisFolder.md b/SdkTests/Test Files/Package 7/My Package/SetupContent/AboutThisFolder.md
new file mode 100644
index 0000000..100db93
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/My Package/SetupContent/AboutThisFolder.md
@@ -0,0 +1 @@
+This directory contains files that can be used by the installer, e.g. by the install script. The files will be put in the C:\Skyline DataMiner\AppPackages\Installed\.\SetupContent directory.
diff --git a/SdkTests/Test Files/Package 7/Package 7.sln b/SdkTests/Test Files/Package 7/Package 7.sln
new file mode 100644
index 0000000..3536f2c
--- /dev/null
+++ b/SdkTests/Test Files/Package 7/Package 7.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.12.35514.174 d17.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "My Package", "My Package\My Package.csproj", "{389C15E0-FFB3-40C3-94B6-8B99886F0A04}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Internal", "Internal", "{83D48836-5BD5-4084-A12A-C2F663BD9002}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Code Analysis", "Code Analysis", "{79CFE612-F7E5-40FD-A6AC-26FABE6091E2}"
+ ProjectSection(SolutionItems) = preProject
+ Internal\Code Analysis\qaction-debug.ruleset = Internal\Code Analysis\qaction-debug.ruleset
+ Internal\Code Analysis\qaction-release.ruleset = Internal\Code Analysis\qaction-release.ruleset
+ Internal\Code Analysis\stylecop.json = Internal\Code Analysis\stylecop.json
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {389C15E0-FFB3-40C3-94B6-8B99886F0A04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {389C15E0-FFB3-40C3-94B6-8B99886F0A04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {389C15E0-FFB3-40C3-94B6-8B99886F0A04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {389C15E0-FFB3-40C3-94B6-8B99886F0A04}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {79CFE612-F7E5-40FD-A6AC-26FABE6091E2} = {83D48836-5BD5-4084-A12A-C2F663BD9002}
+ EndGlobalSection
+EndGlobal