mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-09 10:16:50 +00:00
add per vendor support for check_profile check
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
REM Runs check_profile.ps1, the Windows twin of check_profile.sh, from cmd.
|
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 -ExecutionPolicy Bypass is needed because a Windows client defaults to Restricted,
|
||||||
REM which refuses to run a checked-out .ps1 at all.
|
REM which refuses to run a checked-out .ps1 at all.
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0check_profile.ps1" %*
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0check_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
|
has, is moved aside for the duration of the run and restored on exit. Only one run per work
|
||||||
dir at a time.
|
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
|
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
|
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
|
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
|
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.
|
tree next to the script, so this only redirects the validator checks.
|
||||||
|
|
||||||
|
.PARAMETER Vendor
|
||||||
|
Check only this vendor, named after its <Vendor>.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
|
.PARAMETER Validator
|
||||||
OrcaSlicer_profile_validator.exe to use; also $env:ORCA_PROFILE_VALIDATOR. Default: the local
|
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
|
build*\ Release build (then RelWithDebInfo, MinSizeRel, Debug) for this architecture, else
|
||||||
@@ -58,6 +70,9 @@
|
|||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\check_profile.ps1 validate_system validate_slice
|
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,
|
# PositionalBinding is off so that the check names are the only positional arguments; left on,
|
||||||
@@ -65,6 +80,7 @@
|
|||||||
[CmdletBinding(PositionalBinding = $false)]
|
[CmdletBinding(PositionalBinding = $false)]
|
||||||
param(
|
param(
|
||||||
[Alias('p')] [string] $ProfilesDir,
|
[Alias('p')] [string] $ProfilesDir,
|
||||||
|
[Alias('v')] [string] $Vendor,
|
||||||
[string] $Validator,
|
[string] $Validator,
|
||||||
[string] $WorkDir,
|
[string] $WorkDir,
|
||||||
[Alias('l')] [int] $LogLevel = 2,
|
[Alias('l')] [int] $LogLevel = 2,
|
||||||
@@ -156,12 +172,42 @@ foreach ($name in $Checks) {
|
|||||||
if ($name -like '-*') { Die "unknown option '$name' (try -Help)" }
|
if ($name -like '-*') { Die "unknown option '$name' (try -Help)" }
|
||||||
if ($AllChecks -notcontains $name) { Die "unknown check '$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 $Checks) { $Checks = $AllChecks }
|
||||||
|
|
||||||
if (-not $ProfilesDir) { $ProfilesDir = Join-Path $RepoRoot 'resources\profiles' }
|
if (-not $ProfilesDir) { $ProfilesDir = Join-Path $RepoRoot 'resources\profiles' }
|
||||||
if (-not (Test-Path -LiteralPath $ProfilesDir -PathType Container)) { Die "profile directory not found: $ProfilesDir" }
|
if (-not (Test-Path -LiteralPath $ProfilesDir -PathType Container)) { Die "profile directory not found: $ProfilesDir" }
|
||||||
$ProfilesDir = (Resolve-Path -LiteralPath $ProfilesDir).Path
|
$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 <Vendor>.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
|
||||||
|
# <name>.json with a sibling <name> 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' }
|
if (-not $WorkDir) { $WorkDir = Join-Path $RepoRoot '.test\check_profiles' }
|
||||||
$LogDir = Join-Path $WorkDir 'logs'
|
$LogDir = Join-Path $WorkDir 'logs'
|
||||||
try { New-Item -ItemType Directory -Force -Path $LogDir | Out-Null } catch { Die "cannot create ${LogDir}: $_" }
|
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'
|
Die 'no Python 3 found; install it (or the py launcher) and re-run'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The fixtures name every preset "<vendor>_<parent preset>_orca_test", after the "name" inside
|
||||||
|
# <Vendor>.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
|
# ---------------------------------------------------------------------------- checks
|
||||||
|
|
||||||
$CheckBodies = @{
|
$CheckBodies = @{
|
||||||
|
|
||||||
extra_json_check = {
|
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 = {
|
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.
|
# 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
|
# change_filament_gcode) is expanded - catches undefined-placeholder / invalid-flow bugs the
|
||||||
# static checks cannot see.
|
# static checks cannot see.
|
||||||
validate_slice = {
|
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 = {
|
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
|
# 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
|
# is unpacked over the current system profiles and validated, so a profile change that would
|
||||||
# break an existing user's presets fails here.
|
# 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 "<vendor display name>_"
|
||||||
|
# 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 = {
|
validate_custom = {
|
||||||
$fixturesDir = Join-Path $WorkDir 'profile-fixtures'
|
$fixturesDir = Join-Path $WorkDir 'profile-fixtures'
|
||||||
$outputDir = Join-Path $WorkDir 'custom-preset-validation'
|
$outputDir = Join-Path $WorkDir 'custom-preset-validation'
|
||||||
@@ -392,6 +483,8 @@ $CheckBodies = @{
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$vendorPrefix = if ($Vendor) { "$(Get-VendorDisplayName)_" } else { '' }
|
||||||
|
$totalKept = 0
|
||||||
$status = 0
|
$status = 0
|
||||||
$failedLogs = @()
|
$failedLogs = @()
|
||||||
$summary = @('## Custom Preset Fixture Validation', '', '| Version | Status | Log |', '| --- | --- | --- |')
|
$summary = @('## Custom Preset Fixture Validation', '', '| Version | Status | Log |', '| --- | --- | --- |')
|
||||||
@@ -424,12 +517,23 @@ $CheckBodies = @{
|
|||||||
Remove-Item -LiteralPath $profileTree -Recurse -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath $profileTree -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
New-Item -ItemType Directory -Force -Path $profileTree | Out-Null
|
New-Item -ItemType Directory -Force -Path $profileTree | Out-Null
|
||||||
# Piped rather than copied through <profiles>\*, so a vendor directory whose name
|
# Piped rather than copied through <profiles>\*, so a vendor directory whose name
|
||||||
# holds a wildcard character is still copied by its literal path.
|
# holds a wildcard character is still copied by its literal path. Under -Vendor the
|
||||||
Get-ChildItem -LiteralPath $ProfilesDir -Force | Copy-Item -Destination $profileTree -Recurse -Force
|
# 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
|
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) {
|
if ($result -eq 0) {
|
||||||
$summary += "| $version | PASS | $version.log |"
|
$summary += "| $version | PASS | $version.log |"
|
||||||
# Only failures are worth keeping; each tree is a full copy of resources\profiles.
|
# 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)
|
[IO.File]::WriteAllLines((Join-Path $outputDir 'summary.md'), [string[]] $summary)
|
||||||
$summary | ForEach-Object { Write-CheckLog $_ }
|
$summary | ForEach-Object { Write-CheckLog $_ }
|
||||||
|
|
||||||
@@ -473,7 +583,7 @@ $CommentHeadings = @{
|
|||||||
function Invoke-Check([string] $Name) {
|
function Invoke-Check([string] $Name) {
|
||||||
$log = Join-Path $LogDir "$Name.log"
|
$log = Join-Path $LogDir "$Name.log"
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Host "==> $Name" -ForegroundColor Cyan
|
Write-Host "==> $Name$(if ($Vendor) { " ($Vendor)" })" -ForegroundColor Cyan
|
||||||
|
|
||||||
$script:LogWriter = New-LogWriter $log
|
$script:LogWriter = New-LogWriter $log
|
||||||
try {
|
try {
|
||||||
@@ -510,26 +620,41 @@ try {
|
|||||||
|
|
||||||
if ($Checks | Where-Object { $_ -ne 'extra_json_check' }) { $Validator = Resolve-Validator }
|
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] @{}
|
$results = [ordered] @{}
|
||||||
|
$skipReasons = @{}
|
||||||
foreach ($name in $AllChecks) {
|
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 ''
|
||||||
Write-Host '==> summary' -ForegroundColor Cyan
|
Write-Host '==> summary' -ForegroundColor Cyan
|
||||||
foreach ($name in $results.Keys) {
|
foreach ($name in $results.Keys) {
|
||||||
if ($results[$name]) {
|
switch ($results[$name]) {
|
||||||
Write-Host " PASS $name" -ForegroundColor Green
|
'pass' { Write-Host " PASS $name" -ForegroundColor Green }
|
||||||
} else {
|
'skip' { Write-Host " SKIP $name ($($skipReasons[$name]))" -ForegroundColor Yellow }
|
||||||
Write-Host " FAIL $name ($(Join-Path $LogDir "$name.log"))" -ForegroundColor Red
|
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) {
|
if (-not $failed) {
|
||||||
Remove-Item -LiteralPath (Join-Path $WorkDir 'pr_comment.md') -Force -ErrorAction SilentlyContinue
|
Remove-Item -LiteralPath (Join-Path $WorkDir 'pr_comment.md') -Force -ErrorAction SilentlyContinue
|
||||||
Write-Host ''
|
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
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
# has, is moved aside for the duration of the run and restored on exit. Only one run per work
|
# has, is moved aside for the duration of the run and restored on exit. Only one run per work
|
||||||
# dir at a time.
|
# 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]...
|
# Usage: scripts/check_profile.sh [OPTION]... [CHECK]...
|
||||||
|
|
||||||
# The check_* functions run through run_check, which dispatches on the check name, so
|
# 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"
|
PROFILES_DIR="${REPO_ROOT}/resources/profiles"
|
||||||
WORK_DIR="${REPO_ROOT}/.test/check_profiles"
|
WORK_DIR="${REPO_ROOT}/.test/check_profiles"
|
||||||
VALIDATOR="${ORCA_PROFILE_VALIDATOR:-}"
|
VALIDATOR="${ORCA_PROFILE_VALIDATOR:-}"
|
||||||
|
# Vendor to check, named after its <Vendor>.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
|
LOG_LEVEL=2
|
||||||
PREFER_DOWNLOAD=0
|
PREFER_DOWNLOAD=0
|
||||||
REFRESH=0
|
REFRESH=0
|
||||||
|
|
||||||
ALL_CHECKS=(extra_json_check validate_system validate_slice validate_filament_subtypes validate_custom)
|
ALL_CHECKS=(extra_json_check validate_system validate_slice validate_filament_subtypes validate_custom)
|
||||||
CHECKS=()
|
CHECKS=()
|
||||||
# "<check><TAB>pass|fail" per check that ran; a string rather than an array because bash 3.2
|
# "<check><TAB>pass|fail" per check that ran, plus "<check><TAB>skip<TAB>why" for one a vendor
|
||||||
# (still the /bin/bash on macOS) cannot expand an empty array under `set -u`.
|
# 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=""
|
RESULTS=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
@@ -58,6 +68,7 @@ Checks (default: all, in this order):
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
-p, --profiles DIR profile tree to validate (default: resources/profiles)
|
-p, --profiles DIR profile tree to validate (default: resources/profiles)
|
||||||
|
-v, --vendor NAME check only this vendor, named after its <Vendor>.json (e.g. "Co Print")
|
||||||
--validator BIN OrcaSlicer_profile_validator to use; also \$ORCA_PROFILE_VALIDATOR.
|
--validator BIN OrcaSlicer_profile_validator to use; also \$ORCA_PROFILE_VALIDATOR.
|
||||||
Default: the local build*/ Release build (then RelWithDebInfo, then
|
Default: the local build*/ Release build (then RelWithDebInfo, then
|
||||||
Debug) for this architecture, else the nightly release build is
|
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
|
Note: extra_json_check always looks at the tree next to the script
|
||||||
(<repo>/resources/profiles); --profiles only redirects the validator checks.
|
(<repo>/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
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +105,7 @@ while [ $# -gt 0 ]; do
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
-p|--profiles) [ $# -ge 2 ] || die "$1 needs a directory"; PROFILES_DIR="$2"; shift 2 ;;
|
-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 ;;
|
--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 ;;
|
--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 ;;
|
-l|--log-level) [ $# -ge 2 ] || die "$1 needs a number"; LOG_LEVEL="$2"; shift 2 ;;
|
||||||
@@ -106,11 +124,40 @@ while [ $# -gt 0 ]; do
|
|||||||
esac
|
esac
|
||||||
done
|
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}"
|
[ -d "${PROFILES_DIR}" ] || die "profile directory not found: ${PROFILES_DIR}"
|
||||||
PROFILES_DIR="$(cd -- "${PROFILES_DIR}" && pwd)"
|
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 <Vendor>.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
|
||||||
|
# <name>.json with a sibling <name>/ 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"
|
LOG_DIR="${WORK_DIR}/logs"
|
||||||
mkdir -p "${LOG_DIR}" || die "cannot create ${LOG_DIR}"
|
mkdir -p "${LOG_DIR}" || die "cannot create ${LOG_DIR}"
|
||||||
WORK_DIR="$(cd -- "${WORK_DIR}" && pwd)"
|
WORK_DIR="$(cd -- "${WORK_DIR}" && pwd)"
|
||||||
@@ -315,34 +362,65 @@ resolve_validator() {
|
|||||||
# ---------------------------------------------------------------------------- checks
|
# ---------------------------------------------------------------------------- checks
|
||||||
|
|
||||||
check_extra_json_check() {
|
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() {
|
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)
|
# 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.
|
# is expanded - catches undefined-placeholder / invalid-flow bugs the static checks cannot see.
|
||||||
check_validate_slice() {
|
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() {
|
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 "<vendor>_<parent preset>_orca_test", after the "name" inside
|
||||||
|
# <Vendor>.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
|
# 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
|
# unpacked over the current system profiles and validated, so a profile change that would break
|
||||||
# an existing user's presets fails here.
|
# 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 "<vendor display name>_" 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() {
|
check_validate_custom() {
|
||||||
local fixtures_dir="${WORK_DIR}/profile-fixtures"
|
local fixtures_dir="${WORK_DIR}/profile-fixtures"
|
||||||
local output_dir="${WORK_DIR}/custom-preset-validation"
|
local output_dir="${WORK_DIR}/custom-preset-validation"
|
||||||
local summary="${output_dir}/summary.md"
|
local summary="${output_dir}/summary.md"
|
||||||
local status=0 failed_logs=""
|
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
|
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; }
|
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
|
mkdir -p "${fixtures_dir}" "${output_dir}" || return 1
|
||||||
|
|
||||||
fetch "${FIXTURE_RELEASE_URL}/manifest.json" "${fixtures_dir}/manifest.json" force || 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} ..."
|
msg "validating custom presets from ${version} ..."
|
||||||
rm -rf "${profile_tree}"
|
rm -rf "${profile_tree}"
|
||||||
mkdir -p "${profile_tree}" || return 1
|
mkdir -p "${profile_tree}" || return 1
|
||||||
cp -a "${PROFILES_DIR}/." "${profile_tree}/" || return 1
|
if [ -n "${VENDOR}" ]; then
|
||||||
rm -rf "${profile_tree}/user"
|
# -v has the validator open this vendor and OrcaFilamentLibrary and skip the rest
|
||||||
unzip -q "${fixture_zip}" -d "${profile_tree}" || return 1
|
# (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=$?
|
result=$?
|
||||||
|
|
||||||
if [ "${result}" -eq 0 ]; then
|
if [ "${result}" -eq 0 ]; then
|
||||||
@@ -425,6 +520,12 @@ PY
|
|||||||
fi
|
fi
|
||||||
done < "${fixtures_dir}/fixtures.tsv"
|
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}"
|
cat "${summary}"
|
||||||
if [ -n "${failed_logs}" ]; then
|
if [ -n "${failed_logs}" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
@@ -461,7 +562,7 @@ run_check() {
|
|||||||
local name="$1"
|
local name="$1"
|
||||||
local log="${LOG_DIR}/${name}.log"
|
local log="${LOG_DIR}/${name}.log"
|
||||||
local result
|
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}"
|
"check_${name}" 2>&1 | tee "${log}"
|
||||||
result="${PIPESTATUS[0]}"
|
result="${PIPESTATUS[0]}"
|
||||||
if [ "${result}" -eq 0 ]; then
|
if [ "${result}" -eq 0 ]; then
|
||||||
@@ -477,17 +578,30 @@ if wants validate_system || wants validate_slice || wants validate_filament_subt
|
|||||||
resolve_validator
|
resolve_validator
|
||||||
fi
|
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
|
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
|
done
|
||||||
|
|
||||||
# Summary, plus the comment check_profiles_comment.yml would post when something fails.
|
# Summary, plus the comment check_profiles_comment.yml would post when something fails.
|
||||||
failed=0
|
failed=0
|
||||||
|
skipped=0
|
||||||
printf '\n%s==> summary%s\n' "${C_BOLD}" "${C_RESET}"
|
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
|
[ -n "${name}" ] || continue
|
||||||
if [ "${result}" = "pass" ]; then
|
if [ "${result}" = "pass" ]; then
|
||||||
printf '%s PASS%s %s\n' "${C_GREEN}" "${C_RESET}" "${name}"
|
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
|
else
|
||||||
printf '%s FAIL%s %s (%s)\n' "${C_RED}" "${C_RESET}" "${name}" "${LOG_DIR}/${name}.log"
|
printf '%s FAIL%s %s (%s)\n' "${C_RED}" "${C_RESET}" "${name}" "${LOG_DIR}/${name}.log"
|
||||||
failed=1
|
failed=1
|
||||||
@@ -498,7 +612,12 @@ EOF
|
|||||||
|
|
||||||
if [ "${failed}" -eq 0 ]; then
|
if [ "${failed}" -eq 0 ]; then
|
||||||
rm -f "${WORK_DIR}/pr_comment.md"
|
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
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user