mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-13 19:01:21 +00:00
* Keep User Filament Presets on Printers Their Parent Listed in 2.4.2
User presets store only their diff, so they inherit the parent's
compatible_printers. Profile changes since 2.4.2 renamed and narrowed many
system filament presets, so user presets made against them disappear from
printers they were made for and the saved selection silently falls back to
another filament at startup.
orca_profile_tool.py legacy-compat writes, per current system filament
preset, the printers it covered in an older release and no longer lists,
kept only where a preset with the same filament_id still covers the printer
and they share an extruder variant. The loader gives these printers to user
presets that inherit their parent's list, without writing any file; saving
a preset detached, cloned or with its own list drops them.
* Ask Before Migrating User Filament Presets to Their Legacy Printers
Instead of giving user presets their parent's 2.4.2 printers in memory,
detect the user presets that inherit their parent's compatible_printers
and are missing printers from the legacy map, and offer to migrate them
at startup or from Help > Migrate Filament Presets.
Migrating first backs up the user folder to
filament_preset_migration_<datetime>_backup.zip in the configuration
folder, and migrates nothing if that fails. It then writes the printers
into each preset's compatible_printers, patching only that key so the
preset keeps the parent name it was saved with and older releases can
still resolve it. Cloud-synced presets are marked for upload.
No asks again at the next launch; "Don't remind me again" stops the
startup prompt.
* Key the legacy filament preset map by the parent's 2.4.2 name
* Check the legacy filament preset map in CI
* Limit the 2.4.2 filament preset migration to presets that need it
Only presets that still name their parent as 2.4.2 did and have no printer
list of their own are offered, and a preset changed after it was found is
left alone. The startup prompt appears once, after upgrading from 2.4.2 or
earlier, as a standard dialog that keeps "Don't remind me again"; Help >
Migrate Filament Presets is unchanged. Migrating no longer overwrites an
unsaved printer-list edit or marks the project modified.
* Ask on Every Start and Take the User Presets Lock Once When Migrating Filament Presets
The startup prompt no longer depends on the version the previous run
recorded: whoever still has user filament presets that lost printers is
asked, including users already on a 2.5.0 nightly or alpha. Answering No
asks again at the next start; "Don't remind me again" stops it.
Migration takes the user presets lock once, around the backup and every
file write, instead of once per preset. A lock another instance holds is
reported once ("another OrcaSlicer instance is saving presets") with
nothing written, rather than as one failed preset after another once the
lock's cool-down kicks in. Detection records each preset's file and the
list it will get, so the writes need no collection lock while the user
presets lock is held, the order InstanceLock requires; the loaded presets
are updated afterwards.
* Run the Legacy Compatible Printers Map Check in check_profile.sh and .ps1
CI's "Check profiles" job checks resources/info/legacy_compatible_printers.json
against the v2.4.2 profiles, and its failure message points at these scripts
as the local reproduction, so they run the same check: legacy_compat, right
after profile_tool. The v2.4.2 tree comes from git archive of the tag, is
cached in the work dir like the other downloads, re-extracted with --refresh,
and fetched from upstream first when the checkout lacks the tag.
* Back Up Before the User Presets Lock and Merge Migrated Printers Into the Live List
The lock guard is meant for a few file operations, and zipping a large user
folder is not one, so the backup is taken before it and the guard covers the
writes only; each write re-reads its file, so a save that slipped in before
is still respected.
The in-memory update adds the lost printers to the list the preset holds at
that moment rather than replacing it with the one found at detection, and
leaves a pending cloud sync state alone: a download waiting to be saved kept
its newer list and its "save" flag, which "update" would have cancelled.
The lock message no longer blames another instance alone, since the lock is
also refused for a while after a miss and when the lock file cannot be
opened.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
318 lines
14 KiB
YAML
318 lines
14 KiB
YAML
name: Check profiles
|
|
on:
|
|
pull_request:
|
|
# release/* is included because pr-merge-bot.yml lets delegates merge into
|
|
# it, and it gates on this workflow's result. Without it a delegated merge
|
|
# into a release branch would run no profile validation at all.
|
|
branches:
|
|
- main
|
|
- release/*
|
|
paths:
|
|
- 'resources/profiles/**'
|
|
# orca_profile_tool.py also validates resources/printers/bambu_filament_ids.json, and
|
|
# 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.
|
|
- 'src/libslic3r/PrintConfig.cpp'
|
|
- ".github/workflows/check_profiles.yml"
|
|
|
|
workflow_dispatch:
|
|
inputs:
|
|
logLevel:
|
|
description: 'Log level'
|
|
required: true
|
|
default: 'warning'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
|
|
jobs:
|
|
check_profiles:
|
|
# This job name is the check-run name pr-merge-bot.yml requires before a
|
|
# delegated merge. Renaming it silently disables that gate.
|
|
name: Check profiles
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
|
|
# Deliberately not continue-on-error, unlike every check below: if the tool itself is
|
|
# broken, nothing it then reports about the profiles is worth reading.
|
|
- name: Run the profile tool's own unit tests
|
|
run: python3 -m unittest discover -s scripts/tests -t scripts
|
|
|
|
# What the validator below cannot see. It loads the tree the way the slicer does, so
|
|
# it never notices a profile no <vendor>.json indexes, a preset name two files claim,
|
|
# an id that is not the mint of its own triple, or a file that normalize and
|
|
# update-index would still rewrite.
|
|
# The step id is the handle the PR comment and the failure gate below use; renaming it
|
|
# silently disables them.
|
|
- name: Check profiles (orca_profile_tool.py)
|
|
id: profile_tool
|
|
continue-on-error: true
|
|
run: |
|
|
set +e
|
|
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 }}
|
|
run: |
|
|
curl -L -o OrcaSlicer_profile_validator https://github.com/OrcaSlicer/OrcaSlicer/releases/download/nightly-builds/OrcaSlicer_profile_validator_Linux_Ubuntu2404_nightly
|
|
chmod +x ./OrcaSlicer_profile_validator
|
|
|
|
# Validate all system profiles.
|
|
- name: validate system profiles
|
|
id: validate_system
|
|
continue-on-error: true
|
|
run: |
|
|
set +e
|
|
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_system.log
|
|
exit ${PIPESTATUS[0]}
|
|
# The validator above is the nightly build of main, so it cannot slice profiles that use
|
|
# settings a PR adds to the engine: it reports their placeholders as undefined. A PR that
|
|
# changes src/ also runs Build all, whose Slice check runs this same sweep with the
|
|
# validator built from the PR, so the sweep below only runs for the other PRs.
|
|
- name: Detect engine changes
|
|
id: engine_changes
|
|
if: ${{ github.event_name == 'pull_request' }}
|
|
run: |
|
|
base=${{ github.event.pull_request.base.sha }}
|
|
if git fetch --no-tags --depth=1 origin "$base" && ! git diff --quiet "$base" HEAD -- src/; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
echo "::notice::This PR changes src/, so Build all's Slice check slices the profiles with the PR-built validator."
|
|
fi
|
|
# Slice a two-colour cube through every printer, and through every system process/filament whose
|
|
# templates no printer's own slice reaches, so every custom g-code and filename_format shipped is
|
|
# expanded (names in {if} branches not taken included) - catches undefined-placeholder /
|
|
# invalid-flow bugs the static checks above cannot see.
|
|
- name: validate slice (expand custom g-code)
|
|
id: validate_slice
|
|
if: ${{ steps.engine_changes.outputs.changed != 'true' }}
|
|
continue-on-error: true
|
|
run: |
|
|
set +e
|
|
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -s -l 2 2>&1 | tee ${{ runner.temp }}/validate_slice.log
|
|
exit ${PIPESTATUS[0]}
|
|
# All vendors' filament_id collisions were fixed, so the duplicate-filament-subtype
|
|
# check runs tree-wide.
|
|
- name: validate filament subtype check
|
|
id: validate_filament_subtypes
|
|
continue-on-error: true
|
|
run: |
|
|
set +e
|
|
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
|
|
exit ${PIPESTATUS[0]}
|
|
|
|
- name: validate custom presets
|
|
id: validate_custom
|
|
continue-on-error: true
|
|
working-directory: ${{ github.workspace }}
|
|
run: |
|
|
fixtures_dir="${{ runner.temp }}/profile-fixtures"
|
|
output_dir="${{ runner.temp }}/custom-preset-validation"
|
|
combined_log="${{ runner.temp }}/validate_custom.log"
|
|
summary="${output_dir}/summary.md"
|
|
release_url="https://github.com/OrcaSlicer/OrcaSlicer-profile-validator/releases/download/fixture-archive"
|
|
|
|
rm -rf "${fixtures_dir}" "${output_dir}"
|
|
mkdir -p "${fixtures_dir}" "${output_dir}"
|
|
|
|
curl -fsSL -o "${fixtures_dir}/manifest.json" "${release_url}/manifest.json"
|
|
|
|
MANIFEST_PATH="${fixtures_dir}/manifest.json" python3 <<'PY' > "${fixtures_dir}/fixtures.tsv"
|
|
import json
|
|
import os
|
|
|
|
with open(os.environ["MANIFEST_PATH"], encoding="utf-8") as fh:
|
|
manifest = json.load(fh)
|
|
|
|
if isinstance(manifest, dict):
|
|
entries = manifest.get("fixtures", [])
|
|
else:
|
|
entries = manifest
|
|
|
|
for entry in entries:
|
|
version = entry.get("version", "")
|
|
asset = entry.get("asset", "")
|
|
sha256 = entry.get("asset_sha256", "")
|
|
if not version or not asset:
|
|
continue
|
|
print(f"{version}\t{asset}\t{sha256}")
|
|
PY
|
|
|
|
if [ ! -s "${fixtures_dir}/fixtures.tsv" ]; then
|
|
echo "No custom preset fixtures found in ${release_url}/manifest.json" | tee "${combined_log}"
|
|
exit 1
|
|
fi
|
|
|
|
{
|
|
echo "## Custom Preset Fixture Validation"
|
|
echo ""
|
|
echo "| Version | Status | Log |"
|
|
echo "| --- | --- | --- |"
|
|
} > "${summary}"
|
|
|
|
status=0
|
|
failed_logs=()
|
|
|
|
while IFS=$'\t' read -r version asset expected_sha256; do
|
|
fixture_zip="${fixtures_dir}/${asset}"
|
|
asset_url_name="$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "${asset}")"
|
|
profile_tree="${output_dir}/profiles-${version}"
|
|
log_path="${output_dir}/${version}.log"
|
|
|
|
curl -fsSL -o "${fixture_zip}" "${release_url}/${asset_url_name}"
|
|
|
|
if [ -n "${expected_sha256}" ] && [ "${expected_sha256}" != "<sha256>" ]; then
|
|
echo "${expected_sha256} ${fixture_zip}" | sha256sum -c -
|
|
fi
|
|
|
|
rm -rf "${profile_tree}"
|
|
mkdir -p "${profile_tree}"
|
|
cp -a "${{ github.workspace }}/resources/profiles/." "${profile_tree}/"
|
|
rm -rf "${profile_tree}/user"
|
|
unzip -q "${fixture_zip}" -d "${profile_tree}"
|
|
|
|
set +e
|
|
./OrcaSlicer_profile_validator -p "${profile_tree}" -l 2 > "${log_path}" 2>&1
|
|
result=$?
|
|
set -e
|
|
|
|
if [ "${result}" -eq 0 ]; then
|
|
echo "| ${version} | PASS | ${version}.log |" >> "${summary}"
|
|
else
|
|
echo "| ${version} | FAIL | ${version}.log |" >> "${summary}"
|
|
failed_logs+=("${log_path}")
|
|
status=1
|
|
fi
|
|
done < "${fixtures_dir}/fixtures.tsv"
|
|
|
|
{
|
|
cat "${summary}"
|
|
if [ "${#failed_logs[@]}" -gt 0 ]; then
|
|
echo ""
|
|
echo "## Failed Fixture Logs"
|
|
for log_path in "${failed_logs[@]}"; do
|
|
echo ""
|
|
echo "### $(basename "${log_path}" .log)"
|
|
echo '```'
|
|
head -c 12000 "${log_path}" || echo "No output captured"
|
|
echo '```'
|
|
done
|
|
fi
|
|
} | tee "${combined_log}"
|
|
|
|
exit "${status}"
|
|
|
|
- name: Prepare PR number for comment workflow
|
|
if: ${{ always() && github.event_name == 'pull_request' }}
|
|
run: |
|
|
mkdir -p ${{ runner.temp }}/profile-check-results
|
|
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.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.
|
|
echo "<!-- profile-validation-comment -->"
|
|
echo "## :x: Profile Validation Errors"
|
|
echo ""
|
|
|
|
if [ "${{ steps.profile_tool.outcome }}" = "failure" ]; then
|
|
echo "### Profile Check Failed (orca_profile_tool.py)"
|
|
echo ""
|
|
echo '```'
|
|
head -c 30000 ${{ runner.temp }}/profile_tool.log || echo "No output captured"
|
|
echo '```'
|
|
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 ""
|
|
echo '```'
|
|
head -c 30000 ${{ runner.temp }}/validate_system.log || echo "No output captured"
|
|
echo '```'
|
|
echo ""
|
|
fi
|
|
|
|
if [ "${{ steps.validate_slice.outcome }}" = "failure" ]; then
|
|
echo "### Slice Validation Failed (custom g-code expansion)"
|
|
echo ""
|
|
echo '```'
|
|
head -c 30000 ${{ runner.temp }}/validate_slice.log || echo "No output captured"
|
|
echo '```'
|
|
echo ""
|
|
fi
|
|
|
|
if [ "${{ steps.validate_filament_subtypes.outcome }}" = "failure" ]; then
|
|
echo "### Filament Subtype Validation Failed"
|
|
echo ""
|
|
echo '```'
|
|
head -c 30000 ${{ runner.temp }}/validate_filament_subtypes.log || echo "No output captured"
|
|
echo '```'
|
|
echo ""
|
|
fi
|
|
|
|
if [ "${{ steps.validate_custom.outcome }}" = "failure" ]; then
|
|
echo "### Custom Preset Validation Failed"
|
|
echo ""
|
|
echo '```'
|
|
head -c 30000 ${{ runner.temp }}/validate_custom.log || echo "No output captured"
|
|
echo '```'
|
|
echo ""
|
|
fi
|
|
|
|
echo "---"
|
|
echo '*Fix the errors above and push a new commit. To reproduce this run locally: `scripts/check_profile.sh`, or `scripts\check_profile.bat` on Windows.*'
|
|
} > ${{ runner.temp }}/profile-check-results/pr_comment.md
|
|
|
|
- name: Upload comment artifact
|
|
if: ${{ always() && github.event_name == 'pull_request' }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: profile-check-results
|
|
path: ${{ runner.temp }}/profile-check-results/
|
|
retention-days: 1
|
|
|
|
- name: Fail if any check failed
|
|
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).'
|
|
exit 1
|