-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
326 lines (283 loc) · 15.6 KB
/
Copy pathaction.yml
File metadata and controls
326 lines (283 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
name: 'UiPath Test'
description: 'Runs test cases within a UiPath project'
inputs:
projectFilePaths:
description: 'Used for passing explicit paths to project files to perform analysis. Can be used as multi-line inputs'
required: false
orchestratorUrl:
description: 'Orchestrator instance URL'
required: false
default: "https://cloud.uipath.com/"
orchestratorTenant:
description: 'Tenant on the Orchestrator instance'
required: true
orchestratorFolder:
description: 'Folder path in modern folder setup'
required: true
orchestratorApplicationId:
description: 'Account for authenticating to Orchestrator'
required: true
orchestratorApplicationSecret:
description: 'Password for the Orchestrator account'
required: true
orchestratorApplicationScope:
description: 'Access scope for external application'
required: false
default: "OR.Assets OR.BackgroundTasks OR.Execution OR.Folders OR.Jobs OR.Machines.Read OR.Monitoring OR.Robots.Read OR.Settings.Read OR.TestSets OR.TestSetExecutions OR.TestSetSchedules OR.Users.Read"
orchestratorLogicalName:
description: 'Logical name for Orchestrator organization'
required: true
retryCount:
description: 'Number of retries for failed test cases (by default, no retry is set).'
required: false
projectKey:
description: 'Project key in Test Manager. (Required if using Test Manager parameters)'
required: false
traceLevel:
description: 'Trace level for test execution logs. Possible values are Verbose, Information, Warning, Error. Default is Warning.'
required: false
default: "Warning"
author:
description: 'The package author. If not provided, the GitHub actor will be used as the author of the package'
required: false
default: "${{ github.actor }}"
outputs:
testExecutionLinks:
description: 'Outputs a comma-separated list of Orchestrator links for viewing test results'
value: ${{ steps.parse_test_results.outputs.testExecutionLinks || steps.parse_test_results_test_manager.outputs.testExecutionLinks }}
testResults:
description: 'Markdown formatted table listing the tests that have been run and whether they passed or failed'
value: ${{ steps.parse_test_results.outputs.testResults || steps.parse_test_results_test_manager.outputs.testResults }}
containsPublishableTestCases:
description: 'Boolean value indicating whether any test cases set as publishable were found in the repository'
value: ${{ steps.run_tests.outputs.containsPublishableTestCases }}
testResultsFolder:
description: |
Path to the folder where the test result JSON files created by the UiPath CLI are stored.
Can be used for further processing, such as uploading to Artifacts or sending to another action.
value: ${{ steps.run_tests.outputs.testResultsFolder }}
runs:
using: "composite"
steps:
- id: get_folder_id
name: Get Folder ID
shell: bash
run: |
headers="Content-Type: application/x-www-form-urlencoded"
orchestratorURL="${{ inputs.orchestratorUrl }}"
orchestratorURL="${orchestratorURL%/}"
# URL encode inputs
clientId=$(echo -n "${{ inputs.orchestratorApplicationId }}" | jq -sRr @uri)
clientSecret=$(echo -n "${{ inputs.orchestratorApplicationSecret }}" | jq -sRr @uri)
scope=$(echo -n "${{ inputs.orchestratorApplicationScope }}" | jq -sRr @uri)
# Prepare body for the token request
body="grant_type=client_credentials&client_id=$clientId&client_secret=$clientSecret&scope=$scope"
authResponse=$(curl -s -X POST "$orchestratorURL/identity_/connect/token" -H "${headers[@]}" -d "$body")
accessToken=$(echo "$authResponse" | jq -r '.access_token')
echo "::add-mask::$accessToken"
echo Authentication response: "$authResponse"
if [ -z "$accessToken" ] || [ "$accessToken" == "null" ]; then
echo "Error: Failed to obtain access token. Response: $authResponse"
exit 1
fi
# Prepare folder request
folderName="${{ inputs.orchestratorFolder }}"
parameters="\$filter=FullyQualifiedName eq '$folderName'"
encodedParameters=$(jq -rn --arg params "$parameters" '$params | @uri')
echo $encodedParameters
folderRequestURL="$orchestratorURL/${{ inputs.orchestratorLogicalName }}/${{ inputs.orchestratorTenant }}/orchestrator_/odata/folders?$encodedParameters"
echo "Requesting folder ID from URL: $folderRequestURL"
folderRequestHeaders=("Authorization: Bearer $accessToken")
# Make the request and capture the response and status code
httpStatus=$(curl -s -o response_body.txt -w "%{http_code}" -X GET "$folderRequestURL" -H "${folderRequestHeaders[@]}")
responseBody=$(cat response_body.txt)
# Log the response and status code
echo "HTTP Status: $httpStatus"
echo "Response Body: $responseBody"
# Check if the status code is not 200
if [ "$httpStatus" -ne 200 ]; then
echo "::error title=Get Folder ID failed::Request failed with status code $httpStatus. Response: $responseBody"
exit 1
fi
folderId=$(echo "$responseBody" | jq -r '.value[0].Id')
echo "folderId=$folderId" >> $GITHUB_OUTPUT
- id: set_additional_parameters
name: Set additional parameters
shell: bash
run: |
retryCountParam=""
projectKeyParam=""
if [ -z "${{ inputs.retryCount }}" ]; then
echo "No retry count provided. Defaulting to 0."
else
retryCountParam="--retryCount ${{ inputs.retryCount }}"
echo "Retry count set to ${{ inputs.retryCount }}"
fi
if [ -z "${{ inputs.projectKey }}" ]; then
echo "No Test Manager project key provided."
else
projectKeyParam="--projectKey ${{ inputs.projectKey }}"
echo "Project key set to ${{ inputs.projectKey }}"
fi
echo "additionalParameters=$retryCountParam $projectKeyParam" >> $GITHUB_OUTPUT
- id: run_tests
name: Test
shell: bash
run: |
testsFailed=0
testResultsFolder="${{ github.workspace }}/test-results"
echo "testResultsFolder=$testResultsFolder" >> $GITHUB_OUTPUT
mkdir -p "$testResultsFolder"
testResults=""
orchestratorURL="${{ inputs.orchestratorUrl }}"
orchestratorURL="${orchestratorURL%/}"
testExecutionBaseURL="${orchestratorURL}/${{ inputs.orchestratorLogicalName }}/${{ inputs.orchestratorTenant }}/orchestrator_/test/executions/"
testExecutionURLs=""
repositoryContainsTests=0
if [ -z "${{ inputs.projectFilePaths }}" ]; then
echo "Scanning full repository directory for project.json files"
projectJsonFiles=$(find "${{ github.workspace }}" -type f -name "project.json")
else
echo "Getting full path for files given as inputs"
projectJsonFiles=$(echo "${{ inputs.projectFilePaths }}" | tr '\r\n' '\n' | sed '/^\s*$/d' | while read -r line; do echo "${{ github.workspace }}/$line"; done)
fi
while IFS= read -r projectFile; do
projectInfo=$(cat "$projectFile" | jq '.')
projectName=$(echo "$projectInfo" | jq -r '.name')
targetFramework=$(echo "$projectInfo" | jq -r '.targetFramework')
fileInfoCollection=$(echo "$projectInfo" | jq -r '.designOptions.fileInfoCollection')
echo "::group::uipcli output for testing project ${projectName}"
if [ "$(echo "$fileInfoCollection" | jq length)" -gt 0 ]; then
publishableTests=$(echo "$fileInfoCollection" | jq '[.[] | select(.editingStatus == "Publishable")] | length')
if [ "$publishableTests" -gt 0 ]; then
repositoryContainsTests=1
echo "containsPublishableTestCases=true" >> $GITHUB_OUTPUT
testResultFilePath="$testResultsFolder/$projectName-testresults.json"
echo "Running tests for project $projectName"
uipcli test run "${{ inputs.orchestratorUrl }}" "${{ inputs.orchestratorTenant }}" \
--project-path "$projectFile" \
--accountForApp "${{ inputs.orchestratorLogicalName }}" \
--applicationId "${{ inputs.orchestratorApplicationId }}" \
--applicationSecret "${{ inputs.orchestratorApplicationSecret }}" \
--applicationScope "${{ inputs.orchestratorApplicationScope }}" \
--organizationUnit "${{ inputs.orchestratorFolder }}" \
--out uipath \
--result_path "$testResultFilePath" \
--language en-US \
--repositoryUrl "${{ github.server_url }}/${{ github.repository }}" \
--repositoryCommit "${{ github.sha }}" \
--repositoryBranch "${{ github.head_ref || github.ref_name }}" \
--repositoryType git \
--author "${{ inputs.author }}" \
--traceLevel "${{ inputs.traceLevel }}" \
${{ steps.set_additional_parameters.outputs.additionalParameters }}
if [ $? -ne 0 ]; then
testsFailed=1
fi
else
echo "::warning title=${projectName} contains no test cases set as publishable.:: Project contains no test cases set as publishable. Testing skipped."
testResults+=$'\n- ⚠️ **'"${projectName}"$' contains no test cases set as publishable. Testing skipped.**\n'
fi
else
echo "::warning title=${projectName} contains no test cases.:: Testing skipped."
testResults+=$'\n- ⚠️ **'"${projectName}"$' contains no test cases. Testing skipped.**\n'
fi
echo "::endgroup::"
done <<< "$projectJsonFiles"
# Output that no tests were found
if [ "$repositoryContainsTests" -eq 0 ]; then
echo "::warning title=No publishable UiPath test cases found::Testing has been skipped."
echo "containsPublishableTestCases=false" >> $GITHUB_OUTPUT
fi
echo -e "testResults<<EOF\n$testResults\nEOF" >> $GITHUB_OUTPUT
if [ "$testsFailed" -ne 0 ]; then
echo "::error title=Tests failed::One or more test runs failed."
exit 1
fi
- id: parse_test_results
name: Parse test results (Standard)
if: always() && inputs.projectKey == ''
shell: bash
run: |
testResults="${{ steps.run_tests.outputs.testResults }}"
testExecutionUrls=""
testResultsFolder="${{ steps.run_tests.outputs.testResultsFolder }}"
orchestratorURL="${{ inputs.orchestratorUrl }}"
orchestratorURL="${orchestratorURL%/}"
baseUrl="${orchestratorURL}/${{ inputs.orchestratorLogicalName }}/${{ inputs.orchestratorTenant }}/orchestrator_/test/executions/"
folderId="${{ steps.get_folder_id.outputs.folderId }}"
# Loop through all JSON files in the test results folder
while IFS= read -r testResultFilePath; do
echo "Processing test result file: $testResultFilePath"
testResultData=$(cat "$testResultFilePath" | jq '.')
testCaseExecutions=$(echo "$testResultData" | jq -c '.TestSetExecutions[] | .TestCaseExecutions')
testSetExecutionLink="${baseUrl}$(echo "$testResultData" | jq -r '.TestSetExecutions[0].Id')?fid=$folderId"
echo "Test execution can be viewed in Orchestrator by clicking this link: $testSetExecutionLink"
testResultsTable=$'| Test case | Result |\n| :-- | :-- |'
while IFS= read -r testCase; do
testName=$(echo "$testCase" | jq -r '.Name')
testStatus=$(echo "$testCase" | jq -r '.Status')
testResultsTable+=$'\n| '"$testName"$' | '"$(if [ "$testStatus" == "Passed" ]; then echo "✅ Passed"; else echo "❌ Failed"; fi)"$' |'
done <<< $(echo "$testCaseExecutions" | jq -c '.[]')
testResults+=$'\n### [Test results for '"$(echo "$testResultData" | jq -r '.TestSetExecutions[0].Name')"$']('"$testSetExecutionLink"$')\n'"$testResultsTable"
if [ -z "$testExecutionURLs" ]; then
testExecutionURLs="$testSetExecutionLink"
else
testExecutionURLs+=", $testSetExecutionLink"
fi
done < <(find "$testResultsFolder" -type f -name "*.json")
echo "testExecutionLinks=$testExecutionURLs" >> $GITHUB_OUTPUT
echo -e "testResults<<EOF\n$testResults\nEOF" >> $GITHUB_OUTPUT
echo -e "$testResults" >> $GITHUB_STEP_SUMMARY
echo "$testExecutionLinks"
echo "$testResults"
- id: parse_test_results_test_manager
name: Parse test results (Test Manager)
if: always() && inputs.projectKey != ''
shell: bash
run: |
testResults="${{ steps.run_tests.outputs.testResults }}"
testExecutionUrls=""
testResultsFolder="${{ steps.run_tests.outputs.testResultsFolder }}"
folderId="${{ steps.get_folder_id.outputs.folderId }}"
orchestratorURL="${{ inputs.orchestratorUrl }}"
orchestratorURL="${orchestratorURL%/}"
baseUrl="${orchestratorURL}/${{ inputs.orchestratorLogicalName }}/${{ inputs.orchestratorTenant }}/testmanager_/"
# Loop through all JSON files in the test results folder
while IFS= read -r testResultFilePath; do
echo "Processing test result file: $testResultFilePath"
testResultData=$(cat "$testResultFilePath" | jq '.')
# Loop through each TestManagerTestSetRuns entry
while IFS= read -r testSetRun; do
testSetRunId=$(echo "$testSetRun" | jq -r '.Id')
testSetRunName=$(echo "$testSetRun" | jq -r '.Name')
testSetExecutionLink="${baseUrl}${{ inputs.projectKey }}/testexecutions/$testSetRunId"
echo "Test execution can be viewed in Test Manager by clicking this link: $testSetExecutionLink"
testResultsTable=$'| Test case | Result |\n| :-- | :-- |'
# Loop through each TestCaseExecution within this TestManagerTestSetRun
while IFS= read -r testCase; do
testName=$(echo "$testCase" | jq -r '.Name')
testCaseId=$(echo "$testCase" | jq -r '.TestCaseId')
testStatus=$(echo "$testCase" | jq -r '.Status')
testCaseVariationId=$(echo "$testCase" | jq -r '.DataVariationIdentifier')
if [ "$testCaseVariationId" != "" ] && [ "$testCaseVariationId" != "null" ]; then
testName="$testName (Variation: $testCaseVariationId)"
testCaseLink="${baseUrl}${{ inputs.projectKey }}/testexecution-results/$testSetRunId/$testCaseId?variation=$testCaseVariationId"
else
testCaseLink="${baseUrl}${{ inputs.projectKey }}/testexecution-results/$testSetRunId/$testCaseId"
fi
testResultsTable+=$'\n'"| [$testName]($testCaseLink) | $(if [ "$testStatus" == "Passed" ]; then echo "✅ Passed"; else echo "❌ Failed"; fi) |"
done <<< $(echo "$testSetRun" | jq -c '.TestCaseExecutions[]')
testResults+=$'\n### [Test results for '"$testSetRunName"$']('"$testSetExecutionLink"$')\n'"$testResultsTable"
if [ -z "$testExecutionURLs" ]; then
testExecutionURLs="$testSetExecutionLink"
else
testExecutionURLs+=", $testSetExecutionLink"
fi
done <<< $(echo "$testResultData" | jq -c '.TestManagerTestSetRuns[]')
done < <(find "$testResultsFolder" -type f -name "*.json")
echo "testExecutionLinks=$testExecutionURLs" >> $GITHUB_OUTPUT
echo -e "testResults<<EOF\n$testResults\nEOF" >> $GITHUB_OUTPUT
echo -e "$testResults" >> $GITHUB_STEP_SUMMARY
echo "$testExecutionLinks"
echo "$testResults"