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