mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-13 19:01:21 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2063f0b2a7 | ||
|
|
a146748aa6 | ||
|
|
07bbd45a4e | ||
|
|
904f071b6f | ||
|
|
decd2fb75b | ||
|
|
ff86337026 | ||
|
|
b365b6d1fd | ||
|
|
f681233014 | ||
|
|
65e97cddd2 | ||
|
|
e55ac357b5 |
@@ -13,6 +13,7 @@ on:
|
||||
# both it and its tests live in scripts/, so a PR touching only those must still run
|
||||
# this workflow.
|
||||
- 'resources/printers/**'
|
||||
- 'resources/info/legacy_compatible_printers.json'
|
||||
- 'scripts/**'
|
||||
# orca_profile_tool.py reads the variant key sets from PrintConfig.cpp, and its
|
||||
# tests the obsolete keys, so a PR changing either must be checked against the profiles.
|
||||
@@ -59,6 +60,21 @@ jobs:
|
||||
python3 ./scripts/orca_profile_tool.py check 2>&1 | tee ${{ runner.temp }}/profile_tool.log
|
||||
exit ${PIPESTATUS[0]}
|
||||
|
||||
# The app migrates user filament presets made against 2.4.2 back to the printers their
|
||||
# parent listed then, from resources/info/legacy_compatible_printers.json. A PR that
|
||||
# renames a filament preset or narrows its compatible_printers changes what that map
|
||||
# must say, so it has to regenerate it.
|
||||
- name: Check the legacy compatible printers map
|
||||
id: legacy_compat
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -o pipefail
|
||||
base="${{ runner.temp }}/v2.4.2"
|
||||
git fetch --no-tags --depth=1 origin refs/tags/v2.4.2:refs/tags/v2.4.2
|
||||
mkdir -p "$base"
|
||||
git archive v2.4.2 resources/profiles | tar -x -C "$base"
|
||||
python3 ./scripts/orca_profile_tool.py legacy-compat --base "$base/resources/profiles" --check 2>&1 | tee ${{ runner.temp }}/legacy_compat.log
|
||||
|
||||
# download
|
||||
- name: Download
|
||||
working-directory: ${{ github.workspace }}
|
||||
@@ -217,7 +233,7 @@ jobs:
|
||||
echo "${{ github.event.pull_request.number }}" > ${{ runner.temp }}/profile-check-results/pr_number.txt
|
||||
|
||||
- name: Prepare comment artifact
|
||||
if: ${{ always() && github.event_name == 'pull_request' && (steps.profile_tool.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
if: ${{ always() && github.event_name == 'pull_request' && (steps.profile_tool.outcome == 'failure' || steps.legacy_compat.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
run: |
|
||||
{
|
||||
# Marker matched by check_profiles_comment.yml to delete prior comments.
|
||||
@@ -234,6 +250,17 @@ jobs:
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "${{ steps.legacy_compat.outcome }}" = "failure" ]; then
|
||||
echo "### Legacy Compatible Printers Map Out of Date"
|
||||
echo ""
|
||||
echo '```'
|
||||
head -c 30000 ${{ runner.temp }}/legacy_compat.log || echo "No output captured"
|
||||
echo '```'
|
||||
echo ""
|
||||
echo 'Regenerate it: `git archive v2.4.2 resources/profiles | tar -x -C /tmp/v2.4.2`, then `python3 scripts/orca_profile_tool.py legacy-compat --base /tmp/v2.4.2/resources/profiles`.'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "${{ steps.validate_system.outcome }}" = "failure" ]; then
|
||||
echo "### System Profile Validation Failed"
|
||||
echo ""
|
||||
@@ -283,7 +310,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
- name: Fail if any check failed
|
||||
if: ${{ always() && (steps.profile_tool.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
if: ${{ always() && (steps.profile_tool.outcome == 'failure' || steps.legacy_compat.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
run: |
|
||||
echo "One or more profile checks failed; see the step logs above."
|
||||
echo 'Reproduce the whole run locally with scripts/check_profile.sh (scripts\check_profile.bat on Windows).'
|
||||
|
||||
@@ -0,0 +1,795 @@
|
||||
{
|
||||
"presets": {
|
||||
"Anycubic Generic ABS": [
|
||||
"Anycubic Kobra 3 0.4 nozzle"
|
||||
],
|
||||
"Anycubic Generic TPU": [
|
||||
"Anycubic Kobra 3 0.4 nozzle"
|
||||
],
|
||||
"Bambu ABS @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu ABS @BBL X1C": [
|
||||
"Bambu Lab P1S 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu ASA @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL X1C": [
|
||||
"Bambu Lab P1P 0.6 nozzle",
|
||||
"Bambu Lab P1P 0.8 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab P1S 0.8 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.8 nozzle"
|
||||
],
|
||||
"Bambu PA6-CF @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle",
|
||||
"Bambu Lab P1P 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab P1S 0.8 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.8 nozzle"
|
||||
],
|
||||
"Bambu PAHT-CF @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PC @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PETG @0.2 nozzle": [
|
||||
"Qidi Q1 Pro 0.2 nozzle"
|
||||
],
|
||||
"Bambu PETG Basic @BBL H2D 0.6 nozzle": [
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Basic @BBL H2DP 0.6 nozzle": [
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Basic @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Basic @BBL P2S 0.6 nozzle": [
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Basic @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG HF @BBL P2S 0.6 nozzle": [
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG HF @BBL X1C": [
|
||||
"Bambu Lab P1P 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.4 nozzle"
|
||||
],
|
||||
"Bambu PETG HF @BBL X1C 0.8 nozzle": [
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PETG HF @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Translucent @BBL H2D 0.6 nozzle": [
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Translucent @BBL H2DP 0.6 nozzle": [
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Translucent @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Translucent @BBL P2S 0.6 nozzle": [
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG Translucent @BBL X1C 0.8 nozzle": [
|
||||
"Bambu Lab P1P 0.6 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PETG-CF @BBL H2D 0.6 nozzle": [
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG-CF @BBL H2DP 0.6 nozzle": [
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG-CF @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PETG-CF @BBL P2S 0.6 nozzle": [
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Basic @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Basic @BBL X1C": [
|
||||
"Bambu Lab P1S 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Basic @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Dynamic @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Dynamic @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Galaxy @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Galaxy @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Glow @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Glow @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Lite @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Lite @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Lite @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Lite @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Lite @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Marble @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle",
|
||||
"Bambu Lab P1P 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Marble @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Matte @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Matte @BBL X1C": [
|
||||
"Bambu Lab P1S 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Matte @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Metal @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Metal @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL X1C": [
|
||||
"Bambu Lab P1S 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Silk+ @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Sparkle @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Sparkle @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Tough+ @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Tough+ @BBL X1C": [
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Tough+ @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Translucent @BBL H2D 0.8 nozzle": [
|
||||
"Bambu Lab H2D 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Translucent @BBL H2DP 0.8 nozzle": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Translucent @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA Wood @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA Wood @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu PLA-CF @BBL H2D 0.6 nozzle": [
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA-CF @BBL H2DP 0.6 nozzle": [
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA-CF @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA-CF @BBL P2S 0.6 nozzle": [
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PLA-CF @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PPA-CF @BBL X1C": [
|
||||
"Bambu Lab P1P 0.6 nozzle",
|
||||
"Bambu Lab P1P 0.8 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab P1S 0.8 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.8 nozzle"
|
||||
],
|
||||
"Bambu PVA @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu PVA @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu PVA @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PVA @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu PVA @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA/PETG @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA/PETG @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA/PETG @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA/PETG @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support For PLA/PETG @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support for ABS @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support for ABS @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support for ABS @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support for ABS @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu Support for ABS @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 90A @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 90A @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 95A HF @BBL H2D": [
|
||||
"Bambu Lab H2D 0.4 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 95A HF @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.4 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 95A HF @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 95A HF @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU 95A HF @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL H2D": [
|
||||
"Bambu Lab H2D 0.6 nozzle",
|
||||
"Bambu Lab H2D 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL H2DP": [
|
||||
"Bambu Lab H2D Pro 0.6 nozzle",
|
||||
"Bambu Lab H2D Pro 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL H2S": [
|
||||
"Bambu Lab H2S 0.6 nozzle",
|
||||
"Bambu Lab H2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL P1P": [
|
||||
"Bambu Lab P1P 0.6 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL P2S": [
|
||||
"Bambu Lab P2S 0.6 nozzle",
|
||||
"Bambu Lab P2S 0.8 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL X1C": [
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 Carbon 0.6 nozzle"
|
||||
],
|
||||
"Bambu TPU for AMS @BBL X2D": [
|
||||
"Bambu Lab X2D 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic ABS": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic ASA": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic ASA-CF": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PA": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PA-CF": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PC": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PETG": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PLA": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PLA-CF": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic PVA": [
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Blocks Generic TPU": [
|
||||
"BLOCKS RD50 V2 0.4 nozzle",
|
||||
"BLOCKS RD50 V2 0.6 nozzle",
|
||||
"BLOCKS RD50 V2 0.8 nozzle",
|
||||
"BLOCKS RF50 0.4 nozzle",
|
||||
"BLOCKS RF50 0.6 nozzle",
|
||||
"BLOCKS RF50 0.8 nozzle"
|
||||
],
|
||||
"Creality Generic ABS": [
|
||||
"Creality CR-10 SE 0.2 nozzle",
|
||||
"Creality CR-10 SE 0.4 nozzle",
|
||||
"Creality CR-10 SE 0.6 nozzle",
|
||||
"Creality CR-10 SE 0.8 nozzle",
|
||||
"Creality CR-6 SE 0.2 nozzle",
|
||||
"Creality CR-6 SE 0.4 nozzle",
|
||||
"Creality CR-6 SE 0.6 nozzle",
|
||||
"Creality CR-6 SE 0.8 nozzle",
|
||||
"Creality CR-M4 0.4 nozzle",
|
||||
"Creality Ender-3 V2 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic ABS @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle"
|
||||
],
|
||||
"Creality Generic ABS @Hi-all": [
|
||||
"Creality Hi 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic ABS @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic ABS @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Plus 0.6 nozzle",
|
||||
"Creality K2 Plus 0.8 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic ASA @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle"
|
||||
],
|
||||
"Creality Generic ASA @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic ASA @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PA-CF @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle"
|
||||
],
|
||||
"Creality Generic PA-CF @K1-all": [
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PA-CF @K2-all": [
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Plus 0.6 nozzle",
|
||||
"Creality K2 Plus 0.8 nozzle"
|
||||
],
|
||||
"Creality Generic PC @K1-all": [
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PETG": [
|
||||
"Creality CR-6 SE 0.2 nozzle",
|
||||
"Creality CR-6 SE 0.4 nozzle",
|
||||
"Creality CR-6 SE 0.6 nozzle",
|
||||
"Creality CR-6 SE 0.8 nozzle",
|
||||
"Creality CR-M4 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PETG @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 0.4 nozzle",
|
||||
"Creality Ender-3 V3 0.6 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.2 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.4 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.6 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.8 nozzle"
|
||||
],
|
||||
"Creality Generic PETG @Hi-all": [
|
||||
"Creality Hi 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PETG @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PETG @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Plus 0.6 nozzle",
|
||||
"Creality K2 Plus 0.8 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PLA": [
|
||||
"Creality CR-10 SE 0.2 nozzle",
|
||||
"Creality CR-10 SE 0.4 nozzle",
|
||||
"Creality CR-10 SE 0.6 nozzle",
|
||||
"Creality CR-10 SE 0.8 nozzle",
|
||||
"Creality CR-6 SE 0.2 nozzle",
|
||||
"Creality CR-6 SE 0.4 nozzle",
|
||||
"Creality CR-6 SE 0.6 nozzle",
|
||||
"Creality CR-6 SE 0.8 nozzle",
|
||||
"Creality CR-M4 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PLA @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 0.4 nozzle",
|
||||
"Creality Ender-3 V3 0.6 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle"
|
||||
],
|
||||
"Creality Generic PLA @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PLA @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Plus 0.6 nozzle",
|
||||
"Creality K2 Plus 0.8 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PLA-CF @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic PLA-CF @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic TPU": [
|
||||
"Creality CR-10 SE 0.2 nozzle",
|
||||
"Creality CR-10 SE 0.4 nozzle",
|
||||
"Creality CR-10 SE 0.6 nozzle",
|
||||
"Creality CR-10 SE 0.8 nozzle"
|
||||
],
|
||||
"Creality Generic TPU @Ender-3V3-all": [
|
||||
"Creality Ender-3 V3 0.4 nozzle",
|
||||
"Creality Ender-3 V3 0.6 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.4 nozzle",
|
||||
"Creality Ender-3 V3 Plus 0.6 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.2 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.4 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.6 nozzle",
|
||||
"Creality Ender-3 V3 SE 0.8 nozzle"
|
||||
],
|
||||
"Creality Generic TPU @K1-all": [
|
||||
"Creality K1 SE 0.4 nozzle",
|
||||
"Creality K1C 0.4 nozzle"
|
||||
],
|
||||
"Creality Generic TPU @K2-all": [
|
||||
"Creality K2 0.4 nozzle",
|
||||
"Creality K2 Plus 0.4 nozzle",
|
||||
"Creality K2 Pro 0.4 nozzle"
|
||||
],
|
||||
"Cubicon ABS @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon ABS-A100 @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon ABSk @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PA-CF @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PC @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PETG @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PLA @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PLA+ @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Cubicon PLAi21 @base": [
|
||||
"Cubicon xCeler-Plus 0.4 nozzle"
|
||||
],
|
||||
"Flashforge Generic ABS @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle"
|
||||
],
|
||||
"Flashforge Generic ASA @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle"
|
||||
],
|
||||
"Flashforge Generic HIPS": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle"
|
||||
],
|
||||
"Flashforge Generic PETG @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle",
|
||||
"Flashforge Guider 3 Ultra 0.8 Nozzle"
|
||||
],
|
||||
"Flashforge Generic PETG-CF @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle",
|
||||
"Flashforge Guider 3 Ultra 0.8 Nozzle"
|
||||
],
|
||||
"Flashforge Generic PLA @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle",
|
||||
"Flashforge Guider 3 Ultra 0.8 Nozzle"
|
||||
],
|
||||
"Flashforge Generic PLA-CF @G3U": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle",
|
||||
"Flashforge Guider 3 Ultra 0.8 Nozzle"
|
||||
],
|
||||
"Flashforge HS PLA": [
|
||||
"Flashforge AD5X 0.4 nozzle",
|
||||
"Flashforge AD5X 0.6 nozzle",
|
||||
"Flashforge AD5X 0.8 nozzle"
|
||||
],
|
||||
"Flashforge PETG-CF": [
|
||||
"Flashforge AD5X 0.4 nozzle",
|
||||
"Flashforge AD5X 0.6 nozzle",
|
||||
"Flashforge AD5X 0.8 nozzle"
|
||||
],
|
||||
"Flashforge PLA-CF": [
|
||||
"Flashforge AD5X 0.4 nozzle",
|
||||
"Flashforge AD5X 0.6 nozzle",
|
||||
"Flashforge AD5X 0.8 nozzle"
|
||||
],
|
||||
"Flashforge TPU 95A": [
|
||||
"Flashforge AD5X 0.4 nozzle",
|
||||
"Flashforge AD5X 0.6 nozzle",
|
||||
"Flashforge AD5X 0.8 nozzle"
|
||||
],
|
||||
"FusRock Generic PAHT-CF": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle"
|
||||
],
|
||||
"FusRock Generic PET-CF": [
|
||||
"Flashforge Guider 3 Ultra 0.6 Nozzle"
|
||||
],
|
||||
"Generic PETG HF @BBL X1C": [
|
||||
"Bambu Lab P1S 0.4 nozzle",
|
||||
"Bambu Lab P1S 0.6 nozzle",
|
||||
"Bambu Lab X1 0.4 nozzle",
|
||||
"Bambu Lab X1 0.6 nozzle",
|
||||
"Bambu Lab X1 0.8 nozzle"
|
||||
],
|
||||
"Generic PLA @Hi-all": [
|
||||
"Creality Hi 0.4 nozzle",
|
||||
"Creality Hi 0.6 nozzle"
|
||||
],
|
||||
"HATCHBOX PETG @0.2 nozzle": [
|
||||
"Qidi Q1 Pro 0.2 nozzle"
|
||||
],
|
||||
"Prusa Generic PA": [
|
||||
"Prusa MINIIS 0.25 nozzle",
|
||||
"Prusa MINIIS 0.4 nozzle",
|
||||
"Prusa MINIIS 0.6 nozzle",
|
||||
"Prusa MINIIS 0.8 nozzle"
|
||||
],
|
||||
"Prusa Generic PA-CF": [
|
||||
"Prusa MINIIS 0.25 nozzle",
|
||||
"Prusa MINIIS 0.4 nozzle",
|
||||
"Prusa MINIIS 0.6 nozzle",
|
||||
"Prusa MINIIS 0.8 nozzle"
|
||||
],
|
||||
"Prusa Generic PLA-CF": [
|
||||
"Prusa MINIIS 0.25 nozzle",
|
||||
"Prusa MINIIS 0.4 nozzle",
|
||||
"Prusa MINIIS 0.6 nozzle",
|
||||
"Prusa MINIIS 0.8 nozzle"
|
||||
],
|
||||
"QIDI PETG Basic @0.2 nozzle": [
|
||||
"Qidi Q1 Pro 0.2 nozzle"
|
||||
],
|
||||
"QIDI PETG Rapido @0.2 nozzle": [
|
||||
"Qidi Q1 Pro 0.2 nozzle"
|
||||
],
|
||||
"QIDI PETG Translucent @0.2 nozzle": [
|
||||
"Qidi Q1 Pro 0.2 nozzle"
|
||||
],
|
||||
"Sovol Zero PETG HS Nozzle": [
|
||||
"Sovol Zero 0.4 nozzle"
|
||||
],
|
||||
"Sovol Zero PLA Basic HS Nozzle": [
|
||||
"Sovol Zero 0.4 nozzle"
|
||||
],
|
||||
"Sovol Zero PLA Silk HS Nozzle": [
|
||||
"Sovol Zero 0.4 nozzle"
|
||||
],
|
||||
"re3D Greengate rPETG": [
|
||||
"re3D GigabotX 2 1.75 nozzle",
|
||||
"re3D GigabotX 2 XLT 1.75 nozzle",
|
||||
"re3D TerabotX 2 1.75 nozzle"
|
||||
],
|
||||
"re3D PC": [
|
||||
"re3D Gigabot 4 0.8 nozzle",
|
||||
"re3D Gigabot 4 XLT 0.8 nozzle",
|
||||
"re3D Terabot 4 0.8 nozzle"
|
||||
],
|
||||
"re3D PETG": [
|
||||
"re3D Gigabot 4 0.8 nozzle",
|
||||
"re3D Gigabot 4 XLT 0.8 nozzle",
|
||||
"re3D Terabot 4 0.8 nozzle"
|
||||
],
|
||||
"re3D PLA": [
|
||||
"re3D Gigabot 4 0.8 nozzle",
|
||||
"re3D Gigabot 4 XLT 0.8 nozzle",
|
||||
"re3D Terabot 4 0.8 nozzle"
|
||||
],
|
||||
"re3D rPP": [
|
||||
"re3D GigabotX 2 1.75 nozzle",
|
||||
"re3D GigabotX 2 XLT 1.75 nozzle",
|
||||
"re3D TerabotX 2 1.75 nozzle"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@
|
||||
.DESCRIPTION
|
||||
The Windows counterpart of scripts/check_profile.sh, and kept deliberately close to it.
|
||||
|
||||
Runs the same five checks, in the same order, with the same validator flags, and with the
|
||||
Runs the same six checks, in the same order, with the same validator flags, and with the
|
||||
same semantics: every check runs even after an earlier one fails (the workflow's
|
||||
continue-on-error), then the script exits non-zero once at the end.
|
||||
|
||||
profile_tool scripts/orca_profile_tool.py check
|
||||
legacy_compat scripts/orca_profile_tool.py legacy-compat --check against
|
||||
the v2.4.2 profiles
|
||||
validate_system validator -p <profiles> -l <level>
|
||||
validate_slice validator -p <profiles> -s -l <level>
|
||||
validate_filament_subtypes validator -p <profiles> -l <level> -f
|
||||
@@ -20,8 +22,9 @@
|
||||
never sees a profile no <vendor>.json indexes, a preset name two files claim, or a file
|
||||
normalize and update-index would still rewrite.
|
||||
|
||||
Everything that has to be downloaded - the profile validator and the custom-preset fixture
|
||||
archives - lands under a per-user cache directory (%LOCALAPPDATA%\orca-profile-check) and
|
||||
Everything that has to be downloaded - the profile validator, the custom-preset fixture
|
||||
archives and the v2.4.2 profile tree - lands under a per-user cache directory
|
||||
(%LOCALAPPDATA%\orca-profile-check) and
|
||||
is reused on the next run. Being outside the checkout, that directory is shared by every
|
||||
worktree on the machine. It also holds one log per check plus a copy of the comment CI would
|
||||
post on the PR.
|
||||
@@ -48,7 +51,8 @@
|
||||
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. profile_tool 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.
|
||||
fail on another vendor's files, and legacy_compat is tree-wide too: the map it checks covers
|
||||
every vendor.
|
||||
|
||||
.PARAMETER Validator
|
||||
OrcaSlicer_profile_validator.exe to use; also $env:ORCA_PROFILE_VALIDATOR. Default: the local
|
||||
@@ -116,7 +120,7 @@ $HostArch = switch ($HostArch) {
|
||||
default { 'x86' }
|
||||
}
|
||||
|
||||
$AllChecks = @('profile_tool', 'validate_system', 'validate_slice', 'validate_filament_subtypes', 'validate_custom')
|
||||
$AllChecks = @('profile_tool', 'legacy_compat', 'validate_system', 'validate_slice', 'validate_filament_subtypes', 'validate_custom')
|
||||
|
||||
$script:LogWriter = $null
|
||||
$script:Python = ''
|
||||
@@ -439,6 +443,43 @@ function Expand-VendorPresets([string] $Zip, [string] $Tree, [string] $Prefix) {
|
||||
return $kept
|
||||
}
|
||||
|
||||
# The profile tree of the release resources\info\legacy_compatible_printers.json is generated
|
||||
# against. The tag is immutable, so the extracted tree is cached in the work dir and -Refresh
|
||||
# re-extracts it. A checkout without the tag (a shallow clone, a fork that never fetched tags)
|
||||
# gets it from upstream, as CI does. git archive writes a file rather than a pipe, which
|
||||
# PowerShell would re-encode, and tar.exe ships with Windows 10 and later.
|
||||
$LegacyBaseTag = 'v2.4.2'
|
||||
$LegacyBaseUrl = 'https://github.com/OrcaSlicer/OrcaSlicer.git'
|
||||
|
||||
function Get-LegacyBaseProfiles {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$base = Join-Path (Join-Path $WorkDir 'legacy-base') $LegacyBaseTag
|
||||
$profiles = Join-Path (Join-Path $base 'resources') 'profiles'
|
||||
if ($Refresh -or -not (Test-Path -LiteralPath $profiles -PathType Container)) {
|
||||
& git -C $RepoRoot rev-parse -q --verify "refs/tags/$LegacyBaseTag^{commit}" 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "fetching tag $LegacyBaseTag from $LegacyBaseUrl ..."
|
||||
& git -C $RepoRoot fetch --no-tags $LegacyBaseUrl "refs/tags/${LegacyBaseTag}:refs/tags/${LegacyBaseTag}"
|
||||
if ($LASTEXITCODE -ne 0) { throw "cannot fetch tag $LegacyBaseTag" }
|
||||
}
|
||||
# Extracted beside its final place and moved there whole, so an interrupted extraction is
|
||||
# never mistaken for the tree on the next run.
|
||||
$partial = "$base.partial"
|
||||
foreach ($dir in $base, $partial) {
|
||||
if (Test-Path -LiteralPath $dir) { Remove-Item -LiteralPath $dir -Recurse -Force }
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $partial | Out-Null
|
||||
$archive = Join-Path $partial 'profiles.tar'
|
||||
& git -C $RepoRoot archive -o $archive $LegacyBaseTag resources/profiles
|
||||
if ($LASTEXITCODE -ne 0) { throw "cannot archive resources/profiles of $LegacyBaseTag" }
|
||||
& tar -xf $archive -C $partial
|
||||
if ($LASTEXITCODE -ne 0) { throw "cannot extract $archive" }
|
||||
Remove-Item -LiteralPath $archive -Force
|
||||
Move-Item -LiteralPath $partial -Destination $base
|
||||
}
|
||||
return $profiles
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------- checks
|
||||
|
||||
$CheckBodies = @{
|
||||
@@ -447,6 +488,14 @@ $CheckBodies = @{
|
||||
Invoke-Tool -Exe (Resolve-Python) -Arguments (@((Join-Path $RepoRoot 'scripts\orca_profile_tool.py'), 'check', '--profiles', $ProfilesDir) + $VendorPyArgs)
|
||||
}
|
||||
|
||||
# The map says which printers a user filament preset made against 2.4.2 can be migrated
|
||||
# back to, so a profile change that renames a filament preset or narrows its
|
||||
# compatible_printers has to regenerate it (the CI comment gives the command).
|
||||
legacy_compat = {
|
||||
$base = Get-LegacyBaseProfiles
|
||||
Invoke-Tool -Exe (Resolve-Python) -Arguments @((Join-Path $RepoRoot 'scripts\orca_profile_tool.py'), 'legacy-compat', '--profiles', $ProfilesDir, '--base', $base, '--check')
|
||||
}
|
||||
|
||||
validate_system = {
|
||||
Invoke-Tool -Exe $Validator -Arguments (@('-p', $ProfilesDir) + $VendorArgs + @('-l', "$LogLevel"))
|
||||
}
|
||||
@@ -581,6 +630,7 @@ $CheckBodies = @{
|
||||
# Heading CI puts above this check's log in the PR comment.
|
||||
$CommentHeadings = @{
|
||||
profile_tool = '### Profile Check Failed (orca_profile_tool.py)'
|
||||
legacy_compat = '### Legacy Compatible Printers Map Out of Date'
|
||||
validate_system = '### System Profile Validation Failed'
|
||||
validate_slice = '### Slice Validation Failed (custom g-code expansion)'
|
||||
validate_filament_subtypes = '### Filament Subtype Validation Failed'
|
||||
@@ -627,7 +677,7 @@ try {
|
||||
[Console]::OutputEncoding = New-Object Text.UTF8Encoding($false)
|
||||
Push-UserPresets
|
||||
|
||||
if ($Checks | Where-Object { $_ -ne 'profile_tool' }) { $Validator = Resolve-Validator }
|
||||
if ($Checks | Where-Object { $_ -ne 'profile_tool' -and $_ -ne 'legacy_compat' }) { $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);
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
#
|
||||
# Local twin of the "Check profiles" CI job (.github/workflows/check_profiles.yml).
|
||||
#
|
||||
# Runs the same five checks, in the same order, with the same validator flags, and with the
|
||||
# Runs the same six checks, in the same order, with the same validator flags, and with the
|
||||
# same semantics: every check runs even after an earlier one fails (the workflow's
|
||||
# continue-on-error), then the script exits non-zero once at the end.
|
||||
#
|
||||
# Everything that has to be downloaded - the profile validator and the custom-preset fixture
|
||||
# archives - lands under a per-user cache directory and is reused on the next run. Being outside
|
||||
# Everything that has to be downloaded - the profile validator, the custom-preset fixture
|
||||
# archives and the v2.4.2 profile tree - lands under a per-user cache directory and is reused on
|
||||
# the next run. Being outside
|
||||
# the checkout, that directory is shared by every worktree on the machine. It also holds one log
|
||||
# per check plus a copy of the comment CI would post on the PR.
|
||||
#
|
||||
@@ -58,7 +59,7 @@ LOG_LEVEL=2
|
||||
PREFER_DOWNLOAD=0
|
||||
REFRESH=0
|
||||
|
||||
ALL_CHECKS=(profile_tool validate_system validate_slice validate_filament_subtypes validate_custom)
|
||||
ALL_CHECKS=(profile_tool legacy_compat validate_system validate_slice validate_filament_subtypes validate_custom)
|
||||
CHECKS=()
|
||||
# "<check><TAB>pass|fail" per check that ran, plus "<check><TAB>skip<TAB>why" for one a vendor
|
||||
# scope left out; a string rather than an array because bash 3.2 (still the /bin/bash on macOS)
|
||||
@@ -73,6 +74,8 @@ Usage: scripts/check_profile.sh [OPTION]... [CHECK]...
|
||||
|
||||
Checks (default: all, in this order):
|
||||
profile_tool scripts/orca_profile_tool.py check
|
||||
legacy_compat scripts/orca_profile_tool.py legacy-compat --check against the
|
||||
v2.4.2 profiles
|
||||
validate_system validator -p <profiles> -l <level>
|
||||
validate_slice validator -p <profiles> -s -l <level>
|
||||
validate_filament_subtypes validator -p <profiles> -l <level> -f
|
||||
@@ -100,7 +103,8 @@ Note: --vendor narrows validate_custom too, by keeping only that vendor's preset
|
||||
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.
|
||||
profile_tool 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.
|
||||
scoped run can still fail on another vendor's files, and legacy_compat is tree-wide too: the map
|
||||
it checks covers every vendor.
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -379,6 +383,44 @@ check_profile_tool() {
|
||||
python3 "${REPO_ROOT}/scripts/orca_profile_tool.py" check --profiles "${PROFILES_DIR}" --vendor "${VENDOR}"
|
||||
}
|
||||
|
||||
# The profile tree of the release resources/info/legacy_compatible_printers.json is generated
|
||||
# against. The tag is immutable, so the extracted tree is cached in the work dir and --refresh
|
||||
# re-extracts it. A checkout without the tag (a shallow clone, a fork that never fetched tags)
|
||||
# gets it from upstream, as CI does.
|
||||
LEGACY_BASE_TAG="v2.4.2"
|
||||
LEGACY_BASE_URL="https://github.com/OrcaSlicer/OrcaSlicer.git"
|
||||
|
||||
legacy_base_profiles() {
|
||||
local base="${WORK_DIR}/legacy-base/${LEGACY_BASE_TAG}"
|
||||
if [ "${REFRESH}" -eq 1 ] || [ ! -d "${base}/resources/profiles" ]; then
|
||||
if ! git -C "${REPO_ROOT}" rev-parse -q --verify "refs/tags/${LEGACY_BASE_TAG}^{commit}" >/dev/null; then
|
||||
msg "fetching tag ${LEGACY_BASE_TAG} from ${LEGACY_BASE_URL} ..."
|
||||
git -C "${REPO_ROOT}" fetch --no-tags "${LEGACY_BASE_URL}" \
|
||||
"refs/tags/${LEGACY_BASE_TAG}:refs/tags/${LEGACY_BASE_TAG}" || return 1
|
||||
fi
|
||||
# Extracted beside its final place and moved there whole, so an interrupted extraction is
|
||||
# never mistaken for the tree on the next run.
|
||||
local partial="${base}.partial"
|
||||
rm -rf "${base}" "${partial}"
|
||||
mkdir -p "${partial}" || return 1
|
||||
if ! git -C "${REPO_ROOT}" archive "${LEGACY_BASE_TAG}" resources/profiles | tar -x -C "${partial}"; then
|
||||
rm -rf "${partial}"
|
||||
return 1
|
||||
fi
|
||||
mv "${partial}" "${base}" || return 1
|
||||
fi
|
||||
printf '%s\n' "${base}/resources/profiles"
|
||||
}
|
||||
|
||||
# The map says which printers a user filament preset made against 2.4.2 can be migrated back
|
||||
# to, so a profile change that renames a filament preset or narrows its compatible_printers has
|
||||
# to regenerate it (the CI comment gives the command).
|
||||
check_legacy_compat() {
|
||||
local base
|
||||
base="$(legacy_base_profiles)" || return 1
|
||||
python3 "${REPO_ROOT}/scripts/orca_profile_tool.py" legacy-compat --profiles "${PROFILES_DIR}" --base "${base}" --check
|
||||
}
|
||||
|
||||
check_validate_system() {
|
||||
"${VALIDATOR}" -p "${PROFILES_DIR}" -v "${VENDOR}" -l "${LOG_LEVEL}"
|
||||
}
|
||||
@@ -563,6 +605,7 @@ EOF
|
||||
comment_heading() {
|
||||
case "$1" in
|
||||
profile_tool) echo "### Profile Check Failed (orca_profile_tool.py)" ;;
|
||||
legacy_compat) echo "### Legacy Compatible Printers Map Out of Date" ;;
|
||||
validate_system) echo "### System Profile Validation Failed" ;;
|
||||
validate_slice) echo "### Slice Validation Failed (custom g-code expansion)" ;;
|
||||
validate_filament_subtypes) echo "### Filament Subtype Validation Failed" ;;
|
||||
|
||||
@@ -11,6 +11,7 @@ commands:
|
||||
normalize rewrite profile files into their canonical shape
|
||||
trim delete profile files no <vendor>.json list references
|
||||
update-index regenerate the *_list sections of <vendor>.json
|
||||
legacy-compat write the printers user presets can be migrated back to
|
||||
|
||||
options shared by several commands:
|
||||
--vendor VENDOR act on one vendor bundle only; repeatable, empty means all
|
||||
@@ -24,6 +25,9 @@ After adding, renaming or deleting profile files, run:
|
||||
normalize -> update-index -> generate-id -> check
|
||||
normalize supplies missing types; update-index registers presets before id
|
||||
generation.
|
||||
After renaming filament presets or narrowing their compatible_printers, also
|
||||
rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2
|
||||
can be migrated back to their printers; CI runs it with --check.
|
||||
Use trim only for deliberate cleanup, previewed with --dry-run: it judges against
|
||||
the current index and can delete newly added, unindexed presets.
|
||||
|
||||
@@ -3072,6 +3076,139 @@ def fix_variant_arrays(profiles_dir=PROFILES_DIR, vendors=None, dry_run=False, s
|
||||
return len(changed), errors
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# legacy-compat
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
LEGACY_COMPAT_PATH = os.path.normpath(
|
||||
os.path.join(SCRIPTS_DIR, "..", "resources", "info", "legacy_compatible_printers.json"))
|
||||
|
||||
|
||||
def _renamed_from(data):
|
||||
"""A preset's renamed_from names. Profiles write one ';'-separated string, as the
|
||||
loader's unescape_strings_cstyle reads it; none of them quote or escape a name."""
|
||||
value = data.get("renamed_from") or []
|
||||
return [n for v in _as_list(value) for n in v.split(";") if n]
|
||||
|
||||
|
||||
def _flatten_presets(profiles_dir):
|
||||
"""Every bundle's composed filament and printer presets, for legacy-compat.
|
||||
|
||||
Returns (filaments, printers): filaments maps (vendor, name) to a dict with the
|
||||
resolved compatible_printers, filament_id, extruder variants, renamed_from and
|
||||
whether it is instantiated; printers maps each instantiated printer name to a
|
||||
dict with its renamed_from and extruder variants. instantiation is read from the
|
||||
preset's own file, as the loader requires every file to state it.
|
||||
"""
|
||||
def instantiated(data):
|
||||
return str(data.get("instantiation", "")).lower() == "true"
|
||||
|
||||
ofl = load_vendor_configs(profiles_dir, OFL)["filament"]
|
||||
filaments, printers = {}, {}
|
||||
for vendor in list_vendor_names(profiles_dir):
|
||||
configs = load_vendor_configs(profiles_dir, vendor)
|
||||
cache = {}
|
||||
for name, (_rel, data) in configs["filament"].items():
|
||||
config = compose_config(name, configs["filament"], ofl, cache, vendor == OFL)
|
||||
filaments[(vendor, name)] = {
|
||||
"compatible_printers": set(_as_list(config.get("compatible_printers", ([],))[0])),
|
||||
"filament_id": config.get("filament_id", (None,))[0],
|
||||
"variants": set(_as_list(config.get("filament_extruder_variant", ([],))[0])),
|
||||
"instantiation": instantiated(data),
|
||||
"renamed_from": _renamed_from(data),
|
||||
}
|
||||
cache = {}
|
||||
for name, (_rel, data) in configs["machine"].items():
|
||||
if instantiated(data):
|
||||
config = compose_config(name, configs["machine"], {}, cache)
|
||||
printers[name] = {
|
||||
"renamed_from": _renamed_from(data),
|
||||
"variants": set(_as_list(config.get("printer_extruder_variant", ([],))[0])),
|
||||
}
|
||||
return filaments, printers
|
||||
|
||||
|
||||
def legacy_compatible_printers(base_dir, profiles_dir=PROFILES_DIR):
|
||||
"""Printers each filament preset of the base release covered but the current
|
||||
preset it loads as no longer lists, kept only where a current preset with
|
||||
the same filament_id still covers that printer.
|
||||
|
||||
User presets store only their diff, so they inherit their parent's
|
||||
compatible_printers; when a profile change narrows that list, a user preset
|
||||
made against the base release disappears from printers it was used on. The
|
||||
app offers to write these printers into such user presets. Requiring a same-id
|
||||
successor leaves out printers a filament was deliberately taken off, and
|
||||
requiring a shared extruder variant keeps a printer from picking up another
|
||||
variant's values.
|
||||
|
||||
Keyed by the base release's preset name, which is what such a user preset's
|
||||
inherits still says. One current preset can stand in for several old ones
|
||||
through renamed_from, each with its own old list, so the key tells them
|
||||
apart. An old name resolves as the loader resolves an inherits: to the
|
||||
current selectable preset of that name, else to one listing it in
|
||||
renamed_from. A preset that is not instantiated never enters the loader's
|
||||
collection, so it does not stop the renamed_from lookup. Printer names are
|
||||
translated through the printers' renamed_from. Returns
|
||||
{old preset name: sorted printer names}.
|
||||
"""
|
||||
old_filaments, _ = _flatten_presets(base_dir)
|
||||
new_filaments, new_printers = _flatten_presets(profiles_dir)
|
||||
renamed_printer = {old: new for new, rec in new_printers.items() for old in rec["renamed_from"]}
|
||||
|
||||
by_name, by_old_name = {}, {}
|
||||
for (vendor, name), rec in new_filaments.items():
|
||||
if rec["instantiation"]:
|
||||
by_name.setdefault(name, []).append((vendor, rec))
|
||||
for old_name in rec["renamed_from"]:
|
||||
by_old_name.setdefault(old_name, []).append((vendor, rec))
|
||||
|
||||
covering = {} # filament_id -> printers some current instantiated preset covers
|
||||
for rec in new_filaments.values():
|
||||
if rec["instantiation"] and rec["filament_id"]:
|
||||
covering.setdefault(rec["filament_id"], set()).update(rec["compatible_printers"])
|
||||
|
||||
def shares_variant(filament, printer):
|
||||
variants = new_printers[printer]["variants"]
|
||||
return not filament["variants"] or not variants or bool(filament["variants"] & variants)
|
||||
|
||||
result = {}
|
||||
for (vendor, name), old in sorted(old_filaments.items()):
|
||||
# Only a selectable preset can be a user preset's parent.
|
||||
if not old["instantiation"]:
|
||||
continue
|
||||
candidates = by_name.get(name) or by_old_name.get(name, [])
|
||||
same_vendor = [r for v, r in candidates if v == vendor]
|
||||
rec = (same_vendor or [r for _, r in candidates] or [None])[0]
|
||||
# An empty list means every printer, so nothing can have been lost.
|
||||
if rec is None or not rec["compatible_printers"]:
|
||||
continue
|
||||
lost = {renamed_printer.get(p, p) for p in old["compatible_printers"]} - rec["compatible_printers"]
|
||||
lost &= covering.get(rec["filament_id"], set()) & set(new_printers)
|
||||
lost = {p for p in lost if shares_variant(rec, p)}
|
||||
if lost:
|
||||
result.setdefault(name, set()).update(lost)
|
||||
return {name: sorted(printers) for name, printers in result.items()}
|
||||
|
||||
|
||||
def run_legacy_compat(base_dir, profiles_dir, output, check):
|
||||
presets = legacy_compatible_printers(base_dir, profiles_dir)
|
||||
if check:
|
||||
try:
|
||||
with open(output, encoding="utf-8") as f:
|
||||
current = json.load(f).get("presets")
|
||||
except (OSError, ValueError, AttributeError):
|
||||
current = None
|
||||
if current != presets:
|
||||
print_error(f"{output} is out of date; rerun legacy-compat --base {base_dir}")
|
||||
return 1
|
||||
print_success(f"{output} is up to date")
|
||||
return 0
|
||||
with open(output, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(json.dumps({"presets": presets}, indent=4, sort_keys=True, ensure_ascii=False) + "\n")
|
||||
print_info(f"wrote {len(presets)} presets / {sum(map(len, presets.values()))} printers to {output}")
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -3093,11 +3230,17 @@ examples:
|
||||
preview resizing the variant arrays check rejects in that bundle
|
||||
orca_profile_tool.py check --strict --vendor BBL
|
||||
also hold every preset's inherited variant arrays to its own width
|
||||
orca_profile_tool.py legacy-compat --base /tmp/v2.4.2/resources/profiles
|
||||
rewrite resources/info/legacy_compatible_printers.json against 2.4.2's
|
||||
profiles (git archive v2.4.2 resources/profiles | tar -x -C /tmp/v2.4.2)
|
||||
|
||||
after adding, renaming or deleting profile files, run in this order:
|
||||
normalize -> update-index -> generate-id -> check
|
||||
normalize supplies missing types; update-index registers presets before id
|
||||
generation.
|
||||
After renaming filament presets or narrowing their compatible_printers, also
|
||||
rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2
|
||||
can be migrated back to their printers; CI runs it with --check.
|
||||
Use trim only for deliberate cleanup, previewed with --dry-run: it judges against
|
||||
the current index and can delete newly added, unindexed presets.
|
||||
"""
|
||||
@@ -3227,6 +3370,23 @@ def build_parser():
|
||||
"Use trim only for deliberate unindexed-file cleanup, previewed with\n"
|
||||
"--dry-run; it can also delete newly authored presets.")
|
||||
|
||||
legacy_cmd = add(
|
||||
"legacy-compat", [profiles_opt],
|
||||
"write the printers user presets can be migrated back to",
|
||||
"Compare the tree with an older release's profiles and write, per filament\n"
|
||||
"preset of that release, the printers it covered then that the preset it now\n"
|
||||
"loads as no longer lists, where a preset with the same filament_id still\n"
|
||||
"covers them. The app offers to write these into user presets that inherit\n"
|
||||
"that preset by its old name and do not set compatible_printers, so a\n"
|
||||
"narrowed list does not hide them on printers they were made for.")
|
||||
legacy_cmd.add_argument("--base", required=True, metavar="DIR",
|
||||
help="the older release's resources/profiles directory")
|
||||
legacy_cmd.add_argument("--output", default=LEGACY_COMPAT_PATH, metavar="FILE",
|
||||
help="map to write (default: resources/info/"
|
||||
"legacy_compatible_printers.json)")
|
||||
legacy_cmd.add_argument("--check", action="store_true",
|
||||
help="fail if the map differs from what --base implies; write nothing")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
@@ -3260,6 +3420,9 @@ def main(argv=None):
|
||||
return run_generate_id(profiles_dir, vendors, args.filament_id, args.setting_id,
|
||||
args.dry_run)
|
||||
|
||||
if args.command == "legacy-compat":
|
||||
return run_legacy_compat(args.base, profiles_dir, args.output, args.check)
|
||||
|
||||
if args.command == "fix-variant":
|
||||
_changed, errors = fix_variant_arrays(profiles_dir, vendors, dry_run=args.dry_run,
|
||||
strict=args.strict)
|
||||
|
||||
@@ -1258,6 +1258,118 @@ class TestVariantNames(TreeCase):
|
||||
self.assertIn('holds the legacy variant "Direct Drive Normal"', out)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# legacy-compat
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestLegacyCompat(unittest.TestCase):
|
||||
"""A base release and the current tree, each with one vendor."""
|
||||
|
||||
def setUp(self):
|
||||
self.base, self.cur = Tree(), Tree()
|
||||
self.addCleanup(self.base.cleanup)
|
||||
self.addCleanup(self.cur.cleanup)
|
||||
|
||||
@staticmethod
|
||||
def printer(tree, name, **extra):
|
||||
tree.write("V", f"machine/{name}.json",
|
||||
{"type": "machine", "name": name, "instantiation": "true", **extra})
|
||||
tree.index("V", "machine", name, f"machine/{name}.json")
|
||||
|
||||
@staticmethod
|
||||
def filament(tree, name, printers, filament_id="OFpetg01", **extra):
|
||||
tree.write("V", f"filament/{name}.json",
|
||||
{"type": "filament", "name": name, "instantiation": "true",
|
||||
"filament_id": filament_id, "compatible_printers": printers, **extra})
|
||||
tree.index("V", "filament", name, f"filament/{name}.json")
|
||||
|
||||
def generate(self):
|
||||
return apt.legacy_compatible_printers(self.base.profiles, self.cur.profiles)
|
||||
|
||||
def test_renamed_and_narrowed_preset_keeps_printers_a_same_id_sibling_covers(self):
|
||||
for tree in (self.base, self.cur):
|
||||
for p in ("A 0.4", "A 0.6", "B 0.4"):
|
||||
self.printer(tree, p)
|
||||
self.filament(self.base, "PETG @A-all", ["A 0.4", "A 0.6", "B 0.4"])
|
||||
self.filament(self.cur, "PETG @V A-all", ["A 0.6"], renamed_from="PETG @A-all;PETG A-all")
|
||||
self.filament(self.cur, "PETG @A 0.4", ["A 0.4"])
|
||||
self.assertEqual(self.generate(), {"PETG @A-all": ["A 0.4"]})
|
||||
|
||||
def test_each_old_name_gets_back_only_its_own_list(self):
|
||||
# The current "PC @0.4" existed in the base and also replaces the split "PC".
|
||||
for tree in (self.base, self.cur):
|
||||
for p in ("A 0.4", "A 0.8"):
|
||||
self.printer(tree, p)
|
||||
self.filament(self.base, "PC", ["A 0.4", "A 0.8"])
|
||||
self.filament(self.base, "PC @0.4", ["A 0.4"])
|
||||
self.filament(self.cur, "PC @0.4", ["A 0.4"], renamed_from="PC")
|
||||
self.filament(self.cur, "PC @0.8", ["A 0.8"])
|
||||
self.assertEqual(self.generate(), {"PC": ["A 0.8"]})
|
||||
|
||||
def test_old_name_now_a_base_preset_resolves_through_renamed_from(self):
|
||||
for tree in (self.base, self.cur):
|
||||
for p in ("I 0.4", "Plus 0.4"):
|
||||
self.printer(tree, p)
|
||||
self.filament(self.base, "PLA @base", ["I 0.4", "Plus 0.4"])
|
||||
self.filament(self.cur, "PLA @base", [], instantiation="false")
|
||||
self.filament(self.cur, "PLA @I 0.4", ["I 0.4"], renamed_from="PLA @base")
|
||||
self.filament(self.cur, "PLA @Plus 0.4", ["Plus 0.4"])
|
||||
self.assertEqual(self.generate(), {"PLA @base": ["Plus 0.4"]})
|
||||
|
||||
def test_printer_without_a_successor_is_left_out(self):
|
||||
for tree in (self.base, self.cur):
|
||||
for p in ("A 0.4", "A 0.6"):
|
||||
self.printer(tree, p)
|
||||
self.filament(self.base, "PETG @A", ["A 0.4", "A 0.6"])
|
||||
self.filament(self.cur, "PETG @A", ["A 0.6"])
|
||||
self.filament(self.cur, "Other @A", ["A 0.4"], filament_id="OFother1")
|
||||
self.assertEqual(self.generate(), {})
|
||||
|
||||
def test_printer_names_follow_printer_renames(self):
|
||||
self.printer(self.base, "Old 0.4")
|
||||
self.printer(self.base, "A 0.6")
|
||||
self.printer(self.cur, "New 0.4", renamed_from="Old 0.4")
|
||||
self.printer(self.cur, "A 0.6")
|
||||
self.filament(self.base, "PETG @A", ["Old 0.4", "A 0.6"])
|
||||
self.filament(self.cur, "PETG @A", ["A 0.6"])
|
||||
self.filament(self.cur, "PETG @New", ["New 0.4"])
|
||||
self.assertEqual(self.generate(), {"PETG @A": ["New 0.4"]})
|
||||
|
||||
def test_printer_without_a_shared_extruder_variant_is_left_out(self):
|
||||
for tree in (self.base, self.cur):
|
||||
self.printer(tree, "Direct 0.4", printer_extruder_variant=["Direct Drive Standard"])
|
||||
self.printer(tree, "Bowden 0.4", printer_extruder_variant=["Bowden Standard"])
|
||||
self.filament(self.base, "PETG @all", ["Direct 0.4", "Bowden 0.4"])
|
||||
self.filament(self.cur, "PETG @all", ["Direct 0.4"],
|
||||
filament_extruder_variant=["Direct Drive Standard"])
|
||||
self.filament(self.cur, "PETG @Bowden", ["Bowden 0.4"])
|
||||
self.assertEqual(self.generate(), {})
|
||||
|
||||
def test_preset_now_listing_no_printer_is_skipped(self):
|
||||
for tree in (self.base, self.cur):
|
||||
self.printer(tree, "A 0.4")
|
||||
self.filament(self.base, "PETG @A", ["A 0.4"])
|
||||
self.filament(self.cur, "PETG @A", [])
|
||||
self.assertEqual(self.generate(), {})
|
||||
|
||||
def test_check_passes_on_a_current_map_and_fails_on_a_stale_one(self):
|
||||
for tree in (self.base, self.cur):
|
||||
for p in ("A 0.4", "A 0.6"):
|
||||
self.printer(tree, p)
|
||||
self.filament(self.base, "PETG @A", ["A 0.4", "A 0.6"])
|
||||
self.filament(self.cur, "PETG @A", ["A 0.6"])
|
||||
self.filament(self.cur, "PETG @A 0.4", ["A 0.4"])
|
||||
output = os.path.join(self.cur.dir, "map.json")
|
||||
argv = ["legacy-compat", "--base", self.base.profiles, "--profiles", self.cur.profiles,
|
||||
"--output", output]
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertEqual(apt.main(argv), 0)
|
||||
self.assertEqual(apt.main(argv + ["--check"]), 0)
|
||||
with open(output, "w", encoding="utf-8") as f:
|
||||
json.dump({"presets": {}}, f)
|
||||
self.assertEqual(apt.main(argv + ["--check"]), 1)
|
||||
|
||||
|
||||
class TestDispatch(TreeCase):
|
||||
def test_each_command_reaches_its_own_writer(self):
|
||||
self.t.write("V", "filament/A.json", {"type": "filament", "name": "A"})
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "LifecycleEvents.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "InstanceLock.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include "PlaceholderParser.hpp"
|
||||
@@ -2757,6 +2758,118 @@ void PresetCollection::update_after_user_presets_loaded()
|
||||
return;
|
||||
}
|
||||
|
||||
// A user preset file as written: it holds only the keys the preset sets itself, and an inherits that names a
|
||||
// renamed parent by the old name the preset was made against. Throws if the file can not be read or parsed.
|
||||
static json read_user_preset_file(const std::string &file)
|
||||
{
|
||||
boost::nowide::ifstream in(file);
|
||||
return json::parse(in);
|
||||
}
|
||||
|
||||
std::vector<LegacyUserFilamentPreset> PresetCollection::legacy_user_filament_presets(const std::map<std::string, std::vector<std::string>> &printers_by_parent)
|
||||
{
|
||||
auto has_entry = [&printers_by_parent](const std::string &name) { return printers_by_parent.count(name) > 0; };
|
||||
std::vector<LegacyUserFilamentPreset> legacy_presets;
|
||||
// The preset sync thread can add or delete presets meanwhile.
|
||||
lock();
|
||||
for (const Preset &preset : m_presets) {
|
||||
if (!preset.is_user() || preset.inherits().empty())
|
||||
continue;
|
||||
const Preset *parent = get_preset_parent(preset);
|
||||
if (parent == nullptr || !parent->is_system)
|
||||
continue;
|
||||
// An empty list already covers every printer.
|
||||
const std::vector<std::string> &own = preset.config.option<ConfigOptionStrings>("compatible_printers")->values;
|
||||
if (own.empty())
|
||||
continue;
|
||||
// The parent's name in the older release is its current name or one in its renamed_from, so only the
|
||||
// files of presets whose parent has an entry under one of those are read.
|
||||
if (!has_entry(parent->name) && std::none_of(parent->renamed_from.begin(), parent->renamed_from.end(), has_entry))
|
||||
continue;
|
||||
LegacyUserFilamentPreset legacy{preset.name, parent->name, {}, {}, preset.file, own};
|
||||
try {
|
||||
const json j = read_user_preset_file(preset.file);
|
||||
// A preset whose file sets compatible_printers, even to an empty list, was given its printers on
|
||||
// purpose and keeps exactly those.
|
||||
if (j.contains("compatible_printers"))
|
||||
continue;
|
||||
legacy.inherits = j.value(BBL_JSON_KEY_INHERITS, std::string());
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": can not read " << preset.file << ": " << err.what();
|
||||
continue;
|
||||
}
|
||||
auto it = printers_by_parent.find(legacy.inherits);
|
||||
if (it == printers_by_parent.end())
|
||||
continue;
|
||||
for (const std::string &printer : it->second)
|
||||
if (std::find(own.begin(), own.end(), printer) == own.end()) {
|
||||
legacy.printers.push_back(printer);
|
||||
legacy.compatible_printers.push_back(printer);
|
||||
}
|
||||
if (!legacy.printers.empty())
|
||||
legacy_presets.push_back(std::move(legacy));
|
||||
}
|
||||
unlock();
|
||||
return legacy_presets;
|
||||
}
|
||||
|
||||
bool PresetCollection::write_legacy_user_filament_preset(const LegacyUserFilamentPreset &legacy)
|
||||
{
|
||||
// Only compatible_printers changes in the file. Saving the whole preset would also rewrite an inherits that
|
||||
// names a renamed parent by its old name, the only name older releases know it by.
|
||||
try {
|
||||
json j = read_user_preset_file(legacy.file);
|
||||
// Changed by hand or by another instance since the preset was found: a list of its own stands, and
|
||||
// another parent would need other printers.
|
||||
if (j.contains("compatible_printers") || j.value(BBL_JSON_KEY_INHERITS, std::string()) != legacy.inherits)
|
||||
return false;
|
||||
j["compatible_printers"] = legacy.compatible_printers;
|
||||
if (const std::error_code ec = write_file_atomically(legacy.file, j.dump(1, '\t') + "\n")) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to save " << legacy.file << ": " << ec.message();
|
||||
return false;
|
||||
}
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": can not migrate " << legacy.file << ": " << err.what();
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": user preset " << legacy.preset << " gets " << legacy.printers.size()
|
||||
<< " printer(s) its parent listed in an older release";
|
||||
return true;
|
||||
}
|
||||
|
||||
void PresetCollection::apply_legacy_user_filament_presets(const std::vector<LegacyUserFilamentPreset> &written)
|
||||
{
|
||||
lock();
|
||||
for (const LegacyUserFilamentPreset &legacy : written) {
|
||||
auto it = this->find_preset_internal(legacy.preset);
|
||||
if (it == m_presets.end() || it->name != legacy.preset)
|
||||
continue; // deleted by the preset sync thread meanwhile
|
||||
std::vector<std::string> &saved = it->config.option<ConfigOptionStrings>("compatible_printers", true)->values;
|
||||
std::vector<std::string> &edited = m_edited_preset.config.option<ConfigOptionStrings>("compatible_printers", true)->values;
|
||||
// Added to the list the preset holds now rather than replacing it: the cloud sync may have given the
|
||||
// preset a newer list since it was found, and that list is still to be written out.
|
||||
std::vector<std::string> printers = saved;
|
||||
for (const std::string &printer : legacy.printers)
|
||||
if (std::find(printers.begin(), printers.end(), printer) == printers.end())
|
||||
printers.push_back(printer);
|
||||
// A change to the list pending in the settings tab stays pending. Without one, the edited copy and the
|
||||
// snapshot unsaved changes are measured against take the written list, so nothing reads as modified.
|
||||
if (m_idx_selected == static_cast<size_t>(it - m_presets.begin()) && edited == saved) {
|
||||
edited = printers;
|
||||
if (m_saved_preset.name == it->name)
|
||||
m_saved_preset.config.option<ConfigOptionStrings>("compatible_printers", true)->values = printers;
|
||||
}
|
||||
saved = printers;
|
||||
// Push the change to the cloud copy, as an edit in the settings tab would. A sync already pending for
|
||||
// the preset (a download waiting to be saved, a deletion) keeps its state.
|
||||
if (!it->setting_id.empty() && it->sync_info.empty()) {
|
||||
it->sync_info = "update";
|
||||
it->save_info();
|
||||
}
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
//BBS: validate_preset
|
||||
bool PresetCollection::validate_preset(const std::string &preset_name, std::string &inherit_name)
|
||||
{
|
||||
|
||||
@@ -551,6 +551,20 @@ struct PresetConfigSubstitutions {
|
||||
// PrusaSlicer and reading the user Print / Filament / Printer profiles.
|
||||
using PresetsConfigSubstitutions = std::vector<PresetConfigSubstitutions>;
|
||||
|
||||
// A user filament preset made against an older release that no longer shows on printers its system parent
|
||||
// listed then, and those printers. Migrating it writes them into the preset's own compatible_printers.
|
||||
struct LegacyUserFilamentPreset {
|
||||
std::string preset;
|
||||
std::string parent;
|
||||
// The parent's name as the preset file states it: the name the older release knew it by.
|
||||
std::string inherits;
|
||||
// The printers the preset lost: what the prompt lists.
|
||||
std::vector<std::string> printers;
|
||||
std::string file;
|
||||
// The list the file gets: the parent's current printers plus the lost ones.
|
||||
std::vector<std::string> compatible_printers;
|
||||
};
|
||||
|
||||
// Collections of presets of the same type (one of the Print, Filament or Printer type).
|
||||
class PresetCollection
|
||||
{
|
||||
@@ -601,6 +615,18 @@ public:
|
||||
void save_user_presets(const std::string& dir_path, const std::string& type, std::map<std::string, std::string>& need_to_delete_list);
|
||||
bool load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin = PresetOrigin(PresetOrigin::Kind::User));
|
||||
void update_after_user_presets_loaded();
|
||||
// User presets store only their diff, so a user preset inherits its system parent's compatible_printers
|
||||
// and disappears from a printer once a profile update narrows that list. Lists the user presets whose
|
||||
// files do not set compatible_printers, and the printers their parent covered in an older release but
|
||||
// no longer lists. printers_by_parent is keyed by the parent's name in that release, which is the name
|
||||
// the preset file's inherits still holds.
|
||||
std::vector<LegacyUserFilamentPreset> legacy_user_filament_presets(const std::map<std::string, std::vector<std::string>> &printers_by_parent);
|
||||
// Writes legacy.compatible_printers into the preset's file and keeps every other key. The caller holds the
|
||||
// user presets lock. False if the file has gained a compatible_printers key or another parent since the
|
||||
// preset was found, or could not be written.
|
||||
static bool write_legacy_user_filament_preset(const LegacyUserFilamentPreset &legacy);
|
||||
// Gives the loaded presets the lists their files were just given, and marks cloud copies for upload.
|
||||
void apply_legacy_user_filament_presets(const std::vector<LegacyUserFilamentPreset> &written);
|
||||
//BBS: get user presets
|
||||
int get_user_presets(PresetBundle *preset_bundle, std::vector<Preset> &result_presets);
|
||||
void set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time, const std::string& user_id);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/directory.hpp>
|
||||
#include <boost/filesystem/file_status.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
@@ -38,6 +39,8 @@
|
||||
#include "ParallelResolve.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "PresetCacheFormat.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "Zipper.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "PublishSettings.hpp"
|
||||
#include "FilamentMixer.hpp"
|
||||
@@ -96,6 +99,21 @@ using json = nlohmann::json;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Per system filament preset of an older release, by its name then, the printers it listed that the preset it
|
||||
// now loads as does not (resources/info/legacy_compatible_printers.json, written by orca_profile_tool.py
|
||||
// legacy-compat). Empty if the file is missing or malformed.
|
||||
static std::map<std::string, std::vector<std::string>> legacy_compatible_printers()
|
||||
{
|
||||
const fs::path path = fs::path(resources_dir()) / "info" / "legacy_compatible_printers.json";
|
||||
try {
|
||||
boost::nowide::ifstream in(path.string());
|
||||
return json::parse(in).at("presets").get<std::map<std::string, std::vector<std::string>>>();
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": can not read " << path.string() << ": " << err.what();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Project-level options imported from a loaded 3MF into project_config. s_project_options_published
|
||||
// below is the reduced subset that crosses over in "published" 3MF mode; keep both in sync.
|
||||
// s_project_options_published additionally carries wipe_tower_rotation_angle, which normal
|
||||
@@ -1045,6 +1063,73 @@ bool PresetBundle::backup_user_folder() const
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetBundle::backup_user_folder_to_zip(const std::string &zip_path) const
|
||||
{
|
||||
const fs::path user_dir = fs::path(data_dir()) / PRESET_USER_DIR;
|
||||
BOOST_LOG_TRIVIAL(info) << "Backing up user folder to: " << zip_path;
|
||||
try {
|
||||
Zipper zipper(zip_path);
|
||||
// Directories get their own entries, which extract_archive_confined() needs to recreate them.
|
||||
zipper.add_entry(std::string(PRESET_USER_DIR) + "/", nullptr, 0);
|
||||
// The loader follows symlinked folders, and migration writes the presets in them.
|
||||
for (const fs::directory_entry &entry : fs::recursive_directory_iterator(user_dir, fs::directory_options::follow_directory_symlink)) {
|
||||
const std::string name = (fs::path(PRESET_USER_DIR) / entry.path().lexically_relative(user_dir)).generic_string();
|
||||
if (fs::is_directory(entry.status())) {
|
||||
zipper.add_entry(name + "/", nullptr, 0);
|
||||
continue;
|
||||
}
|
||||
if (!fs::is_regular_file(entry.status()))
|
||||
continue;
|
||||
boost::nowide::ifstream in(entry.path().string(), std::ios::binary);
|
||||
const std::string data((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
if (!in.good() && !in.eof())
|
||||
throw Slic3r::RuntimeError("can not read " + entry.path().string());
|
||||
zipper.add_entry(name, data.data(), data.size());
|
||||
}
|
||||
zipper.finalize();
|
||||
return true;
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Exception during user folder backup: " << ex.what();
|
||||
boost::system::error_code ec;
|
||||
fs::remove(zip_path, ec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LegacyUserFilamentPreset> PresetBundle::legacy_user_filament_presets()
|
||||
{
|
||||
return filaments.legacy_user_filament_presets(legacy_compatible_printers());
|
||||
}
|
||||
|
||||
PresetBundle::LegacyUserFilamentPresetMigration PresetBundle::migrate_legacy_user_filament_presets(const std::vector<LegacyUserFilamentPreset> &legacy_presets)
|
||||
{
|
||||
LegacyUserFilamentPresetMigration result;
|
||||
std::vector<LegacyUserFilamentPreset> written;
|
||||
const std::string zip_path = data_dir() + "/filament_preset_migration_" +
|
||||
Utils::time2str(Utils::TimeZone::utc, Utils::TimeFormat::iso8601Z) + "_backup.zip";
|
||||
// Before the lock: zipping a large folder takes longer than a guard may be held.
|
||||
if (!backup_user_folder_to_zip(zip_path))
|
||||
return result;
|
||||
result.backup = zip_path;
|
||||
{
|
||||
// One hold for every write, so a lock another instance keeps is one outcome rather than a failure per
|
||||
// preset (the lock backs off for a while after a miss). Held without the collection mutex, the order
|
||||
// InstanceLock requires; each write re-reads its file, so a save that slipped in before is respected.
|
||||
const std::string lock_path = user_presets_lock_path();
|
||||
InstanceLock instance_lock(lock_path);
|
||||
if (!lock_path.empty() && !instance_lock.locked())
|
||||
return result;
|
||||
result.locked = true;
|
||||
for (const LegacyUserFilamentPreset &legacy : legacy_presets)
|
||||
if (PresetCollection::write_legacy_user_filament_preset(legacy))
|
||||
written.push_back(legacy);
|
||||
else
|
||||
result.failed.push_back(legacy.preset);
|
||||
}
|
||||
filaments.apply_legacy_user_filament_presets(written);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<FilamentBaseInfo> PresetBundle::get_filament_by_filament_id(const std::string& filament_id, const std::string& printer_name) const
|
||||
{
|
||||
if (filament_id.empty())
|
||||
|
||||
@@ -376,6 +376,21 @@ public:
|
||||
bool use_bbl_device_tab();
|
||||
|
||||
bool backup_user_folder() const;
|
||||
// Writes the user preset folder into a zip archive at zip_path, its entries under "user/".
|
||||
bool backup_user_folder_to_zip(const std::string &zip_path) const;
|
||||
|
||||
// User filament presets made against an older release that no longer show on printers their parent
|
||||
// listed then (resources/info/legacy_compatible_printers.json).
|
||||
std::vector<LegacyUserFilamentPreset> legacy_user_filament_presets();
|
||||
// Backs up the user preset folder into a zip in data_dir(), then writes each preset's printers into its file
|
||||
// under one hold of the user presets lock. Nothing is written when the backup failed (backup empty) or the
|
||||
// lock could not be taken (locked false); otherwise failed names the presets whose files could not be written.
|
||||
struct LegacyUserFilamentPresetMigration {
|
||||
bool locked{false};
|
||||
std::string backup;
|
||||
std::vector<std::string> failed;
|
||||
};
|
||||
LegacyUserFilamentPresetMigration migrate_legacy_user_filament_presets(const std::vector<LegacyUserFilamentPreset> &legacy_presets);
|
||||
|
||||
//BBS: project embedded preset logic
|
||||
PresetsConfigSubstitutions load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule);
|
||||
|
||||
@@ -1109,6 +1109,8 @@ void GUI_App::post_init()
|
||||
}
|
||||
|
||||
this->check_new_version_sf();
|
||||
if (!cw_showed)
|
||||
this->check_legacy_user_filament_presets(false);
|
||||
const auto cloud_provider = get_printer_cloud_provider();
|
||||
if (is_user_login(cloud_provider) && !app_config->get_stealth_mode()) {
|
||||
// this->check_privacy_version(0);
|
||||
@@ -6816,6 +6818,71 @@ void GUI_App::reload_settings()
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_App::check_legacy_user_filament_presets(bool by_user)
|
||||
{
|
||||
if (!by_user && app_config->get_bool("legacy_user_filament_presets_dont_remind"))
|
||||
return;
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets = preset_bundle->legacy_user_filament_presets();
|
||||
if (legacy_presets.empty()) {
|
||||
if (by_user)
|
||||
MessageDialog(mainframe, _L("All your filament presets are available on the printers they were made for."),
|
||||
_L("Migrate Filament Presets"), wxOK | wxICON_INFORMATION).ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
wxString message = _L("OrcaSlicer 2.5.0 restructured many system filament presets to fix incorrect profiles and prepare "
|
||||
"for future features. As a result, the presets below no longer show on some printers they were made "
|
||||
"for. Sorry for the trouble; this is a one-time fix.\n\n"
|
||||
"Add those printers back to your presets now? Your presets are backed up first. You can also do this "
|
||||
"later from Help > Migrate Filament Presets.") + "\n";
|
||||
for (const LegacyUserFilamentPreset &legacy : legacy_presets) {
|
||||
message += "\n" + format_wxstr(_L("%1% (based on %2%)"), legacy.preset, legacy.parent);
|
||||
for (const std::string &printer : legacy.printers)
|
||||
message += "\n " + from_u8(printer);
|
||||
}
|
||||
RichMessageDialog dlg(mainframe, message, _L("Migrate Filament Presets"), wxYES | wxNO | wxICON_QUESTION);
|
||||
if (!by_user)
|
||||
dlg.ShowCheckBox(_L("Don't remind me again"));
|
||||
const bool migrate = dlg.ShowModal() == wxID_YES;
|
||||
if (dlg.IsCheckBoxChecked())
|
||||
app_config->set_bool("legacy_user_filament_presets_dont_remind", true);
|
||||
if (!migrate)
|
||||
return;
|
||||
|
||||
const PresetBundle::LegacyUserFilamentPresetMigration migration = preset_bundle->migrate_legacy_user_filament_presets(legacy_presets);
|
||||
const std::string &backup = migration.backup;
|
||||
const std::vector<std::string> &failed = migration.failed;
|
||||
if (backup.empty()) {
|
||||
show_error(mainframe, _L("Your presets could not be backed up, so they were not migrated."));
|
||||
return;
|
||||
}
|
||||
if (!migration.locked) {
|
||||
// The lock is also refused for a while after a miss, and when the lock file itself cannot be opened.
|
||||
show_error(mainframe, _L("The user presets folder could not be locked, so nothing was migrated. Another OrcaSlicer "
|
||||
"instance may be saving presets; try again later from Help > Migrate Filament Presets."));
|
||||
return;
|
||||
}
|
||||
if (failed.size() == legacy_presets.size()) {
|
||||
show_error(mainframe, format_wxstr(_L("Your filament presets could not be migrated. A backup of your presets was saved to:\n%1%"), backup));
|
||||
return;
|
||||
}
|
||||
preset_bundle->update_compatible(PresetSelectCompatibleType::Never);
|
||||
mainframe->update_side_preset_ui();
|
||||
for (auto tab : tabs_list) {
|
||||
tab->reload_config();
|
||||
tab->update_changed_ui();
|
||||
}
|
||||
|
||||
message = format_wxstr(_L("Your filament presets were migrated. Select them again in the filament list where you used them.\n\n"
|
||||
"A backup of your presets was saved to:\n%1%"), backup);
|
||||
if (!failed.empty()) {
|
||||
message += "\n\n" + _L("These presets could not be migrated:");
|
||||
for (const std::string &name : failed)
|
||||
message += "\n" + from_u8(name);
|
||||
}
|
||||
MessageDialog(mainframe, message, _L("Migrate Filament Presets"), wxOK | (failed.empty() ? wxICON_INFORMATION : wxICON_WARNING)).ShowModal();
|
||||
}
|
||||
|
||||
//BBS reload when logout
|
||||
void GUI_App::remove_user_presets()
|
||||
{
|
||||
|
||||
@@ -567,6 +567,10 @@ public:
|
||||
void check_update(bool show_tips, int by_user);
|
||||
void check_new_version(bool show_tips = false, int by_user = 0);
|
||||
void check_new_version_sf(bool show_tips = false, int by_user = 0);
|
||||
// Offers to migrate user filament presets made against an older release that lost printers their parent
|
||||
// listed then. At startup it asks on every start that finds such presets, unless the user chose not to be
|
||||
// reminded; by_user also reports when there is nothing to migrate.
|
||||
void check_legacy_user_filament_presets(bool by_user);
|
||||
bool process_network_msg(std::string dev_id, std::string msg);
|
||||
void request_new_version(int by_user);
|
||||
void enter_force_upgrade();
|
||||
|
||||
@@ -2827,6 +2827,10 @@ wxMenu* MainFrame::generate_help_menu()
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Show Configuration Folder"), _L("Show Configuration Folder"),
|
||||
[](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
|
||||
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Migrate Filament Presets"),
|
||||
_L("Bring back filament presets that a profile update removed from printers they were made for"),
|
||||
[](wxCommandEvent&) { wxGetApp().check_legacy_user_filament_presets(true); });
|
||||
|
||||
helpMenu->AppendSeparator();
|
||||
|
||||
// Troubleshoot center
|
||||
|
||||
@@ -589,6 +589,10 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
Slic3r::GUI::desktop_open_datadir_folder();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_migrate_filament_presets", L("Migrate Filament Presets"), L("Help"), [](const std::string&) {
|
||||
wxGetApp().check_legacy_user_filament_presets(true);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_troubleshoot", L("Troubleshoot Center"), L("Help"), [](const std::string&) {
|
||||
wxGetApp().troubleshoot();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PublishSettings.hpp"
|
||||
#include <set>
|
||||
@@ -7261,3 +7262,233 @@ TEST_CASE("A user filament whose derived compatible printer cannot be written ba
|
||||
CHECK(read_file(file) == before);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// A filament collection that exposes the rename map update, which PresetBundle runs after loading system presets.
|
||||
struct RenameTestFilaments : public PresetCollection
|
||||
{
|
||||
RenameTestFilaments()
|
||||
: PresetCollection(Preset::TYPE_FILAMENT, Preset::filament_options(),
|
||||
static_cast<const PrintRegionConfig &>(FullPrintConfig::defaults()))
|
||||
{}
|
||||
using PresetCollection::update_map_system_profile_renamed;
|
||||
};
|
||||
|
||||
// A system filament preset that lists the given printers and is known by the given former names.
|
||||
void add_system_filament(PresetCollection &filaments, const std::string &name, std::vector<std::string> printers,
|
||||
std::vector<std::string> renamed_from = {})
|
||||
{
|
||||
add_inmemory_preset(filaments, name).is_system = true;
|
||||
filaments.find_preset(name)->config.option<ConfigOptionStrings>("compatible_printers", true)->values = std::move(printers);
|
||||
set_renamed_from(filaments, name, std::move(renamed_from));
|
||||
}
|
||||
|
||||
// A user filament preset file as the settings tab saves it: only its diff, so unless extra sets
|
||||
// compatible_printers, the printer list comes from the parent.
|
||||
fs::path write_user_filament(const fs::path &dir, const std::string &name, const std::string &inherits, const std::string &extra = {})
|
||||
{
|
||||
const fs::path file = dir / PRESET_FILAMENT_NAME / (name + ".json");
|
||||
fs::create_directories(file.parent_path());
|
||||
std::ofstream(file.string()) << R"({"type":"filament","name":")" << name << R"(","from":"User","version":"1.0.0","inherits":")"
|
||||
<< inherits << '"' << extra << '}';
|
||||
return file;
|
||||
}
|
||||
|
||||
void load_user_filaments(PresetCollection &filaments, const fs::path &dir)
|
||||
{
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
filaments.load_presets(dir.string(), PRESET_FILAMENT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
}
|
||||
|
||||
const std::string k1_parent = "Generic PETG @Creality K1-all";
|
||||
const std::string k1_old_parent = "Creality Generic PETG @K1-all";
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("User filaments made against an older release that lost printers their narrowed system parent listed are found", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
add_system_filament(filaments, k1_parent, { "Creality K1C 0.6 nozzle" }, { k1_old_parent });
|
||||
filaments.update_map_system_profile_renamed();
|
||||
write_user_filament(temp_dir.path(), "Old PETG", k1_old_parent);
|
||||
write_user_filament(temp_dir.path(), "New PETG", k1_parent);
|
||||
write_user_filament(temp_dir.path(), "Own list PETG", k1_old_parent, R"(,"compatible_printers":["Creality K1C 0.6 nozzle"])");
|
||||
write_user_filament(temp_dir.path(), "Narrowed PETG", k1_old_parent, R"(,"compatible_printers":["Creality K1C 0.8 nozzle"])");
|
||||
write_user_filament(temp_dir.path(), "Empty list PETG", k1_old_parent, R"(,"compatible_printers":[])");
|
||||
load_user_filaments(filaments, temp_dir.path());
|
||||
|
||||
// The map is keyed by the parent's old name, which only the file still holds: a preset made against the
|
||||
// parent's current name is left alone, as is one whose file sets compatible_printers at all, whether to the
|
||||
// parent's list, to other printers or to an empty list. A printer the parent still lists is not reported again.
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
filaments.legacy_user_filament_presets({ { k1_old_parent, { "Creality K1C 0.4 nozzle", "Creality K1C 0.6 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
CHECK(legacy_presets[0].preset == "Old PETG");
|
||||
CHECK(legacy_presets[0].parent == k1_parent);
|
||||
CHECK(legacy_presets[0].inherits == k1_old_parent);
|
||||
CHECK(legacy_presets[0].printers == std::vector<std::string>{ "Creality K1C 0.4 nozzle" });
|
||||
CHECK(legacy_presets[0].file == (temp_dir.path() / PRESET_FILAMENT_NAME / "Old PETG.json").string());
|
||||
CHECK(legacy_presets[0].compatible_printers == std::vector<std::string>{ "Creality K1C 0.6 nozzle", "Creality K1C 0.4 nozzle" });
|
||||
}
|
||||
|
||||
TEST_CASE("Migrating a user filament backs up the user folder to a zip and writes the legacy printers to its file", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
ScopedDataDir data(temp_dir.path());
|
||||
const fs::path user_dir = temp_dir.path() / PRESET_USER_DIR / "default";
|
||||
const fs::path file = write_user_filament(user_dir, "My PETG", k1_parent);
|
||||
const std::string original = read_file(file);
|
||||
PresetBundle bundle;
|
||||
add_system_filament(bundle.filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
load_user_filaments(bundle.filaments, user_dir);
|
||||
const std::map<std::string, std::vector<std::string>> map{ { k1_parent, { "Creality K1C 0.4 nozzle" } } };
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets = bundle.filaments.legacy_user_filament_presets(map);
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
|
||||
const PresetBundle::LegacyUserFilamentPresetMigration migration = bundle.migrate_legacy_user_filament_presets(legacy_presets);
|
||||
const std::string &backup = migration.backup;
|
||||
CHECK(migration.locked);
|
||||
REQUIRE_FALSE(backup.empty());
|
||||
CHECK(std::regex_match(fs::path(backup).filename().string(), std::regex("filament_preset_migration_\\d{8}T\\d{6}Z_backup\\.zip")));
|
||||
CHECK(migration.failed.empty());
|
||||
const fs::path extracted = temp_dir.path() / "extracted";
|
||||
REQUIRE(extract_archive_confined(backup, extracted.string()));
|
||||
CHECK(read_file(extracted / PRESET_USER_DIR / "default" / PRESET_FILAMENT_NAME / "My PETG.json") == original);
|
||||
|
||||
// Reloaded from disk, the preset carries both printers itself and needs no further migration.
|
||||
PresetBundle reloaded;
|
||||
add_system_filament(reloaded.filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
load_user_filaments(reloaded.filaments, user_dir);
|
||||
const std::vector<std::string> expected{ "Creality K1C 0.6 nozzle", "Creality K1C 0.4 nozzle" };
|
||||
CHECK(reloaded.filaments.find_preset("My PETG")->config.option<ConfigOptionStrings>("compatible_printers")->values == expected);
|
||||
CHECK(reloaded.filaments.legacy_user_filament_presets(map).empty());
|
||||
// The file now sets its own list, so the migration found earlier no longer applies to it.
|
||||
CHECK(reloaded.migrate_legacy_user_filament_presets(legacy_presets).failed == std::vector<std::string>{ "My PETG" });
|
||||
}
|
||||
|
||||
TEST_CASE("Migrating a user filament keeps the old name of its renamed parent", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
add_system_filament(filaments, k1_parent, { "Creality K1C 0.6 nozzle" }, { k1_old_parent });
|
||||
filaments.update_map_system_profile_renamed();
|
||||
const fs::path file = write_user_filament(temp_dir.path(), "My PETG", k1_old_parent);
|
||||
load_user_filaments(filaments, temp_dir.path());
|
||||
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
filaments.legacy_user_filament_presets({ { k1_old_parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
REQUIRE(PresetCollection::write_legacy_user_filament_preset(legacy_presets[0]));
|
||||
// An older release knows the parent only by its old name.
|
||||
const std::string saved = read_file(file);
|
||||
CHECK(saved.find("\"inherits\": \"" + k1_old_parent + "\"") != std::string::npos);
|
||||
CHECK(saved.find("Creality K1C 0.4 nozzle") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("Migrating a user filament leaves a file another instance changed after it was found", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
add_system_filament(filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
const fs::path file = write_user_filament(temp_dir.path(), "My PETG", k1_parent);
|
||||
load_user_filaments(filaments, temp_dir.path());
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
filaments.legacy_user_filament_presets({ { k1_parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
CHECK(legacy_presets[0].inherits == k1_parent);
|
||||
|
||||
SECTION("to set its own printers") {
|
||||
write_user_filament(temp_dir.path(), "My PETG", k1_parent, R"(,"compatible_printers":["Creality K1C 0.8 nozzle"])");
|
||||
}
|
||||
SECTION("to inherit another parent") {
|
||||
write_user_filament(temp_dir.path(), "My PETG", "Generic PETG @System");
|
||||
}
|
||||
const std::string edited = read_file(file);
|
||||
CHECK_FALSE(PresetCollection::write_legacy_user_filament_preset(legacy_presets[0]));
|
||||
CHECK(read_file(file) == edited);
|
||||
}
|
||||
|
||||
TEST_CASE("Migrating user filaments backs up and writes nothing while it can not take the user presets lock", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
ScopedDataDir data(temp_dir.path());
|
||||
const fs::path user_dir = temp_dir.path() / PRESET_USER_DIR / "default";
|
||||
const fs::path file = write_user_filament(user_dir, "My PETG", k1_parent);
|
||||
const std::string original = read_file(file);
|
||||
PresetBundle bundle;
|
||||
add_system_filament(bundle.filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
load_user_filaments(bundle.filaments, user_dir);
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
bundle.filaments.legacy_user_filament_presets({ { k1_parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
|
||||
// A folder in the lock file's place can not be opened as the lock; a fresh data dir, since loading has
|
||||
// already opened the lock file of the first one. The backup is taken before the lock, then one hold covers
|
||||
// every write, so a lock held elsewhere is one outcome, not a failure per preset.
|
||||
const fs::path other_data = temp_dir.path() / "data";
|
||||
fs::create_directories(other_data / PRESET_USER_DIR);
|
||||
ScopedDataDir data_for_migration(other_data);
|
||||
fs::create_directories(fs::path(user_presets_lock_path()));
|
||||
const PresetBundle::LegacyUserFilamentPresetMigration migration = bundle.migrate_legacy_user_filament_presets(legacy_presets);
|
||||
CHECK_FALSE(migration.backup.empty());
|
||||
CHECK_FALSE(migration.locked);
|
||||
CHECK(migration.failed.empty());
|
||||
CHECK(read_file(file) == original);
|
||||
}
|
||||
|
||||
TEST_CASE("Applying a migration adds the printers to the list the preset holds now and keeps a pending cloud sync", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
add_system_filament(filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
write_user_filament(temp_dir.path(), "My PETG", k1_parent);
|
||||
load_user_filaments(filaments, temp_dir.path());
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
filaments.legacy_user_filament_presets({ { k1_parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
|
||||
// Between detection and the write, the cloud sync gave the preset a newer list that is still to be saved.
|
||||
Preset *preset = filaments.find_preset("My PETG", false, true);
|
||||
preset->config.option<ConfigOptionStrings>("compatible_printers", true)->values = { "Creality K1C 0.8 nozzle" };
|
||||
preset->setting_id = "GFSA01";
|
||||
preset->sync_info = "save";
|
||||
filaments.apply_legacy_user_filament_presets(legacy_presets);
|
||||
CHECK(preset->config.option<ConfigOptionStrings>("compatible_printers")->values ==
|
||||
std::vector<std::string>{ "Creality K1C 0.8 nozzle", "Creality K1C 0.4 nozzle" });
|
||||
CHECK(preset->sync_info == "save");
|
||||
}
|
||||
|
||||
TEST_CASE("Migrating the selected user filament keeps a printer list edit pending in the settings tab", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
add_system_filament(filaments, k1_parent, { "Creality K1C 0.6 nozzle" });
|
||||
write_user_filament(temp_dir.path(), "My PETG", k1_parent);
|
||||
load_user_filaments(filaments, temp_dir.path());
|
||||
REQUIRE(filaments.select_preset_by_name("My PETG", true));
|
||||
const std::vector<LegacyUserFilamentPreset> legacy_presets =
|
||||
filaments.legacy_user_filament_presets({ { k1_parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(legacy_presets.size() == 1);
|
||||
const std::vector<std::string> migrated{ "Creality K1C 0.6 nozzle", "Creality K1C 0.4 nozzle" };
|
||||
auto edited_printers = [&filaments]() -> std::vector<std::string> & {
|
||||
return filaments.get_edited_preset().config.option<ConfigOptionStrings>("compatible_printers", true)->values;
|
||||
};
|
||||
|
||||
std::vector<std::string> expected_edited = migrated;
|
||||
bool expected_dirty = false;
|
||||
SECTION("an unedited list follows the saved one") {}
|
||||
SECTION("an edited list stays as edited") {
|
||||
edited_printers() = { "Creality K1C 0.8 nozzle" };
|
||||
expected_edited = edited_printers();
|
||||
expected_dirty = true;
|
||||
}
|
||||
REQUIRE(PresetCollection::write_legacy_user_filament_preset(legacy_presets[0]));
|
||||
filaments.apply_legacy_user_filament_presets(legacy_presets);
|
||||
// find_preset() returns the edited copy of the selected preset unless asked for the real one.
|
||||
CHECK(filaments.find_preset("My PETG", false, true)->config.option<ConfigOptionStrings>("compatible_printers")->values == migrated);
|
||||
CHECK(edited_printers() == expected_edited);
|
||||
// What marks the project as having unsaved preset changes.
|
||||
CHECK(filaments.saved_is_dirty() == expected_dirty);
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ TEST_CASE("Native command catalog includes the Help and developer-mode commands"
|
||||
|
||||
const Slic3r::GUI::NativeCommand* first = find("help_keyboard_shortcuts");
|
||||
REQUIRE(first != nullptr);
|
||||
for (const char* key : {"help_setup_wizard", "help_open_config_folder", "help_troubleshoot", "help_network_test",
|
||||
for (const char* key : {"help_setup_wizard", "help_open_config_folder", "help_migrate_filament_presets", "help_troubleshoot", "help_network_test",
|
||||
"help_benchmark_3d_scene", "help_tip_of_the_day", "help_check_updates", "help_about", "open_wiki",
|
||||
"open_youtube"}) {
|
||||
const Slic3r::GUI::NativeCommand* c = find(key);
|
||||
|
||||
Reference in New Issue
Block a user