Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
102 changes: 102 additions & 0 deletions .github/workflows/installer-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Installer Smoke

on:
push:
branches: [main]
paths:
- "install-macos.sh"
- "install-windows.ps1"
- "pyproject.toml"
- "setup.py"
- "README.md"
- "src/**"
- ".github/workflows/installer-smoke.yml"
pull_request:
paths:
- "install-macos.sh"
- "install-windows.ps1"
- "pyproject.toml"
- "setup.py"
- "README.md"
- "src/**"
- ".github/workflows/installer-smoke.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
macos-installer:
name: macOS installer
runs-on: macos-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Run installer
env:
SLICER_URI_BRIDGE_PROJECT_SPEC: ${{ github.workspace }}
HOME: ${{ runner.temp }}/home
XDG_CONFIG_HOME: ${{ runner.temp }}/xdg-config
URI_BRIDGE_MACOS_APP_DIR: ${{ runner.temp }}/Applications
run: |
mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$URI_BRIDGE_MACOS_APP_DIR"
bash install-macos.sh

- name: Verify installation
env:
HOME: ${{ runner.temp }}/home
XDG_CONFIG_HOME: ${{ runner.temp }}/xdg-config
URI_BRIDGE_MACOS_APP_DIR: ${{ runner.temp }}/Applications
run: |
export PATH="$HOME/.local/bin:$PATH"
command -v slicer-uri-bridge
slicer-uri-bridge --version
slicer-uri-bridge status
test -f "$URI_BRIDGE_MACOS_APP_DIR/SlicerURIBridge.app/Contents/Info.plist"

windows-installer:
name: Windows installer
runs-on: windows-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Run installer
shell: powershell
env:
SLICER_URI_BRIDGE_PROJECT_SPEC: ${{ github.workspace }}
LOCALAPPDATA: ${{ runner.temp }}\LocalAppData
APPDATA: ${{ runner.temp }}\AppData
run: .\install-windows.ps1

- name: Verify installation
shell: powershell
env:
LOCALAPPDATA: ${{ runner.temp }}\LocalAppData
APPDATA: ${{ runner.temp }}\AppData
run: |
$bridge = Join-Path $env:LOCALAPPDATA 'slicer-uri-bridge\venv\Scripts\slicer-uri-bridge.exe'
if (-not (Test-Path -LiteralPath $bridge)) {
throw "Bridge command was not installed: $bridge"
}

& $bridge --version
& $bridge status

$command = (Get-Item -LiteralPath 'HKCU:\Software\Classes\bambustudioopen\shell\open\command').GetValue('')
if ($command -notmatch 'slicer_uri_bridge\.handler') {
throw "Unexpected bambustudioopen handler command: $command"
}
40 changes: 40 additions & 0 deletions .github/workflows/macos-applescript.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: macOS AppleScript

on:
push:
branches: [main]
paths:
- "src/slicer_uri_bridge/manager.py"
- "src/slicer_uri_bridge/resources/macos-launcher.applescript"
- "tests/test_manager_macos.py"
- ".github/workflows/macos-applescript.yml"
Comment thread
mbv06 marked this conversation as resolved.
pull_request:
paths:
- "src/slicer_uri_bridge/manager.py"
- "src/slicer_uri_bridge/resources/macos-launcher.applescript"
- "tests/test_manager_macos.py"
- ".github/workflows/macos-applescript.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
osacompile:
name: osacompile integration
runs-on: macos-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install package
run: python -m pip install -e .

- name: Run macOS AppleScript tests
run: python -m unittest tests.test_manager_macos
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project are documented here.

## v0.1.3 - 2026-06-03

### Added

- Add `allow_local_resolved_hosts` as a security setting for cases where you intentionally want to allow downloads from trusted hosts that resolve to local addresses.

## v0.1.2 - 2026-05-20

### Added
Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![Platform: Windows | macOS | Linux](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey.svg)]()
[![Tests](https://img.shields.io/github/actions/workflow/status/mbv06/slicer-uri-bridge/ci.yml?label=tests)](https://github.com/mbv06/slicer-uri-bridge/actions/workflows/ci.yml)

[Installation](#installation) · [Security Model](#security-model) · [Changelog](CHANGELOG.md)

Expand Down Expand Up @@ -58,7 +59,7 @@ First, install Python 3.11 or newer on the target system:
Then install the package from GitHub:

```bash
python -m pip install https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip
python -m pip install --upgrade https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip
```

Installation only installs the CLI and Python package. It does not register URI handlers automatically.
Expand Down Expand Up @@ -92,22 +93,30 @@ slicer-uri-bridge status

## Uninstall

Unregister all URI handlers managed by this package:
First, unregister all URI handlers managed by this package:

```bash
slicer-uri-bridge unregister --auto
```

You can also delete the config and log files manually. Find their location with:
Then remove the installed package or app files for your installation type.

Manual install:

```bash
slicer-uri-bridge config-path
python -m pip uninstall slicer-uri-bridge
```

Automatic Windows install:

```powershell
Remove-Item -LiteralPath (Join-Path $env:LOCALAPPDATA 'slicer-uri-bridge') -Recurse -Force
```

Then remove the package:
Automatic macOS install:

```bash
pip uninstall slicer-uri-bridge
rm -rf "$HOME/.local/share/slicer-uri-bridge" "$HOME/Applications/SlicerURIBridge.app"; rm -f "$HOME/.local/bin/slicer-uri-bridge"
```

## How It Works
Expand All @@ -133,7 +142,7 @@ The bridge validates downloads before opening them:

* only HTTPS URLs are allowed unless `allow_plain_http = true`
* URLs with embedded credentials are rejected
* resolved hosts must not point to local/private/reserved addresses
* resolved hosts must not point to local/private/reserved addresses unless `allow_local_resolved_hosts = true`
* redirect targets are revalidated
* downloaded files must use an allowed model extension
* empty files and obvious executable formats are refused
Expand Down
2 changes: 1 addition & 1 deletion install-macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ set -euo pipefail
#
# To update later, run this installer again.

PROJECT_SPEC="https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip"
PROJECT_SPEC="${SLICER_URI_BRIDGE_PROJECT_SPEC:-https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip}"

APP_HOME="${HOME}/.local/share/slicer-uri-bridge"
VENV="${APP_HOME}/venv"
Expand Down
3 changes: 2 additions & 1 deletion install-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$ProjectSpec = 'https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip'
$DefaultProjectSpec = 'https://github.com/mbv06/slicer-uri-bridge/archive/refs/heads/main.zip'
$ProjectSpec = if ($env:SLICER_URI_BRIDGE_PROJECT_SPEC) { $env:SLICER_URI_BRIDGE_PROJECT_SPEC } else { $DefaultProjectSpec }
$AppHome = Join-Path $env:LOCALAPPDATA 'slicer-uri-bridge'
$VenvDir = Join-Path $AppHome 'venv'
$ScriptsDir = Join-Path $VenvDir 'Scripts'
Expand Down
42 changes: 37 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"

[project]
name = "slicer-uri-bridge"
version = "0.1.2"
description = "Register slicer URI handlers and bridge slicer links to Bambu Studio."
version = "0.1.3"
description = "Register slicer URI handlers and bridge slicer links to your favorite slicer app."
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
Expand All @@ -15,9 +15,6 @@ authors = [
keywords = ["uri-handler", "slicer", "bambu-studio", "cura", "prusaslicer", "orcaslicer"]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
"Operating System :: POSIX :: Linux",
Expand All @@ -31,3 +28,38 @@ where = ["src"]

[tool.setuptools.package-data]
slicer_uri_bridge = ["resources/default_config.toml", "resources/macos-launcher.applescript"]

[tool.ruff]
line-length = 120

[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"SIM", # flake8-simplify
]
ignore = [
"SIM102", # nested if statements
"SIM108", # use ternary operator
"SIM117", # nested with statements
]


[project.optional-dependencies]
dev = [
"mypy>=2.1.0",
"ruff>=0.15.15",
]

[tool.mypy]
python_version = "3.11"
check_untyped_defs = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
files = ["src", "tests"]
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from setuptools import setup


setup()
8 changes: 5 additions & 3 deletions src/slicer_uri_bridge/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import subprocess
import sys
import tomllib
from importlib.metadata import PackageNotFoundError, version as package_version
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as package_version
from pathlib import Path

from .config import config_matches_default, init_user_config, user_config_path
from .manager import main as manager_main


PACKAGE_NAME = "slicer-uri-bridge"
YES_VALUES = {"y", "yes"}
NO_VALUES = {"n", "no"}
Expand Down Expand Up @@ -124,7 +124,9 @@ def warn_if_bambu_target_missing(config_path: Path) -> None:
eprint(f"Warning: Bambu Studio path from config was not found: {configured}")
eprint(f"Edit {config_path} and update [bambu_studio].{key}.")
if key != "windows":
eprint("Fallback: if this path stays invalid, the bridge will try to open models with your default application.")
eprint(
"Fallback: if this path stays invalid, the bridge will try to open models with your default application."
)


def interactive_onboarding() -> int:
Expand Down
8 changes: 2 additions & 6 deletions src/slicer_uri_bridge/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import os
import sys
from collections.abc import Mapping
from importlib import resources
from pathlib import Path
from typing import Mapping

CONFIG_DIR_NAME = "slicer-uri-bridge"
CONFIG_FILE_NAME = "config.toml"
Expand Down Expand Up @@ -51,11 +51,7 @@ def user_log_path(


def default_config_text() -> str:
return (
resources.files("slicer_uri_bridge")
.joinpath("resources", "default_config.toml")
.read_text(encoding="utf-8")
)
return resources.files("slicer_uri_bridge").joinpath("resources", "default_config.toml").read_text(encoding="utf-8")


def init_user_config(*, force: bool = False) -> tuple[Path, bool]:
Expand Down
Loading
Loading