Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fca0394
first commit, app opening!
pgilfernandez Nov 21, 2025
59eda65
app icon
pgilfernandez Nov 23, 2025
42c6ab0
importing and 2D view working
pgilfernandez Nov 23, 2025
2b30075
update macos script
pgilfernandez Dec 3, 2025
3349212
Use CairoSVG fallback for SVG rendering on macOS
pgilfernandez Dec 3, 2025
c4e5d45
SVG color preview working
pgilfernandez Dec 4, 2025
394eea3
get vector paths for all objects inside SVG file
pgilfernandez Dec 4, 2025
18a91ee
trace all objects (paths) independently even if they are overlapped
pgilfernandez Dec 4, 2025
7f909a8
Fix 3D preview orientation
pgilfernandez Dec 8, 2025
f969a5b
remove SVG rendered pictures for debugging, they are not needed anymore
pgilfernandez Dec 4, 2025
89049c4
update version
pgilfernandez Dec 4, 2025
e1a9c70
update version
pgilfernandez Dec 8, 2025
f0121ef
add GitHub macOS workflows
pgilfernandez Dec 8, 2025
b14ea13
update macos workflows
pgilfernandez Dec 9, 2025
86816ed
update macOS workflow
pgilfernandez Jan 20, 2026
6320acc
update macOS branch to 0.28.4
pgilfernandez Feb 2, 2026
595ee7d
update and fixes for version 1.0.1
pgilfernandez Feb 2, 2026
cec457c
add missing dependencies in the bundle
pgilfernandez Feb 3, 2026
623ccb3
fix calculating cutting paths
pgilfernandez Feb 3, 2026
ea6e1af
fix preview paths
pgilfernandez Feb 3, 2026
9f9dd12
improve layer preview
pgilfernandez Feb 4, 2026
33a155e
tweak "preview gcode"
pgilfernandez Feb 4, 2026
ab24aa4
use thread-based workpiece generation in bundle
pgilfernandez Feb 4, 2026
b4221e1
various macos bundle fixes
pgilfernandez Feb 4, 2026
2dd0f87
fix 3D view
pgilfernandez Feb 4, 2026
ebe5b80
enable OpenGL on macOS
pgilfernandez Feb 4, 2026
066b550
fix bundle dependencies packaging
pgilfernandez Feb 5, 2026
4ec26f5
add new dependencies to macos script
pgilfernandez Feb 5, 2026
5aec2a3
use CMD as primary modifier on macOS
pgilfernandez Feb 5, 2026
b8b56d5
tweak mac_build.sh script
pgilfernandez Feb 5, 2026
f5ed4c0
add DMG packaging
pgilfernandez Feb 5, 2026
f04c5f9
fix DMG packaging title
pgilfernandez Feb 5, 2026
f48f5e2
clean .whl and .tar.gz after .app creation
pgilfernandez Feb 5, 2026
0adb9fc
update GitHub macOS workflows
pgilfernandez Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions .github/workflows/build-macos-arm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Build macOS App (Apple Silicon)

on:
push:
branches:
- "**"
tags:
- "*"
pull_request:
branches:
- "**"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build macOS Bundle (Apple Silicon)
runs-on: macos-14
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Set Version
id: set-version
shell: bash
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
VERSION=${{ github.ref_name }}
elif git describe --tags >/dev/null 2>&1; then
VERSION=$(git describe --tags)
else
VERSION="v0.0.0-$(git rev-parse --short HEAD)"
fi
if [ -z "$VERSION" ]; then
echo "Error: No git version number found!"
exit 1
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: requirements.txt

- name: Install Pixi
uses: prefix-dev/setup-pixi@v0.8.1
with:
pixi-version: v0.48.2
cache: true
cache-key: ${{ github.sha }}-macos-arm

- name: Install macOS dependencies
run: bash scripts/mac_setup.sh --install

- name: Lint
run: pixi run lint

- name: Test
run: pixi run test

- name: Build macOS artifacts
run: |
printf "4\n" | bash scripts/mac_build.sh --version "${{ env.VERSION }}"

- name: Package .app bundle
run: |
APP_PATH="dist/Rayforge.app"
if [ ! -d "$APP_PATH" ]; then
echo "App bundle not found at $APP_PATH"
exit 1
fi
ZIP_NAME="rayforge-${{ steps.set-version.outputs.version }}-macos-arm-app.zip"
ditto -c -k --keepParent "$APP_PATH" "$ZIP_NAME"

- name: Rename DMG
run: |
DMG_PATH="dist/Rayforge_${{ env.VERSION }}.dmg"
if [ ! -f "$DMG_PATH" ]; then
echo "DMG not found at $DMG_PATH"
exit 1
fi
OUT_DMG="rayforge-${{ steps.set-version.outputs.version }}-macos-arm.dmg"
mv "$DMG_PATH" "$OUT_DMG"

- name: Upload macOS bundle artifact
uses: actions/upload-artifact@v4
with:
name: rayforge-${{ steps.set-version.outputs.version }}-macos-arm-app.zip
path: rayforge-${{ steps.set-version.outputs.version }}-macos-arm-app.zip
compression-level: 9

- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: rayforge-${{ steps.set-version.outputs.version }}-macos-arm-dmg
path: rayforge-${{ steps.set-version.outputs.version }}-macos-arm.dmg

release:
name: Create GitHub Release (Apple Silicon)
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'barebaric/rayforge'
permissions:
contents: write
steps:
- name: Download macOS app artifact
uses: actions/download-artifact@v4
with:
name: rayforge-${{ needs.build.outputs.version }}-macos-arm-app.zip

- name: Download DMG artifact
uses: actions/download-artifact@v4
with:
name: rayforge-${{ needs.build.outputs.version }}-macos-arm-dmg

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
rayforge-${{ needs.build.outputs.version }}-macos-arm-app.zip
rayforge-${{ needs.build.outputs.version }}-macos-arm.dmg
draft: false
prerelease: false
name: Release ${{ needs.build.outputs.version }}
tag_name: ${{ github.ref_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
139 changes: 139 additions & 0 deletions .github/workflows/build-macos-intel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Build macOS App (Intel)

on:
push:
branches:
- "**"
tags:
- "*"
pull_request:
branches:
- "**"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build macOS Bundle (Intel)
runs-on: macos-13
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Set Version
id: set-version
shell: bash
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
VERSION=${{ github.ref_name }}
elif git describe --tags >/dev/null 2>&1; then
VERSION=$(git describe --tags)
else
VERSION="v0.0.0-$(git rev-parse --short HEAD)"
fi
if [ -z "$VERSION" ]; then
echo "Error: No git version number found!"
exit 1
fi
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: requirements.txt

- name: Install Pixi
uses: prefix-dev/setup-pixi@v0.8.1
with:
pixi-version: v0.48.2
cache: true
cache-key: ${{ github.sha }}-macos-intel

- name: Install macOS dependencies
run: bash scripts/mac_setup.sh --install

- name: Lint
run: pixi run lint

- name: Test
run: pixi run test

- name: Build macOS artifacts
run: |
printf "4\n" | bash scripts/mac_build.sh --version "${{ env.VERSION }}"

- name: Package .app bundle
run: |
APP_PATH="dist/Rayforge.app"
if [ ! -d "$APP_PATH" ]; then
echo "App bundle not found at $APP_PATH"
exit 1
fi
ZIP_NAME="rayforge-${{ steps.set-version.outputs.version }}-macos-intel-app.zip"
ditto -c -k --keepParent "$APP_PATH" "$ZIP_NAME"

- name: Rename DMG
run: |
DMG_PATH="dist/Rayforge_${{ env.VERSION }}.dmg"
if [ ! -f "$DMG_PATH" ]; then
echo "DMG not found at $DMG_PATH"
exit 1
fi
OUT_DMG="rayforge-${{ steps.set-version.outputs.version }}-macos-intel.dmg"
mv "$DMG_PATH" "$OUT_DMG"

- name: Upload macOS bundle artifact
uses: actions/upload-artifact@v4
with:
name: rayforge-${{ steps.set-version.outputs.version }}-macos-intel-app.zip
path: rayforge-${{ steps.set-version.outputs.version }}-macos-intel-app.zip
compression-level: 9

- name: Upload DMG artifact
uses: actions/upload-artifact@v4
with:
name: rayforge-${{ steps.set-version.outputs.version }}-macos-intel-dmg
path: rayforge-${{ steps.set-version.outputs.version }}-macos-intel.dmg

release:
name: Create GitHub Release (Intel)
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'barebaric/rayforge'
permissions:
contents: write
steps:
- name: Download macOS app artifact
uses: actions/download-artifact@v4
with:
name: rayforge-${{ needs.build.outputs.version }}-macos-intel-app.zip

- name: Download DMG artifact
uses: actions/download-artifact@v4
with:
name: rayforge-${{ needs.build.outputs.version }}-macos-intel-dmg

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
rayforge-${{ needs.build.outputs.version }}-macos-intel-app.zip
rayforge-${{ needs.build.outputs.version }}-macos-intel.dmg
draft: false
prerelease: false
name: Release ${{ needs.build.outputs.version }}
tag_name: ${{ github.ref_name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
venv
.venv
.venv-mac
*.swp
*.py[oc]
__pycache__
Expand Down
10 changes: 10 additions & 0 deletions .mac_env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export BREW_PREFIX="/usr/local"
export LIBFFI_PREFIX="/usr/local/opt/libffi"
export VENV_PATH="$PWD/.venv-mac"
export PATH="$VENV_PATH/bin:/usr/local/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/Cellar/ffmpeg/4.3.1_2/bin:/usr/local/bin/python3:/usr/local/Cellar/python@3.11/3.11.2_1/bin/python3:/opt/X11/bin:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/var/folders/qy/j2t2sz113nl3p_l7kc22ybb80000gn/T/.tmpr0xjAy:/usr/local/opt/node@20/bin:/opt/local/ffmpeg42/lib/pkgconfig:/Applications/Inkscape.app/Contents/MacOS:/opt/local/bin:/opt/local/sbin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/Python:/usr/local/bin/python:/usr/local/opt/openexr@2/bin:/usr/local/sbin:/Users/pablo/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/pablo/.vscode/extensions/openai.chatgpt-0.4.46-darwin-x64/bin/macos-x86_64"
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig:/opt/local/lib/pkgconfig:/opt/local/ffmpeg42/lib/pkgconfig:/Applications/Inkscape.app/Contents/MacOS:/usr/local/bin:/opt/local/bin:/opt/local/sbin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/Python:/usr/local/bin/python3:/usr/local/bin/python:/usr/local/opt/openexr@2/bin:/usr/local/sbin:/usr/local/bin:/Users/pablo/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/Cellar/ffmpeg/4.3.1_2/bin:/usr/local/bin/python3:/usr/local/Cellar/python@3.11/3.11.2_1/bin/python3:/opt/X11/bin:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands"
export GI_TYPELIB_PATH="/usr/local/lib/girepository-1.0:"
export DYLD_FALLBACK_LIBRARY_PATH="/usr/local/lib:"
if ! echo "/opt/local/lib/pkgconfig:/opt/local/ffmpeg42/lib/pkgconfig:/Applications/Inkscape.app/Contents/MacOS:/usr/local/bin:/opt/local/bin:/opt/local/sbin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/Python:/usr/local/bin/python3:/usr/local/bin/python:/usr/local/opt/openexr@2/bin:/usr/local/sbin:/usr/local/bin:/Users/pablo/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/Cellar/ffmpeg/4.3.1_2/bin:/usr/local/bin/python3:/usr/local/Cellar/python@3.11/3.11.2_1/bin/python3:/opt/X11/bin:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands" | tr ':' '\n' | grep -qx "/usr/local/lib/pkgconfig"; then
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/opt/local/lib/pkgconfig:/opt/local/ffmpeg42/lib/pkgconfig:/Applications/Inkscape.app/Contents/MacOS:/usr/local/bin:/opt/local/bin:/opt/local/sbin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/Python:/usr/local/bin/python3:/usr/local/bin/python:/usr/local/opt/openexr@2/bin:/usr/local/sbin:/usr/local/bin:/Users/pablo/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/Cellar/ffmpeg/4.3.1_2/bin:/usr/local/bin/python3:/usr/local/Cellar/python@3.11/3.11.2_1/bin/python3:/opt/X11/bin:/Library/Apple/usr/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands"
fi
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ For detailed information about developing for Rayforge, including setup instruct
testing, and contribution guidelines, please see the
[Developer Documentation](https://rayforge.org/docs/latest/developer/getting-started/).

## macOS build notes

macOS builds rely on Homebrew because libadwaita is not available from the
conda-forge stack used by the Linux tooling. To prepare a build:

1. Run `scripts/mac_setup.sh --install` to install the native GTK stack
(gtk4, libadwaita, libvips, etc.) and write `.mac_env`.
2. Source the environment with `source .mac_env`.
3. Build the wheel with `scripts/mac_build.sh`. Pass `--bundle` to also produce
a PyInstaller `.app` directory that uses the Homebrew libraries on the host.

## License

This project is licensed under the **MIT License**. See the `LICENSE` file for details.
69 changes: 69 additions & 0 deletions Rayforge.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- mode: python ; coding: utf-8 -*-


from PyInstaller.utils.hooks import collect_submodules

hiddenimports = ['gi._gi_cairo', 'cairosvg']
hiddenimports += collect_submodules('rayforge.ui_gtk.canvas2d')
hiddenimports += collect_submodules('rayforge.ui_gtk.canvas2d.elements')
hiddenimports.append('rayforge.ui_gtk.canvas2d.elements.workpiece')

a = Analysis(
['rayforge/app.py'],
pathex=['.'],
binaries=[],
datas=[
('rayforge/version.txt', 'rayforge'),
('rayforge/resources', 'rayforge/resources'),
('rayforge/locale', 'rayforge/locale'),
],
hiddenimports=hiddenimports,
hookspath=['hooks'],
hooksconfig={
'gi': {
'module-versions': {
'Gtk': '4.0',
'Adw': '1',
},
},
},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='Rayforge',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['rayforge/resources/icons/icon.icns'],
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='Rayforge',
)
app = BUNDLE(
coll,
name='Rayforge.app',
icon='rayforge/resources/icons/icon.icns',
bundle_identifier='org.rayforge.rayforge',
)
Loading
Loading