Add Snap Store packaging

Publish OrcaSlicer to the Snap Store (classic confinement) on channels
matching the release tiers: stable / candidate / beta / edge. The Linux
build (both arches) repackages the AppImage AppDir into a snap; nightly
builds go to edge, and tagged releases publish to the matching channel.

Stacked on the Linux ARM64 AppImage change.
This commit is contained in:
SoftFever
2026-06-19 14:14:20 +08:00
parent 0a6a42ecb5
commit 617df79260
7 changed files with 275 additions and 3 deletions

69
snap/README.md Normal file
View File

@@ -0,0 +1,69 @@
# Snap packaging
OrcaSlicer ships a [snap](https://snapcraft.io/orcaslicer) built by **repackaging the AppImage
build output** (`build/package`) — the compiled binary, bundled private libraries and resources
are reused as-is.
The snap uses **classic confinement**: like the AppImage, it runs in the host namespace and
resolves the desktop stack (GTK / WebKitGTK / GStreamer / GLU) from the host. Classic is required
for full hardware/filesystem access — notably the **3D mouse** (3Dconnexion SpaceMouse via the host
`spacenavd` socket at `/run/spnav.sock`), which no strict-confinement interface can reach.
- `snapcraft.yaml` — the manifest (`plugin: dump` of `build/package`, classic confinement).
- `local/launcher` — the runtime wrapper (sets `LD_LIBRARY_PATH`, `LC_NUMERIC=C`, `SPNAV_SOCKET`).
## CI flow
| Trigger | Where | Snap action |
|---|---|---|
| push to `main` | `build_orca.yml` (amd64 + aarch64) | build both arches + publish to **edge** |
| PR | (none) | snap is not built on PRs (the AppImage build still runs) |
| release (manual) | `publish_release.yml` | push the build run's `.snap` artifacts to the channel below |
The snap is **Store-only** — unlike the AppImage/Flatpak it is *not* attached to GitHub releases
(a downloaded `.snap` is useless without `snap install`). Both arches go through the same
`build_orca.yml` Linux build, reusing the AppDir (`build/package`) it just produced for the snap.
`build_all.yml`'s `build_linux` job matrixes over amd64 and aarch64 on every event; aarch64 always
uses a GitHub-hosted arm runner (amd64 honors the self-hosted runner when configured).
## Channel mapping (tag suffix → Snap Store channel)
| Tag | Channel |
|---|---|
| `vX.Y.Z` (release) | `stable` |
| `-rc` / `-rcN` | `candidate` |
| `-beta` / `-alpha` | `beta` |
| nightly (push to `main`) | `edge` |
## One-time maintainer setup
1. `snapcraft login` then `snapcraft register orcaslicer` (the name must be free; if not, change
`name:` in `snapcraft.yaml` and the asset names in the workflows).
2. **Request classic confinement** for `orcaslicer` on the [snapcraft forum](https://forum.snapcraft.io/)
(Store Requests category). Justification: a desktop slicer needs the host `spacenavd` socket
(`/run/spnav.sock`) for 3D mice plus arbitrary user/network filesystem paths that strict
interfaces cannot provide. **Until this is granted, uploads to every channel are held for manual
review**, so the automated publish below will not go live yet.
3. Export CI credentials:
`snapcraft export-login --snaps orcaslicer --channels stable,candidate,beta,edge --acls package_push,package_release exported.txt`
4. Add the file contents as the GitHub Actions secret **`SNAPCRAFT_STORE_CREDENTIALS`**.
## Notes
- Cross-distro library behavior matches the AppImage (relies on host libs): the host must provide
the GTK/WebKitGTK/GStreamer/GLU/OpenGL stack (the same packages the AppImage documents).
- The `classic`/`library` snapcraft linters are silenced in `snapcraft.yaml` because they assume a
self-contained snap and would flag every host-resolved library. Runtime smoke tests are the real
check.
## Local build / test
```shell
sudo snap install snapcraft --classic
sudo snap install lxd && sudo lxd init --auto
./build_linux.sh -dsir -l -L # produces build/package
snapcraft # -> orcaslicer_<ver>_amd64.snap
sudo snap install --dangerous --classic ./orcaslicer_*.snap
snap run orcaslicer
# Smoke test: load an STL, slice, USB/serial printer, network share, and a SpaceMouse if available.
```

24
snap/local/launcher Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
# Snap launch wrapper for OrcaSlicer (classic confinement).
#
# Classic confinement runs in the host namespace and uses host libraries, just
# like the AppImage. This wrapper sets up only the load-bearing environment and
# execs the binary. It deliberately does NOT reuse the AppImage's orca-slicer-env
# (which probes the host for WebKitGTK and exit 1's if absent, and sets LC_ALL=C
# which would break translations) nor the Flatpak entrypoint (which assumes /app).
# Keep the C numeric locale (decimal separator) without overriding the UI
# language, matching the Flatpak entrypoint. Otherwise some locales corrupt
# G-code coordinates.
export LC_NUMERIC=C
# Resolve OrcaSlicer's bundled private libraries first (OpenSSL 1.1.x, CURL,
# etc.). The desktop stack (GTK / WebKitGTK / GStreamer / GLU) resolves from the
# host, exactly as in the AppImage.
export LD_LIBRARY_PATH="$SNAP/lib/orca-runtime:$SNAP/bin${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
# 3D mouse (3Dconnexion SpaceMouse via the host spacenavd daemon). Classic
# confinement can reach the host socket; mirrors the Flatpak's SPNAV_SOCKET env.
export SPNAV_SOCKET=/run/spnav.sock
exec "$SNAP/bin/orca-slicer" "$@"

89
snap/snapcraft.yaml Normal file
View File

@@ -0,0 +1,89 @@
name: orcaslicer
# Snap name must be lowercase with no dots; it has to be registered in the Snap
# Store before the first upload (`snapcraft register orcaslicer`).
base: core24
adopt-info: metadata # version (from version.inc) + summary/description (from the metainfo)
grade: stable
# Classic confinement: like the AppImage, OrcaSlicer runs in the host namespace
# and uses host libraries. This is required for full hardware/filesystem access —
# notably the 3D mouse (SpaceMouse via the host spacenavd socket at
# /run/spnav.sock), which no strict-confinement interface can reach.
# NOTE: classic confinement must be granted once by the Snap Store (forum request)
# before uploads to any channel are accepted. See snap/README.md.
confinement: classic
license: AGPL-3.0-only
icon: resources/images/OrcaSlicer_192px.png
compression: lzo # faster first-launch decompression for a large (~1 GB) snap
# As with the AppImage, the desktop stack (GTK / WebKitGTK / GStreamer / GLU) is
# resolved from the host, not bundled. The classic/library linters assume a
# self-contained snap and would flag every host-resolved library, so silence them;
# runtime smoke tests are the real check (see snap/README.md).
lint:
ignore:
- classic
- library
platforms:
amd64:
arm64:
apps:
orcaslicer:
command: bin/launcher # snap/local/launcher (NOT the AppImage orca-slicer-env)
common-id: com.orcaslicer.OrcaSlicer # ties the app to the AppStream component id
desktop: usr/share/applications/com.orcaslicer.OrcaSlicer.desktop
parts:
# The compiled application, reused verbatim from the AppImage build output
# (build/package = bin/orca-slicer + lib/orca-runtime + resources + share).
# Dumped at the snap root so the binary finds its data at bin/../resources,
# exactly as it does inside the AppImage. The host supplies the desktop stack
# (GTK / WebKitGTK / GStreamer / GLU) just as it does for the AppImage.
orcaslicer:
plugin: dump
source: build/package
# Desktop integration + Store metadata. Reuses the existing desktop file,
# metainfo and icon (no forks). Mirrors the Flatpak post-install step.
metadata:
plugin: nil
parse-info: [usr/share/metainfo/com.orcaslicer.OrcaSlicer.metainfo.xml]
build-packages: [desktop-file-utils]
override-build: |
set -e
# AppStream metainfo (drives the Store listing: summary, description, screenshots)
install -Dm644 "$CRAFT_PROJECT_DIR/scripts/flatpak/com.orcaslicer.OrcaSlicer.metainfo.xml" \
"$CRAFT_PART_INSTALL/usr/share/metainfo/com.orcaslicer.OrcaSlicer.metainfo.xml"
# Desktop entry: reuse the canonical one, point Exec/Icon at the snap.
install -Dm644 "$CRAFT_PROJECT_DIR/src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop" \
"$CRAFT_PART_INSTALL/usr/share/applications/com.orcaslicer.OrcaSlicer.desktop"
desktop-file-edit \
--set-key=Exec --set-value="orcaslicer %U" \
--set-icon="orcaslicer" \
"$CRAFT_PART_INSTALL/usr/share/applications/com.orcaslicer.OrcaSlicer.desktop"
# Icons (name must match the desktop Icon= key and the snap name).
install -Dm644 "$CRAFT_PROJECT_DIR/resources/images/OrcaSlicer.svg" \
"$CRAFT_PART_INSTALL/usr/share/icons/hicolor/scalable/apps/orcaslicer.svg"
install -Dm644 "$CRAFT_PROJECT_DIR/resources/images/OrcaSlicer_192px.png" \
"$CRAFT_PART_INSTALL/usr/share/icons/hicolor/192x192/apps/orcaslicer.png"
# Ship the bundled fonts in a fontconfig-scanned directory so Pango knows
# them before initialization (avoids the AddPrivateFont/ensure_faces crash
# the Flatpak guards against).
install -d "$CRAFT_PART_INSTALL/usr/share/fonts/OrcaSlicer"
install -m644 "$CRAFT_PROJECT_DIR"/resources/fonts/*.ttf \
"$CRAFT_PART_INSTALL/usr/share/fonts/OrcaSlicer/"
# Version straight from version.inc (single source of truth, same as AppImage/Flatpak).
ver=$(grep 'set(SoftFever_VERSION' "$CRAFT_PROJECT_DIR/version.inc" | cut -d '"' -f2)
craftctl set version="$ver"
# Minimal snap launch wrapper (replaces the AppImage orca-slicer-env / Flatpak entrypoint).
launcher:
plugin: dump
source: snap/local
organize:
launcher: bin/launcher