forked from LabVIEW-Community-CI-CD/compare-vi-cli-action
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaction.yml
More file actions
318 lines (302 loc) · 14.4 KB
/
action.yml
File metadata and controls
318 lines (302 loc) · 14.4 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
name: "Compare VI (composite)"
description: "Compare two LabVIEW (.vi) files using NI LVCompare (LabVIEW 2025 Q3). Composite action for self-hosted Windows runners with full CLI flag pass-through."
author: "compare-vi-cli-action maintainers"
branding:
icon: "git-merge"
color: "blue"
inputs:
base:
description: "Path to the base .vi file"
required: true
head:
description: "Path to the head .vi file"
required: true
loop-enabled:
description: "Enable iterative compare loop mode (aggregates metrics/percentiles)"
required: false
default: 'false'
loop-max-iterations:
description: "Max iterations for loop mode (0 = until diff when fail-on-diff=false, or single pass)."
required: false
default: '1'
loop-interval-seconds:
description: "Interval between iterations in loop mode (fractional seconds supported)."
required: false
default: '0'
quantile-strategy:
description: "Quantile strategy for loop mode: Exact | StreamingReservoir | Hybrid"
required: false
default: 'StreamingReservoir'
stream-capacity:
description: "StreamingReservoir capacity (samples retained; min 10)."
required: false
default: '500'
reconcile-every:
description: "Rebuild streaming reservoir every N iterations (0 = disabled)."
required: false
default: '0'
hybrid-exact-threshold:
description: "Hybrid strategy iterations to seed exact before switching to streaming."
required: false
default: '200'
histogram-bins:
description: "Number of histogram bins to compute in loop mode (0 = disabled)."
required: false
default: '0'
loop-simulate:
description: "Use an internal mock executor (no real LVCompare) for loop mode (useful in CI without LabVIEW)."
required: false
default: 'true'
loop-simulate-exit-code:
description: "Exit code returned by simulated executor (1 = diff; 0 = no diff)."
required: false
default: '1'
lvComparePath:
description: "Full path to LVCompare.exe, if not on PATH"
required: false
lvCompareArgs:
description: "Additional CLI flags for LVCompare.exe (space-delimited)"
required: false
default: ""
lvCompareBitness:
description: "Preferred LVCompare bitness to run (auto | x64 | x86)"
required: false
default: "auto"
fail-on-diff:
description: "Fail the job if differences are found"
required: false
default: "true"
working-directory:
description: "Directory to run LVCompare from (sets process CWD for relative paths)"
required: false
default: ""
outputs:
diff:
description: "true if differences were found"
value: ${{ steps.run_compare.outputs.diff }}
exitCode:
description: "Raw exit code from LVCompare"
value: ${{ steps.run_compare.outputs.exitCode }}
cliPath:
description: "Resolved path to LVCompare.exe"
value: ${{ steps.run_compare.outputs.cliPath }}
command:
description: "Exact command line executed (for auditing)"
value: ${{ steps.run_compare.outputs.command }}
compareDurationSeconds:
description: "Execution duration in seconds (floating point with millisecond precision)"
value: ${{ steps.run_compare.outputs.compareDurationSeconds }}
compareDurationNanoseconds:
description: "Execution duration in nanoseconds (high-resolution)"
value: ${{ steps.run_compare.outputs.compareDurationNanoseconds }}
compareSummaryPath:
description: "Path to generated JSON summary (comparison metadata)"
value: ${{ steps.run_compare.outputs.compareSummaryPath }}
iterations:
description: "Loop mode: total iterations executed"
value: ${{ steps.run_compare.outputs.iterations }}
diffCount:
description: "Loop mode: number of diff iterations"
value: ${{ steps.run_compare.outputs.diffCount }}
errorCount:
description: "Loop mode: number of error iterations"
value: ${{ steps.run_compare.outputs.errorCount }}
averageSeconds:
description: "Loop mode: average iteration duration (s)"
value: ${{ steps.run_compare.outputs.averageSeconds }}
totalSeconds:
description: "Loop mode: total elapsed time (s)"
value: ${{ steps.run_compare.outputs.totalSeconds }}
p50:
description: "Loop mode: latency p50 (seconds)"
value: ${{ steps.run_compare.outputs.p50 }}
p90:
description: "Loop mode: latency p90 (seconds)"
value: ${{ steps.run_compare.outputs.p90 }}
p99:
description: "Loop mode: latency p99 (seconds)"
value: ${{ steps.run_compare.outputs.p99 }}
quantileStrategy:
description: "Loop mode: effective quantile strategy used"
value: ${{ steps.run_compare.outputs.quantileStrategy }}
streamingWindowCount:
description: "Loop mode: sample count retained in streaming reservoir"
value: ${{ steps.run_compare.outputs.streamingWindowCount }}
loopResultPath:
description: "Loop mode: path to JSON summary of loop aggregate metrics"
value: ${{ steps.run_compare.outputs.loopResultPath }}
histogramPath:
description: "Loop mode: path to histogram JSON (if bins > 0)"
value: ${{ steps.run_compare.outputs.histogramPath }}
shortCircuitedIdentical:
description: "true when base and head resolved to the same absolute path and LVCompare invocation was short-circuited"
value: ${{ steps.run_compare.outputs.shortCircuitedIdentical }}
runs:
using: "composite"
steps:
- id: run_compare
name: Run LVCompare
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$ActionPath = '${{ github.action_path }}'
. (Join-Path $ActionPath 'scripts' 'CompareVI.ps1')
$bitnessPreference = '${{ inputs.lvCompareBitness }}'
$loopEnabled = [System.Convert]::ToBoolean('${{ inputs.loop-enabled }}')
if ($loopEnabled) {
# --- Loop Mode Branch ---
$outPath = $env:GITHUB_OUTPUT
$sumPath = $env:GITHUB_STEP_SUMMARY
$modulePath = Join-Path $ActionPath 'module' 'CompareLoop' 'CompareLoop.psd1'
if (-not (Test-Path -LiteralPath $modulePath)) { throw "CompareLoop module not found: $modulePath" }
Import-Module $modulePath -Force
$maxIter = [int]'${{ inputs.loop-max-iterations }}'
$intervalSec = [double]'${{ inputs.loop-interval-seconds }}'
$qStrategy = '${{ inputs.quantile-strategy }}'
$streamCap = [int]'${{ inputs.stream-capacity }}'
$reconEvery = [int]'${{ inputs.reconcile-every }}'
$hybridThresh = [int]'${{ inputs.hybrid-exact-threshold }}'
$histBins = [int]'${{ inputs.histogram-bins }}'
$simulate = [System.Convert]::ToBoolean('${{ inputs.loop-simulate }}')
$simExit = [int]'${{ inputs.loop-simulate-exit-code }}'
$executor = $null
if ($simulate) {
$executor = { param($cli,$base,$head,$args) Start-Sleep -Milliseconds 3; return $simExit }
}
# We skip validation & canonical CLI when simulating
$loopParams = @{
Base='${{ inputs.base }}'; Head='${{ inputs.head }}'; MaxIterations=$maxIter; IntervalSeconds=$intervalSec;
QuantileStrategy=$qStrategy; StreamCapacity=$streamCap; ReconcileEvery=$reconEvery; HybridExactThreshold=$hybridThresh;
HistogramBins=$histBins; Quiet=$true; SkipValidation=$simulate; PassThroughPaths=$simulate; BypassCliValidation=$simulate
}
if (-not [string]::IsNullOrWhiteSpace($bitnessPreference)) { $loopParams.LvCompareBitness = $bitnessPreference }
if ($executor) { $loopParams.CompareExecutor = $executor }
$loopRes = Invoke-IntegrationCompareLoop @loopParams
if (-not $loopRes) { throw 'Loop execution returned null result.' }
# Basic outputs reused for backward compatibility (single compare semantics not available in loop path)
"diff=$(if ($loopRes.DiffCount -gt 0) { 'true' } else { 'false' })" >> $env:GITHUB_OUTPUT
"exitCode=0" >> $env:GITHUB_OUTPUT
"cliPath=(loop-mode)" >> $env:GITHUB_OUTPUT
"command=(loop-mode)" >> $env:GITHUB_OUTPUT
"compareDurationSeconds=$($loopRes.AverageSeconds)" >> $env:GITHUB_OUTPUT
"compareDurationNanoseconds=0" >> $env:GITHUB_OUTPUT
"shortCircuitedIdentical=false" >> $env:GITHUB_OUTPUT
# Loop specific outputs
"iterations=$($loopRes.Iterations)" >> $env:GITHUB_OUTPUT
"diffCount=$($loopRes.DiffCount)" >> $env:GITHUB_OUTPUT
"errorCount=$($loopRes.ErrorCount)" >> $env:GITHUB_OUTPUT
"averageSeconds=$($loopRes.AverageSeconds)" >> $env:GITHUB_OUTPUT
"totalSeconds=$($loopRes.TotalSeconds)" >> $env:GITHUB_OUTPUT
if ($loopRes.Percentiles) {
"p50=$($loopRes.Percentiles.p50)" >> $env:GITHUB_OUTPUT
"p90=$($loopRes.Percentiles.p90)" >> $env:GITHUB_OUTPUT
"p99=$($loopRes.Percentiles.p99)" >> $env:GITHUB_OUTPUT
} else {
"p50=" >> $env:GITHUB_OUTPUT
"p90=" >> $env:GITHUB_OUTPUT
"p99=" >> $env:GITHUB_OUTPUT
}
"quantileStrategy=$($loopRes.QuantileStrategy)" >> $env:GITHUB_OUTPUT
"streamingWindowCount=$($loopRes.StreamingWindowCount)" >> $env:GITHUB_OUTPUT
# Serialize loop summary (exclude large fields like Records if very large)
$loopSummary = [pscustomobject]@{
iterations = $loopRes.Iterations
diffCount = $loopRes.DiffCount
errorCount = $loopRes.ErrorCount
averageSeconds = $loopRes.AverageSeconds
totalSeconds = $loopRes.TotalSeconds
quantileStrategy = $loopRes.QuantileStrategy
streamingWindowCount = $loopRes.StreamingWindowCount
percentiles = $loopRes.Percentiles
histogram = $loopRes.Histogram
schema = 'loop-summary-v1'
generatedUtc = [DateTime]::UtcNow.ToString('o')
}
$loopResultPath = Join-Path $env:RUNNER_TEMP 'compare-loop-summary.json'
$loopSummary | ConvertTo-Json -Depth 6 | Out-File -FilePath $loopResultPath -Encoding utf8
"loopResultPath=$loopResultPath" >> $env:GITHUB_OUTPUT
if ($loopRes.Histogram) {
$histPath = Join-Path $env:RUNNER_TEMP 'compare-loop-histogram.json'
$loopRes.Histogram | ConvertTo-Json -Depth 4 | Out-File -FilePath $histPath -Encoding utf8
"histogramPath=$histPath" >> $env:GITHUB_OUTPUT
} else { "histogramPath=" >> $env:GITHUB_OUTPUT }
# Step summary (concise)
if ($sumPath) {
$lines = @(
'### Compare VI Loop Mode',
"- Base: $($loopRes.BasePath)",
"- Head: $($loopRes.HeadPath)",
"- Iterations: $($loopRes.Iterations)",
"- Diffs: $($loopRes.DiffCount)",
"- Errors: $($loopRes.ErrorCount)",
"- Avg (s): $($loopRes.AverageSeconds)",
"- Total (s): $($loopRes.TotalSeconds)",
"- Quantile Strategy: $($loopRes.QuantileStrategy)",
$(if (
$loopRes.Percentiles -and
$null -ne $loopRes.Percentiles.p50 -and
$null -ne $loopRes.Percentiles.p90 -and
$null -ne $loopRes.Percentiles.p99
) {
"- p50/p90/p99 (s): $($loopRes.Percentiles.p50)/$($loopRes.Percentiles.p90)/$($loopRes.Percentiles.p99)"
} else {
'- Percentiles: (n/a)'
}),
"- Streaming Window Count: $($loopRes.StreamingWindowCount)"
)
($lines -join "`n") | Out-File -FilePath $sumPath -Append -Encoding utf8
}
# Provide a stub summary path for backward compat consumers
$summaryJsonPath = Join-Path $env:RUNNER_TEMP 'compare-summary.json'
[pscustomobject]@{
mode = 'loop'
base = $loopRes.BasePath
head = $loopRes.HeadPath
iterations = $loopRes.Iterations
diffCount = $loopRes.DiffCount
errorCount = $loopRes.ErrorCount
averageSeconds = $loopRes.AverageSeconds
totalSeconds = $loopRes.TotalSeconds
percentiles = $loopRes.Percentiles
quantileStrategy = $loopRes.QuantileStrategy
streamingWindowCount = $loopRes.StreamingWindowCount
generatedUtc = [DateTime]::UtcNow.ToString('o')
} | ConvertTo-Json -Depth 6 | Out-File -FilePath $summaryJsonPath -Encoding utf8
"compareSummaryPath=$summaryJsonPath" >> $env:GITHUB_OUTPUT
exit 0
}
$wd = '${{ inputs.working-directory }}'
$outPath = $env:GITHUB_OUTPUT
$sumPath = $env:GITHUB_STEP_SUMMARY
$result = Invoke-CompareVI -Base '${{ inputs.base }}' -Head '${{ inputs.head }}' `
-LvComparePath '${{ inputs.lvComparePath }}' `
-LvCompareBitness $bitnessPreference `
-LvCompareArgs '${{ inputs.lvCompareArgs }}' `
-WorkingDirectory $wd `
-FailOnDiff ([System.Convert]::ToBoolean('${{ inputs.fail-on-diff }}')) `
-GitHubOutputPath $outPath `
-GitHubStepSummaryPath $sumPath
# Expose outputs explicitly in case Invoke-CompareVI was called with custom paths
"exitCode=$($result.ExitCode)" >> $env:GITHUB_OUTPUT
"cliPath=$($result.CliPath)" >> $env:GITHUB_OUTPUT
"command=$($result.Command)" >> $env:GITHUB_OUTPUT
$diffLower = if ($result.Diff) { 'true' } else { 'false' }
"diff=$diffLower" >> $env:GITHUB_OUTPUT
"compareDurationSeconds=$($result.CompareDurationSeconds)" >> $env:GITHUB_OUTPUT
"compareDurationNanoseconds=$($result.CompareDurationNanoseconds)" >> $env:GITHUB_OUTPUT
$shortCircuitLower = if ($result.ShortCircuitedIdenticalPath -eq $true) { 'true' } else { 'false' }
"shortCircuitedIdentical=$shortCircuitLower" >> $env:GITHUB_OUTPUT
$summaryJsonPath = Join-Path $env:RUNNER_TEMP 'compare-summary.json'
[pscustomobject]@{
base = $result.Base
head = $result.Head
cliPath = $result.CliPath
command = $result.Command
exitCode = $result.ExitCode
diff = $result.Diff
compareDurationSeconds = $result.CompareDurationSeconds
compareDurationNanoseconds = $result.CompareDurationNanoseconds
shortCircuitedIdentical = ($result.ShortCircuitedIdenticalPath -eq $true)
generatedUtc = [DateTime]::UtcNow.ToString('o')
} | ConvertTo-Json -Depth 4 | Out-File -FilePath $summaryJsonPath -Encoding utf8
"compareSummaryPath=$summaryJsonPath" >> $env:GITHUB_OUTPUT