diff --git a/scripts/check_profile.bat b/scripts/check_profile.bat index 231fa721d4..e92f9fe0e8 100644 --- a/scripts/check_profile.bat +++ b/scripts/check_profile.bat @@ -1,5 +1,7 @@ @echo off REM Runs check_profile.ps1, the Windows twin of check_profile.sh, from cmd. +REM Arguments are passed straight through, so anything the .ps1 takes works here: +REM scripts\check_profile.bat -Vendor Elegoo validate_custom REM -ExecutionPolicy Bypass is needed because a Windows client defaults to Restricted, REM which refuses to run a checked-out .ps1 at all. powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0check_profile.ps1" %* diff --git a/scripts/check_profile.ps1 b/scripts/check_profile.ps1 index 5c5dcb20c8..9c9bb6c506 100644 --- a/scripts/check_profile.ps1 +++ b/scripts/check_profile.ps1 @@ -23,6 +23,10 @@ has, is moved aside for the duration of the run and restored on exit. Only one run per work dir at a time. + -Vendor narrows a run to one vendor while working on that vendor's profiles - the one + deliberate divergence from CI, which always checks the whole tree. A check that cannot be + narrowed is left out of the run and reported as skipped. + x64 and ARM64 hosts are both supported. A locally built validator is chosen by the machine type in its PE header rather than by the name of its build tree, so build\ (x64) and build-arm64\ side by side resolve correctly; the published nightly is x64 only and runs @@ -32,6 +36,14 @@ Profile tree to validate (default: resources\profiles). extra_json_check always looks at the tree next to the script, so this only redirects the validator checks. +.PARAMETER Vendor + Check only this vendor, named after its .json (e.g. "Co Print"). validate_custom is + narrowed with it too, by keeping only that vendor's presets in each fixture tree. The one + check it cannot narrow is validate_slice for a vendor that ships no printers; the summary + reports that one as skipped, and naming it explicitly still runs it. extra_json_check keeps + its two cross-vendor checks (setting_id and filament_id) tree-wide, so a scoped run can still + fail on another vendor's files. + .PARAMETER Validator OrcaSlicer_profile_validator.exe to use; also $env:ORCA_PROFILE_VALIDATOR. Default: the local build*\ Release build (then RelWithDebInfo, MinSizeRel, Debug) for this architecture, else @@ -58,6 +70,9 @@ .EXAMPLE powershell -ExecutionPolicy Bypass -File scripts\check_profile.ps1 validate_system validate_slice + +.EXAMPLE + scripts\check_profile.bat -Vendor Elegoo #> # PositionalBinding is off so that the check names are the only positional arguments; left on, @@ -65,6 +80,7 @@ [CmdletBinding(PositionalBinding = $false)] param( [Alias('p')] [string] $ProfilesDir, + [Alias('v')] [string] $Vendor, [string] $Validator, [string] $WorkDir, [Alias('l')] [int] $LogLevel = 2, @@ -156,12 +172,42 @@ foreach ($name in $Checks) { if ($name -like '-*') { Die "unknown option '$name' (try -Help)" } if ($AllChecks -notcontains $name) { Die "unknown check '$name' (try -Help)" } } +# A check named on the command line always runs; only the default set is narrowed (see the run +# loop below). +$NamedChecks = [bool] $Checks if (-not $Checks) { $Checks = $AllChecks } if (-not $ProfilesDir) { $ProfilesDir = Join-Path $RepoRoot 'resources\profiles' } if (-not (Test-Path -LiteralPath $ProfilesDir -PathType Container)) { Die "profile directory not found: $ProfilesDir" } $ProfilesDir = (Resolve-Path -LiteralPath $ProfilesDir).Path +# A vendor neither tool knows is not an error to them: the static checks load nothing of their own +# and still report success, so a typo would otherwise be three green checks and one baffling slice +# failure. The match has to be made on the names themselves rather than with a Test-Path - the +# validator compares the .json stem case-sensitively, while Windows' case-insensitive +# filesystem would let "creality" pass a path test and then match no vendor. A vendor is a +# .json with a sibling directory; that pair is also what tells one apart from +# blacklist.json and retired_filament_ids.json, which sit in the same folder. +if ($Vendor) { + $found = $false + $suggestion = '' + foreach ($file in (Get-ChildItem -LiteralPath $ProfilesDir -Filter '*.json' -File)) { + $name = [IO.Path]::GetFileNameWithoutExtension($file.Name) + if (-not (Test-Path -LiteralPath (Join-Path $ProfilesDir $name) -PathType Container)) { continue } + if ($name -ceq $Vendor) { $found = $true; break } + if ($name -eq $Vendor) { $suggestion = $name } + } + if (-not $found) { + if ($suggestion) { Die "unknown vendor '$Vendor'; vendor names are case-sensitive, did you mean '$suggestion'?" } + Die "unknown vendor '$Vendor': no such vendor in $ProfilesDir" + } +} + +# The validator's -v and orca_extra_profile_check.py's --vendor both take that stem; an unscoped +# run passes neither, so the checks below splat these in either way. +$VendorArgs = if ($Vendor) { @('-v', $Vendor) } else { @() } +$VendorPyArgs = if ($Vendor) { @('--vendor', $Vendor) } else { @() } + if (-not $WorkDir) { $WorkDir = Join-Path $RepoRoot '.test\check_profiles' } $LogDir = Join-Path $WorkDir 'logs' try { New-Item -ItemType Directory -Force -Path $LogDir | Out-Null } catch { Die "cannot create ${LogDir}: $_" } @@ -350,32 +396,77 @@ function Resolve-Python { Die 'no Python 3 found; install it (or the py launcher) and re-run' } +# The fixtures name every preset "__orca_test", after the "name" inside +# .json rather than the file stem -Vendor takes - BBL.json is "Bambulab", iQ.json is +# "innovatiQ". Falls back to the stem for a vendor file with no readable name. +function Get-VendorDisplayName { + $name = '' + try { $name = (Get-Content -LiteralPath (Join-Path $ProfilesDir "$Vendor.json") -Raw -ErrorAction Stop | ConvertFrom-Json).name } catch { } + if ($name) { return "$name" } + return $Vendor +} + +# Unpack just the presets one vendor's profiles own, and return how many that was. Each fixture +# preset was generated from a single system preset and inherits it by name - none inherits another +# user preset - so the selection is self-contained. +function Expand-VendorPresets([string] $Zip, [string] $Tree, [string] $Prefix) { + # .NET rather than Expand-Archive: a fixture holds around 20,000 entries, and unpacking every + # one of them to keep a twentieth costs more than the validation this is speeding up. + Add-Type -AssemblyName System.IO.Compression.FileSystem + $kept = 0 + $archive = [IO.Compression.ZipFile]::OpenRead($Zip) + try { + foreach ($entry in $archive.Entries) { + # A directory entry has an empty Name, so it never matches and is never created. + if (-not $entry.Name.StartsWith($Prefix, [StringComparison]::Ordinal)) { continue } + $dest = Join-Path $Tree $entry.FullName.Replace('/', [IO.Path]::DirectorySeparatorChar) + [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($dest)) | Out-Null + [IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $dest, $true) + $kept++ + } + } finally { + $archive.Dispose() + } + return $kept +} + # ---------------------------------------------------------------------------- checks $CheckBodies = @{ extra_json_check = { - Invoke-Tool -Exe (Resolve-Python) -Arguments @((Join-Path $RepoRoot 'scripts\orca_extra_profile_check.py')) + Invoke-Tool -Exe (Resolve-Python) -Arguments (@((Join-Path $RepoRoot 'scripts\orca_extra_profile_check.py')) + $VendorPyArgs) } validate_system = { - Invoke-Tool -Exe $Validator -Arguments @('-p', $ProfilesDir, '-l', "$LogLevel") + Invoke-Tool -Exe $Validator -Arguments (@('-p', $ProfilesDir) + $VendorArgs + @('-l', "$LogLevel")) } # Slices a two-colour cube through every printer so all custom g-code (incl. # change_filament_gcode) is expanded - catches undefined-placeholder / invalid-flow bugs the # static checks cannot see. validate_slice = { - Invoke-Tool -Exe $Validator -Arguments @('-p', $ProfilesDir, '-s', '-l', "$LogLevel") + Invoke-Tool -Exe $Validator -Arguments (@('-p', $ProfilesDir) + $VendorArgs + @('-s', '-l', "$LogLevel")) } validate_filament_subtypes = { - Invoke-Tool -Exe $Validator -Arguments @('-p', $ProfilesDir, '-l', "$LogLevel", '-f') + Invoke-Tool -Exe $Validator -Arguments (@('-p', $ProfilesDir) + $VendorArgs + @('-l', "$LogLevel", '-f')) } # Every released fixture is a snapshot of user presets saved by that OrcaSlicer version; each # is unpacked over the current system profiles and validated, so a profile change that would # break an existing user's presets fails here. + # + # Under -Vendor only that vendor's presets are unpacked, which is what makes the validator's + # -v usable here: -v filters the system vendors but never the user presets, so on a whole-tree + # snapshot it reports every other vendor's presets as unresolvable parents - thousands of + # errors saying nothing about the vendor under test. Teaching -v to filter user presets too is + # the deeper fix, but this runs against whatever validator is to hand, the published nightly + # included, so the picking has to happen here. It picks on the "_" + # filename prefix that generate_custom_presets() (the validator's -g mode) gave every preset in + # these fixtures; a fixture cut from a renamed generator would surface as the "checked nothing" + # warning below. Presets whose source had no vendor carry no prefix - a few of those still name + # one in the middle, like "PET @BBL A1_orca_test" - and only an unscoped run covers them. validate_custom = { $fixturesDir = Join-Path $WorkDir 'profile-fixtures' $outputDir = Join-Path $WorkDir 'custom-preset-validation' @@ -392,6 +483,8 @@ $CheckBodies = @{ return 1 } + $vendorPrefix = if ($Vendor) { "$(Get-VendorDisplayName)_" } else { '' } + $totalKept = 0 $status = 0 $failedLogs = @() $summary = @('## Custom Preset Fixture Validation', '', '| Version | Status | Log |', '| --- | --- | --- |') @@ -424,12 +517,23 @@ $CheckBodies = @{ Remove-Item -LiteralPath $profileTree -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $profileTree | Out-Null # Piped rather than copied through \*, so a vendor directory whose name - # holds a wildcard character is still copied by its literal path. - Get-ChildItem -LiteralPath $ProfilesDir -Force | Copy-Item -Destination $profileTree -Recurse -Force + # holds a wildcard character is still copied by its literal path. Under -Vendor the + # validator opens only that vendor and OrcaFilamentLibrary and skips the other sixty-odd + # (PresetBundle::load_system_presets_from_json), so the rest are left out of the copy: + # ~3s a fixture that was going on files nothing opens. + Get-ChildItem -LiteralPath $ProfilesDir -Force | + Where-Object { -not $Vendor -or -not $_.PSIsContainer -or $_.Name -eq $Vendor -or $_.Name -eq 'OrcaFilamentLibrary' } | + Copy-Item -Destination $profileTree -Recurse -Force Remove-Item -LiteralPath (Join-Path $profileTree 'user') -Recurse -Force -ErrorAction SilentlyContinue - Expand-Archive -LiteralPath $fixtureZip -DestinationPath $profileTree -Force + if ($Vendor) { + $kept = Expand-VendorPresets $fixtureZip $profileTree $vendorPrefix + $totalKept += $kept + Write-CheckLog " $kept $Vendor preset file(s)" + } else { + Expand-Archive -LiteralPath $fixtureZip -DestinationPath $profileTree -Force + } - $result = Invoke-Tool -Exe $Validator -Arguments @('-p', $profileTree, '-l', "$LogLevel") -OutFile $logPath + $result = Invoke-Tool -Exe $Validator -Arguments (@('-p', $profileTree) + $VendorArgs + @('-l', "$LogLevel")) -OutFile $logPath if ($result -eq 0) { $summary += "| $version | PASS | $version.log |" # Only failures are worth keeping; each tree is a full copy of resources\profiles. @@ -441,6 +545,12 @@ $CheckBodies = @{ } } + # A vendor added after the newest fixture was cut appears in none of them: nothing failed, + # but nothing was checked either. + if ($Vendor -and $totalKept -eq 0) { + Write-CheckLog "no $Vendor presets in any fixture; validate_custom checked nothing" + } + [IO.File]::WriteAllLines((Join-Path $outputDir 'summary.md'), [string[]] $summary) $summary | ForEach-Object { Write-CheckLog $_ } @@ -473,7 +583,7 @@ $CommentHeadings = @{ function Invoke-Check([string] $Name) { $log = Join-Path $LogDir "$Name.log" Write-Host '' - Write-Host "==> $Name" -ForegroundColor Cyan + Write-Host "==> $Name$(if ($Vendor) { " ($Vendor)" })" -ForegroundColor Cyan $script:LogWriter = New-LogWriter $log try { @@ -510,26 +620,41 @@ try { if ($Checks | Where-Object { $_ -ne 'extra_json_check' }) { $Validator = Resolve-Validator } + # An empty printer set is a failure to the sweep, so validate_slice is recorded as skipped + # rather than run for a vendor that ships no printers (the filament-only OrcaFilamentLibrary); + # naming the check explicitly still runs it. $results = [ordered] @{} + $skipReasons = @{} foreach ($name in $AllChecks) { - if ($Checks -contains $name) { $results[$name] = Invoke-Check $name } + if ($Checks -notcontains $name) { continue } + if ($name -eq 'validate_slice' -and -not $NamedChecks -and $Vendor -and + -not (Test-Path -LiteralPath (Join-Path (Join-Path $ProfilesDir $Vendor) 'machine') -PathType Container)) { + $results[$name] = 'skip' + $skipReasons[$name] = "$Vendor ships no printers" + } else { + $results[$name] = if (Invoke-Check $name) { 'pass' } else { 'fail' } + } } Write-Host '' Write-Host '==> summary' -ForegroundColor Cyan foreach ($name in $results.Keys) { - if ($results[$name]) { - Write-Host " PASS $name" -ForegroundColor Green - } else { - Write-Host " FAIL $name ($(Join-Path $LogDir "$name.log"))" -ForegroundColor Red + switch ($results[$name]) { + 'pass' { Write-Host " PASS $name" -ForegroundColor Green } + 'skip' { Write-Host " SKIP $name ($($skipReasons[$name]))" -ForegroundColor Yellow } + default { Write-Host " FAIL $name ($(Join-Path $LogDir "$name.log"))" -ForegroundColor Red } } } - $failed = @($results.Keys | Where-Object { -not $results[$_] }) + $failed = @($results.Keys | Where-Object { $results[$_] -eq 'fail' }) if (-not $failed) { Remove-Item -LiteralPath (Join-Path $WorkDir 'pr_comment.md') -Force -ErrorAction SilentlyContinue Write-Host '' - Write-Host "All checks passed. Logs: $LogDir" -ForegroundColor Green + if (@($results.Values) -contains 'skip') { + Write-Host "Every check that ran passed, but CI runs the skipped ones too. Logs: $LogDir" -ForegroundColor Green + } else { + Write-Host "All checks passed. Logs: $LogDir" -ForegroundColor Green + } exit 0 } diff --git a/scripts/check_profile.sh b/scripts/check_profile.sh index 9b841d0612..cb56258c9f 100755 --- a/scripts/check_profile.sh +++ b/scripts/check_profile.sh @@ -14,6 +14,10 @@ # has, is moved aside for the duration of the run and restored on exit. Only one run per work # dir at a time. # +# -v/--vendor narrows a run to one vendor while working on that vendor's profiles - the one +# deliberate divergence from CI, which always checks the whole tree. A check that cannot be +# narrowed is left out of the run and reported as skipped; see usage(). +# # Usage: scripts/check_profile.sh [OPTION]... [CHECK]... # The check_* functions run through run_check, which dispatches on the check name, so @@ -33,14 +37,20 @@ HOST_ARCH="$(uname -m)" PROFILES_DIR="${REPO_ROOT}/resources/profiles" WORK_DIR="${REPO_ROOT}/.test/check_profiles" VALIDATOR="${ORCA_PROFILE_VALIDATOR:-}" +# Vendor to check, named after its .json - empty means every vendor, which is exactly what +# both the validator's -v and orca_extra_profile_check.py's --vendor take an empty value to mean. +# So the flag is passed unconditionally below rather than kept in an array bash 3.2 cannot expand +# empty under `set -u`. +VENDOR="" LOG_LEVEL=2 PREFER_DOWNLOAD=0 REFRESH=0 ALL_CHECKS=(extra_json_check validate_system validate_slice validate_filament_subtypes validate_custom) CHECKS=() -# "pass|fail" per check that ran; a string rather than an array because bash 3.2 -# (still the /bin/bash on macOS) cannot expand an empty array under `set -u`. +# "pass|fail" per check that ran, plus "skipwhy" for one a vendor +# scope left out; a string rather than an array because bash 3.2 (still the /bin/bash on macOS) +# cannot expand an empty array under `set -u`. RESULTS="" usage() { @@ -58,6 +68,7 @@ Checks (default: all, in this order): Options: -p, --profiles DIR profile tree to validate (default: resources/profiles) + -v, --vendor NAME check only this vendor, named after its .json (e.g. "Co Print") --validator BIN OrcaSlicer_profile_validator to use; also \$ORCA_PROFILE_VALIDATOR. Default: the local build*/ Release build (then RelWithDebInfo, then Debug) for this architecture, else the nightly release build is @@ -70,6 +81,12 @@ Options: Note: extra_json_check always looks at the tree next to the script (/resources/profiles); --profiles only redirects the validator checks. + +Note: --vendor narrows validate_custom too, by keeping only that vendor's presets in each +fixture tree. The one check it cannot narrow is validate_slice for a vendor that ships no +printers; the summary reports that one as skipped, and naming it explicitly still runs it. +extra_json_check keeps its two cross-vendor checks (setting_id and filament_id) tree-wide, +so a scoped run can still fail on another vendor's files. EOF } @@ -88,6 +105,7 @@ while [ $# -gt 0 ]; do case "$1" in -h|--help) usage; exit 0 ;; -p|--profiles) [ $# -ge 2 ] || die "$1 needs a directory"; PROFILES_DIR="$2"; shift 2 ;; + -v|--vendor) [ $# -ge 2 ] || die "$1 needs a vendor name"; VENDOR="$2"; shift 2 ;; --validator) [ $# -ge 2 ] || die "$1 needs a path"; VALIDATOR="$2"; shift 2 ;; --work-dir) [ $# -ge 2 ] || die "$1 needs a directory"; WORK_DIR="$2"; shift 2 ;; -l|--log-level) [ $# -ge 2 ] || die "$1 needs a number"; LOG_LEVEL="$2"; shift 2 ;; @@ -106,11 +124,40 @@ while [ $# -gt 0 ]; do esac done -[ "${#CHECKS[@]}" -gt 0 ] || CHECKS=("${ALL_CHECKS[@]}") +# A check named on the command line always runs; only the default set is narrowed (see the run +# loop below). +NAMED_CHECKS="${#CHECKS[@]}" +[ "${NAMED_CHECKS}" -gt 0 ] || CHECKS=("${ALL_CHECKS[@]}") [ -d "${PROFILES_DIR}" ] || die "profile directory not found: ${PROFILES_DIR}" PROFILES_DIR="$(cd -- "${PROFILES_DIR}" && pwd)" +# A vendor neither tool knows is not an error to them: the static checks load nothing of their own +# and still report success, so a typo would otherwise be three green checks and one baffling slice +# failure. The match has to be made on the names themselves rather than with a -f test - the +# validator compares the .json stem case-sensitively, while a case-insensitive filesystem +# (macOS, Windows) would let "creality" pass a file test and then match no vendor. A vendor is a +# .json with a sibling / directory; that pair is also what tells one apart from +# blacklist.json and retired_filament_ids.json, which sit in the same folder. +if [ -n "${VENDOR}" ]; then + wanted="$(printf '%s' "${VENDOR}" | tr '[:upper:]' '[:lower:]')" + found="" + suggestion="" + for file in "${PROFILES_DIR}"/*.json; do + name="${file##*/}"; name="${name%.json}" + [ -d "${PROFILES_DIR}/${name}" ] || continue + if [ "${name}" = "${VENDOR}" ]; then + found=1 + break + fi + [ "$(printf '%s' "${name}" | tr '[:upper:]' '[:lower:]')" = "${wanted}" ] && suggestion="${name}" + done + if [ -z "${found}" ]; then + [ -z "${suggestion}" ] || die "unknown vendor '${VENDOR}'; vendor names are case-sensitive, did you mean '${suggestion}'?" + die "unknown vendor '${VENDOR}': no such vendor in ${PROFILES_DIR}" + fi +fi + LOG_DIR="${WORK_DIR}/logs" mkdir -p "${LOG_DIR}" || die "cannot create ${LOG_DIR}" WORK_DIR="$(cd -- "${WORK_DIR}" && pwd)" @@ -315,34 +362,65 @@ resolve_validator() { # ---------------------------------------------------------------------------- checks check_extra_json_check() { - python3 "${REPO_ROOT}/scripts/orca_extra_profile_check.py" + python3 "${REPO_ROOT}/scripts/orca_extra_profile_check.py" --vendor "${VENDOR}" } check_validate_system() { - "${VALIDATOR}" -p "${PROFILES_DIR}" -l "${LOG_LEVEL}" + "${VALIDATOR}" -p "${PROFILES_DIR}" -v "${VENDOR}" -l "${LOG_LEVEL}" } # Slices a two-colour cube through every printer so all custom g-code (incl. change_filament_gcode) # is expanded - catches undefined-placeholder / invalid-flow bugs the static checks cannot see. check_validate_slice() { - "${VALIDATOR}" -p "${PROFILES_DIR}" -s -l "${LOG_LEVEL}" + "${VALIDATOR}" -p "${PROFILES_DIR}" -v "${VENDOR}" -s -l "${LOG_LEVEL}" } check_validate_filament_subtypes() { - "${VALIDATOR}" -p "${PROFILES_DIR}" -l "${LOG_LEVEL}" -f + "${VALIDATOR}" -p "${PROFILES_DIR}" -v "${VENDOR}" -l "${LOG_LEVEL}" -f +} + +# The fixtures name every preset "__orca_test", after the "name" inside +# .json rather than the file stem --vendor takes - BBL.json is "Bambulab", iQ.json is +# "innovatiQ". Falls back to the stem for a vendor file with no readable name. +vendor_display_name() { + local name + name="$(VENDOR_JSON="${PROFILES_DIR}/${VENDOR}.json" python3 - <<'PY' +import json +import os + +try: + with open(os.environ["VENDOR_JSON"], encoding="utf-8") as fh: + print(json.load(fh).get("name", "")) +except Exception: + pass +PY +)" + printf '%s\n' "${name:-${VENDOR}}" } # Every released fixture is a snapshot of user presets saved by that OrcaSlicer version; each is # unpacked over the current system profiles and validated, so a profile change that would break # an existing user's presets fails here. +# +# Under --vendor only that vendor's presets are unpacked, which is what makes the validator's -v +# usable here: -v filters the system vendors but never the user presets, so on a whole-tree +# snapshot it reports every other vendor's presets as unresolvable parents - thousands of errors +# saying nothing about the vendor under test. Teaching -v to filter user presets too is the deeper +# fix, but this runs against whatever validator is to hand, the published nightly included, so the +# picking has to happen here. It picks on the "_" filename prefix that +# generate_custom_presets() (the validator's -g mode) gave every preset in these fixtures; a +# fixture cut from a renamed generator would surface as the "checked nothing" warning below. +# Presets whose source had no vendor carry no prefix - a few of those still name one in the +# middle, like "PET @BBL A1_orca_test" - and only an unscoped run covers them. check_validate_custom() { local fixtures_dir="${WORK_DIR}/profile-fixtures" local output_dir="${WORK_DIR}/custom-preset-validation" local summary="${output_dir}/summary.md" - local status=0 failed_logs="" - local version asset expected_sha256 asset_url fixture_zip profile_tree log_path actual_sha256 result + local status=0 failed_logs="" vendor_prefix="" total_kept=0 + local version asset expected_sha256 asset_url fixture_zip profile_tree log_path actual_sha256 result kept command -v unzip >/dev/null 2>&1 || { msg "unzip is required for validate_custom"; return 1; } + [ -z "${VENDOR}" ] || vendor_prefix="$(vendor_display_name)_" mkdir -p "${fixtures_dir}" "${output_dir}" || return 1 fetch "${FIXTURE_RELEASE_URL}/manifest.json" "${fixtures_dir}/manifest.json" force || return 1 @@ -407,11 +485,28 @@ PY msg "validating custom presets from ${version} ..." rm -rf "${profile_tree}" mkdir -p "${profile_tree}" || return 1 - cp -a "${PROFILES_DIR}/." "${profile_tree}/" || return 1 - rm -rf "${profile_tree}/user" - unzip -q "${fixture_zip}" -d "${profile_tree}" || return 1 + if [ -n "${VENDOR}" ]; then + # -v has the validator open this vendor and OrcaFilamentLibrary and skip the rest + # (PresetBundle::load_system_presets_from_json), and unzip's * spans '/', so one + # pattern reaches every preset type. Copying and unpacking only those turns a ~5s + # setup per fixture into ~0.5s. Naming the vendor twice, when it is the library + # itself, is not an error to cp; exit 11 is unzip's "nothing matched", which is the + # fixture-predates-the-vendor case the warning below reports. + cp -a "${PROFILES_DIR}"/*.json "${PROFILES_DIR}/${VENDOR}" \ + "${PROFILES_DIR}/OrcaFilamentLibrary" "${profile_tree}/" || return 1 + unzip -q "${fixture_zip}" "user/*/${vendor_prefix}*" -d "${profile_tree}" + result=$? + [ "${result}" -eq 0 ] || [ "${result}" -eq 11 ] || return 1 + kept="$(find "${profile_tree}/user" -type f 2>/dev/null | wc -l | tr -d ' ')" + total_kept=$((total_kept + kept)) + msg " ${kept} ${VENDOR} preset file(s)" + else + cp -a "${PROFILES_DIR}/." "${profile_tree}/" || return 1 + rm -rf "${profile_tree}/user" + unzip -q "${fixture_zip}" -d "${profile_tree}" || return 1 + fi - "${VALIDATOR}" -p "${profile_tree}" -l "${LOG_LEVEL}" > "${log_path}" 2>&1 + "${VALIDATOR}" -p "${profile_tree}" -v "${VENDOR}" -l "${LOG_LEVEL}" > "${log_path}" 2>&1 result=$? if [ "${result}" -eq 0 ]; then @@ -425,6 +520,12 @@ PY fi done < "${fixtures_dir}/fixtures.tsv" + # A vendor added after the newest fixture was cut appears in none of them: nothing failed, but + # nothing was checked either. + if [ -n "${VENDOR}" ] && [ "${total_kept}" -eq 0 ]; then + msg "no ${VENDOR} presets in any fixture; validate_custom checked nothing" + fi + cat "${summary}" if [ -n "${failed_logs}" ]; then echo "" @@ -461,7 +562,7 @@ run_check() { local name="$1" local log="${LOG_DIR}/${name}.log" local result - printf '\n%s==> %s%s\n' "${C_BOLD}" "${name}" "${C_RESET}" + printf '\n%s==> %s%s%s\n' "${C_BOLD}" "${name}" "${VENDOR:+ (${VENDOR})}" "${C_RESET}" "check_${name}" 2>&1 | tee "${log}" result="${PIPESTATUS[0]}" if [ "${result}" -eq 0 ]; then @@ -477,17 +578,30 @@ if wants validate_system || wants validate_slice || wants validate_filament_subt resolve_validator fi +# An empty printer set is a failure to the sweep, so validate_slice is recorded as skipped rather +# than run for a vendor that ships no printers (the filament-only OrcaFilamentLibrary); naming the +# check explicitly still runs it. for check in "${ALL_CHECKS[@]}"; do - wants "${check}" && run_check "${check}" + wants "${check}" || continue + if [ "${check}" = validate_slice ] && [ "${NAMED_CHECKS}" -eq 0 ] && [ -n "${VENDOR}" ] && + [ ! -d "${PROFILES_DIR}/${VENDOR}/machine" ]; then + RESULTS="${RESULTS}${check}"$'\t'"skip"$'\t'"${VENDOR} ships no printers"$'\n' + else + run_check "${check}" + fi done # Summary, plus the comment check_profiles_comment.yml would post when something fails. failed=0 +skipped=0 printf '\n%s==> summary%s\n' "${C_BOLD}" "${C_RESET}" -while IFS=$'\t' read -r name result; do +while IFS=$'\t' read -r name result why; do [ -n "${name}" ] || continue if [ "${result}" = "pass" ]; then printf '%s PASS%s %s\n' "${C_GREEN}" "${C_RESET}" "${name}" + elif [ "${result}" = "skip" ]; then + printf '%s SKIP%s %s (%s)\n' "${C_BOLD}" "${C_RESET}" "${name}" "${why}" + skipped=1 else printf '%s FAIL%s %s (%s)\n' "${C_RED}" "${C_RESET}" "${name}" "${LOG_DIR}/${name}.log" failed=1 @@ -498,7 +612,12 @@ EOF if [ "${failed}" -eq 0 ]; then rm -f "${WORK_DIR}/pr_comment.md" - printf '\n%sAll checks passed.%s Logs: %s\n' "${C_GREEN}" "${C_RESET}" "${LOG_DIR}" + if [ "${skipped}" -eq 0 ]; then + printf '\n%sAll checks passed.%s Logs: %s\n' "${C_GREEN}" "${C_RESET}" "${LOG_DIR}" + else + printf '\n%sEvery check that ran passed%s, but CI runs the skipped ones too. Logs: %s\n' \ + "${C_GREEN}" "${C_RESET}" "${LOG_DIR}" + fi exit 0 fi