Skip to content

Commit 23324a0

Browse files
svelderrainruizGitHub Copilot
andauthored
NI Linux runtime bootstrap: generate single-container VI history suites (#1008) (#1009)
* Org release plane: rehearse runtime-harness package publish (#1007) * NI Linux runtime bootstrap: generate single-container VI history suites (#1008) * NI Linux runtime bootstrap: drop accidental dist artifacts (#1008) * NI Linux runtime bootstrap: retrigger current-head review (#1008) * NI Linux runtime bootstrap: address Copilot review on current head (#1008) * #1009 Containerize Linux pre-push history scenarios * #1009 Read single-container matrix CLI logs * #1009 Normalize hosted VI history review bundle * #1009 Explicitly scope hosted VI history refs * #1008 Honor explicit vi history baseline refs * #1008 Harden Linux history git contracts and review guards * #1008 Fix JS package source-spec detection * #1008 Stream PrePush log tail reads * #1008 Harden JS release path guards * #1008 Containerize history split tests and harden guards * #1008 Address latest Copilot review fixes * fix: handle bare -Headless in bootstrap (#1008) --------- Co-authored-by: GitHub Copilot <copilot@users.noreply.github.com>
1 parent c3e4de6 commit 23324a0

40 files changed

Lines changed: 10429 additions & 313 deletions

.github/workflows/fixture-drift.yml

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ jobs:
166166
ni-linux-container-hosted:
167167
name: NI Linux Container Compare (Hosted)
168168
runs-on: ubuntu-latest
169-
timeout-minutes: 12
169+
timeout-minutes: 20
170170
env:
171171
NI_LINUX_IMAGE: nationalinstruments/labview:2026q1-linux
172172
NI_LINUX_LABVIEW_PATH: /usr/local/natinst/LabVIEW-2026-64/labview
@@ -196,64 +196,42 @@ jobs:
196196
shell: bash
197197
run: docker pull "${NI_LINUX_IMAGE}"
198198

199-
- name: NI Linux container compare evidence
200-
id: ni_linux_compare
199+
- name: NI Linux review suite artifacts
200+
id: ni_linux_review_suite
201201
shell: pwsh
202202
run: |
203203
$ErrorActionPreference = 'Stop'
204204
$outDir = 'results/fixture-drift/ni-linux-container'
205+
$historyBranchRef = '${{ github.sha }}'
206+
$historyBaselineRef = ''
207+
if ('${{ github.event_name }}' -eq 'pull_request') {
208+
$historyBranchRef = '${{ github.event.pull_request.head.sha }}'
209+
$historyBaselineRef = '${{ github.event.pull_request.base.sha }}'
210+
}
205211
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
206-
$reportPath = Join-Path $outDir 'compare-report.html'
207-
$runtimeSnapshotPath = Join-Path $outDir 'runtime-determinism.json'
208212
209-
pwsh -NoLogo -NoProfile -File tools/Run-NILinuxContainerCompare.ps1 `
213+
pwsh -NoLogo -NoProfile -File tools/Invoke-NILinuxReviewSuite.ps1 `
210214
-BaseVi '${{ steps.prep.outputs.base }}' `
211215
-HeadVi '${{ steps.prep.outputs.head }}' `
216+
-ResultsRoot $outDir `
212217
-Image $env:NI_LINUX_IMAGE `
213218
-LabVIEWPath $env:NI_LINUX_LABVIEW_PATH `
214-
-ReportPath $reportPath `
215-
-TimeoutSeconds 180 `
219+
-HistoryTargetPath 'fixtures/vi-attr/Head.vi' `
220+
-HistoryBranchRef $historyBranchRef `
221+
-HistoryBaselineRef $historyBaselineRef `
222+
-TimeoutSeconds 240 `
216223
-HeartbeatSeconds 15 `
217-
-AutoRepairRuntime:$true `
218224
-RuntimeEngineReadyTimeoutSeconds 120 `
219225
-RuntimeEngineReadyPollSeconds 3 `
220-
-RuntimeSnapshotPath $runtimeSnapshotPath
221-
222-
$capturePath = Join-Path $outDir 'ni-linux-container-capture.json'
223-
if (-not (Test-Path -LiteralPath $capturePath -PathType Leaf)) {
224-
throw ("NI Linux container capture missing: {0}" -f $capturePath)
225-
}
226-
227-
$capture = Get-Content -LiteralPath $capturePath -Raw | ConvertFrom-Json -Depth 24
228-
$gateOutcome = if ($capture.PSObject.Properties['gateOutcome']) { [string]$capture.gateOutcome } else { '' }
229-
$resultClass = if ($capture.PSObject.Properties['resultClass']) { [string]$capture.resultClass } else { '' }
230-
$imageUsed = if ($capture.PSObject.Properties['image']) { [string]$capture.image } else { '' }
231-
if ([string]::IsNullOrWhiteSpace($imageUsed)) {
232-
throw 'NI Linux container compare did not report an image tag.'
233-
}
234-
if (-not [string]::Equals($imageUsed, $env:NI_LINUX_IMAGE, [System.StringComparison]::OrdinalIgnoreCase)) {
235-
throw ("Unexpected NI Linux image. expected={0} actual={1}" -f $env:NI_LINUX_IMAGE, $imageUsed)
236-
}
237-
if (-not [string]::Equals($gateOutcome, 'pass', [System.StringComparison]::OrdinalIgnoreCase)) {
238-
throw ("NI Linux container compare gate outcome is not pass (resultClass={0}, gateOutcome={1})" -f $resultClass, $gateOutcome)
239-
}
240-
241-
"capture_path=$capturePath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
242-
"report_path=$reportPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
243-
"runtime_snapshot_path=$runtimeSnapshotPath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
226+
-GitHubOutputPath $env:GITHUB_OUTPUT `
227+
-GitHubStepSummaryPath $env:GITHUB_STEP_SUMMARY
244228
245229
- name: Upload NI Linux container compare artifacts
246230
if: always()
247231
uses: actions/upload-artifact@v5
248232
with:
249233
name: fixture-drift-ni-linux-container
250-
path: |
251-
results/fixture-drift/ni-linux-container/compare-report.html
252-
results/fixture-drift/ni-linux-container/ni-linux-container-capture.json
253-
results/fixture-drift/ni-linux-container/ni-linux-container-stdout.txt
254-
results/fixture-drift/ni-linux-container/ni-linux-container-stderr.txt
255-
results/fixture-drift/ni-linux-container/runtime-determinism.json
256-
results/fixture-drift/ni-linux-container/container-export/**
234+
path: results/fixture-drift/ni-linux-container/**
257235
if-no-files-found: warn
258236

259237
validate-windows:
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
name: Runtime Harness Package Rehearsal
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Package version (for example: 0.1.0 or 0.1.0-rc.1)'
8+
required: true
9+
type: string
10+
channel:
11+
description: 'Release channel'
12+
required: false
13+
default: stable
14+
type: choice
15+
options:
16+
- stable
17+
- rc
18+
publish:
19+
description: 'Publish the candidate to GitHub Packages before verification'
20+
required: false
21+
default: false
22+
type: boolean
23+
24+
permissions:
25+
actions: read
26+
contents: read
27+
packages: write
28+
29+
jobs:
30+
rehearse-runtime-harness:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v5
35+
36+
- name: Assert promotion contract alignment
37+
shell: pwsh
38+
run: |
39+
pwsh -NoLogo -NoProfile -File tools/Assert-PromotionContractAlignment.ps1 -OutputJsonPath tests/results/promotion-contract/runtime-harness-package-alignment.json -StepSummaryPath $env:GITHUB_STEP_SUMMARY
40+
41+
- name: Setup Node
42+
uses: actions/setup-node@v5
43+
with:
44+
node-version: '20'
45+
cache: npm
46+
registry-url: https://npm.pkg.github.com
47+
scope: '@labview-community-ci-cd'
48+
49+
- name: Install repository dependencies
50+
run: npm ci --ignore-scripts
51+
52+
- name: Resolve runtime-harness publish context
53+
id: context
54+
shell: bash
55+
run: |
56+
set -euo pipefail
57+
node tools/priority/js-package-release.mjs \
58+
--action resolve \
59+
--package-dir packages/runtime-harness \
60+
--version "${{ inputs.version }}" \
61+
--channel "${{ inputs.channel }}" \
62+
--publish "${{ inputs.publish }}" \
63+
--repo "${{ github.repository }}" \
64+
--owner "${{ github.repository_owner }}" \
65+
--server-url "${{ github.server_url }}" \
66+
--report tests/results/_agent/release/runtime-harness-package-context.json \
67+
--github-output "$GITHUB_OUTPUT"
68+
69+
- name: Stage runtime-harness package candidate
70+
id: stage
71+
shell: bash
72+
run: |
73+
set -euo pipefail
74+
node tools/priority/js-package-release.mjs \
75+
--action stage \
76+
--package-dir packages/runtime-harness \
77+
--version "${{ inputs.version }}" \
78+
--channel "${{ inputs.channel }}" \
79+
--publish "${{ inputs.publish }}" \
80+
--repo "${{ github.repository }}" \
81+
--owner "${{ github.repository_owner }}" \
82+
--server-url "${{ github.server_url }}" \
83+
--staging-dir tests/results/_agent/release/runtime-harness-package/staging \
84+
--tarball-dir tests/results/_agent/release/runtime-harness-package/tarballs \
85+
--copy-license-from LICENSE \
86+
--report tests/results/_agent/release/runtime-harness-package-stage.json \
87+
--github-output "$GITHUB_OUTPUT"
88+
89+
- name: Publish runtime-harness candidate
90+
if: steps.context.outputs.publish == 'true'
91+
env:
92+
NODE_AUTH_TOKEN: ${{ github.token }}
93+
run: >
94+
node tools/npm/cli.mjs publish "${{ steps.stage.outputs.tarball_path }}"
95+
--tag "${{ steps.context.outputs.dist_tag }}"
96+
97+
- name: Verify runtime-harness candidate from clean consumer context
98+
id: verify
99+
env:
100+
NODE_AUTH_TOKEN: ${{ github.token }}
101+
shell: bash
102+
run: |
103+
set -euo pipefail
104+
verify_source="${{ steps.stage.outputs.tarball_path }}"
105+
if [ "${{ steps.context.outputs.publish }}" = "true" ]; then
106+
verify_source="${{ steps.context.outputs.package_spec }}"
107+
fi
108+
node tools/priority/js-package-release.mjs \
109+
--action verify \
110+
--package-dir packages/runtime-harness \
111+
--version "${{ inputs.version }}" \
112+
--channel "${{ inputs.channel }}" \
113+
--publish "${{ inputs.publish }}" \
114+
--repo "${{ github.repository }}" \
115+
--owner "${{ github.repository_owner }}" \
116+
--server-url "${{ github.server_url }}" \
117+
--source-spec "$verify_source" \
118+
--consumer-dir tests/results/_agent/release/runtime-harness-package/consumer \
119+
--registry-url "${{ steps.context.outputs.registry_url }}" \
120+
--report tests/results/_agent/release/runtime-harness-package-verification.json \
121+
--github-output "$GITHUB_OUTPUT"
122+
123+
- name: Validate release reports
124+
run: |
125+
node tools/npm/run-script.mjs schema:validate -- --schema docs/schemas/js-package-release-v1.schema.json --data tests/results/_agent/release/runtime-harness-package-context.json --data tests/results/_agent/release/runtime-harness-package-stage.json --data tests/results/_agent/release/runtime-harness-package-verification.json
126+
127+
- name: Write rehearsal summary
128+
shell: bash
129+
run: |
130+
set -euo pipefail
131+
mkdir -p tests/results/_agent/release
132+
summary_path="tests/results/_agent/release/runtime-harness-package-summary.md"
133+
verification_source="packed tarball"
134+
if [ "${{ steps.context.outputs.publish }}" = "true" ]; then
135+
verification_source="published package spec"
136+
fi
137+
{
138+
echo "# runtime-harness package rehearsal summary"
139+
echo ""
140+
echo "- Package: \`${{ steps.context.outputs.package_name }}\`"
141+
echo "- Version: \`${{ steps.context.outputs.version }}\`"
142+
echo "- Channel: \`${{ steps.context.outputs.channel }}\`"
143+
echo "- Published: \`${{ steps.context.outputs.publish }}\`"
144+
echo "- Dist tag: \`${{ steps.context.outputs.dist_tag }}\`"
145+
echo "- Registry: \`${{ steps.context.outputs.registry_url }}\`"
146+
echo "- Package spec: \`${{ steps.context.outputs.package_spec }}\`"
147+
echo "- Verification source: \`${verification_source}\`"
148+
echo "- Tarball: \`${{ steps.stage.outputs.tarball_path }}\`"
149+
} | tee "$summary_path"
150+
151+
- name: Append summary
152+
shell: bash
153+
run: |
154+
cat tests/results/_agent/release/runtime-harness-package-summary.md >> "$GITHUB_STEP_SUMMARY"
155+
156+
- name: Write promotion evidence ledger
157+
if: always()
158+
shell: pwsh
159+
run: |
160+
$gateStatus = switch ('${{ job.status }}') {
161+
'success' { 'pass' }
162+
'cancelled' { 'blocked' }
163+
default { 'fail' }
164+
}
165+
$channel = if ([string]::IsNullOrWhiteSpace('${{ steps.context.outputs.channel }}')) { 'unknown' } else { '${{ steps.context.outputs.channel }}' }
166+
$version = if ([string]::IsNullOrWhiteSpace('${{ steps.context.outputs.version }}')) { 'unknown' } else { '${{ steps.context.outputs.version }}' }
167+
pwsh -NoLogo -NoProfile -File tools/Write-PromotionEvidenceLedger.ps1 -OutputPath tests/results/promotion-contract/runtime-harness-package-ledger.json -WorkflowName '${{ github.workflow }}' -Stream '@labview-community-ci-cd/runtime-harness' -Channel $channel -Version $version -GateStatus $gateStatus -GateReason 'runtime-harness-package-rehearsal workflow result' -SummaryPath 'tests/results/_agent/release/runtime-harness-package-summary.md' -StepSummaryPath $env:GITHUB_STEP_SUMMARY
168+
169+
- name: Emit SLO metrics
170+
if: always()
171+
shell: bash
172+
env:
173+
GH_TOKEN: ${{ github.token }}
174+
run: |
175+
node tools/priority/slo-metrics.mjs \
176+
--repo "${{ github.repository }}" \
177+
--output tests/results/_agent/slo/runtime-harness-package-rehearsal-slo-metrics.json
178+
179+
- name: Upload rehearsal artifacts
180+
if: always()
181+
uses: actions/upload-artifact@v5
182+
with:
183+
name: runtime-harness-package-rehearsal-${{ github.run_id }}
184+
path: |
185+
tests/results/_agent/release/runtime-harness-package-context.json
186+
tests/results/_agent/release/runtime-harness-package-stage.json
187+
tests/results/_agent/release/runtime-harness-package-verification.json
188+
tests/results/_agent/release/runtime-harness-package-summary.md
189+
tests/results/promotion-contract/runtime-harness-package-ledger.json
190+
tests/results/_agent/slo/runtime-harness-package-rehearsal-slo-metrics.json
191+
tests/results/_agent/release/runtime-harness-package/tarballs/*.tgz
192+
if-no-files-found: error

AGENTS.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,10 @@ line buffers).
195195
- Installs `actionlint` (`vars.ACTIONLINT_VERSION`, default 1.7.7) if missing.
196196
- Runs `actionlint` across `.github/workflows`.
197197
- Runs safe PR watch task contract validation (`safe-watch:contract`).
198-
- Runs NI image known-flag scenarios via `tests/Run-NIWindowsContainerCompare.Tests.ps1` (Pester 5 cached under
199-
`%LOCALAPPDATA%\compare-vi-cli-action\PowerShell\Modules` on Windows).
198+
- Runs the NI Linux image flag-combination matrix via `tools/Run-NILinuxContainerCompare.ps1`, covering the no-flags
199+
baseline plus every combination of `-noattr`, `-nofppos`, and `-nobdcosm`.
200+
- Runs the VI history suite smoke inside the same Linux container contract so `history-report.md`,
201+
`history-report.html`, and `history-summary.json` are generated in-container before push.
200202
- Optionally round-trips YAML with `ruamel.yaml` (if Python available).
201203
- Validate safe PR watch task contracts manually before task/workspace changes when iterating locally:
202204
- `node tools/npm/run-script.mjs safe-watch:contract`

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,13 @@ module consumers. Compatibility expectations for that asset are strict:
376376
- when a downstream repo uses hosted NI Linux diagnostics, resolve
377377
`tools/Run-NILinuxContainerCompare.ps1` from the extracted bundle root and
378378
keep its adjacent support scripts in place
379+
- for single-container NI Linux smoke/bootstrap lanes, prefer the runner's
380+
runtime-injection surface (`-RuntimeInjectionScriptPath`,
381+
`-RuntimeInjectionEnv`, `-RuntimeInjectionMount`) or the explicit
382+
`-RuntimeBootstrapContractPath` `viHistory` block so config/dependency setup,
383+
repo-branch materialization, bounded sequential VI-history pair execution,
384+
and the compare invocation stay inside one container execution
385+
- when the smoke lane is bound to a VI-history source branch, keep the
386+
`maxCommitCount` safeguard in the bootstrap contract so oversized branches
387+
fail before the container turns into a full-history sweep; budget divergence
388+
from `develop`, not the entire baseline history

0 commit comments

Comments
 (0)