Skip to content

Fix uninitialized memory read#1054

Closed
kintel wants to merge 1 commit intoAngusJohnson:mainfrom
kintel:kintel-error_code
Closed

Fix uninitialized memory read#1054
kintel wants to merge 1 commit intoAngusJohnson:mainfrom
kintel:kintel-error_code

Conversation

@kintel
Copy link
Copy Markdown
Contributor

@kintel kintel commented Jan 11, 2026

Fixes #1052

This is a clear uninitialized memory read.

Note that some compilers are able to detect this kind of memory access through static analysis when -Wall is enabled (specifically through -Werror=maybe-uninitialized). However, the capabilities of this features varies between compiler versions. gcc-11.4.0 in Ubuntu-22.04 seems to be particularly good at this, but this compiler is not used by Clipper's CI, which is why this issue went undetected.

@kintel kintel force-pushed the kintel-error_code branch from cc36ccc to c7f820f Compare January 11, 2026 15:15
kintel added a commit to openscad/openscad that referenced this pull request Jan 13, 2026
kintel added a commit to openscad/openscad that referenced this pull request Jan 13, 2026
kintel added a commit to openscad/openscad that referenced this pull request Jan 14, 2026
* Bumped Clipper to 2.0.1
* Add bugfix for AngusJohnson/Clipper2#1054
nomike pushed a commit to pythonscad/pythonscad that referenced this pull request Jan 14, 2026
nomike pushed a commit to pythonscad/pythonscad that referenced this pull request Jan 14, 2026
nomike pushed a commit to pythonscad/pythonscad that referenced this pull request Jan 15, 2026
nomike added a commit to pythonscad/pythonscad that referenced this pull request Jan 15, 2026
* Run once daily.

* Update (and run) translation scripts; require bash; enable error options.

* Bumped Clipper to 2.0.1 (openscad#6494)

* Bumped Clipper to 2.0.1
* Add bugfix for AngusJohnson/Clipper2#1054

---------

Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Marius Kintel <marius@kintel.net>
gsohler pushed a commit to pythonscad/pythonscad that referenced this pull request Jan 18, 2026
* Run once daily.

* Update (and run) translation scripts; require bash; enable error options.

* Bumped Clipper to 2.0.1 (openscad#6494)

* Bumped Clipper to 2.0.1
* Add bugfix for AngusJohnson/Clipper2#1054

---------

Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Marius Kintel <marius@kintel.net>
gsohler added a commit to pythonscad/pythonscad that referenced this pull request Feb 5, 2026
* feat: more properties in __getitem__

* chore: cleanup

* chore: add tests

* chore: merge 91 commits from openscad/master (#277)

* add correct StartupWMClass to set the correct icon on Gnome

I believe this is the XDG way to do this, but I am not sure.

* Check for save/discard/cancel on quit.  Fixes #3971. (#6311)

* Bump CGAL to 6.1.0 (#6318)

* Make vector swizzle experimental (#6307)

* Don't log to stderr (#6323)

* Fully separate zoom levels for each tab.  Fixes #4742. (#6320)

* Fix/cleanup compiler warnings openscad/openscad#6245 (#6300)

* Remove obsolete HTML example converter (#6328)

* reformat python tests scripts in pep8 (#6330)

* Bump the github-actions group with 3 updates

Bumps the github-actions group with 3 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [actions/stale](https://github.com/actions/stale).

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Updates `github/codeql-action` from 3 to 4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

Updates `actions/stale` from 9 to 10
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Check print service name only if remote print service is selected.

This fixes the issue that the print dialog is always shown if the local
application is selected for printing.

* Don't crash MouseSelector, debug GL init (#6334)

* beautify (#6342)

* Set QSCINTILLA_DLL for MINGW (and not MXECROSS). (#6343)

* Warn on offset() with both r and delta specified (#6348)

* Fix QFile::Open nodiscard warning with Qt6 6.10.0. (#6364)

* Update cmdline help to show Manifold as default (#6350)

* Rm unused function.  Fixes #6352. (#6353)

* Add Color module spec (#6339)

* Enables "Ctrl/Cmd-Mouse-wheel zooms text" config option to work  (Preliminary fix for #6368)

* Entirely removed superfluous EditorInterface::wheelEvent method

* Bump Qt to 6.5.7 (#6371)

* CircleCI: Update macOS resources to 26.1 on M4 (#6324)

* Refactor Discretization Special Usage (#6251)

* Tessallation specials $fn$fs$fa now grouped in an object

Instead of fn,fs,fa as three doubles, they are passed as an object
packaged up with the methods on them.

* Repair basic functionality (#6376)

* Fix indentation of CC0 template text. (#6375)

Also clean up line endings.
Add templates/README.md

* Fix a number of non-ASCII file name issues (#6136)

* Try caching MSYS2 packages

Fixes #6381 if it works

* Fix `if` in yaml wrong

* Don't want `if`s anyway

doesn't matter I did them wrong

* Update msys2 gh actions caching

Make one new cache a day from scratch. Leave note why we don't find
the most recent cache. Skip saving since caches are immutable.

* Comment updates + build up cache over a month

* We can read the determined msys2 directory

We're not restoring before the setup, like I originally planned.

* Updating script to output list of pacboy packages to workflow

Easier than my other approach, actually. Ah, whatever.

* Don't change $MSYSTEM in GH Actions

* Oops, I need to specify the bash shell I think

* Use arrays instead of backslash line termination

* Don't fail fast because it fails due to caching a lot

well, until we actually get one

* std::wstring_convert and std::codecvt_utf8_utf16 is going to be depreciated. Use boost::nowide::narrow (#6356)

* Add Unit tests to fix measuring (#6299)

* Starting point of release build CI workflow (#6374)

* Define executors separately
* Added OpenSCAD release build job with create-tarball

* Centralize version string management (#6388)

* Centralize version string management

* Identify build tag in OPENSCAD_VERSION, and correctly extract OPENSCAD_SHORTVERSION

* cimg/python: no 'latest' tag. Use 3.14 (#6399)

* Disable WIP macOS release build (#6398)

* CircleCI Bugfix: Use OPENSCAD_VERSION instead of VERSION (#6400)

* create-tarball: Amend files manually since git-archive-all doesn't consider unversioned files (#6401)

* cd to /tmp/artifacts before creating shasums (#6402)

* Add CMake support + shell helpers for VERSION.txt and COMMIT.txt (#6403)

* Ingest VERSION.txt and COMMIT.txt at cmake time
* Shell helper for establishing version from VERSION.txt and COMMIT.txt

* Snapshot builds now uses the version helpers to unify version management, also deprecated 32-bit windows (#6404)

* Bugfix: binary name on macOS needs correct case (#6409)

* Parametrize macOS build for release vs. snapshot (#6406)

* Parametrize macOS build for release vs. snapshot
* Use establish_version.sh and don't require git
* Always checkout in the same way; with submodules

* Bump actions/checkout from 5 to 6 in the github-actions group (#6418)

Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).

Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Delete discretizer keyword args

PyArg_ParseTupleAndKeywords doesn't allow keyword args it doesn't know
about, so we delete them if found.

Manually testing the fix.

* Fix formatting of "Browser" header (#6433)

Markdown rendered is confused by non-breaking space (`U+A0`) - replaced with a regular space.

* Make find highlighting case-insensitive

* chore: codegen version into manpage (#6448)

* chore: codegen version into manpage

testing process:

- created a VERSION.txt file
- ran the CMake build
- confirm that the output `build/doc/openscad.1` has the same version as
  VERSION.txt

* remove extra comment

* Added job parameters openscad_build_type and openscad_platform (#6449)

* Remove deprecated child() and assign(). (#6428)

* Remove deprecated child() and assign().
Fixes #5557.
Fixes #6425.

* Update releases/next.md:  child(), assign(), object(), textmetrics(), fontmetrics
Remove deprecated child() and assign().
Add object().
Reclassify textmetrics() and fontmetrics() as functions.

* More CircleCI cleanup (#6450)

* Bump manifold to 3.3.2 and onetbb to 2022.3.0 (#6455)

* Added failing test for #6456 (#6459)

* Bumped manifold to fix #6235 (#6467)

* Temporarily restore exact job names used by circleci-download-artifacts.py (#6469)

* CircleCI: Update name of wasm build (#6470)

* CircleCI release builds (#6412)

* Move version info to .c file (#6463)

* chore: bump nix-shell for latest version

* convert to flake (for lockfile)

* use stable release

* remove duplicate lock entry

* Add warning to cmake output that ENABLE_CGAL=OFF is a dev-only flag.

* Fix font sample rendering in font list window.

* Remove obsolete FontListDialog.

* Change console font size via mouse wheel event (fixes #6186).

This is only changing the current console widget, not the value in the
preferences.

* Align naming of release jobs, correct naming of tarball (#6484)

* Bugfix: Forgot to update dependencies on tarball (#6485)

* Bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/cache](https://github.com/actions/cache).

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix premature assignment expiration warning (#6489)

* CI: enable sccache (#6473)

This avoids doing redundant work by caching compiler results. Because CI
does less work, we can run more redundant CI jobs and avoid the mental
overhead of manually de-duping jobs.

* fix: resurrect LoadShareDesignDialog.h which got lost during merge from OpenSCAD

* fix: return type of findModelObject which got inadvertently changed during merge from OpenSCAD

* fix: version handling which got broken during merge from OpenSCAD

* fix: add missing conv. for editor file path in parseDocument which got lost during OpenSCAD merge

* fix: correct variable names in argument parsing for python_cylinder function after OpenSCAD merge

* fix: revert test result for use-tests-expected which was inadvertently changed during OpenSCAD merge

* fix: update argument parsing for python_cylinder function after OpenSCAD merge

* fix: correct discretizer handling in python_cylinder after OpenSCAD merge

The OpenSCAD merge introduced CurveDiscretizer to centralize fn/fa/fs
parameter handling. However, python_cylinder was creating the discretizer
twice: once before argument parsing (which removes fn/fa/fs from kwargs)
and again when creating the CylinderNode (from already-modified kwargs).

This caused cylinders to ignore fn/fa/fs parameters and render with
incorrect facet counts (e.g., 6 sides instead of specified 12).

Fixed by:
- Removing fn/fa/fs from kwlist (handled by CreateCurveDiscretizer)
- Removing unused fn/fa/fs local variables
- Using single discretizer instance throughout
- Updating format string to match reduced parameter list

Resolves incorrect cylinder rendering in hinge and spline tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add merge-mode-functions flag to gcovr to fix coverage collection

After the OpenSCAD merge, gcovr encounters functions in AST.h that appear
on multiple lines across different compilation units, causing coverage
merging to fail with:

  GcovrMergeAssertionError: Got function <unknown function> on multiple lines

Adding --merge-mode-functions=merge-use-line-0 tells gcovr to use line 0
as the canonical line for functions when merging, resolving the conflict.

This fixes the Linux CI coverage step failures while maintaining the same
test results (all tests still pass).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Sohler Günther <guenther.sohler@gmail.com>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: add CLAUDE.md for project guidance and development workf (#288)

* feat(python): make add_parameter return value and add feature toggle (#290)

- Modified add_parameter() to return the parameter value instead of None
- Added feature toggle 'add-parameter-pure-function' to control global
  variable creation
- When feature is disabled (default): returns value AND creates global
  variable (backward compatible)
- When feature is enabled: returns value WITHOUT creating global variable
  (pure function)
- Added two test cases to verify both behaviors

This allows gradual deprecation of global variable creation while
maintaining backward compatibility.

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.8 (#289)

* fix: evalauting fn for path_extrude again (#287)

* fix: evaluating fn for path_extrude again

* fix: also wrap works again

---------

Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>

* feat: add children() function

* fix(rpm): resolve RPM build failures by forcing static linking (#341)

* fix(rpm): force static linking to match Debian package behavior

RPM builds were creating shared libraries (libOpenSCADPy.so,
libmanifold.so.3, libClipper2.so.1) but not installing them, causing
runtime dependency failures. Debian builds statically link these
libraries into the main binary, resulting in a working package.

Root cause: Fedora's %cmake macro historically sets BUILD_SHARED_LIBS=ON
by default, overriding the project's BUILD_SHARED_LIBS=OFF setting.
Debian builds don't use this macro and respect the project default.

Changes:
- Add -DBUILD_SHARED_LIBS=OFF to CMake flags to force static linking
- Remove libClipper2.so, libOpenSCADPy.so, libmanifold.so from %files
  section (these are now statically linked)
- Update __requires_exclude to only filter libfive.so (the only
  bundled shared library that is actually installed)

This matches Debian's proven working approach and produces a
self-contained binary with only libfive.so as a separate library.

Fixes build failures in v0.8.20-v0.8.23 and broken v0.8.19 packages.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(cmake): use CMAKE_INSTALL_LIBDIR for libfive install path

The libfive install rule was using hardcoded "lib" destination, but
on 64-bit systems libraries should go to lib64. This caused RPM
packaging to fail because the spec file expects libraries in
%{_libdir} (which expands to lib64).

Changed from:
  install(FILES ... DESTINATION "lib")
To:
  install(FILES ... DESTINATION ${CMAKE_INSTALL_LIBDIR})

This matches how Debian's rules use CMAKE_INSTALL_RPATH=/usr/lib
and ensures libfive.so is installed to the correct architecture-
specific library directory.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: add installation documentation to website (#342)

* feat: add installation documentation to website

* feat(web): add installation instructions for GNU Guix

* chore(master): release 0.8.24 (#339)

* fix(ci): resolve RPM signing passphrase issue (#345)

The RPM signing step was failing because the GPG passphrase wasn't being
correctly passed through stdin to the rpm/gpg subprocess. The previous
approach using --passphrase-fd 0 didn't work in the GitHub Actions
container environment.

This fix:
- Creates a temporary file for the GPG passphrase instead of piping
- Configures the %__gpg_sign_cmd RPM macro to use --passphrase-file
- Uses rpmsign instead of rpm for clarity
- Properly cleans up the passphrase file after signing

The passphrase file approach is more reliable across different
environments and avoids the stdin redirection issues.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20981164976

* fix(ci): AppImage libfive dependency, Qt5 support, ARM64 support, and distribution updates (#347)

* fix(ci): Fix EOL date for Debian Trixie

* ci(debian): stop building packages for Ubuntu 24.10 oracular as it's EOL

* ci(debian): fix EOL date for Ubuntu 22.04 LTS jammy

* ci(debian): fix EOL date for Ubuntu 24.04 LTS noble

* fix(ci): resolve AppImage libfive dependency and add Qt5/ARM64 support

This commit fixes the AppImage build and adds multi-architecture support:

1. **Libfive dependency issue**: The AppImage build started failing after
   commit 50591251c which changed libfive install path from hardcoded "lib"
   to CMAKE_INSTALL_LIBDIR.

   Root cause:
   - On Ubuntu 24.04, CMAKE_INSTALL_LIBDIR defaults to "lib/x86_64-linux-gnu"
   - libfive.so is now installed to usr/lib/x86_64-linux-gnu/libfive.so
   - linuxdeploy expects libraries in usr/lib/ and can't find it

   Error:
   ```
   ERROR: Could not find dependency: libfive.so
   ERROR: Failed to deploy dependencies for existing files
   ```

   Solution:
   - Override CMAKE_INSTALL_LIBDIR=lib for AppImage builds
   - This keeps libfive.so in usr/lib/ where linuxdeploy expects it
   - Appropriate for AppImage which uses a simplified /usr layout

2. **Add Qt5 support**: Converted the AppImage workflow to a matrix build
   that creates both Qt5 and Qt6 versions of the AppImage.

3. **Add ARM64 (aarch64) support**: Extended the matrix to build for both
   x86_64 and aarch64 architectures.

   Implementation:
   - Added architecture detection (auto-detect via uname -m or ARCH env var)
   - Download correct architecture versions of linuxdeploy, appimagetool,
     and linuxdeploy-plugin-qt
   - Use QEMU emulation for ARM64 builds on x86_64 runners
   - Include architecture and Qt version in AppImage filename to avoid
     naming conflicts

Changes:
- Added ARCH environment variable to create_appimage.sh
- Script auto-detects architecture or uses ARCH environment variable
- Workflow builds 4 variants: Qt5/Qt6 × x86_64/aarch64
- Each variant uses the appropriate dependency profile
- AppImage filenames include Qt version and architecture:
  - PythonSCAD-<version>-qt5-x86_64.AppImage
  - PythonSCAD-<version>-qt5-aarch64.AppImage
  - PythonSCAD-<version>-qt6-x86_64.AppImage
  - PythonSCAD-<version>-qt6-aarch64.AppImage

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20981164946

* feat: add ARM64 AppImage support using Docker containers

Implements ARM64 (aarch64) AppImage builds using Docker containers,
following the same pattern as Debian package builds. This approach
uses multi-arch Docker images with QEMU emulation at the container
level, allowing proper execution of native ARM64 tools.

Changes:
- Add arch matrix dimension (x86_64, aarch64) to workflow
- Use ubuntu:24.04 Docker container for ARM64 builds
- Keep native builds for x86_64 (no container overhead)
- Split dependency installation for native vs container contexts
- Update artifact naming to include architecture
- Pass ARCH environment variable to build script

This enables building AppImages for both x86_64 and ARM64 across
Qt5 and Qt6, resulting in 4 total AppImage variants.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): use explicit docker run for ARM64 AppImage builds

The previous approach using the container field didn't work because
GitHub Actions doesn't support specifying platform/architecture for
the container image. This caused Docker to pull the amd64 image even
for ARM64 builds, resulting in "cannot execute binary file" errors.

Solution:
- x86_64 builds: Continue running natively on ubuntu-24.04 runner
- aarch64 builds: Use explicit 'docker run --platform linux/arm64'
  command after setting up QEMU

This approach ensures the ARM64 Docker container pulls the correct
arm64 image and all tools inside run as native ARM64 binaries.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): fix ARM64 builds for Debian and RPM packages

Both Debian and RPM workflows had the same issue as the AppImage workflow:
using the container field pulls amd64 images even for ARM64 matrix entries,
resulting in mislabeled packages (amd64 packages with arm64 in filename).

Issue discovered:
- Debian packages named *_arm64.deb were actually Architecture: amd64
- This occurred because container field doesn't respect platform/arch
- QEMU setup never ran due to condition: matrix.qemu && !matrix.docker_image

Solution (same approach as AppImage):
- amd64 builds: Continue using container field (existing behavior)
- arm64 builds: Use explicit 'docker run --platform linux/arm64'
- Set up QEMU before ARM64 builds
- Wrap all build steps (dependencies, build, signing) in docker run

This ensures ARM64 packages are actually built for ARM64 architecture.

Testing:
- Previous v0.8.24 arm64.deb packages are actually amd64
- New builds will produce genuine ARM64 binaries

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): escape multi-line secrets in ARM64 RPM build script

The ARM64 RPM build was failing with 'unexpected EOF while looking for
matching quote' error. This was caused by multi-line secret values (GPG
keys and passphrases) being substituted into echo commands with double
quotes inside a bash -c '...' single-quoted string.

When GitHub Actions substitutes a multi-line secret like DEB_SIGNING_KEY
into:
  echo "${{ secrets.DEB_SIGNING_KEY }}"
inside bash -c '...', it creates:
  echo "-----BEGIN PGP...
  line2
  line3..."
which breaks bash syntax with unclosed quotes.

Solution: Use single quotes around the secrets (with proper escaping for
the bash -c context) to preserve them literally:
  echo '\''...'\'\'

This matches the pattern already used successfully in the Debian workflow.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20997281343

* fix(ci): add docker images for Ubuntu jammy/noble ARM64 builds

Ubuntu jammy and noble had docker_image set to null because they use native
GitHub runners for amd64 builds. However, ARM64 builds require Docker with
the --platform flag, which needs a valid image name.

When docker_image was null, the Docker command was malformed:
  docker run ... ${{ matrix.docker_image }} bash -c ...
  docker run ... bash -c ...  # docker_image=null becomes empty string

Docker then interpreted 'bash' as the image name and pulled bash:latest,
which doesn't have apt-get, causing the build to fail.

Fix: Add explicit docker_image values (ubuntu:jammy, ubuntu:noble) so ARM64
builds work correctly. The amd64 builds still use native runners via the
runner field.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20997278400

* ci: do not build for arm64 at the moment as it is too slow

* chore: remove ARM64 from repository website templates

ARM64 builds are temporarily disabled due to slow QEMU emulation.
Updated HTML templates in update-apt-repo.sh and update-yum-repo.sh
to remove arm64/aarch64 from supported architectures list.

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.25 (#346)

* feat(web): improve releases page and add cheat sheet (#348)

* feat(ci): implement draft release workflow with auto-publish

- Add 'draft: true' to release-please config to create draft releases
- Create publish-release workflow that monitors build workflows
- Auto-publish when all builds succeed
- Keep draft and add status report when builds fail
- Ensures downloads page only shows complete releases

* fix: fix cheatsheet URL to adapt to new website layout

* fix(web): correct HTML structure in cheatsheet

- Fix missing closing </div> tags in multiple sections
- Change incorrect </section> tags to </div>
- Fix <<div typo to <div>
- Add missing <div class='grid'> wrappers
- Add missing closing </code></pre> tags
- Fixes layout issues where sections overlapped or displayed incorrectly

* docs: add cheat-sheet page to website

* chore(master): release 0.8.26 (#349)

* fix(ci): use prerelease instead of draft for release workflow (#351)

Problem: Draft releases don't trigger the 'release: [created]' event,
so build workflows weren't running after release-please created releases.

Solution: Use 'prerelease: true' instead of 'draft: true':
- Prereleases DO trigger the release:created event
- Build workflows now trigger properly
- Publish workflow clears prerelease flag when all builds succeed
- Users still won't see incomplete releases (prereleases are hidden)

Changes:
- .release-please-config.json: Changed draft:true to prerelease:true
- .github/workflows/publish-release.yml: Updated to handle prereleases
  - Check for prerelease flag instead of draft
  - Clear prerelease flag when publishing
  - Updated error messages

* chore: update from OpenSCAD (#344)

* Run once daily.

* Update (and run) translation scripts; require bash; enable error options.

* Bumped Clipper to 2.0.1 (#6494)

* Bumped Clipper to 2.0.1
* Add bugfix for https://github.com/AngusJohnson/Clipper2/pull/1054

---------

Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Marius Kintel <marius@kintel.net>

* chore: remove obsolete Python 3.11 binaries (#353)

* chore: remove uncrustify config as it is no longer in use (#355)

* chore(master): release 0.8.27 (#356)

* fix(ci): fix GPG signing and unify build matrices (#357)

* fix(ci): fix GPG signing in non-TTY environments

The RPM signing was failing in GitHub Actions with:
- "Could not set GPG_TTY to stdin: Inappropriate ioctl for device"
- "gpg: signing failed: Bad passphrase"

Root causes identified and fixed:

1. Duplicate gpg in macro: `%{__gpg} gpg` expanded to `/usr/bin/gpg gpg`
   - Fixed by removing the duplicate `gpg`

2. Wrong option order: When `--pinentry-mode loopback` came first, GPG
   didn't recognize subsequent options like `--batch`
   - Fixed by putting `--batch` first in the options list

3. Missing gpg-agent configuration: The agent needs `allow-loopback-pinentry`
   in gpg-agent.conf to allow passphrase injection without a TTY
   - Fixed by creating gpg-agent.conf before importing keys

4. Using long-form options: Changed `--sign --detach-sign --output` to `-sbo`
   (the short form used by rpm's default macro)

5. Set GPG_TTY="" to suppress harmless TTY warnings in CI

Tested in Docker without TTY to confirm the fix works before applying
to the workflow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(ci): read RPM build matrix from supported-distributions.json

- Add Fedora 42, Fedora 43, and Rocky Linux 9 (EL) entries to
  supported-distributions.json
- Refactor build-rpm-packages.yml to use prepare-matrix job that reads
  from the JSON file, matching the pattern used by build-debian-packages.yml
- Both workflows now share a single source of truth for supported
  distributions and architectures
- Update notes section to document supported families

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove arm64 architecture support for Fedora 42 and 43

Drop arm64 from the supported architectures list for Fedora 42 and 43 distributions as
build times are currently way too long.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(web): use theme CSS variables for cheatsheet dark mode support (#358)

Replace hardcoded light-mode colors with Material for MkDocs CSS
variables so the cheatsheet page correctly adapts to dark mode.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.28 (#359)

* fix(ci): import GPG public key to RPM keyring for signature verification (#361)

* chore(master): release 0.8.29 (#362)

* fix(ci): fix RPM signing for Fedora 43 (RPM 6.x) (#363)

RPM 6.0 introduced breaking changes where custom %__gpg_sign_cmd macro
overrides no longer work due to parametric macros.

This commit adds version-aware signing:
- RPM 4.x (Fedora 42, Rocky 9): Use existing approach with %__gpg_sign_cmd
- RPM 6.x (Fedora 43): Use gpg-preset-passphrase to cache passphrase in
  gpg-agent and use %_openpgp_sign_id macro

Fixes signing failure on Fedora 43 while maintaining compatibility with
older RPM versions.

* chore(master): release 0.8.30 (#364)

* Update lodepng to 20260102 and add a README

* Add to readme where the lexertl source came from

Wrote a bash script to confirm this, but it was HEAD of the other repo
at the time of the original commit.

* Estimate cache size for manifold objects (#6499)

* clang-format: Add AllowShortFunctionsOnASingleLine: InlineOnly (#6530)

* Correctly preferences after OpenSCADApp has finished initializing (#6533)

* Add hidapi README (#6532)

Based on comments to https://github.com/openscad/openscad/issues/6513
add a README.md for hidapi like the other external libraries.

* Add color list window.

* Add support for double-click to insert the color name / color() call.

* Ensure newline after HtmlLink message; Skip messages in ErrorList window.

* Require "xkcd:" prefix to switch to the XKCD color map.

* Suppress log messages of type HtmlLink on command line.

* Handle API change between Qt5 and Qt6 (qreal -> float) in QColor::getHslF().

* fix: used other version from regression

* fix:udpated tests

* fix(ci): fix keygrip extraction for RPM 6.x signing (#366)

The previous grep pattern '^\s*sec' expected whitespace before "sec"
but GPG output starts with just "sec" (no leading whitespace). This
caused the keygrip extraction to fail on Fedora 43 (RPM 6.x).

Fixed by using awk to find lines starting with "sec" and then
extracting the keygrip from the following line containing "Keygrip".

Also added debug output to show the full GPG key details for easier
troubleshooting.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.31 (#367)

* chore: merge 91 commits from openscad/master (#277)

* add correct StartupWMClass to set the correct icon on Gnome

I believe this is the XDG way to do this, but I am not sure.

* Check for save/discard/cancel on quit.  Fixes #3971. (#6311)

* Bump CGAL to 6.1.0 (#6318)

* Make vector swizzle experimental (#6307)

* Don't log to stderr (#6323)

* Fully separate zoom levels for each tab.  Fixes #4742. (#6320)

* Fix/cleanup compiler warnings openscad/openscad#6245 (#6300)

* Remove obsolete HTML example converter (#6328)

* reformat python tests scripts in pep8 (#6330)

* Bump the github-actions group with 3 updates

Bumps the github-actions group with 3 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [actions/stale](https://github.com/actions/stale).

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Updates `github/codeql-action` from 3 to 4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

Updates `actions/stale` from 9 to 10
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Check print service name only if remote print service is selected.

This fixes the issue that the print dialog is always shown if the local
application is selected for printing.

* Don't crash MouseSelector, debug GL init (#6334)

* beautify (#6342)

* Set QSCINTILLA_DLL for MINGW (and not MXECROSS). (#6343)

* Warn on offset() with both r and delta specified (#6348)

* Fix QFile::Open nodiscard warning with Qt6 6.10.0. (#6364)

* Update cmdline help to show Manifold as default (#6350)

* Rm unused function.  Fixes #6352. (#6353)

* Add Color module spec (#6339)

* Enables "Ctrl/Cmd-Mouse-wheel zooms text" config option to work  (Preliminary fix for #6368)

* Entirely removed superfluous EditorInterface::wheelEvent method

* Bump Qt to 6.5.7 (#6371)

* CircleCI: Update macOS resources to 26.1 on M4 (#6324)

* Refactor Discretization Special Usage (#6251)

* Tessallation specials $fn$fs$fa now grouped in an object

Instead of fn,fs,fa as three doubles, they are passed as an object
packaged up with the methods on them.

* Repair basic functionality (#6376)

* Fix indentation of CC0 template text. (#6375)

Also clean up line endings.
Add templates/README.md

* Fix a number of non-ASCII file name issues (#6136)

* Try caching MSYS2 packages

Fixes #6381 if it works

* Fix `if` in yaml wrong

* Don't want `if`s anyway

doesn't matter I did them wrong

* Update msys2 gh actions caching

Make one new cache a day from scratch. Leave note why we don't find
the most recent cache. Skip saving since caches are immutable.

* Comment updates + build up cache over a month

* We can read the determined msys2 directory

We're not restoring before the setup, like I originally planned.

* Updating script to output list of pacboy packages to workflow

Easier than my other approach, actually. Ah, whatever.

* Don't change $MSYSTEM in GH Actions

* Oops, I need to specify the bash shell I think

* Use arrays instead of backslash line termination

* Don't fail fast because it fails due to caching a lot

well, until we actually get one

* std::wstring_convert and std::codecvt_utf8_utf16 is going to be depreciated. Use boost::nowide::narrow (#6356)

* Add Unit tests to fix measuring (#6299)

* Starting point of release build CI workflow (#6374)

* Define executors separately
* Added OpenSCAD release build job with create-tarball

* Centralize version string management (#6388)

* Centralize version string management

* Identify build tag in OPENSCAD_VERSION, and correctly extract OPENSCAD_SHORTVERSION

* cimg/python: no 'latest' tag. Use 3.14 (#6399)

* Disable WIP macOS release build (#6398)

* CircleCI Bugfix: Use OPENSCAD_VERSION instead of VERSION (#6400)

* create-tarball: Amend files manually since git-archive-all doesn't consider unversioned files (#6401)

* cd to /tmp/artifacts before creating shasums (#6402)

* Add CMake support + shell helpers for VERSION.txt and COMMIT.txt (#6403)

* Ingest VERSION.txt and COMMIT.txt at cmake time
* Shell helper for establishing version from VERSION.txt and COMMIT.txt

* Snapshot builds now uses the version helpers to unify version management, also deprecated 32-bit windows (#6404)

* Bugfix: binary name on macOS needs correct case (#6409)

* Parametrize macOS build for release vs. snapshot (#6406)

* Parametrize macOS build for release vs. snapshot
* Use establish_version.sh and don't require git
* Always checkout in the same way; with submodules

* Bump actions/checkout from 5 to 6 in the github-actions group (#6418)

Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).

Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Delete discretizer keyword args

PyArg_ParseTupleAndKeywords doesn't allow keyword args it doesn't know
about, so we delete them if found.

Manually testing the fix.

* Fix formatting of "Browser" header (#6433)

Markdown rendered is confused by non-breaking space (`U+A0`) - replaced with a regular space.

* Make find highlighting case-insensitive

* chore: codegen version into manpage (#6448)

* chore: codegen version into manpage

testing process:

- created a VERSION.txt file
- ran the CMake build
- confirm that the output `build/doc/openscad.1` has the same version as
  VERSION.txt

* remove extra comment

* Added job parameters openscad_build_type and openscad_platform (#6449)

* Remove deprecated child() and assign(). (#6428)

* Remove deprecated child() and assign().
Fixes #5557.
Fixes #6425.

* Update releases/next.md:  child(), assign(), object(), textmetrics(), fontmetrics
Remove deprecated child() and assign().
Add object().
Reclassify textmetrics() and fontmetrics() as functions.

* More CircleCI cleanup (#6450)

* Bump manifold to 3.3.2 and onetbb to 2022.3.0 (#6455)

* Added failing test for #6456 (#6459)

* Bumped manifold to fix #6235 (#6467)

* Temporarily restore exact job names used by circleci-download-artifacts.py (#6469)

* CircleCI: Update name of wasm build (#6470)

* CircleCI release builds (#6412)

* Move version info to .c file (#6463)

* chore: bump nix-shell for latest version

* convert to flake (for lockfile)

* use stable release

* remove duplicate lock entry

* Add warning to cmake output that ENABLE_CGAL=OFF is a dev-only flag.

* Fix font sample rendering in font list window.

* Remove obsolete FontListDialog.

* Change console font size via mouse wheel event (fixes #6186).

This is only changing the current console widget, not the value in the
preferences.

* Align naming of release jobs, correct naming of tarball (#6484)

* Bugfix: Forgot to update dependencies on tarball (#6485)

* Bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/cache](https://github.com/actions/cache).

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix premature assignment expiration warning (#6489)

* CI: enable sccache (#6473)

This avoids doing redundant work by caching compiler results. Because CI
does less work, we can run more redundant CI jobs and avoid the mental
overhead of manually de-duping jobs.

* fix: resurrect LoadShareDesignDialog.h which got lost during merge from OpenSCAD

* fix: return type of findModelObject which got inadvertently changed during merge from OpenSCAD

* fix: version handling which got broken during merge from OpenSCAD

* fix: add missing conv. for editor file path in parseDocument which got lost during OpenSCAD merge

* fix: correct variable names in argument parsing for python_cylinder function after OpenSCAD merge

* fix: revert test result for use-tests-expected which was inadvertently changed during OpenSCAD merge

* fix: update argument parsing for python_cylinder function after OpenSCAD merge

* fix: correct discretizer handling in python_cylinder after OpenSCAD merge

The OpenSCAD merge introduced CurveDiscretizer to centralize fn/fa/fs
parameter handling. However, python_cylinder was creating the discretizer
twice: once before argument parsing (which removes fn/fa/fs from kwargs)
and again when creating the CylinderNode (from already-modified kwargs).

This caused cylinders to ignore fn/fa/fs parameters and render with
incorrect facet counts (e.g., 6 sides instead of specified 12).

Fixed by:
- Removing fn/fa/fs from kwlist (handled by CreateCurveDiscretizer)
- Removing unused fn/fa/fs local variables
- Using single discretizer instance throughout
- Updating format string to match reduced parameter list

Resolves incorrect cylinder rendering in hinge and spline tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add merge-mode-functions flag to gcovr to fix coverage collection

After the OpenSCAD merge, gcovr encounters functions in AST.h that appear
on multiple lines across different compilation units, causing coverage
merging to fail with:

  GcovrMergeAssertionError: Got function <unknown function> on multiple lines

Adding --merge-mode-functions=merge-use-line-0 tells gcovr to use line 0
as the canonical line for functions when merging, resolving the conflict.

This fixes the Linux CI coverage step failures while maintaining the same
test results (all tests still pass).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Sohler Günther <guenther.sohler@gmail.com>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: multmatrix operates on Vector3d

* fix:removed wrong printf

* added lasercutr lib

* support #2 cuts

* feat: start allow to alter existing solid paramters

* feat(ci): add native Windows packaging workflow (#371)

* feat(ci): add native Windows packaging workflow

Add a new GitHub Actions workflow that builds PythonSCAD natively on
Windows using MSYS2, packages it with CPack (ZIP + NSIS installer),
and runs a sanity test (pythonscad --info) to verify the executable
works before release.

This addresses issue #360 where MXE cross-compiled builds are missing
MinGW runtime DLLs (libgcc_s_seh-1.dll), causing the application to
fail to launch on Windows.

The native MSYS2 build correctly bundles all required DLLs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): use workspace-relative path for artifacts

Changed from /tmp/out/ (MSYS2-only path) to artifacts/ (workspace-relative)
to fix upload-artifact action which runs in native Windows context.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): bundle MSYS2 runtime DLLs in package

Add step to use ldd to find and copy all MSYS2 DLL dependencies
recursively into the staging directory before packaging.

This fixes missing DLL errors:
- libgcc_s_seh-1.dll
- libcairo-2.dll
- libwinpthread-1.dll
- libstdc++-6.dll
And any other transitive dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): rewrite DLL bundling to avoid bash subshell issues

Changed from recursive function to iterative loop to avoid
bash 'local' keyword failures in subshells created by pipes.

Uses process substitution instead of pipes and iterates up to
10 times to find all transitive DLL dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): use CMake's install(RUNTIME_DEPENDENCY_SET) for DLL bundling

Instead of manually bundling DLLs with ldd, use CMake 3.21+'s native
runtime dependency resolution. This ensures CPack packages include all
required MSYS2 runtime DLLs (libgcc_s_seh-1.dll, libstdc++-6.dll, etc.).

The RUNTIME_DEPENDENCY_SET feature automatically:
- Finds all DLL dependencies of the executable
- Excludes Windows system DLLs (api-ms-*, system32, etc.)
- Installs them to the package destination

This is the proper CMake-based solution rather than shell scripting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): add Qt plugins for native MSYS2 Windows builds

Qt applications require platform plugins (qwindows.dll) to start.
Also install style, imageformat, and iconengine plugins for full
functionality.

Plugins installed:
- platforms/qwindows.dll (mandatory)
- styles/*.dll (native Windows look)
- imageformats/*.dll (image loading)
- iconengines/*.dll (SVG icons)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): add qt.conf for Qt plugin discovery on Windows

Qt needs a qt.conf file to know where to find plugins when they're
not in the default 'plugins/' subdirectory. This file tells Qt that
the plugin directories (platforms/, imageformats/, etc.) are in the
same directory as the executable.

Contents of qt.conf:
[Paths]
Plugins = .

This should fix the missing toolbar icons issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): bundle Qt SVG runtime DLL

* fix(windows): use MSYS2 Python stdlib instead of embed package

For native MSYS2 Windows builds, the executable is linked against
MSYS2's libpython3.13.dll. The RUNTIME_DEPENDENCY_SET copies this
DLL to the install directory, but the Python standard library was
being installed from python.org's embed package, which has an
incompatible structure (python313.dll + python313.zip vs
libpython3.13.dll + lib/python3.13/).

This mismatch caused Python initialization to fail with:
"Could not find platform independent libraries <prefix>"
"ERROR: Python 3.13.11 not found. Is it installed?"

Fix by installing the MSYS2 Python standard library from the system
installation, which matches the ABI of the linked libpython DLL.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ci: disable MXE cross compiled Windows builds

They are currently broken and are being replaced by native MSYS2 builds.

* feat(windows): improve NSIS installer with start menu, launch option, and multiuser support

- Fix NSIS configuration to work for both MXE cross-compile and native MSYS2 builds
- Add explicit start menu shortcut creation in PythonSCAD folder and root
- Register .scad file association with pythonscad.exe instead of openscad.exe
- Add "Run PythonSCAD" checkbox on installer finish page
- Add multiuser.nsh for per-user installation support infrastructure
- Update URLs and icon references from OpenSCAD to PythonSCAD
- Properly clean up shortcuts and file associations on uninstall

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): convert NSIS include paths to native Windows format

NSIS requires backslashes for include paths on Windows. Using
file(TO_NATIVE_PATH) to convert CMake paths to native format.

Also removes unused multiuser.nsh include that had unmet dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): use relative paths for NSIS includes

CPack runs NSIS from build/_CPack_Packages/<system>/NSIS/, so use
relative paths (../../../) to reference files in the build root.

Also copies mingw-file-association.nsh to build dir for consistency.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ci(windows): remove experimental suffix from native build artifacts

The MSYS2-based native Windows build is now the primary Windows build,
so artifact filenames no longer need the msys2-experimental suffix.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: disable pre-major version bump settings in release-please

Configure release-please to use standard semver bumping by setting
bump-minor-pre-major and bump-patch-for-minor-pre-major to false

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(python): extend add_parameter() with customizer options (#368)

* fix: memory bugs in add_parameter Python string handling

PyUnicode_AsEncodedString creates temporary bytes objects.
PyBytes_AS_STRING returns pointer to internal buffer.
Without proper cleanup, this creates dangling pointers causing segfaults.
Fixed by copying to std::string before calling Py_DECREF().
Affects: default string values, options lists, and dict labels.

* fix: check event type before casting to QKeyEvent

Static cast of QEvent to QKeyEvent without checking event type causes
heap-use-after-free when event is not actually a key event.
Fixed by checking event->type() == QEvent::KeyPress before cast.

* fix: heap-use-after-free in add_parameter annotations

Assignment::addAnnotations stores raw pointers to Annotation objects.
Stack-allocated AnnotationList was being destroyed when function returned,
leaving dangling pointers.
Fixed by heap-allocating AnnotationList with new, matching the pattern
used in CommentParser.cc.

* fix(gui): fix memory bugs in customizer parameter handling

- ParameterWidget: Fix use-after-free when slider is dragged during F5 re-render
  - Use deleteLater() for widget cleanup to handle pending Qt events
  - Keep old parameters alive until widgets are fully deleted
  - Process events after scheduling deletion to handle in-flight events

- MainWindow: Fix crash on window close due to RubberBandManager access
  - Add isBeingDestroyed flag to guard eventFilter during destruction
  - Set flag at start of destructor before any cleanup

* test: add expected output for add_parameter extended tests

* fix(ci): import GPG public key to RPM keyring for signature verification (#361)

* Add color list window.

* Add support for double-click to insert the color name / color() call.

* Suppress log messages of type HtmlLink on command line.

* fix: used other version from regression

* fix:udpated tests

* path points is not writable

* sync to master

* fix; sync regression

* sycn with master

* fix: convert face to polyghons when not already

* fix: improve lasercut library

* fix: face/face cuts working now

* fix: create also lasercutmodels from whole shapes instead from faces

* fix: returning the final result rather than showing it

* make sure, that face2face joints get better slices

* feat: implemented export GCODE

also  pylaser.py got a maximum result size parameter

* fix: add missed file

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: nomike <nomike@nomike.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: pythonscad-gh-bot <pythonscad-github-bot@info.nomike.com>
gsohler added a commit to pythonscad/pythonscad that referenced this pull request Feb 7, 2026
* feat: more properties in __getitem__

* chore: cleanup

* chore: add tests

* chore: merge 91 commits from openscad/master (#277)

* add correct StartupWMClass to set the correct icon on Gnome

I believe this is the XDG way to do this, but I am not sure.

* Check for save/discard/cancel on quit.  Fixes #3971. (#6311)

* Bump CGAL to 6.1.0 (#6318)

* Make vector swizzle experimental (#6307)

* Don't log to stderr (#6323)

* Fully separate zoom levels for each tab.  Fixes #4742. (#6320)

* Fix/cleanup compiler warnings openscad/openscad#6245 (#6300)

* Remove obsolete HTML example converter (#6328)

* reformat python tests scripts in pep8 (#6330)

* Bump the github-actions group with 3 updates

Bumps the github-actions group with 3 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [actions/stale](https://github.com/actions/stale).

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Updates `github/codeql-action` from 3 to 4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

Updates `actions/stale` from 9 to 10
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Check print service name only if remote print service is selected.

This fixes the issue that the print dialog is always shown if the local
application is selected for printing.

* Don't crash MouseSelector, debug GL init (#6334)

* beautify (#6342)

* Set QSCINTILLA_DLL for MINGW (and not MXECROSS). (#6343)

* Warn on offset() with both r and delta specified (#6348)

* Fix QFile::Open nodiscard warning with Qt6 6.10.0. (#6364)

* Update cmdline help to show Manifold as default (#6350)

* Rm unused function.  Fixes #6352. (#6353)

* Add Color module spec (#6339)

* Enables "Ctrl/Cmd-Mouse-wheel zooms text" config option to work  (Preliminary fix for #6368)

* Entirely removed superfluous EditorInterface::wheelEvent method

* Bump Qt to 6.5.7 (#6371)

* CircleCI: Update macOS resources to 26.1 on M4 (#6324)

* Refactor Discretization Special Usage (#6251)

* Tessallation specials $fn$fs$fa now grouped in an object

Instead of fn,fs,fa as three doubles, they are passed as an object
packaged up with the methods on them.

* Repair basic functionality (#6376)

* Fix indentation of CC0 template text. (#6375)

Also clean up line endings.
Add templates/README.md

* Fix a number of non-ASCII file name issues (#6136)

* Try caching MSYS2 packages

Fixes #6381 if it works

* Fix `if` in yaml wrong

* Don't want `if`s anyway

doesn't matter I did them wrong

* Update msys2 gh actions caching

Make one new cache a day from scratch. Leave note why we don't find
the most recent cache. Skip saving since caches are immutable.

* Comment updates + build up cache over a month

* We can read the determined msys2 directory

We're not restoring before the setup, like I originally planned.

* Updating script to output list of pacboy packages to workflow

Easier than my other approach, actually. Ah, whatever.

* Don't change $MSYSTEM in GH Actions

* Oops, I need to specify the bash shell I think

* Use arrays instead of backslash line termination

* Don't fail fast because it fails due to caching a lot

well, until we actually get one

* std::wstring_convert and std::codecvt_utf8_utf16 is going to be depreciated. Use boost::nowide::narrow (#6356)

* Add Unit tests to fix measuring (#6299)

* Starting point of release build CI workflow (#6374)

* Define executors separately
* Added OpenSCAD release build job with create-tarball

* Centralize version string management (#6388)

* Centralize version string management

* Identify build tag in OPENSCAD_VERSION, and correctly extract OPENSCAD_SHORTVERSION

* cimg/python: no 'latest' tag. Use 3.14 (#6399)

* Disable WIP macOS release build (#6398)

* CircleCI Bugfix: Use OPENSCAD_VERSION instead of VERSION (#6400)

* create-tarball: Amend files manually since git-archive-all doesn't consider unversioned files (#6401)

* cd to /tmp/artifacts before creating shasums (#6402)

* Add CMake support + shell helpers for VERSION.txt and COMMIT.txt (#6403)

* Ingest VERSION.txt and COMMIT.txt at cmake time
* Shell helper for establishing version from VERSION.txt and COMMIT.txt

* Snapshot builds now uses the version helpers to unify version management, also deprecated 32-bit windows (#6404)

* Bugfix: binary name on macOS needs correct case (#6409)

* Parametrize macOS build for release vs. snapshot (#6406)

* Parametrize macOS build for release vs. snapshot
* Use establish_version.sh and don't require git
* Always checkout in the same way; with submodules

* Bump actions/checkout from 5 to 6 in the github-actions group (#6418)

Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).

Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Delete discretizer keyword args

PyArg_ParseTupleAndKeywords doesn't allow keyword args it doesn't know
about, so we delete them if found.

Manually testing the fix.

* Fix formatting of "Browser" header (#6433)

Markdown rendered is confused by non-breaking space (`U+A0`) - replaced with a regular space.

* Make find highlighting case-insensitive

* chore: codegen version into manpage (#6448)

* chore: codegen version into manpage

testing process:

- created a VERSION.txt file
- ran the CMake build
- confirm that the output `build/doc/openscad.1` has the same version as
  VERSION.txt

* remove extra comment

* Added job parameters openscad_build_type and openscad_platform (#6449)

* Remove deprecated child() and assign(). (#6428)

* Remove deprecated child() and assign().
Fixes #5557.
Fixes #6425.

* Update releases/next.md:  child(), assign(), object(), textmetrics(), fontmetrics
Remove deprecated child() and assign().
Add object().
Reclassify textmetrics() and fontmetrics() as functions.

* More CircleCI cleanup (#6450)

* Bump manifold to 3.3.2 and onetbb to 2022.3.0 (#6455)

* Added failing test for #6456 (#6459)

* Bumped manifold to fix #6235 (#6467)

* Temporarily restore exact job names used by circleci-download-artifacts.py (#6469)

* CircleCI: Update name of wasm build (#6470)

* CircleCI release builds (#6412)

* Move version info to .c file (#6463)

* chore: bump nix-shell for latest version

* convert to flake (for lockfile)

* use stable release

* remove duplicate lock entry

* Add warning to cmake output that ENABLE_CGAL=OFF is a dev-only flag.

* Fix font sample rendering in font list window.

* Remove obsolete FontListDialog.

* Change console font size via mouse wheel event (fixes #6186).

This is only changing the current console widget, not the value in the
preferences.

* Align naming of release jobs, correct naming of tarball (#6484)

* Bugfix: Forgot to update dependencies on tarball (#6485)

* Bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/cache](https://github.com/actions/cache).

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix premature assignment expiration warning (#6489)

* CI: enable sccache (#6473)

This avoids doing redundant work by caching compiler results. Because CI
does less work, we can run more redundant CI jobs and avoid the mental
overhead of manually de-duping jobs.

* fix: resurrect LoadShareDesignDialog.h which got lost during merge from OpenSCAD

* fix: return type of findModelObject which got inadvertently changed during merge from OpenSCAD

* fix: version handling which got broken during merge from OpenSCAD

* fix: add missing conv. for editor file path in parseDocument which got lost during OpenSCAD merge

* fix: correct variable names in argument parsing for python_cylinder function after OpenSCAD merge

* fix: revert test result for use-tests-expected which was inadvertently changed during OpenSCAD merge

* fix: update argument parsing for python_cylinder function after OpenSCAD merge

* fix: correct discretizer handling in python_cylinder after OpenSCAD merge

The OpenSCAD merge introduced CurveDiscretizer to centralize fn/fa/fs
parameter handling. However, python_cylinder was creating the discretizer
twice: once before argument parsing (which removes fn/fa/fs from kwargs)
and again when creating the CylinderNode (from already-modified kwargs).

This caused cylinders to ignore fn/fa/fs parameters and render with
incorrect facet counts (e.g., 6 sides instead of specified 12).

Fixed by:
- Removing fn/fa/fs from kwlist (handled by CreateCurveDiscretizer)
- Removing unused fn/fa/fs local variables
- Using single discretizer instance throughout
- Updating format string to match reduced parameter list

Resolves incorrect cylinder rendering in hinge and spline tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add merge-mode-functions flag to gcovr to fix coverage collection

After the OpenSCAD merge, gcovr encounters functions in AST.h that appear
on multiple lines across different compilation units, causing coverage
merging to fail with:

  GcovrMergeAssertionError: Got function <unknown function> on multiple lines

Adding --merge-mode-functions=merge-use-line-0 tells gcovr to use line 0
as the canonical line for functions when merging, resolving the conflict.

This fixes the Linux CI coverage step failures while maintaining the same
test results (all tests still pass).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Sohler Günther <guenther.sohler@gmail.com>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: add CLAUDE.md for project guidance and development workf (#288)

* feat(python): make add_parameter return value and add feature toggle (#290)

- Modified add_parameter() to return the parameter value instead of None
- Added feature toggle 'add-parameter-pure-function' to control global
  variable creation
- When feature is disabled (default): returns value AND creates global
  variable (backward compatible)
- When feature is enabled: returns value WITHOUT creating global variable
  (pure function)
- Added two test cases to verify both behaviors

This allows gradual deprecation of global variable creation while
maintaining backward compatibility.

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.8 (#289)

* fix: evalauting fn for path_extrude again (#287)

* fix: evaluating fn for path_extrude again

* fix: also wrap works again

---------

Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>

* feat: add children() function

* fix(rpm): resolve RPM build failures by forcing static linking (#341)

* fix(rpm): force static linking to match Debian package behavior

RPM builds were creating shared libraries (libOpenSCADPy.so,
libmanifold.so.3, libClipper2.so.1) but not installing them, causing
runtime dependency failures. Debian builds statically link these
libraries into the main binary, resulting in a working package.

Root cause: Fedora's %cmake macro historically sets BUILD_SHARED_LIBS=ON
by default, overriding the project's BUILD_SHARED_LIBS=OFF setting.
Debian builds don't use this macro and respect the project default.

Changes:
- Add -DBUILD_SHARED_LIBS=OFF to CMake flags to force static linking
- Remove libClipper2.so, libOpenSCADPy.so, libmanifold.so from %files
  section (these are now statically linked)
- Update __requires_exclude to only filter libfive.so (the only
  bundled shared library that is actually installed)

This matches Debian's proven working approach and produces a
self-contained binary with only libfive.so as a separate library.

Fixes build failures in v0.8.20-v0.8.23 and broken v0.8.19 packages.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(cmake): use CMAKE_INSTALL_LIBDIR for libfive install path

The libfive install rule was using hardcoded "lib" destination, but
on 64-bit systems libraries should go to lib64. This caused RPM
packaging to fail because the spec file expects libraries in
%{_libdir} (which expands to lib64).

Changed from:
  install(FILES ... DESTINATION "lib")
To:
  install(FILES ... DESTINATION ${CMAKE_INSTALL_LIBDIR})

This matches how Debian's rules use CMAKE_INSTALL_RPATH=/usr/lib
and ensures libfive.so is installed to the correct architecture-
specific library directory.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: add installation documentation to website (#342)

* feat: add installation documentation to website

* feat(web): add installation instructions for GNU Guix

* chore(master): release 0.8.24 (#339)

* fix(ci): resolve RPM signing passphrase issue (#345)

The RPM signing step was failing because the GPG passphrase wasn't being
correctly passed through stdin to the rpm/gpg subprocess. The previous
approach using --passphrase-fd 0 didn't work in the GitHub Actions
container environment.

This fix:
- Creates a temporary file for the GPG passphrase instead of piping
- Configures the %__gpg_sign_cmd RPM macro to use --passphrase-file
- Uses rpmsign instead of rpm for clarity
- Properly cleans up the passphrase file after signing

The passphrase file approach is more reliable across different
environments and avoids the stdin redirection issues.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20981164976

* fix(ci): AppImage libfive dependency, Qt5 support, ARM64 support, and distribution updates (#347)

* fix(ci): Fix EOL date for Debian Trixie

* ci(debian): stop building packages for Ubuntu 24.10 oracular as it's EOL

* ci(debian): fix EOL date for Ubuntu 22.04 LTS jammy

* ci(debian): fix EOL date for Ubuntu 24.04 LTS noble

* fix(ci): resolve AppImage libfive dependency and add Qt5/ARM64 support

This commit fixes the AppImage build and adds multi-architecture support:

1. **Libfive dependency issue**: The AppImage build started failing after
   commit 50591251c which changed libfive install path from hardcoded "lib"
   to CMAKE_INSTALL_LIBDIR.

   Root cause:
   - On Ubuntu 24.04, CMAKE_INSTALL_LIBDIR defaults to "lib/x86_64-linux-gnu"
   - libfive.so is now installed to usr/lib/x86_64-linux-gnu/libfive.so
   - linuxdeploy expects libraries in usr/lib/ and can't find it

   Error:
   ```
   ERROR: Could not find dependency: libfive.so
   ERROR: Failed to deploy dependencies for existing files
   ```

   Solution:
   - Override CMAKE_INSTALL_LIBDIR=lib for AppImage builds
   - This keeps libfive.so in usr/lib/ where linuxdeploy expects it
   - Appropriate for AppImage which uses a simplified /usr layout

2. **Add Qt5 support**: Converted the AppImage workflow to a matrix build
   that creates both Qt5 and Qt6 versions of the AppImage.

3. **Add ARM64 (aarch64) support**: Extended the matrix to build for both
   x86_64 and aarch64 architectures.

   Implementation:
   - Added architecture detection (auto-detect via uname -m or ARCH env var)
   - Download correct architecture versions of linuxdeploy, appimagetool,
     and linuxdeploy-plugin-qt
   - Use QEMU emulation for ARM64 builds on x86_64 runners
   - Include architecture and Qt version in AppImage filename to avoid
     naming conflicts

Changes:
- Added ARCH environment variable to create_appimage.sh
- Script auto-detects architecture or uses ARCH environment variable
- Workflow builds 4 variants: Qt5/Qt6 × x86_64/aarch64
- Each variant uses the appropriate dependency profile
- AppImage filenames include Qt version and architecture:
  - PythonSCAD-<version>-qt5-x86_64.AppImage
  - PythonSCAD-<version>-qt5-aarch64.AppImage
  - PythonSCAD-<version>-qt6-x86_64.AppImage
  - PythonSCAD-<version>-qt6-aarch64.AppImage

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20981164946

* feat: add ARM64 AppImage support using Docker containers

Implements ARM64 (aarch64) AppImage builds using Docker containers,
following the same pattern as Debian package builds. This approach
uses multi-arch Docker images with QEMU emulation at the container
level, allowing proper execution of native ARM64 tools.

Changes:
- Add arch matrix dimension (x86_64, aarch64) to workflow
- Use ubuntu:24.04 Docker container for ARM64 builds
- Keep native builds for x86_64 (no container overhead)
- Split dependency installation for native vs container contexts
- Update artifact naming to include architecture
- Pass ARCH environment variable to build script

This enables building AppImages for both x86_64 and ARM64 across
Qt5 and Qt6, resulting in 4 total AppImage variants.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): use explicit docker run for ARM64 AppImage builds

The previous approach using the container field didn't work because
GitHub Actions doesn't support specifying platform/architecture for
the container image. This caused Docker to pull the amd64 image even
for ARM64 builds, resulting in "cannot execute binary file" errors.

Solution:
- x86_64 builds: Continue running natively on ubuntu-24.04 runner
- aarch64 builds: Use explicit 'docker run --platform linux/arm64'
  command after setting up QEMU

This approach ensures the ARM64 Docker container pulls the correct
arm64 image and all tools inside run as native ARM64 binaries.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): fix ARM64 builds for Debian and RPM packages

Both Debian and RPM workflows had the same issue as the AppImage workflow:
using the container field pulls amd64 images even for ARM64 matrix entries,
resulting in mislabeled packages (amd64 packages with arm64 in filename).

Issue discovered:
- Debian packages named *_arm64.deb were actually Architecture: amd64
- This occurred because container field doesn't respect platform/arch
- QEMU setup never ran due to condition: matrix.qemu && !matrix.docker_image

Solution (same approach as AppImage):
- amd64 builds: Continue using container field (existing behavior)
- arm64 builds: Use explicit 'docker run --platform linux/arm64'
- Set up QEMU before ARM64 builds
- Wrap all build steps (dependencies, build, signing) in docker run

This ensures ARM64 packages are actually built for ARM64 architecture.

Testing:
- Previous v0.8.24 arm64.deb packages are actually amd64
- New builds will produce genuine ARM64 binaries

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): escape multi-line secrets in ARM64 RPM build script

The ARM64 RPM build was failing with 'unexpected EOF while looking for
matching quote' error. This was caused by multi-line secret values (GPG
keys and passphrases) being substituted into echo commands with double
quotes inside a bash -c '...' single-quoted string.

When GitHub Actions substitutes a multi-line secret like DEB_SIGNING_KEY
into:
  echo "${{ secrets.DEB_SIGNING_KEY }}"
inside bash -c '...', it creates:
  echo "-----BEGIN PGP...
  line2
  line3..."
which breaks bash syntax with unclosed quotes.

Solution: Use single quotes around the secrets (with proper escaping for
the bash -c context) to preserve them literally:
  echo '\''...'\'\'

This matches the pattern already used successfully in the Debian workflow.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20997281343

* fix(ci): add docker images for Ubuntu jammy/noble ARM64 builds

Ubuntu jammy and noble had docker_image set to null because they use native
GitHub runners for amd64 builds. However, ARM64 builds require Docker with
the --platform flag, which needs a valid image name.

When docker_image was null, the Docker command was malformed:
  docker run ... ${{ matrix.docker_image }} bash -c ...
  docker run ... bash -c ...  # docker_image=null becomes empty string

Docker then interpreted 'bash' as the image name and pulled bash:latest,
which doesn't have apt-get, causing the build to fail.

Fix: Add explicit docker_image values (ubuntu:jammy, ubuntu:noble) so ARM64
builds work correctly. The amd64 builds still use native runners via the
runner field.

Fixes: https://github.com/pythonscad/pythonscad/actions/runs/20997278400

* ci: do not build for arm64 at the moment as it is too slow

* chore: remove ARM64 from repository website templates

ARM64 builds are temporarily disabled due to slow QEMU emulation.
Updated HTML templates in update-apt-repo.sh and update-yum-repo.sh
to remove arm64/aarch64 from supported architectures list.

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.25 (#346)

* feat(web): improve releases page and add cheat sheet (#348)

* feat(ci): implement draft release workflow with auto-publish

- Add 'draft: true' to release-please config to create draft releases
- Create publish-release workflow that monitors build workflows
- Auto-publish when all builds succeed
- Keep draft and add status report when builds fail
- Ensures downloads page only shows complete releases

* fix: fix cheatsheet URL to adapt to new website layout

* fix(web): correct HTML structure in cheatsheet

- Fix missing closing </div> tags in multiple sections
- Change incorrect </section> tags to </div>
- Fix <<div typo to <div>
- Add missing <div class='grid'> wrappers
- Add missing closing </code></pre> tags
- Fixes layout issues where sections overlapped or displayed incorrectly

* docs: add cheat-sheet page to website

* chore(master): release 0.8.26 (#349)

* fix(ci): use prerelease instead of draft for release workflow (#351)

Problem: Draft releases don't trigger the 'release: [created]' event,
so build workflows weren't running after release-please created releases.

Solution: Use 'prerelease: true' instead of 'draft: true':
- Prereleases DO trigger the release:created event
- Build workflows now trigger properly
- Publish workflow clears prerelease flag when all builds succeed
- Users still won't see incomplete releases (prereleases are hidden)

Changes:
- .release-please-config.json: Changed draft:true to prerelease:true
- .github/workflows/publish-release.yml: Updated to handle prereleases
  - Check for prerelease flag instead of draft
  - Clear prerelease flag when publishing
  - Updated error messages

* chore: update from OpenSCAD (#344)

* Run once daily.

* Update (and run) translation scripts; require bash; enable error options.

* Bumped Clipper to 2.0.1 (#6494)

* Bumped Clipper to 2.0.1
* Add bugfix for https://github.com/AngusJohnson/Clipper2/pull/1054

---------

Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Marius Kintel <marius@kintel.net>

* chore: remove obsolete Python 3.11 binaries (#353)

* chore: remove uncrustify config as it is no longer in use (#355)

* chore(master): release 0.8.27 (#356)

* fix(ci): fix GPG signing and unify build matrices (#357)

* fix(ci): fix GPG signing in non-TTY environments

The RPM signing was failing in GitHub Actions with:
- "Could not set GPG_TTY to stdin: Inappropriate ioctl for device"
- "gpg: signing failed: Bad passphrase"

Root causes identified and fixed:

1. Duplicate gpg in macro: `%{__gpg} gpg` expanded to `/usr/bin/gpg gpg`
   - Fixed by removing the duplicate `gpg`

2. Wrong option order: When `--pinentry-mode loopback` came first, GPG
   didn't recognize subsequent options like `--batch`
   - Fixed by putting `--batch` first in the options list

3. Missing gpg-agent configuration: The agent needs `allow-loopback-pinentry`
   in gpg-agent.conf to allow passphrase injection without a TTY
   - Fixed by creating gpg-agent.conf before importing keys

4. Using long-form options: Changed `--sign --detach-sign --output` to `-sbo`
   (the short form used by rpm's default macro)

5. Set GPG_TTY="" to suppress harmless TTY warnings in CI

Tested in Docker without TTY to confirm the fix works before applying
to the workflow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(ci): read RPM build matrix from supported-distributions.json

- Add Fedora 42, Fedora 43, and Rocky Linux 9 (EL) entries to
  supported-distributions.json
- Refactor build-rpm-packages.yml to use prepare-matrix job that reads
  from the JSON file, matching the pattern used by build-debian-packages.yml
- Both workflows now share a single source of truth for supported
  distributions and architectures
- Update notes section to document supported families

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: remove arm64 architecture support for Fedora 42 and 43

Drop arm64 from the supported architectures list for Fedora 42 and 43 distributions as
build times are currently way too long.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(web): use theme CSS variables for cheatsheet dark mode support (#358)

Replace hardcoded light-mode colors with Material for MkDocs CSS
variables so the cheatsheet page correctly adapts to dark mode.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.28 (#359)

* fix(ci): import GPG public key to RPM keyring for signature verification (#361)

* chore(master): release 0.8.29 (#362)

* fix(ci): fix RPM signing for Fedora 43 (RPM 6.x) (#363)

RPM 6.0 introduced breaking changes where custom %__gpg_sign_cmd macro
overrides no longer work due to parametric macros.

This commit adds version-aware signing:
- RPM 4.x (Fedora 42, Rocky 9): Use existing approach with %__gpg_sign_cmd
- RPM 6.x (Fedora 43): Use gpg-preset-passphrase to cache passphrase in
  gpg-agent and use %_openpgp_sign_id macro

Fixes signing failure on Fedora 43 while maintaining compatibility with
older RPM versions.

* chore(master): release 0.8.30 (#364)

* Update lodepng to 20260102 and add a README

* Add to readme where the lexertl source came from

Wrote a bash script to confirm this, but it was HEAD of the other repo
at the time of the original commit.

* Estimate cache size for manifold objects (#6499)

* clang-format: Add AllowShortFunctionsOnASingleLine: InlineOnly (#6530)

* Correctly preferences after OpenSCADApp has finished initializing (#6533)

* Add hidapi README (#6532)

Based on comments to https://github.com/openscad/openscad/issues/6513
add a README.md for hidapi like the other external libraries.

* Add color list window.

* Add support for double-click to insert the color name / color() call.

* Ensure newline after HtmlLink message; Skip messages in ErrorList window.

* Require "xkcd:" prefix to switch to the XKCD color map.

* Suppress log messages of type HtmlLink on command line.

* Handle API change between Qt5 and Qt6 (qreal -> float) in QColor::getHslF().

* fix: used other version from regression

* fix:udpated tests

* fix(ci): fix keygrip extraction for RPM 6.x signing (#366)

The previous grep pattern '^\s*sec' expected whitespace before "sec"
but GPG output starts with just "sec" (no leading whitespace). This
caused the keygrip extraction to fail on Fedora 43 (RPM 6.x).

Fixed by using awk to find lines starting with "sec" and then
extracting the keygrip from the following line containing "Keygrip".

Also added debug output to show the full GPG key details for easier
troubleshooting.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* chore(master): release 0.8.31 (#367)

* chore: merge 91 commits from openscad/master (#277)

* add correct StartupWMClass to set the correct icon on Gnome

I believe this is the XDG way to do this, but I am not sure.

* Check for save/discard/cancel on quit.  Fixes #3971. (#6311)

* Bump CGAL to 6.1.0 (#6318)

* Make vector swizzle experimental (#6307)

* Don't log to stderr (#6323)

* Fully separate zoom levels for each tab.  Fixes #4742. (#6320)

* Fix/cleanup compiler warnings openscad/openscad#6245 (#6300)

* Remove obsolete HTML example converter (#6328)

* reformat python tests scripts in pep8 (#6330)

* Bump the github-actions group with 3 updates

Bumps the github-actions group with 3 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [actions/stale](https://github.com/actions/stale).

Updates `actions/upload-artifact` from 4 to 5
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

Updates `github/codeql-action` from 3 to 4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

Updates `actions/stale` from 9 to 10
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Check print service name only if remote print service is selected.

This fixes the issue that the print dialog is always shown if the local
application is selected for printing.

* Don't crash MouseSelector, debug GL init (#6334)

* beautify (#6342)

* Set QSCINTILLA_DLL for MINGW (and not MXECROSS). (#6343)

* Warn on offset() with both r and delta specified (#6348)

* Fix QFile::Open nodiscard warning with Qt6 6.10.0. (#6364)

* Update cmdline help to show Manifold as default (#6350)

* Rm unused function.  Fixes #6352. (#6353)

* Add Color module spec (#6339)

* Enables "Ctrl/Cmd-Mouse-wheel zooms text" config option to work  (Preliminary fix for #6368)

* Entirely removed superfluous EditorInterface::wheelEvent method

* Bump Qt to 6.5.7 (#6371)

* CircleCI: Update macOS resources to 26.1 on M4 (#6324)

* Refactor Discretization Special Usage (#6251)

* Tessallation specials $fn$fs$fa now grouped in an object

Instead of fn,fs,fa as three doubles, they are passed as an object
packaged up with the methods on them.

* Repair basic functionality (#6376)

* Fix indentation of CC0 template text. (#6375)

Also clean up line endings.
Add templates/README.md

* Fix a number of non-ASCII file name issues (#6136)

* Try caching MSYS2 packages

Fixes #6381 if it works

* Fix `if` in yaml wrong

* Don't want `if`s anyway

doesn't matter I did them wrong

* Update msys2 gh actions caching

Make one new cache a day from scratch. Leave note why we don't find
the most recent cache. Skip saving since caches are immutable.

* Comment updates + build up cache over a month

* We can read the determined msys2 directory

We're not restoring before the setup, like I originally planned.

* Updating script to output list of pacboy packages to workflow

Easier than my other approach, actually. Ah, whatever.

* Don't change $MSYSTEM in GH Actions

* Oops, I need to specify the bash shell I think

* Use arrays instead of backslash line termination

* Don't fail fast because it fails due to caching a lot

well, until we actually get one

* std::wstring_convert and std::codecvt_utf8_utf16 is going to be depreciated. Use boost::nowide::narrow (#6356)

* Add Unit tests to fix measuring (#6299)

* Starting point of release build CI workflow (#6374)

* Define executors separately
* Added OpenSCAD release build job with create-tarball

* Centralize version string management (#6388)

* Centralize version string management

* Identify build tag in OPENSCAD_VERSION, and correctly extract OPENSCAD_SHORTVERSION

* cimg/python: no 'latest' tag. Use 3.14 (#6399)

* Disable WIP macOS release build (#6398)

* CircleCI Bugfix: Use OPENSCAD_VERSION instead of VERSION (#6400)

* create-tarball: Amend files manually since git-archive-all doesn't consider unversioned files (#6401)

* cd to /tmp/artifacts before creating shasums (#6402)

* Add CMake support + shell helpers for VERSION.txt and COMMIT.txt (#6403)

* Ingest VERSION.txt and COMMIT.txt at cmake time
* Shell helper for establishing version from VERSION.txt and COMMIT.txt

* Snapshot builds now uses the version helpers to unify version management, also deprecated 32-bit windows (#6404)

* Bugfix: binary name on macOS needs correct case (#6409)

* Parametrize macOS build for release vs. snapshot (#6406)

* Parametrize macOS build for release vs. snapshot
* Use establish_version.sh and don't require git
* Always checkout in the same way; with submodules

* Bump actions/checkout from 5 to 6 in the github-actions group (#6418)

Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).

Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Delete discretizer keyword args

PyArg_ParseTupleAndKeywords doesn't allow keyword args it doesn't know
about, so we delete them if found.

Manually testing the fix.

* Fix formatting of "Browser" header (#6433)

Markdown rendered is confused by non-breaking space (`U+A0`) - replaced with a regular space.

* Make find highlighting case-insensitive

* chore: codegen version into manpage (#6448)

* chore: codegen version into manpage

testing process:

- created a VERSION.txt file
- ran the CMake build
- confirm that the output `build/doc/openscad.1` has the same version as
  VERSION.txt

* remove extra comment

* Added job parameters openscad_build_type and openscad_platform (#6449)

* Remove deprecated child() and assign(). (#6428)

* Remove deprecated child() and assign().
Fixes #5557.
Fixes #6425.

* Update releases/next.md:  child(), assign(), object(), textmetrics(), fontmetrics
Remove deprecated child() and assign().
Add object().
Reclassify textmetrics() and fontmetrics() as functions.

* More CircleCI cleanup (#6450)

* Bump manifold to 3.3.2 and onetbb to 2022.3.0 (#6455)

* Added failing test for #6456 (#6459)

* Bumped manifold to fix #6235 (#6467)

* Temporarily restore exact job names used by circleci-download-artifacts.py (#6469)

* CircleCI: Update name of wasm build (#6470)

* CircleCI release builds (#6412)

* Move version info to .c file (#6463)

* chore: bump nix-shell for latest version

* convert to flake (for lockfile)

* use stable release

* remove duplicate lock entry

* Add warning to cmake output that ENABLE_CGAL=OFF is a dev-only flag.

* Fix font sample rendering in font list window.

* Remove obsolete FontListDialog.

* Change console font size via mouse wheel event (fixes #6186).

This is only changing the current console widget, not the value in the
preferences.

* Align naming of release jobs, correct naming of tarball (#6484)

* Bugfix: Forgot to update dependencies on tarball (#6485)

* Bump the github-actions group with 2 updates

Bumps the github-actions group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/cache](https://github.com/actions/cache).

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

Updates `actions/cache` from 4 to 5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix premature assignment expiration warning (#6489)

* CI: enable sccache (#6473)

This avoids doing redundant work by caching compiler results. Because CI
does less work, we can run more redundant CI jobs and avoid the mental
overhead of manually de-duping jobs.

* fix: resurrect LoadShareDesignDialog.h which got lost during merge from OpenSCAD

* fix: return type of findModelObject which got inadvertently changed during merge from OpenSCAD

* fix: version handling which got broken during merge from OpenSCAD

* fix: add missing conv. for editor file path in parseDocument which got lost during OpenSCAD merge

* fix: correct variable names in argument parsing for python_cylinder function after OpenSCAD merge

* fix: revert test result for use-tests-expected which was inadvertently changed during OpenSCAD merge

* fix: update argument parsing for python_cylinder function after OpenSCAD merge

* fix: correct discretizer handling in python_cylinder after OpenSCAD merge

The OpenSCAD merge introduced CurveDiscretizer to centralize fn/fa/fs
parameter handling. However, python_cylinder was creating the discretizer
twice: once before argument parsing (which removes fn/fa/fs from kwargs)
and again when creating the CylinderNode (from already-modified kwargs).

This caused cylinders to ignore fn/fa/fs parameters and render with
incorrect facet counts (e.g., 6 sides instead of specified 12).

Fixed by:
- Removing fn/fa/fs from kwlist (handled by CreateCurveDiscretizer)
- Removing unused fn/fa/fs local variables
- Using single discretizer instance throughout
- Updating format string to match reduced parameter list

Resolves incorrect cylinder rendering in hinge and spline tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: add merge-mode-functions flag to gcovr to fix coverage collection

After the OpenSCAD merge, gcovr encounters functions in AST.h that appear
on multiple lines across different compilation units, causing coverage
merging to fail with:

  GcovrMergeAssertionError: Got function <unknown function> on multiple lines

Adding --merge-mode-functions=merge-use-line-0 tells gcovr to use line 0
as the canonical line for functions when merging, resolving the conflict.

This fixes the Linux CI coverage step failures while maintaining the same
test results (all tests still pass).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Sohler Günther <guenther.sohler@gmail.com>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: multmatrix operates on Vector3d

* fix:removed wrong printf

* added lasercutr lib

* support #2 cuts

* feat: start allow to alter existing solid paramters

* feat(ci): add native Windows packaging workflow (#371)

* feat(ci): add native Windows packaging workflow

Add a new GitHub Actions workflow that builds PythonSCAD natively on
Windows using MSYS2, packages it with CPack (ZIP + NSIS installer),
and runs a sanity test (pythonscad --info) to verify the executable
works before release.

This addresses issue #360 where MXE cross-compiled builds are missing
MinGW runtime DLLs (libgcc_s_seh-1.dll), causing the application to
fail to launch on Windows.

The native MSYS2 build correctly bundles all required DLLs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): use workspace-relative path for artifacts

Changed from /tmp/out/ (MSYS2-only path) to artifacts/ (workspace-relative)
to fix upload-artifact action which runs in native Windows context.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): bundle MSYS2 runtime DLLs in package

Add step to use ldd to find and copy all MSYS2 DLL dependencies
recursively into the staging directory before packaging.

This fixes missing DLL errors:
- libgcc_s_seh-1.dll
- libcairo-2.dll
- libwinpthread-1.dll
- libstdc++-6.dll
And any other transitive dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): rewrite DLL bundling to avoid bash subshell issues

Changed from recursive function to iterative loop to avoid
bash 'local' keyword failures in subshells created by pipes.

Uses process substitution instead of pipes and iterates up to
10 times to find all transitive DLL dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): use CMake's install(RUNTIME_DEPENDENCY_SET) for DLL bundling

Instead of manually bundling DLLs with ldd, use CMake 3.21+'s native
runtime dependency resolution. This ensures CPack packages include all
required MSYS2 runtime DLLs (libgcc_s_seh-1.dll, libstdc++-6.dll, etc.).

The RUNTIME_DEPENDENCY_SET feature automatically:
- Finds all DLL dependencies of the executable
- Excludes Windows system DLLs (api-ms-*, system32, etc.)
- Installs them to the package destination

This is the proper CMake-based solution rather than shell scripting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): add Qt plugins for native MSYS2 Windows builds

Qt applications require platform plugins (qwindows.dll) to start.
Also install style, imageformat, and iconengine plugins for full
functionality.

Plugins installed:
- platforms/qwindows.dll (mandatory)
- styles/*.dll (native Windows look)
- imageformats/*.dll (image loading)
- iconengines/*.dll (SVG icons)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): add qt.conf for Qt plugin discovery on Windows

Qt needs a qt.conf file to know where to find plugins when they're
not in the default 'plugins/' subdirectory. This file tells Qt that
the plugin directories (platforms/, imageformats/, etc.) are in the
same directory as the executable.

Contents of qt.conf:
[Paths]
Plugins = .

This should fix the missing toolbar icons issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): bundle Qt SVG runtime DLL

* fix(windows): use MSYS2 Python stdlib instead of embed package

For native MSYS2 Windows builds, the executable is linked against
MSYS2's libpython3.13.dll. The RUNTIME_DEPENDENCY_SET copies this
DLL to the install directory, but the Python standard library was
being installed from python.org's embed package, which has an
incompatible structure (python313.dll + python313.zip vs
libpython3.13.dll + lib/python3.13/).

This mismatch caused Python initialization to fail with:
"Could not find platform independent libraries <prefix>"
"ERROR: Python 3.13.11 not found. Is it installed?"

Fix by installing the MSYS2 Python standard library from the system
installation, which matches the ABI of the linked libpython DLL.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ci: disable MXE cross compiled Windows builds

They are currently broken and are being replaced by native MSYS2 builds.

* feat(windows): improve NSIS installer with start menu, launch option, and multiuser support

- Fix NSIS configuration to work for both MXE cross-compile and native MSYS2 builds
- Add explicit start menu shortcut creation in PythonSCAD folder and root
- Register .scad file association with pythonscad.exe instead of openscad.exe
- Add "Run PythonSCAD" checkbox on installer finish page
- Add multiuser.nsh for per-user installation support infrastructure
- Update URLs and icon references from OpenSCAD to PythonSCAD
- Properly clean up shortcuts and file associations on uninstall

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): convert NSIS include paths to native Windows format

NSIS requires backslashes for include paths on Windows. Using
file(TO_NATIVE_PATH) to convert CMake paths to native format.

Also removes unused multiuser.nsh include that had unmet dependencies.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(windows): use relative paths for NSIS includes

CPack runs NSIS from build/_CPack_Packages/<system>/NSIS/, so use
relative paths (../../../) to reference files in the build root.

Also copies mingw-file-association.nsh to build dir for consistency.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ci(windows): remove experimental suffix from native build artifacts

The MSYS2-based native Windows build is now the primary Windows build,
so artifact filenames no longer need the msys2-experimental suffix.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: disable pre-major version bump settings in release-please

Configure release-please to use standard semver bumping by setting
bump-minor-pre-major and bump-patch-for-minor-pre-major to false

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(python): extend add_parameter() with customizer options (#368)

* fix: memory bugs in add_parameter Python string handling

PyUnicode_AsEncodedString creates temporary bytes objects.
PyBytes_AS_STRING returns pointer to internal buffer.
Without proper cleanup, this creates dangling pointers causing segfaults.
Fixed by copying to std::string before calling Py_DECREF().
Affects: default string values, options lists, and dict labels.

* fix: check event type before casting to QKeyEvent

Static cast of QEvent to QKeyEvent without checking event type causes
heap-use-after-free when event is not actually a key event.
Fixed by checking event->type() == QEvent::KeyPress before cast.

* fix: heap-use-after-free in add_parameter annotations

Assignment::addAnnotations stores raw pointers to Annotation objects.
Stack-allocated AnnotationList was being destroyed when function returned,
leaving dangling pointers.
Fixed by heap-allocating AnnotationList with new, matching the pattern
used in CommentParser.cc.

* fix(gui): fix memory bugs in customizer parameter handling

- ParameterWidget: Fix use-after-free when slider is dragged during F5 re-render
  - Use deleteLater() for widget cleanup to handle pending Qt events
  - Keep old parameters alive until widgets are fully deleted
  - Process events after scheduling deletion to handle in-flight events

- MainWindow: Fix crash on window close due to RubberBandManager access
  - Add isBeingDestroyed flag to guard eventFilter during destruction
  - Set flag at start of destructor before any cleanup

* test: add expected output for add_parameter extended tests

* fix(ci): import GPG public key to RPM keyring for signature verification (#361)

* Add color list window.

* Add support for double-click to insert the color name / color() call.

* Suppress log messages of type HtmlLink on command line.

* fix: used other version from regression

* fix:udpated tests

* path points is not writable

* sync to master

* fix; sync regression

* sycn with master

* fix: convert face to polyghons when not already

* fix: improve lasercut library

* fix: face/face cuts working now

* fix: create also lasercutmodels from whole shapes instead from faces

* fix: returning the final result rather than showing it

* make sure, that face2face joints get better slices

* feat: implemented export GCODE

also  pylaser.py got a maximum result size parameter

* fix: add missed file

* feat: scaffolding gcode export options

* feat: added Gcode file format option menu

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Guenther Sohler <guenther.sohler@photeon.com>
Co-authored-by: nomike <nomike@nomike.com>
Co-authored-by: Nicolas Vaagen <nicolasvaagen@gmail.com>
Co-authored-by: Torsten Paul <Torsten.Paul@gmx.de>
Co-authored-by: Jordan Brown <31949071+jordanbrown0@users.noreply.github.com>
Co-authored-by: Marius Kintel <marius@kintel.net>
Co-authored-by: Ma-XX-oN <Ma-XX-oN@users.noreply.github.com>
Co-authored-by: AaronVerDow <2530548+AaronVerDow@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: coryrc <github@corycross.org>
Co-authored-by: Alex Kent Hajnal <software@alephnull.net>
Co-authored-by: Nicolas Vaagen <37600758+nicvagn@users.noreply.github.com>
Co-authored-by: Glebs Ivanovskis <gl.ivanovsky@gmail.com>
Co-authored-by: pakhare <prasadkhare261@gmail.com>
Co-authored-by: Flaviu Tamas <me@flaviutamas.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: pythonscad-gh-bot <pythonscad-github-bot@info.nomike.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build Failure: Error code in Triangulate() reads uninitialized memory

2 participants