Files
OrcaSlicer/.github/workflows/check_profiles.yml
T
harrierpigeon 2cb72dce0a Check profiles: leave engine PRs' slice sweep to Build all
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.
2026-10-05 14:02:15 -05:00

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