mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
Merge upstream/main into cad-mainline (530 commits)
Catches the fork up from449a4cf9fc(2026-06-28) tod6cb667b89(2026-07-24). Upstream touched 2326 files; 17 of them overlap the 164 this branch touches. 16 of the 17 auto-merged, including all three CMakeLists.txt, the build_all.yml CI workflow, and every GUI file. The CAD core never conflicts: CadDocument, SketchEngine, SketchSolver, McpControl and test_caddocument are files this fork adds, so upstream does not touch them. The one conflict, tests/libslic3r/test_3mf.cpp, was purely additive in all three hunks and is resolved as a union: our test pinning that store_bbs_3mf embeds the CAD recipe as Metadata/SnapOrca_cad.bin, upstream's multi-nozzle plate-metadata round-trip tests, and both sets of includes. All three were verified present after resolution rather than assumed. NOT BUILD-VERIFIED, for a reason that predates this merge and is not caused by it: this fork cannot be configured on nativedev at all. Its CMakeLists has required Eigen3 5.0.1 since before the merge (line 592 pre-merge), while the only deps image on the machine is snaporca-deps, built for snaporca's find_package(Eigen3 3.3). CMake fails at configure, so nothing compiles. That means this fork's Catch2 suite has never run. Every "suite green" figure recorded for M1-M8 was snaporca's suite; the ports were verified by patch-apply plus the CAD sources being byte-identical to snaporca's. Building an orca_cad deps image with Eigen 5.0.1 is what would finally close that gap. Pre-merge state is preserved at branch cad-mainline-pre-upstream-2026-07-25 (30d54f0074). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
@@ -58,17 +58,13 @@ jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# Build both arches on every event (PRs included), through the same
|
||||
# build_check_cache -> build_deps -> build_orca chain (the AppImage).
|
||||
# aarch64 always uses the GitHub-hosted arm runner (there is no arm
|
||||
# self-hosted server). amd64's empty arch is load-bearing: it keeps the
|
||||
# historical 'linux-clang' deps cache key and the unsuffixed asset names.
|
||||
# SELF_HOSTED skips arm64 (no arm self-hosted server). amd64's empty arch
|
||||
# is load-bearing: it keeps the historical 'linux-clang' deps cache key and
|
||||
# the unsuffixed asset names.
|
||||
matrix:
|
||||
include:
|
||||
- arch: ""
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||
- arch: "aarch64"
|
||||
os: ubuntu-24.04-arm
|
||||
include: ${{ fromJSON(vars.SELF_HOSTED
|
||||
&& '[{"arch":"","os":"orca-lnx-server"}]'
|
||||
|| '[{"arch":"","os":"ubuntu-24.04"},{"arch":"aarch64","os":"ubuntu-24.04-arm"}]') }}
|
||||
# Don't run scheduled builds on forks:
|
||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_check_cache.yml
|
||||
@@ -81,18 +77,16 @@ jobs:
|
||||
name: Build Windows ${{ matrix.arch }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# SELF_HOSTED skips arm64 (the self-hosted Windows server is x64-only).
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
os: windows-latest
|
||||
- arch: arm64
|
||||
os: windows-11-arm
|
||||
include: ${{ fromJSON(vars.SELF_HOSTED
|
||||
&& '[{"arch":"x64","os":"orca-win-server"}]'
|
||||
|| '[{"arch":"x64","os":"windows-latest"},{"arch":"arm64","os":"windows-11-arm"}]') }}
|
||||
# Don't run scheduled builds on forks:
|
||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_check_cache.yml
|
||||
with:
|
||||
# Self-hosted runner is x64-only; ARM64 always uses the GitHub-hosted runner.
|
||||
os: ${{ (matrix.arch == 'x64' && vars.SELF_HOSTED) && 'orca-win-server' || matrix.os }}
|
||||
os: ${{ matrix.os }}
|
||||
arch: ${{ matrix.arch }}
|
||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||
force-build: ${{ github.event_name == 'schedule' }}
|
||||
@@ -123,54 +117,97 @@ jobs:
|
||||
arch: universal
|
||||
macos-combine-only: true
|
||||
secrets: inherit
|
||||
unit_tests:
|
||||
name: Unit Tests
|
||||
# Tests are built on the aarch64 leg by default (faster GitHub arm runner),
|
||||
# so run them there; self-hosted builds them on the amd64 server instead.
|
||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
|
||||
# One test job per built arch, on the runner that built it.
|
||||
unit_tests_linux_x86_64:
|
||||
name: Linux x86_64
|
||||
needs: build_linux
|
||||
if: ${{ !cancelled() && success() }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||
artifact: ${{ github.sha }}-tests-linux-x86_64
|
||||
unit_tests_linux_aarch64:
|
||||
name: Linux aarch64
|
||||
needs: build_linux
|
||||
if: ${{ !cancelled() && success() && !vars.SELF_HOSTED }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: ubuntu-24.04-arm
|
||||
artifact: ${{ github.sha }}-tests-linux-aarch64
|
||||
unit_tests_windows_x64:
|
||||
name: Windows x64
|
||||
needs: build_windows
|
||||
if: ${{ !cancelled() && success() }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-win-server' || 'windows-latest' }}
|
||||
artifact: ${{ github.sha }}-tests-windows-x64
|
||||
unit_tests_windows_arm64:
|
||||
name: Windows arm64
|
||||
needs: build_windows
|
||||
if: ${{ !cancelled() && success() && !vars.SELF_HOSTED }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: windows-11-arm
|
||||
artifact: ${{ github.sha }}-tests-windows-arm64
|
||||
test-dir: build-arm64/tests
|
||||
unit_tests_macos_arm64:
|
||||
name: macOS arm64
|
||||
needs: build_macos_arch
|
||||
if: ${{ !cancelled() && success() }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
|
||||
artifact: ${{ github.sha }}-tests-macos-arm64
|
||||
test-dir: build/arm64/tests
|
||||
# Slice a two-colour cube through every shipped printer so all custom g-code
|
||||
# (change_filament_gcode, machine start/end, etc.) is expanded - catches
|
||||
# slicing regressions the static profile checks and unit tests can't see.
|
||||
# Profile-only PRs are covered by check_profiles.yml's nightly binary; this
|
||||
# covers src/engine PRs with the PR-built binary.
|
||||
slice_check_linux:
|
||||
name: Slice check (Linux ${{ vars.SELF_HOSTED && 'x86_64' || 'aarch64' }})
|
||||
needs: build_linux
|
||||
if: ${{ !cancelled() && success() }}
|
||||
# Follows whichever Linux leg built the validator.
|
||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github
|
||||
scripts
|
||||
tests
|
||||
- name: Apt-Install Dependencies
|
||||
if: ${{ !vars.SELF_HOSTED }}
|
||||
uses: ./.github/actions/apt-install-deps
|
||||
- name: Restore Test Artifact
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
- name: Download profile validator
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ github.sha }}-tests
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||
useLocalCache: true # <--= Use the local cache (default is 'false').
|
||||
useCloudCache: true
|
||||
- name: Unpackage and Run Unit Tests
|
||||
timeout-minutes: 20
|
||||
name: ${{ github.sha }}-profile-validator-linux-${{ vars.SELF_HOSTED && 'x86_64' || 'aarch64' }}
|
||||
path: validator-bin
|
||||
- name: Validate slice (expand custom g-code)
|
||||
timeout-minutes: 60
|
||||
run: |
|
||||
tar -xvf build_tests.tar
|
||||
scripts/run_unit_tests.sh
|
||||
- name: Upload Test Logs
|
||||
uses: actions/upload-artifact@v7
|
||||
if: ${{ failure() }}
|
||||
chmod +x validator-bin/OrcaSlicer_profile_validator
|
||||
./validator-bin/OrcaSlicer_profile_validator -p "${{ github.workspace }}/resources/profiles" -s -l 2
|
||||
publish_test_results:
|
||||
name: Publish Test Results
|
||||
needs: [unit_tests_linux_x86_64, unit_tests_linux_aarch64, unit_tests_windows_x64, unit_tests_windows_arm64, unit_tests_macos_arm64]
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Test Results
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: unit-test-logs
|
||||
path: build/tests/**/*.log
|
||||
pattern: test-results-*
|
||||
path: test-results
|
||||
# Best-effort: a read-only token (e.g. fork PRs) can't write the check, so
|
||||
# don't let a publish failure fail the run. The test jobs gate correctness.
|
||||
- name: Publish Test Results
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
uses: EnricoMi/publish-unit-test-result-action@v2
|
||||
with:
|
||||
files: "ctest_results.xml"
|
||||
- name: Delete Test Artifact
|
||||
files: "test-results/**/*.xml"
|
||||
- name: Delete Test Results
|
||||
if: success()
|
||||
uses: geekyeggo/delete-artifact@v6
|
||||
with:
|
||||
name: ${{ github.sha }}-tests
|
||||
name: test-results-*
|
||||
failOnError: false
|
||||
flatpak:
|
||||
name: "Flatpak"
|
||||
container:
|
||||
@@ -205,7 +242,7 @@ jobs:
|
||||
- name: "Remove unneeded stuff to free disk space"
|
||||
run:
|
||||
rm -rf /usr/local/lib/android/* /usr/share/dotnet/* /opt/ghc1/* "/usr/local/share/boost1/*" /opt/hostedtoolcache1/*
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Get the version and date
|
||||
run: |
|
||||
ver_pure=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
|
||||
@@ -224,14 +261,14 @@ jobs:
|
||||
# Manage flatpak-builder cache externally so PRs restore but never upload
|
||||
- name: Restore flatpak-builder cache
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v6
|
||||
with:
|
||||
path: .flatpak-builder
|
||||
key: flatpak-builder-${{ matrix.variant.arch }}-${{ github.event.pull_request.base.sha }}
|
||||
restore-keys: flatpak-builder-${{ matrix.variant.arch }}-
|
||||
- name: Save/restore flatpak-builder cache
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: .flatpak-builder
|
||||
key: flatpak-builder-${{ matrix.variant.arch }}-${{ github.sha }}
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
lfs: 'false'
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
- name: load cache
|
||||
id: cache_deps
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.set_outputs.outputs.cache-path }}
|
||||
key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||
|
||||
@@ -34,12 +34,12 @@ jobs:
|
||||
|
||||
# Setup the environment
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
lfs: 'false'
|
||||
|
||||
- name: load cached deps
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ inputs.cache-path }}
|
||||
key: ${{ inputs.cache-key }}
|
||||
|
||||
@@ -37,13 +37,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
lfs: 'false'
|
||||
|
||||
- name: load cached deps
|
||||
if: ${{ !(runner.os == 'macOS' && inputs.macos-combine-only) }}
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ inputs.cache-path }}
|
||||
key: ${{ inputs.cache-key }}
|
||||
@@ -141,8 +141,26 @@ jobs:
|
||||
- name: Build slicer mac
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||
working-directory: ${{ github.workspace }}
|
||||
# arm64 only: build the tests here; the unit_tests_macos job runs them.
|
||||
env:
|
||||
ORCA_TESTS_BUILD_ONLY: ${{ inputs.arch == 'arm64' && '1' || '' }}
|
||||
run: |
|
||||
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
|
||||
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15 ${{ inputs.arch == 'arm64' && '-T' || '' }}
|
||||
|
||||
- name: Pack unit tests mac
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: tar -cvf build_tests.tar build/arm64/tests
|
||||
|
||||
- name: Upload Test Artifact mac
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}-tests-macos-arm64
|
||||
overwrite: true
|
||||
path: build_tests.tar
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Pack macOS app bundle ${{ inputs.arch }}
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||
@@ -203,10 +221,41 @@ jobs:
|
||||
security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app
|
||||
# codesign --deep is deprecated and cannot sign the bundled Python
|
||||
# runtime (see relocate_python_runtime in build_release_macos.sh).
|
||||
# Sign every nested Mach-O in the bundle explicitly -- notarization
|
||||
# requires the hardened-runtime signature on each -- then seal the
|
||||
# bundle itself last, and verify so coverage gaps fail at CI time
|
||||
# rather than at notarization.
|
||||
ENTITLEMENTS=${{ github.workspace }}/scripts/disable_validation.entitlements
|
||||
sign() {
|
||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" "$@"
|
||||
}
|
||||
sign_app() {
|
||||
local app="$1"
|
||||
local machos=()
|
||||
# file is batched via xargs (per-file it costs minutes); universal
|
||||
# binaries also print per-architecture lines, dropped by grep -v.
|
||||
while IFS= read -r f; do machos+=("$f"); done < <(
|
||||
find "$app" -type f -print0 |
|
||||
xargs -0 file --no-pad --mime-type -- |
|
||||
grep -v ' (for architecture ' |
|
||||
grep ': application/x-mach-binary$' |
|
||||
sed 's|: application/x-mach-binary$||'
|
||||
)
|
||||
if [ "${#machos[@]}" -eq 0 ]; then
|
||||
echo "ERROR: no Mach-O files found in $app -- detection is broken" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "signing ${#machos[@]} Mach-O files in $app"
|
||||
sign "${machos[@]}"
|
||||
sign --verbose "$app"
|
||||
codesign --verify --deep --strict --verbose=2 "$app"
|
||||
}
|
||||
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app"
|
||||
# Sign OrcaSlicer_profile_validator.app if it exists
|
||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app
|
||||
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app"
|
||||
fi
|
||||
|
||||
# Create main OrcaSlicer DMG without the profile validator helper
|
||||
@@ -215,7 +264,7 @@ jobs:
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
|
||||
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
|
||||
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||
@@ -224,7 +273,7 @@ jobs:
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
||||
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
fi
|
||||
|
||||
# Notarize main DMG
|
||||
@@ -335,11 +384,28 @@ jobs:
|
||||
# env:
|
||||
# WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
||||
# WindowsSDKVersion: '10.0.26100.0\'
|
||||
# "tests" builds the unit tests too; the unit_tests_windows_* jobs run them.
|
||||
run: |
|
||||
$arch = "${{ inputs.arch }}"
|
||||
if ($arch -eq "arm64") { .\build_release_vs.bat slicer arm64 } else { .\build_release_vs.bat slicer }
|
||||
if ($arch -eq "arm64") { .\build_release_vs.bat slicer arm64 tests } else { .\build_release_vs.bat slicer tests }
|
||||
shell: pwsh
|
||||
|
||||
- name: Pack unit tests Win
|
||||
if: runner.os == 'Windows'
|
||||
working-directory: ${{ github.workspace }}
|
||||
shell: pwsh
|
||||
run: tar -cvf build_tests.tar ${{ env.BUILD_DIR }}/tests
|
||||
|
||||
- name: Upload Test Artifact Win
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}-tests-windows-${{ inputs.arch }}
|
||||
overwrite: true
|
||||
path: build_tests.tar
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
# NSIS is x86-only; it runs (and the installer it emits runs) under ARM64's
|
||||
# x86 emulation, packaging the native arm64 payload from build-arm64.
|
||||
- name: Create installer Win
|
||||
@@ -452,31 +518,41 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
# Build + tar the unit tests (-t) only on the leg that runs them: the
|
||||
# aarch64 leg by default (faster GitHub arm runner), or amd64 when using
|
||||
# self-hosted runners (no arm self-hosted server). unit_tests downloads
|
||||
# this tarball. The profile validator is built with -s, so amd64 keeps it.
|
||||
tests=${{ (!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64') }}
|
||||
if $tests; then flags=-istrlL; else flags=-isrlL; fi
|
||||
./build_linux.sh "$flags"
|
||||
# Build + tar the unit tests (-t) on both Linux legs so each arch
|
||||
# (x86_64 + aarch64) gets tested by its own unit_tests_linux_* job.
|
||||
./build_linux.sh -istrlL
|
||||
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
||||
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
|
||||
chmod +x "$appimage"
|
||||
if $tests; then tar -cvpf build_tests.tar build/tests; fi
|
||||
tar -cvf build_tests.tar build/tests
|
||||
|
||||
# Use tar because upload-artifacts won't always preserve directory structure
|
||||
# and doesn't preserve file permissions
|
||||
- name: Upload Test Artifact
|
||||
if: runner.os == 'Linux' && ((!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64'))
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}-tests
|
||||
name: ${{ github.sha }}-tests-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
|
||||
overwrite: true
|
||||
path: build_tests.tar
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
# Ship the freshly-built validator so slice_check_linux (build_all.yml)
|
||||
# can slice-sweep the shipped profiles with this PR's engine. Taken from
|
||||
# the aarch64 leg so the sweep also exercises the arm build; x86_64 on
|
||||
# SELF_HOSTED, which skips arm64.
|
||||
- name: Upload profile validator (for slice check)
|
||||
if: ${{ runner.os == 'Linux' && (vars.SELF_HOSTED && inputs.arch != 'aarch64' || !vars.SELF_HOSTED && inputs.arch == 'aarch64') }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}-profile-validator-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
|
||||
overwrite: true
|
||||
path: ./build/src/Release/OrcaSlicer_profile_validator
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Run external slicer regression tests
|
||||
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
|
||||
timeout-minutes: 20
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Install gettext
|
||||
run: |
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Run extra JSON check
|
||||
id: extra_json_check
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
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 profiles
|
||||
# Validate all system profiles.
|
||||
- name: validate system profiles
|
||||
id: validate_system
|
||||
continue-on-error: true
|
||||
@@ -49,6 +49,15 @@ jobs:
|
||||
set +e
|
||||
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_system.log
|
||||
exit ${PIPESTATUS[0]}
|
||||
# Slice a two-colour cube through every printer so all custom g-code (incl. change_filament_gcode)
|
||||
# is expanded - catches undefined-placeholder / invalid-flow bugs the static checks above cannot see.
|
||||
- name: validate slice (expand custom g-code)
|
||||
id: validate_slice
|
||||
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]}
|
||||
# For now run filament subtype check only for BBL profiles until we fix other vendors' profiles.
|
||||
- name: validate filament subtype check for BBL profiles
|
||||
id: validate_filament_subtypes
|
||||
@@ -63,11 +72,101 @@ jobs:
|
||||
continue-on-error: true
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
set +e
|
||||
curl -LJO https://github.com/OrcaSlicer/OrcaSlicer/releases/download/nightly-builds/orca_custom_preset_tests.zip
|
||||
unzip -q ./orca_custom_preset_tests.zip -d ${{ github.workspace }}/resources/profiles
|
||||
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_custom.log
|
||||
exit ${PIPESTATUS[0]}
|
||||
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' }}
|
||||
@@ -76,7 +175,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.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
if: ${{ always() && github.event_name == 'pull_request' && (steps.extra_json_check.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.
|
||||
@@ -102,6 +201,15 @@ jobs:
|
||||
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 "### BBL Filament Subtype Validation Failed"
|
||||
echo ""
|
||||
@@ -133,7 +241,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
- name: Fail if any check failed
|
||||
if: ${{ always() && (steps.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||
if: ${{ always() && (steps.extra_json_check.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 above for details."
|
||||
exit 1
|
||||
|
||||
@@ -20,14 +20,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Run Claude Code slash command
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||
model: "claude-sonnet-4-5-20250929"
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
remove-existing-swap-files: true
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Install Doxygen and Graphviz
|
||||
run: |
|
||||
|
||||
@@ -79,6 +79,92 @@ jobs:
|
||||
throw error;
|
||||
}
|
||||
|
||||
localization-pr:
|
||||
if: github.event_name == 'pull_request_target'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Auto-label and remind about the localization glossary
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
function isPermissionDenied(error) {
|
||||
return error && error.status === 403 && /Resource not accessible by integration/i.test(error.message || '');
|
||||
}
|
||||
|
||||
const pr = context.payload.pull_request;
|
||||
|
||||
// List changed files once (mirrors the `localization/**` paths filter in check_locale.yml)
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
per_page: 100
|
||||
});
|
||||
const touchesLocalization = files.some((file) => file.filename.startsWith('localization/'));
|
||||
const onlyPoFiles = files.length > 0 && files.every((file) => file.filename.endsWith('.po'));
|
||||
|
||||
// If the PR changes only .po files, automatically apply the Localization label
|
||||
if (onlyPoFiles) {
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels: ['Localization']
|
||||
});
|
||||
core.info('Applied Localization label (PR changes only .po files).');
|
||||
} catch (error) {
|
||||
if (isPermissionDenied(error)) {
|
||||
core.warning('Cannot add Localization label because token cannot write.');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!touchesLocalization) {
|
||||
core.info('No localization changes detected; skipping glossary reminder.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid posting the reminder twice (e.g. on reopen)
|
||||
const marker = '<!-- localization-glossary-reminder -->';
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
per_page: 100
|
||||
});
|
||||
if (comments.some((comment) => (comment.body || '').includes(marker))) {
|
||||
core.info('Glossary reminder already present; skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body:
|
||||
`${marker}\n` +
|
||||
`Hi @${pr.user.login}, this PR changes translations (\`localization/**\`).\n\n` +
|
||||
`Please make sure recurring terms follow the [Localization glossary](https://www.orcaslicer.com/wiki/localization_glossary), ` +
|
||||
`so the same English term is always rendered the same way within a language and terms that must stay in English ` +
|
||||
`(brand/product names, acronyms, file formats, G-code, macros/variables) are not translated.`
|
||||
});
|
||||
} catch (error) {
|
||||
if (isPermissionDenied(error)) {
|
||||
core.warning('Skipping glossary reminder because token cannot write comments.');
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
apply-label:
|
||||
if: github.event_name == 'issue_comment'
|
||||
permissions:
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
steps:
|
||||
- name: Cache shellcheck download
|
||||
id: cache-shellcheck-v0_11
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/shellcheck
|
||||
key: ${{ runner.os }}-shellcheck-v0_11
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
tar -xvf ~/sc.tar.xz -C ~
|
||||
mv ~/shellcheck-"${INPUT_VERSION}"/shellcheck ~/shellcheck
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
name: Unit Tests
|
||||
|
||||
# Download a platform's test artifact, run ctest, and upload the JUnit
|
||||
# results for aggregation. Called once per arch from build_all.yml.
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: true
|
||||
type: string
|
||||
artifact:
|
||||
description: Test artifact uploaded by the build leg
|
||||
required: true
|
||||
type: string
|
||||
test-dir:
|
||||
description: Built tests dir; defaults to build/tests, override for arch-separated builds
|
||||
required: false
|
||||
type: string
|
||||
default: build/tests
|
||||
|
||||
jobs:
|
||||
unit_tests:
|
||||
# Static "Unit Tests"; the per-arch label is the caller's job name, so the
|
||||
# graph shows e.g. "Windows x64 / Unit Tests".
|
||||
name: Unit Tests
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
# Tests reach outside tests/ at runtime: tests/data (TEST_DATA_DIR) and
|
||||
# resources/profiles (PROFILES_DIR) by baked-in absolute path, plus
|
||||
# resources/info (nozzle data) via resources_dir() during a real slice.
|
||||
# Check out all of resources/ so no test hits a missing-file path.
|
||||
sparse-checkout: |
|
||||
.github
|
||||
scripts
|
||||
tests
|
||||
resources
|
||||
- name: Apt-Install Dependencies
|
||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||
uses: ./.github/actions/apt-install-deps
|
||||
- name: Restore Test Artifact
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||
useLocalCache: true
|
||||
useCloudCache: true
|
||||
- name: Unpackage and Run Unit Tests
|
||||
timeout-minutes: 20
|
||||
shell: bash
|
||||
run: |
|
||||
tar -xvf build_tests.tar
|
||||
# Multi-config generators (Windows/macOS) need a config; Linux is single-config.
|
||||
scripts/run_unit_tests.sh "${{ inputs.test-dir }}" "${{ runner.os != 'Linux' && 'Release' || '' }}"
|
||||
- name: Upload Test Logs
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: unit-test-logs-${{ inputs.artifact }}
|
||||
path: ${{ inputs.test-dir }}/**/*.log
|
||||
- name: Upload Test Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: test-results-${{ inputs.artifact }}
|
||||
path: ctest_results.xml
|
||||
retention-days: 5
|
||||
if-no-files-found: warn
|
||||
- name: Delete Test Artifact
|
||||
if: success()
|
||||
uses: geekyeggo/delete-artifact@v6
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
Reference in New Issue
Block a user