Skip to content

Commit e599a88

Browse files
committed
Add unit test project and fix ProjectReferencesHelper
1 parent d3eefcb commit e599a88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2890
-13
lines changed

Sdk/Helpers/ProjectReferencesHelper.cs

+39-13
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static bool TryResolveProjectReferences(Project packageProject, out List<
1919
includedProjectPaths = null;
2020
errorMessage = null;
2121

22-
string rootFolder = FileSystem.Instance.Directory.GetParentDirectory(packageProject.ProjectDirectory);
22+
string rootFolder = packageProject.ProjectDirectory;
2323
const string xmlFileName = "ProjectReferences.xml";
2424
string xmlFilePath = FileSystem.Instance.Path.Combine(packageProject.ProjectDirectory, "PackageContent", xmlFileName);
2525

@@ -101,28 +101,54 @@ private static List<string> ResolveProjectReferences(string xmlFilePath, string
101101
var includedProjects = new HashSet<string>();
102102
foreach (var pattern in includePatterns)
103103
{
104-
var resolvedPaths = FileSystem.Instance.Directory.GetFiles(rootFolder, "*.csproj", SearchOption.AllDirectories)
105-
.Where(path => MatchesPattern(path, pattern));
106-
foreach (var path in resolvedPaths)
107-
{
108-
includedProjects.Add(path);
109-
}
104+
includedProjects.UnionWith(ResolveAndSearchFiles(rootFolder, pattern));
110105
}
111106

112107
// Apply Exclude patterns
113108
foreach (var pattern in excludePatterns)
114109
{
115-
var resolvedPaths = FileSystem.Instance.Directory.GetFiles(rootFolder, "*.csproj", SearchOption.AllDirectories)
116-
.Where(path => MatchesPattern(path, pattern));
117-
foreach (var path in resolvedPaths)
118-
{
119-
includedProjects.Remove(path);
120-
}
110+
includedProjects.ExceptWith(ResolveAndSearchFiles(rootFolder, pattern));
121111
}
122112

123113
return includedProjects.Distinct().ToList();
124114
}
125115

116+
private static IEnumerable<string> ResolveAndSearchFiles(string rootFolder, string pattern)
117+
{
118+
pattern = pattern.Replace('/', FileSystem.Instance.Path.DirectorySeparatorChar)
119+
.Replace('\\', FileSystem.Instance.Path.DirectorySeparatorChar);
120+
121+
string directoryPattern = FileSystem.Instance.Path.GetDirectoryName(pattern) ?? String.Empty;
122+
string filePattern = FileSystem.Instance.Path.GetFileName(pattern) ?? String.Empty;
123+
124+
string searchBase = rootFolder;
125+
string elevate = ".." + FileSystem.Instance.Path.DirectorySeparatorChar;
126+
while (directoryPattern.StartsWith(elevate))
127+
{
128+
searchBase = FileSystem.Instance.Directory.GetParentDirectory(searchBase) ?? throw new Exception("Invalid path traversal");
129+
directoryPattern = directoryPattern.Substring(3);
130+
}
131+
132+
IEnumerable<string> directories = ExpandDirectories(searchBase, directoryPattern);
133+
134+
return directories.SelectMany(dir => FileSystem.Instance.Directory.GetFiles(dir, filePattern, SearchOption.TopDirectoryOnly));
135+
}
136+
137+
private static IEnumerable<string> ExpandDirectories(string baseDirectory, string pattern)
138+
{
139+
if (String.IsNullOrEmpty(pattern)) return new List<string> { baseDirectory };
140+
141+
string[] parts = pattern.Split(new []{ FileSystem.Instance.Path.DirectorySeparatorChar }, 2);
142+
string currentPart = parts[0];
143+
string remainingPattern = parts.Length > 1 ? parts[1] : "";
144+
145+
// Get directories matching the current part
146+
IEnumerable<string> matchingDirs = Directory.GetDirectories(baseDirectory, currentPart, SearchOption.TopDirectoryOnly);
147+
148+
// Recurse for deeper directories if needed
149+
return matchingDirs.SelectMany(dir => ExpandDirectories(dir, remainingPattern));
150+
}
151+
126152
private static bool MatchesPattern(string filePath, string pattern)
127153
{
128154
// Convert the pattern to a regex for matching, if necessary

Sdk/Sdk.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
<PackageReference Include="System.Text.Json" Version="9.0.1" />
4646
</ItemGroup>
4747

48+
<ItemGroup>
49+
<InternalsVisibleTo Include="SdkTests" />
50+
</ItemGroup>
51+
4852
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
4953
<ItemGroup>
5054
<!-- The TargetPath is the path inside the package that the source file will be placed. This is already precomputed in the ReferenceCopyLocalPaths items' DestinationSubPath, so reuse it here. -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace SdkTests.Helpers
2+
{
3+
using FluentAssertions;
4+
using Skyline.DataMiner.CICD.Parsers.Common.VisualStudio.Projects;
5+
using Skyline.DataMiner.Sdk.Helpers;
6+
7+
[TestClass]
8+
public class ProjectReferencesHelperTests
9+
{
10+
[TestMethod]
11+
public void TryResolveProjectReferencesTest_PackageSolution1()
12+
{
13+
// Arrange
14+
string packageProjectPath = Path.Combine(TestHelper.GetTestFilesDirectory(), "PackageSolution1", "MyPackage", "MyPackage.csproj");
15+
Project packageProject = Project.Load(packageProjectPath);
16+
17+
// Act
18+
bool result = ProjectReferencesHelper.TryResolveProjectReferences(packageProject, out List<string> includedProjectPaths, out string errorMessage);
19+
20+
// Assert
21+
result.Should().BeTrue();
22+
errorMessage.Should().BeNullOrEmpty();
23+
includedProjectPaths.Should().NotBeNull();
24+
includedProjectPaths.Should().HaveCount(2);
25+
26+
includedProjectPaths.Should().ContainMatch("*MyPackage.csproj*");
27+
includedProjectPaths.Should().ContainMatch("*My Script.csproj*");
28+
}
29+
30+
[TestMethod]
31+
public void TryResolveProjectReferencesTest_PackageSolution2()
32+
{
33+
/*
34+
* ProjectReferences will try to reference only the package project (*.csproj).
35+
*/
36+
37+
// Arrange
38+
string packageProjectPath = Path.Combine(TestHelper.GetTestFilesDirectory(), "PackageSolution2", "MyPackage", "MyPackage.csproj");
39+
Project packageProject = Project.Load(packageProjectPath);
40+
41+
// Act
42+
bool result = ProjectReferencesHelper.TryResolveProjectReferences(packageProject, out List<string> includedProjectPaths, out string errorMessage);
43+
44+
// Assert
45+
result.Should().BeTrue();
46+
errorMessage.Should().BeNullOrEmpty();
47+
includedProjectPaths.Should().NotBeNull();
48+
includedProjectPaths.Should().HaveCount(1);
49+
50+
includedProjectPaths.Should().ContainMatch("*MyPackage.csproj*");
51+
}
52+
53+
[TestMethod]
54+
public void TryResolveProjectReferencesTest_PackageProject1()
55+
{
56+
// Arrange
57+
string packageProjectPath = Path.Combine(TestHelper.GetTestFilesDirectory(), "NonSolution1", "PackageProject1", "Package Project 1.csproj");
58+
Project packageProject = Project.Load(packageProjectPath);
59+
60+
// Act
61+
bool result = ProjectReferencesHelper.TryResolveProjectReferences(packageProject, out List<string> includedProjectPaths, out string errorMessage);
62+
63+
// Assert
64+
result.Should().BeTrue();
65+
errorMessage.Should().BeNullOrEmpty();
66+
includedProjectPaths.Should().NotBeNull();
67+
includedProjectPaths.Should().HaveCount(1);
68+
69+
includedProjectPaths.Should().ContainMatch("*Package Project 1.csproj*");
70+
}
71+
}
72+
}

SdkTests/SdkTests.csproj

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net4.8;net8.0</TargetFrameworks>
5+
<LangVersion>latest</LangVersion>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="FluentAssertions" Version="7.1.0" />
12+
<PackageReference Include="MSTest" Version="3.7.3" />
13+
<PackageReference Include="Skyline.DataMiner.CICD.Assemblers.Automation" Version="1.1.0" />
14+
<PackageReference Include="System.Text.Json" Version="9.0.1" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\Sdk\Sdk.csproj" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<Compile Remove="Test Files\**" />
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<Content Include="Test Files\**">
31+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
32+
</Content>
33+
</ItemGroup>
34+
35+
<ItemGroup>
36+
<Folder Include="Test Files\PackageSolution2\" />
37+
<Folder Include="Test Files\NonSolution1\PackageProject1\" />
38+
<Folder Include="Test Files\PackageSolution1\" />
39+
</ItemGroup>
40+
41+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Getting Started with Skyline DataMiner DevOps
2+
3+
Welcome to the Skyline DataMiner DevOps environment!
4+
This quick-start guide will help you get up and running.
5+
For more details and comprehensive instructions, please visit [DataMiner Docs](https://docs.dataminer.services/).
6+
7+
## Creating a DataMiner Application Package
8+
9+
This project is configured to create a `.dmapp` file every time you build the project.
10+
Simply compile or build the project, and you will find the generated `.dmapp` in the standard output folder, typically the `bin` folder of your project.
11+
12+
When publishing, this project will become its own item on the online catalog.
13+
14+
## The DataMiner Package Project
15+
16+
This project is designed to create multi-artifact packages in a straightforward manner.
17+
18+
### Other DataMiner Projects in the Same Solution
19+
20+
Every **Skyline.DataMiner.SDK** project, except other DataMiner Package Projects, will by default be included within the `.dmapp` created by this project.
21+
You can customize this behavior using the **PackageContent/ProjectReferences.xml** file. This allows you to add filters to include or exclude projects as needed.
22+
23+
### Adding Content from the Catalog
24+
25+
You can reference and include additional content from the catalog using the **PackageContent/CatalogReferences.xml** file provided in this project.
26+
27+
### Importing from DataMiner
28+
29+
You can import specific items directly from a DataMiner agent:
30+
31+
1. Connect to an agent via **Extensions > DIS > DMA > Connect**.
32+
2. Once connected, you can import specific DataMiner artifacts.
33+
3. Navigate to folders such as **PackageContent/Dashboards** or **PackageContent/LowCodeApps**, right-click, select **Add**, and choose **Import DataMiner Dashboard/LowCodeApp** or the equivalent.
34+
35+
## Execute Additional Code on Installation
36+
37+
Open the **Package Project 1.cs** file to write custom installation code. Common actions include creating elements, services, or views.
38+
39+
**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.
40+
41+
## Does Your Installation Code Need Configuration Files?
42+
43+
You can add configuration files (e.g., `.json`, `.xml`) to the **SetupContent** folder, which can be accessed during installation.
44+
45+
Access them in your code using:
46+
```csharp
47+
string setupContentPath = installer.GetSetupContentDirectory();
48+
```
49+
50+
51+
## Publishing to the Catalog
52+
53+
This project was created with support for publishing to the DataMiner catalog.
54+
You can publish your artifact manually through Visual Studio or by setting up a CI/CD workflow.
55+
56+
### Manual Publishing
57+
58+
1. Obtain an **Organization Key** from [admin.dataminer.services](https://admin.dataminer.services/) with the following scopes:
59+
- **Register catalog items**
60+
- **Read catalog items**
61+
62+
2. Securely store the key using Visual Studio User Secrets:
63+
- Right-click the project and select **Manage User Secrets**.
64+
- Add the key in the following format:
65+
66+
```json
67+
{
68+
"skyline": {
69+
"sdk": {
70+
"catalogpublishtoken": "MyKeyHere"
71+
}
72+
}
73+
}
74+
```
75+
76+
3. Publish the package using the **Publish** option in Visual Studio.
77+
78+
**Recommendation:** For stable releases, consider using a CI/CD setup to run **dotnet publish** after passing quality checks.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
3+
using Skyline.AppInstaller;
4+
using Skyline.DataMiner.Automation;
5+
using Skyline.DataMiner.Net.AppPackages;
6+
7+
/// <summary>
8+
/// DataMiner Script Class.
9+
/// </summary>
10+
internal class Script
11+
{
12+
/// <summary>
13+
/// The script entry point.
14+
/// </summary>
15+
/// <param name="engine">Provides access to the Automation engine.</param>
16+
/// <param name="context">Provides access to the installation context.</param>
17+
[AutomationEntryPoint(AutomationEntryPointType.Types.InstallAppPackage)]
18+
public void Install(IEngine engine, AppInstallContext context)
19+
{
20+
try
21+
{
22+
engine.Timeout = new TimeSpan(0, 10, 0);
23+
engine.GenerateInformation("Starting installation");
24+
var installer = new AppInstaller(Engine.SLNetRaw, context);
25+
installer.InstallDefaultContent();
26+
27+
string setupContentPath = installer.GetSetupContentDirectory();
28+
29+
// Custom installation logic can be added here for each individual install package.
30+
}
31+
catch (Exception e)
32+
{
33+
engine.ExitFail($"Exception encountered during installation: {e}");
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Skyline.DataMiner.Sdk/0.0.4">
2+
<PropertyGroup>
3+
<TargetFramework>net48</TargetFramework>
4+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
5+
</PropertyGroup>
6+
<PropertyGroup>
7+
<DataMinerType>Package</DataMinerType>
8+
<GenerateDataMinerPackage>True</GenerateDataMinerPackage>
9+
<Version>1.0.0</Version>
10+
<VersionComment>Initial Version</VersionComment>
11+
<CatalogDefaultDownloadKeyName>skyline:sdk:catalogdownloadtoken</CatalogDefaultDownloadKeyName>
12+
</PropertyGroup>
13+
<ItemGroup>
14+
<PackageReference Include="Skyline.DataMiner.Core.AppPackageInstaller" Version="2.0.1" />
15+
<PackageReference Include="Skyline.DataMiner.Dev.Automation" Version="10.2.0.6" />
16+
<PackageReference Include="Skyline.DataMiner.Utils.SecureCoding.Analyzers" Version="1.0.0">
17+
<PrivateAssets>all</PrivateAssets>
18+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19+
</PackageReference>
20+
</ItemGroup>
21+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<DMSScript options="272" xmlns="http://www.skyline.be/automation">
3+
<Name>Package Project 1</Name>
4+
<Description></Description>
5+
<Type>Automation</Type>
6+
<Author>MOD</Author>
7+
<CheckSets>FALSE</CheckSets>
8+
<Folder></Folder>
9+
10+
<Protocols>
11+
</Protocols>
12+
13+
<Memory>
14+
</Memory>
15+
16+
<Parameters>
17+
</Parameters>
18+
19+
<Script>
20+
<Exe id="1" type="csharp">
21+
<Value><![CDATA[[Project:Package Project 1]]]></Value>
22+
<!--<Param type="debug">true</Param>-->
23+
<Message></Message>
24+
</Exe>
25+
</Script>
26+
</DMSScript>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<CatalogReferences xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.skyline.be/catalogReferences">
2+
<!-- Currently not supported -->
3+
</CatalogReferences>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This folder can contain .zip files containing Dashboards exported from DataMiner. They will be imported during installation of a .dmapp.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This folder can contain .zip files containing Low-Code-Apps exported from DataMiner. They will be imported during installation of a .dmapp.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<ProjectReferences xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.skyline.be/projectReferences">
2+
<!-- Default include all -->
3+
<ProjectReference Include="..\*\*.csproj" />
4+
<!--
5+
<SolutionFilter Include="..\MySolutionFilter.slnf" />
6+
<ProjectReference Include="..\ProjectToInclude\ProjectToInclude.csproj" />
7+
<ProjectReference Exclude="..\ProjectToExclude\ProjectToExclude.csproj" />
8+
-->
9+
</ProjectReferences>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Technical Documentation for Package Project 1

0 commit comments

Comments
 (0)