mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 15:21:09 +00:00
The slice sweep here runs the nightly validator, built from main, so it cannot expand custom G-code that uses a setting the PR adds to the engine and reports it as an undefined placeholder. The belt printer PR fails on exactly that: the BabyBelt Pro start G-code passes [belt_slice_rotation_angle] to its firmware, and main has no such setting. A PR that changes src/ also runs Build all, whose Slice check runs the same sweep with the validator built from the PR (it passes on that PR). So the sweep here now runs only for PRs that leave src/ alone, which are the profile-only PRs it exists for and the ones pr-merge-bot gates on. If the base commit cannot be fetched, the sweep runs as before.
291 lines
12 KiB
YAML
291 lines
12 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/**'
|
|
- '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]}
|
|
|
|
# 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.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.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.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
|