Skip to content

Commit e161ede

Browse files
committed
Custom workflow
1 parent 76d3719 commit e161ede

File tree

6 files changed

+359
-5
lines changed

6 files changed

+359
-5
lines changed
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
name: DataMiner App Packages (Custom)
2+
3+
# Controls when the workflow will run
4+
on:
5+
# Allows you to run this workflow from another workflow
6+
workflow_call:
7+
inputs:
8+
referenceName:
9+
required: true
10+
type: string
11+
runNumber:
12+
required: true
13+
type: string
14+
referenceType:
15+
required: true
16+
type: string
17+
repository:
18+
required: true
19+
type: string
20+
owner:
21+
required: true
22+
type: string
23+
sonarCloudProjectName:
24+
required: true
25+
type: string
26+
configuration:
27+
required: true
28+
type: string
29+
solutionFilterName:
30+
required: false
31+
type: string
32+
debug:
33+
required: false
34+
type: boolean
35+
secrets:
36+
sonarCloudToken:
37+
required: true
38+
dataminerToken:
39+
required: false
40+
azureToken:
41+
required: false
42+
overrideCatalogDownloadToken:
43+
required: false
44+
45+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
46+
jobs:
47+
skyline_cicd:
48+
name: Skyline CICD
49+
runs-on: ubuntu-latest
50+
steps:
51+
- uses: actions/checkout@v4
52+
with:
53+
fetch-depth: 0
54+
55+
- name: Setup .NET Core
56+
uses: actions/setup-dotnet@v4
57+
with:
58+
dotnet-version: '8.0.x'
59+
60+
- name: Cache and Install Mono
61+
uses: awalsh128/[email protected]
62+
with:
63+
packages: mono-complete
64+
65+
- name: Validate SonarCloud Project Name
66+
id: validate-sonar-name
67+
run: |
68+
if [[ -z "${{ inputs.sonarCloudProjectName }}" ]]; then
69+
echo "Error: sonarCloudProjectName is not set."
70+
echo "Please create a SonarCloud project by visiting: https://sonarcloud.io/projects/create and copy the id of the project as mentioned in the sonarcloud project url."
71+
repo_url="https://github.com/${{ github.repository }}/settings/variables/actions"
72+
echo "Then set a SONAR_NAME variable in your repository settings: $repo_url"
73+
echo "Alternatively, if you do not wish to use the Skyline Quality Gate but intend to publish your results to the catalog, you may create your workflow to include the equivalent of a dotnet publish step as shown below (remove the \\):"
74+
echo " - name: Publish"
75+
echo " env:"
76+
echo " api-key: $\{{ secrets.DATAMINER_TOKEN }}"
77+
echo " run: dotnet publish -p:Version=\"0.0.$\{{ github.run_number }}\" -p:VersionComment=\"Iterative Development\" -p:CatalogPublishKeyName=api-key"
78+
exit 1
79+
fi
80+
81+
- name: Validate SonarCloud Secret Token
82+
id: validate-sonar-token
83+
run: |
84+
if [[ -z "${{ secrets.sonarCloudToken }}" ]]; then
85+
echo "Error: sonarCloudToken is not set."
86+
echo "Please create a SonarCloud token by visiting: https://sonarcloud.io/account/security and copy the value of the created token."
87+
repo_url="https://github.com/${{ github.repository }}/settings/secrets/actions"
88+
echo "Then set a SONAR_TOKEN secret in your repository settings: $repo_url"
89+
echo "Alternatively, if you do not wish to use the Skyline Quality Gate but intend to publish your results to the catalog, you may create your workflow to include the equivalent of a dotnet publish step as shown below (remove the \\):"
90+
echo " - name: Publish"
91+
echo " env:"
92+
echo " api-key: $\{{ secrets.DATAMINER_TOKEN }}"
93+
echo " run: dotnet publish -p:Version=\"0.0.$\{{ github.run_number }}\" -p:VersionComment=\"Iterative Development\" -p:CatalogPublishKeyName=api-key"
94+
exit 1
95+
fi
96+
97+
- name: Validate DataMiner Secret Token
98+
id: validate-dataminer-token
99+
if: inputs.referenceType == 'tag'
100+
run: |
101+
if [[ -z "${{ secrets.dataminerToken }}" ]]; then
102+
echo "Error: dataminerToken is not set. Release not possible!"
103+
echo "Please create or re-use an admin.dataminer.services token by visiting: https://admin.dataminer.services/."
104+
echo "Navigate to the right organization, then go to Keys and create or find a key with the permissions Register catalog items, Download catalog versions, and Read catalog items."
105+
echo "Copy the value of the token."
106+
repo_url="https://github.com/${{ github.repository }}/settings/secrets/actions"
107+
echo "Then set a DATAMINER_TOKEN secret in your repository settings: $repo_url"
108+
exit 1
109+
fi
110+
111+
- name: Find .sln file
112+
id: findSlnFile
113+
run: |
114+
if [[ -z "${{ inputs.solutionFilterName }}" ]]; then
115+
echo solutionFilePath=$(find . -type f -name '*.sln') >> $GITHUB_OUTPUT
116+
else
117+
echo solutionFilePath=$(find . -type f -name '${{ inputs.solutionFilterName }}') >> $GITHUB_OUTPUT
118+
fi
119+
shell: bash
120+
121+
- name: Enable Skyline NuGet Registries
122+
if: inputs.owner == 'SkylineCommunications'
123+
env:
124+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
125+
AZURE_TOKEN: ${{ secrets.azureToken }}
126+
run: |
127+
$sources = @(
128+
@{ Name = "PrivateGitHubNugets"; URL = "https://nuget.pkg.github.com/SkylineCommunications/index.json"; Username = "USERNAME"; Password = "${{ secrets.GITHUB_TOKEN }}" },
129+
@{ Name = "CloudNuGets"; URL = "https://pkgs.dev.azure.com/skyline-cloud/Cloud_NuGets/_packaging/CloudNuGet/nuget/v3/index.json"; Username = "az"; Password = "${{ secrets.azureToken }}" },
130+
@{ Name = "PrivateAzureNuGets"; URL = "https://pkgs.dev.azure.com/skyline-cloud/_packaging/skyline-private-nugets/nuget/v3/index.json"; Username = "az"; Password = "${{ secrets.azureToken }}" }
131+
)
132+
133+
foreach ($source in $sources) {
134+
if ($source.Password -ne "") {
135+
Write-Host "Checking source $($source.Name)..."
136+
137+
if (dotnet nuget list source | Select-String -Pattern $source.Name) {
138+
Write-Host "Updating existing source $($source.Name)."
139+
dotnet nuget update source $source.Name --source $source.URL --username $source.Username --password $source.Password --store-password-in-clear-text
140+
} else {
141+
Write-Host "Adding new source $($source.Name)."
142+
dotnet nuget add source $source.URL --name $source.Name --username $source.Username --password $source.Password --store-password-in-clear-text
143+
}
144+
} else {
145+
Write-Host "Skipping $($source.Name) because the password is not set."
146+
}
147+
}
148+
shell: pwsh
149+
150+
- name: Install Tools
151+
run: |
152+
dotnet tool install dotnet-sonarscanner --global
153+
dotnet tool install Skyline.DataMiner.CICD.Tools.Sbom --global --version 1.0.*
154+
dotnet tool install -g Skyline.DataMiner.CICD.Tools.CatalogUpload --version 3.0.*
155+
156+
- name: Prepare SonarCloud Variables
157+
id: prepSonarCloudVar
158+
run: |
159+
import os
160+
env_file = os.getenv('GITHUB_ENV')
161+
with open(env_file, "a") as myfile:
162+
myfile.write("lowerCaseOwner=" + str.lower("${{ inputs.owner }}"))
163+
shell: python
164+
165+
- name: Get SonarCloud Status
166+
id: get-sonarcloud-status
167+
run: |
168+
sonarCloudProjectStatus=$(curl -s -u "${{ secrets.sonarCloudToken }}:" "https://sonarcloud.io/api/qualitygates/project_status?projectKey=${{ inputs.sonarCloudProjectName }}")
169+
170+
# Check if the response is empty or not valid JSON
171+
if [ -z "$sonarCloudProjectStatus" ] || ! echo "$sonarCloudProjectStatus" | jq . > /dev/null 2>&1; then
172+
echo "Error: The SONAR_TOKEN is invalid, expired, or the response is empty. Please check: https://sonarcloud.io/account/security and update your token: https://github.com/${{ github.repository }}/settings/secrets/actions" >&2
173+
echo "Returned response: $sonarCloudProjectStatus" >&2
174+
exit 1
175+
fi
176+
177+
# Output the JSON response if valid
178+
echo "Returned response: $sonarCloudProjectStatus"
179+
echo "sonarCloudProjectStatus=$sonarCloudProjectStatus" >> $GITHUB_OUTPUT
180+
continue-on-error: false
181+
shell: bash
182+
183+
- name: Trigger Initial Analysis
184+
if: ${{ fromJson(steps.get-sonarcloud-status.outputs.sonarCloudProjectStatus).projectStatus.status == 'NONE' }}
185+
env:
186+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
187+
SONAR_TOKEN: ${{ secrets.sonarCloudToken }}
188+
run: |
189+
dotnet sonarscanner begin /k:"${{ inputs.sonarCloudProjectName }}" /o:"${{ env.lowerCaseOwner }}" /d:sonar.token="${{ secrets.sonarCloudToken }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="**/TestResults/**/coverage.opencover.xml" /d:sonar.cs.vstest.reportsPaths="**/TestResults/**.trx"
190+
dotnet build "${{ steps.findSlnFile.outputs.solutionFilePath }}" `
191+
-p:GenerateDataMinerPackage=false `
192+
--configuration ${{ inputs.configuration }} `
193+
-nodeReuse:false
194+
dotnet sonarscanner end /d:sonar.token="${{ secrets.sonarCloudToken }}"
195+
continue-on-error: true
196+
shell: pwsh
197+
198+
- name: Start Analysis
199+
env:
200+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
201+
SONAR_TOKEN: ${{ secrets.sonarCloudToken }}
202+
run: |
203+
dotnet sonarscanner begin /k:"${{ inputs.sonarCloudProjectName }}" /o:"${{ env.lowerCaseOwner }}" /d:sonar.token="${{ secrets.sonarCloudToken }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="**/TestResults/**/coverage.opencover.xml" /d:sonar.cs.vstest.reportsPaths="**/TestResults/**.trx"
204+
continue-on-error: true
205+
206+
- name: Build for pre-release
207+
if: inputs.referenceType == 'branch'
208+
env:
209+
DATAMINER_TOKEN: ${{ secrets.dataminerToken }}
210+
OVERRIDE_CATALOG_DOWNLOAD_TOKEN: ${{ secrets.overrideCatalogDownloadToken }}
211+
run: |
212+
dotnet build "${{ steps.findSlnFile.outputs.solutionFilePath }}" `
213+
-p:Version="0.0.${{ inputs.runNumber }}" `
214+
--configuration ${{ inputs.configuration }} `
215+
-p:CatalogPublishKeyName="DATAMINER_TOKEN" `
216+
-p:CatalogDefaultDownloadKeyName="OVERRIDE_CATALOG_DOWNLOAD_TOKEN" `
217+
-p:SkylineDataMinerSdkDebug="${{ inputs.debug }}" `
218+
-nodeReuse:false
219+
shell: pwsh
220+
221+
- name: Build for release
222+
if: inputs.referenceType == 'tag'
223+
env:
224+
DATAMINER_TOKEN: ${{ secrets.dataminerToken }}
225+
OVERRIDE_CATALOG_DOWNLOAD_TOKEN: ${{ secrets.overrideCatalogDownloadToken }}
226+
run: |
227+
dotnet build "${{ steps.findSlnFile.outputs.solutionFilePath }}" `
228+
-p:Version="${{ inputs.referenceName }}" `
229+
--configuration ${{ inputs.configuration }} `
230+
-p:CatalogPublishKeyName="DATAMINER_TOKEN" `
231+
-p:CatalogDefaultDownloadKeyName="OVERRIDE_CATALOG_DOWNLOAD_TOKEN" `
232+
-p:SkylineDataMinerSdkDebug="${{ inputs.debug }}" `
233+
-nodeReuse:false
234+
shell: pwsh
235+
236+
- name: Remove WebApiLib dll
237+
run: |
238+
echo "Listing all files with details:"
239+
find "${{ github.workspace }}" -type f -exec ls -lh {} \;
240+
241+
- name: Unit Tests
242+
# when not using MSTest you'll need to install coverlet.collector nuget in your test solutions
243+
id: unit-tests
244+
run: dotnet test "${{ steps.findSlnFile.outputs.solutionFilePath }}" --no-build --configuration ${{ inputs.configuration }} --filter TestCategory!=IntegrationTest --logger "trx;logfilename=unitTestResults.trx" --collect "XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura,opencover
245+
246+
- name: Stop Analysis
247+
env:
248+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
249+
SONAR_TOKEN: ${{ secrets.sonarCloudToken }}
250+
run: |
251+
dotnet sonarscanner end /d:sonar.token="${{ secrets.sonarCloudToken }}"
252+
continue-on-error: true
253+
254+
- name: SonarCloud Quality Gate check
255+
if: inputs.referenceType == 'branch'
256+
id: sonarcloud-quality-gate-check
257+
uses: sonarsource/sonarqube-quality-gate-action@master
258+
with:
259+
scanMetadataReportFile: .sonarqube/out/.sonar/report-task.txt
260+
timeout-minutes: 5
261+
env:
262+
SONAR_TOKEN: ${{ secrets.sonarCloudToken }}
263+
264+
- name: Create package name
265+
if: inputs.referenceType == 'tag'
266+
id: packageName
267+
run: |
268+
$tempName = "${{ inputs.repository }}"
269+
$safeName = $tempName -replace '[\"\/\\<>|:*?]', '_'
270+
echo "name=$safeName" >> $env:GITHUB_OUTPUT
271+
shell: pwsh
272+
273+
- name: Generate SBOM file
274+
if: inputs.referenceType == 'tag'
275+
run: |
276+
find . -type f -name "*.dmapp" -print0 | while IFS= read -r -d '' file; do
277+
echo "Generating SBOM for $file"
278+
dataminer-sbom generate-and-add \
279+
--solution-path "${{ steps.findSlnFile.outputs.solutionFilePath }}" \
280+
--package-file "$file" \
281+
--package-name "${{ steps.packageName.outputs.name }}" \
282+
--package-version "${{ inputs.referenceName }}" \
283+
--package-supplier "Skyline Communications" \
284+
--debug "${{ inputs.debug }}"
285+
done
286+
287+
- uses: actions/upload-artifact@v4
288+
with:
289+
name: DataMiner Installation Packages (${{ inputs.configuration }} ${{ inputs.solutionFilterName }})
290+
path: |
291+
**/bin/${{ inputs.configuration }}/*.dmapp
292+
**/bin/${{ inputs.configuration }}/*.zip
293+
**/bin/${{ inputs.configuration }}/**/*.dmapp
294+
**/bin/${{ inputs.configuration }}/**/*.zip
295+
continue-on-error: true
296+
297+
- name: Authenticate with GitHub CLI
298+
if: inputs.referenceType == 'tag'
299+
run: gh auth login --with-token <<< "${{ secrets.GITHUB_TOKEN }}"
300+
301+
- name: Find Version Comment
302+
if: inputs.referenceType == 'tag'
303+
id: findVersionComment
304+
run: |
305+
echo "Checking for release notes associated with the reference: '${{ inputs.referenceName }}'"
306+
307+
# Retrieve the release note body
308+
RELEASE_NOTE=$(gh release view "${{ inputs.referenceName }}" --json body -q '.body' 2>/dev/null || echo "")
309+
310+
escape_special_chars() {
311+
echo "$1" | sed -e 's/,/%2c/g' -e 's/"/%22/g' -e 's/;/%3b/g'
312+
}
313+
314+
if [[ -n "$RELEASE_NOTE" ]]; then
315+
ESCAPED_RELEASE_NOTE=$(escape_special_chars "$RELEASE_NOTE")
316+
echo "Release note found for '${{ inputs.referenceName }}': $ESCAPED_RELEASE_NOTE"
317+
# Escape multiline string for GITHUB_OUTPUT
318+
echo "versionComment<<EOF" >> $GITHUB_OUTPUT
319+
echo "$ESCAPED_RELEASE_NOTE" >> $GITHUB_OUTPUT
320+
echo "EOF" >> $GITHUB_OUTPUT
321+
else
322+
echo "No release note found for '${{ inputs.referenceName }}'. Falling back to tag or commit message."
323+
VERSION_COMMENT=$(git describe --tags --exact-match 2>/dev/null || git log -1 --pretty=format:%s)
324+
ESCAPED_VERSION_COMMENT=$(escape_special_chars "$VERSION_COMMENT")
325+
echo "Fallback version comment: $ESCAPED_VERSION_COMMENT"
326+
# Escape fallback as well
327+
echo "versionComment=$ESCAPED_VERSION_COMMENT" >> $GITHUB_OUTPUT
328+
fi
329+
shell: bash
330+
331+
- name: Publish Catalog Details
332+
if: inputs.referenceType == 'branch' && inputs.referenceName == '1.0.0.X'
333+
shell: pwsh
334+
run: |
335+
dataminer-catalog-upload update-catalog-details `
336+
--path-to-catalog-yml "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/manifest.yml" `
337+
--path-to-readme "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/README.md"
338+
--path-to-images "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/Images"
339+
--dm-catalog-token "${{ secrets.dataminerToken }}" `
340+
--debug "${{ inputs.debug }}"
341+
342+
- name: Publish To Catalog
343+
if: inputs.referenceType == 'tag' && inputs.referenceName == '1.0.0.X'
344+
env:
345+
DATAMINER_TOKEN: ${{ secrets.dataminerToken }}
346+
shell: pwsh
347+
run: |
348+
dataminer-catalog-upload with-registration `
349+
--path-to-catalog-yml "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/manifest.yml" `
350+
--path-to-readme "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/README.md"
351+
--path-to-images "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/Images"
352+
--dm-catalog-token "${{ secrets.dataminerToken }}" `
353+
--debug "${{ inputs.debug }}"

.github/workflows/complete.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ on:
1818
jobs:
1919

2020
CI:
21-
uses: SkylineCommunications/_ReusableWorkflows/.github/workflows/DataMiner App Packages Master Workflow.yml@main
21+
uses: SkylineCommunications/Low-Code-App-Editor/.github/workflows/DataMiner App Packages Master Workflow Custom.yml@16-duplicate-page-is-not-possible
2222
with:
2323
configuration: Release
2424
referenceName: ${{ github.ref_name }}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<ProjectReferences xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.skyline.be/projectReferences">
22
<!-- Default include all -->
33
<ProjectReference Include="..\*\*.csproj" />
4+
<ProjectReference Exclude="..\Low Code App Editor Installer\Low Code App Editor Installer.csproj" />
45
<!--
56
<SolutionFilter Include="..\MySolutionFilter.slnf" />
67
<ProjectReference Include="..\ProjectToInclude\ProjectToInclude.csproj" />
7-
<ProjectReference Exclude="..\ProjectToExclude\ProjectToExclude.csproj" />
88
-->
99
</ProjectReferences>

Low Code App Editor/DOM/DomExporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private void IncrementProgressCounter(int items)
172172

173173
private sealed class Writer : IDisposable
174174
{
175-
private StringBuilder sb;
175+
private readonly StringBuilder sb;
176176

177177
public Writer()
178178
{

0 commit comments

Comments
 (0)