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+ 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 : List all the files (Debug)
237+ run : |
238+ echo "Listing all files with details:"
239+ echo "${{ github.workspace }}"
240+ find "${{ github.workspace }}/Low Code App Editor Package" -type f -exec ls -lh {} \;
241+
242+ - name : Remove WebApiLib dll
243+ run : |
244+ find . -type f -name "*.dmapp" -print0 | while IFS= read -r -d '' file; do
245+ echo "Updating $file"
246+
247+ # Create a temp directory
248+ temp_dir=$(mktemp -d)
249+
250+ # Unzip the .dmapp file into the temp dir
251+ unzip -q "$file" -d "$temp_dir"
252+
253+ echo "📂 Files before removal:"
254+ find "$temp_dir" -type f
255+
256+ # Find and delete all files ending in WebApiLib.dll
257+ find "$temp_dir" -type f -name "*WebApiLib.dll" -exec rm -f {} \;
258+
259+ echo "📂 Files after removal:"
260+ find "$temp_dir" -type f
261+
262+ # Recreate the .dmapp file
263+ (cd "$temp_dir" && zip -qr "../temp.dmapp" .)
264+
265+ # Move the new archive back to the original file
266+ mv -f "$temp_dir/../temp.dmapp" "$file"
267+
268+ # Cleanup
269+ rm -rf "$temp_dir"
270+
271+ echo "Cleaned $file"
272+
273+ done
274+
275+ - name : List all the files (Debug)
276+ run : |
277+ echo "Listing all files with details:"
278+ echo "${{ github.workspace }}"
279+ find "${{ github.workspace }}/Low Code App Editor Package" -type f -exec ls -lh {} \;
280+
281+ - name : Unit Tests
282+ # when not using MSTest you'll need to install coverlet.collector nuget in your test solutions
283+ id : unit-tests
284+ 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
285+
286+ - name : Stop Analysis
287+ env :
288+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
289+ SONAR_TOKEN : ${{ secrets.sonarCloudToken }}
290+ run : |
291+ dotnet sonarscanner end /d:sonar.token="${{ secrets.sonarCloudToken }}"
292+ continue-on-error : true
293+
294+ - name : SonarCloud Quality Gate check
295+ if : inputs.referenceType == 'branch'
296+ id : sonarcloud-quality-gate-check
297+ uses : sonarsource/sonarqube-quality-gate-action@master
298+ with :
299+ scanMetadataReportFile : .sonarqube/out/.sonar/report-task.txt
300+ timeout-minutes : 5
301+ env :
302+ SONAR_TOKEN : ${{ secrets.sonarCloudToken }}
303+
304+ - name : Create package name
305+ if : inputs.referenceType == 'tag'
306+ id : packageName
307+ run : |
308+ $tempName = "${{ inputs.repository }}"
309+ $safeName = $tempName -replace '[\"\/\\<>|:*?]', '_'
310+ echo "name=$safeName" >> $env:GITHUB_OUTPUT
311+ shell : pwsh
312+
313+ - name : Generate SBOM file
314+ if : inputs.referenceType == 'tag'
315+ run : |
316+ find . -type f -name "*.dmapp" -print0 | while IFS= read -r -d '' file; do
317+ echo "Generating SBOM for $file"
318+ dataminer-sbom generate-and-add \
319+ --solution-path "${{ steps.findSlnFile.outputs.solutionFilePath }}" \
320+ --package-file "$file" \
321+ --package-name "${{ steps.packageName.outputs.name }}" \
322+ --package-version "${{ inputs.referenceName }}" \
323+ --package-supplier "Skyline Communications" \
324+ --debug "${{ inputs.debug }}"
325+ done
326+
327+ - uses : actions/upload-artifact@v4
328+ with :
329+ name : DataMiner Installation Packages (${{ inputs.configuration }} ${{ inputs.solutionFilterName }})
330+ path : |
331+ **/bin/${{ inputs.configuration }}/*.dmapp
332+ **/bin/${{ inputs.configuration }}/*.zip
333+ **/bin/${{ inputs.configuration }}/**/*.dmapp
334+ **/bin/${{ inputs.configuration }}/**/*.zip
335+ continue-on-error : true
336+
337+ - name : Authenticate with GitHub CLI
338+ if : inputs.referenceType == 'tag'
339+ run : gh auth login --with-token <<< "${{ secrets.GITHUB_TOKEN }}"
340+
341+ - name : Find Version Comment
342+ if : inputs.referenceType == 'tag'
343+ id : findVersionComment
344+ run : |
345+ echo "Checking for release notes associated with the reference: '${{ inputs.referenceName }}'"
346+
347+ # Retrieve the release note body
348+ RELEASE_NOTE=$(gh release view "${{ inputs.referenceName }}" --json body -q '.body' 2>/dev/null || echo "")
349+
350+ escape_special_chars() {
351+ echo "$1" | sed -e 's/,/%2c/g' -e 's/"/%22/g' -e 's/;/%3b/g'
352+ }
353+
354+ if [[ -n "$RELEASE_NOTE" ]]; then
355+ ESCAPED_RELEASE_NOTE=$(escape_special_chars "$RELEASE_NOTE")
356+ echo "Release note found for '${{ inputs.referenceName }}' : $ESCAPED_RELEASE_NOTE"
357+ # Escape multiline string for GITHUB_OUTPUT
358+ echo "versionComment<<EOF" >> $GITHUB_OUTPUT
359+ echo "$ESCAPED_RELEASE_NOTE" >> $GITHUB_OUTPUT
360+ echo "EOF" >> $GITHUB_OUTPUT
361+ else
362+ echo "No release note found for '${{ inputs.referenceName }}'. Falling back to tag or commit message."
363+ VERSION_COMMENT=$(git describe --tags --exact-match 2>/dev/null || git log -1 --pretty=format:%s)
364+ ESCAPED_VERSION_COMMENT=$(escape_special_chars "$VERSION_COMMENT")
365+ echo "Fallback version comment : $ESCAPED_VERSION_COMMENT"
366+ # Escape fallback as well
367+ echo "versionComment=$ESCAPED_VERSION_COMMENT" >> $GITHUB_OUTPUT
368+ fi
369+ shell : bash
370+
371+ - name : Publish Catalog Details
372+ if : inputs.referenceType == 'branch' && inputs.referenceName == '1.0.0.X'
373+ shell : pwsh
374+ run : |
375+ dataminer-catalog-upload update-catalog-details `
376+ --path-to-catalog-yml "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/manifest.yml" `
377+ --path-to-readme "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/README.md"
378+ --path-to-images "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/Images"
379+ --dm-catalog-token "${{ secrets.dataminerToken }}" `
380+ --debug "${{ inputs.debug }}"
381+
382+ - name : Publish To Catalog
383+ if : inputs.referenceType == 'tag' && inputs.referenceName == '1.0.0.X'
384+ env :
385+ DATAMINER_TOKEN : ${{ secrets.dataminerToken }}
386+ shell : pwsh
387+ run : |
388+ dataminer-catalog-upload with-registration `
389+ --path-to-catalog-yml "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/manifest.yml" `
390+ --path-to-readme "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/README.md"
391+ --path-to-images "${{ github.workspace }}/Low Code App Editor Package/CatalogInformation/Images"
392+ --dm-catalog-token "${{ secrets.dataminerToken }}" `
393+ --debug "${{ inputs.debug }}"
0 commit comments