Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/actions/_probe-versioning-fastfail/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: "_probe versioning fast-fail"
description: >
THROWAWAY probe for CI_Toolkit ci-versioning H1 fix. Exercises the fast-fail
step's blast radius in isolation: real gh release download of the baseline
IncludedDLLs.txt, and the vendored Assert-VersioningPrerequisites.ps1 resolved
via GITHUB_ACTION_PATH and invoked with -Force:$force under pwsh 7. Assemblies
dir is deliberately empty so every canonical DLL is "missing", which is what
drives the force / deletion-only guards. Not for production use.

inputs:
force:
required: true
baseline_installer_repo:
required: true
changed_list_path:
description: Path to the ACMRT change list produced by compute-changed-files.
required: true

runs:
using: composite
steps:
- name: Fast-fail on missing versioning-critical DLLs (probe)
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$baselineRepo = '${{ inputs.baseline_installer_repo }}'
$baselineDir = Join-Path $env:RUNNER_TEMP 'versioning-baseline'
New-Item -ItemType Directory -Force -Path $baselineDir | Out-Null

$downloaded = $false
try {
$latestTag = gh release list --repo $baselineRepo --limit 1 --json tagName --jq '.[0].tagName' 2>$null
if ($LASTEXITCODE -eq 0 -and $latestTag) {
gh release download $latestTag --repo $baselineRepo `
--pattern 'IncludedDLLs.txt' `
--dir $baselineDir 2>$null
if ($LASTEXITCODE -eq 0 -and (Test-Path (Join-Path $baselineDir 'IncludedDLLs.txt'))) {
$downloaded = $true
}
}
} catch { }

if (-not $downloaded) {
Write-Host "::error::PROBE PRECONDITION FAILED: no IncludedDLLs.txt on $baselineRepo (cannot exercise fast-fail)."
exit 1
}
Write-Host "Baseline: $baselineRepo@$latestTag"

# Empty assemblies dir => every canonical expected DLL is missing.
$emptyAsm = Join-Path $env:RUNNER_TEMP 'empty-assemblies'
New-Item -ItemType Directory -Force -Path $emptyAsm | Out-Null

$force = '${{ inputs.force }}' -eq 'true'
& "$env:GITHUB_ACTION_PATH\scripts\Assert-VersioningPrerequisites.ps1" `
-BaselineFile (Join-Path $baselineDir 'IncludedDLLs.txt') `
-AssembliesDir $emptyAsm `
-ChangedListPath '${{ inputs.changed_list_path }}' `
-Force:$force
exit $LASTEXITCODE
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[CmdletBinding()]
param(
# IncludedDLLs.txt from the baseline installer's latest published release.
[Parameter(Mandatory)][string]$BaselineFile,
# Folder produced by dependency resolution + primary build.
[Parameter(Mandatory)][string]$AssembliesDir,
# ACMRT change list (deletions excluded) written by compute-changed-files.
# An existing-but-empty file means every matched change in the PR was a
# deletion. Absent (non-PR run) or non-empty means apply the fast-fail.
[string]$ChangedListPath = '',
# Skip the fast-fail and run versioning regardless. Mirrors BHoMBot's
# -force: used when a PR legitimately reduces the produced DLL set.
[switch]$Force
)

$ErrorActionPreference = 'Stop'

# Filter to canonical BHoM DLLs. Third-party shared deps (Newtonsoft.Json, WiX
# bundles etc.) legitimately vary between builds; the canonical set is what
# versioning genuinely depends on. Same regex intent as BHoMBot's Versioning.cs:187.
$canonicalRegex = '(?i)(oM|_Engine|_Adapter)\.dll$|(?i)Revit_.+20\d{2}\.dll$'

$expected = Get-Content $BaselineFile |
ForEach-Object { [System.IO.Path]::GetFileName($_.Trim()) } |
Where-Object { $_ -match $canonicalRegex } |
Sort-Object -Unique

$present = @(Get-ChildItem $AssembliesDir -Filter '*.dll' -ErrorAction SilentlyContinue |
ForEach-Object { $_.Name })

$missing = @($expected | Where-Object { $_ -notin $present })

if ($missing.Count -eq 0) {
Write-Host "::notice::Versioning prerequisites present ($($expected.Count) canonical DLLs matched from baseline)."
exit 0
}

$missingList = $missing -join ', '

if ($Force) {
Write-Host "::warning::Versioning prerequisites missing ($missingList), but force=true, so running versioning anyway."
exit 0
}

# Deletion-only guard: mirrors BHoMBot Versioning.cs:209 (csFiles.Any(File.Exists)).
# A PR that only removes source legitimately produces fewer DLLs, so a missing
# DLL is expected: run versioning rather than blocking it here. The check applies
# only when the change list exists (PR run) and is empty (all matched changes
# were deletions); an absent list (non-PR run) or a non-empty list falls through
# to the fast-fail.
if ($ChangedListPath -and (Test-Path $ChangedListPath)) {
$nonDeleted = @(Get-Content $ChangedListPath | Where-Object { $_.Trim() })
if ($nonDeleted.Count -eq 0) {
Write-Host "::warning::Versioning prerequisites missing ($missingList), but this PR only deletes source files, so fewer DLLs is expected. Skipping fast-fail and running versioning."
exit 0
}
}

Write-Host "::error title=Versioning prerequisites missing::The following DLLs are expected (per the last published baseline release) but were not produced by dependency resolution + primary build: $missingList. Fix the upstream failure before re-running versioning."
exit 1
77 changes: 77 additions & 0 deletions .github/workflows/_probe-versioning-fastfail.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: _probe versioning fast-fail

# THROWAWAY validation harness for the CI_Toolkit ci-versioning H1 fix.
# Runs on PRs that touch this branch's probe files. Delete after validating.

on:
pull_request:
paths:
- '.github/actions/_probe-versioning-fastfail/**'
- '.github/workflows/_probe-versioning-fastfail.yml'
- 'probe-src/**'

permissions:
contents: read

jobs:
probe:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- name: missing-fail # canonical DLLs missing, no force, real source change -> fail
force: 'false'
empty: 'false'
expect: failure
- name: force-skip # force bypass -> succeed
force: 'true'
empty: 'false'
expect: success
- name: deletion-only-skip # empty ACMRT list (deletion-only PR) -> succeed
force: 'false'
empty: 'true'
expect: success
steps:
- name: Checkout PR branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Compute changed files
uses: BuroHappoldEngineeringAdmin/CI_Toolkit/.github/actions/compute-changed-files@develop
with:
patterns: '*.cs'
skip_if_not_pr: 'false'
output_file: changed_versioning_files.txt

- name: Show / tamper change list
shell: bash
run: |
echo "--- changed_versioning_files.txt (as produced) ---"
cat changed_versioning_files.txt || echo "(absent)"
if [ "${{ matrix.empty }}" = "true" ]; then
: > changed_versioning_files.txt
echo "--- emptied to simulate a deletion-only PR ---"
fi

- name: Run fast-fail probe
id: probe
continue-on-error: true
uses: ./.github/actions/_probe-versioning-fastfail
with:
force: ${{ matrix.force }}
baseline_installer_repo: BuroHappoldEngineeringSandbox/BHoM_Installer
changed_list_path: ${{ github.workspace }}/changed_versioning_files.txt

- name: Assert outcome
shell: bash
run: |
got='${{ steps.probe.outcome }}'
want='${{ matrix.expect }}'
echo "leg=${{ matrix.name }} force=${{ matrix.force }} empty=${{ matrix.empty }} outcome=$got expected=$want"
if [ "$got" != "$want" ]; then
echo "::error::leg ${{ matrix.name }}: probe outcome '$got' != expected '$want'"
exit 1
fi
echo "leg ${{ matrix.name }}: OK"
1 change: 1 addition & 0 deletions probe-src/Dummy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
namespace Probe { public class Dummy { public int V => 1; } }
Loading