Release under the SolidCraft name: binaries, assets and installers (#383)

* Release under the SolidCraft name: binaries, assets and installers

The desktop binary is now `solidcraft` and the CLI `solidcraft-cli` (the two app packages
are renamed; their folders move with the rest of the rename later). Every release asset is
named solidcraft-<ver>-… / SolidCraft-<ver>-windows-x64.exe, and the installers show the
SolidCraft product name.

Kept so existing installs keep updating (docs/compatibility.md):
- `solvecraft-cli` alias binary in every package, forwarding to solidcraft-cli with a
  deprecation note on stderr (tests/legacy_alias.rs).
- Linux app ID ai.storyteller.solvecraft (Flatpak ID, desktop entry, Wayland app_id).
- MSI UpgradeCode, so the MSI upgrades in place.
- deb Replaces/Conflicts/Provides and rpm Obsoletes/Provides solvecraft.
- An old-named .zsync copy so earlier AppImages find updates; a legacy .flatpakrepo copy.
- .solvecraft file associations; SOLVECRAFT_* fallbacks in the packaging scripts.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

* MSI: register the legacy .solvecraft extension without a duplicate open verb

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-12 16:05:35 -04:00
1 parent f06b76e14f
commit 6090e89b3f
50 files changed
+478 -317

No files matched your search

+6 -4
View File
@@ -3,20 +3,22 @@
# Adapted from PhotoCraft's AppRun (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
#
# On Wayland the compositor resolves a window's dock icon by taking the window's
# app_id (ai.storyteller.solvecraft) and looking up an installed .desktop file.
# app_id (ai.storyteller.solvecraft, kept as a compatibility shim) and looking up an installed .desktop file.
# An AppImage installs nothing, so every AppImage run would otherwise show the
# generic "Wayland" icon. Best-effort desktop integration: on first run
# (and whenever the AppImage's path changes) install the .desktop entry and the
# hicolor icons into $XDG_DATA_HOME, then launch. Every failure here must leave
# the launch untouched. Opt out with SOLVECRAFT_NO_DESKTOP_INTEGRATION=1.
# the launch untouched. Opt out with SOLIDCRAFT_NO_DESKTOP_INTEGRATION=1 (the pre-rename
# SOLVECRAFT_NO_DESKTOP_INTEGRATION still works: compatibility shim, docs/compatibility.md).
set -u
APPDIR="$(dirname "$(readlink -f "$0")")"
# Compatibility shim (docs/compatibility.md): the pre-rename reverse-DNS id.
DESK_ID=ai.storyteller.solvecraft
integrate() {
case "${SOLVECRAFT_NO_DESKTOP_INTEGRATION:-}" in
case "${SOLIDCRAFT_NO_DESKTOP_INTEGRATION:-${SOLVECRAFT_NO_DESKTOP_INTEGRATION:-}}" in
1 | true | yes) return 0 ;;
esac
# The AppImage runtime exports $APPIMAGE; without it (an extracted AppDir) there
@@ -70,4 +72,4 @@ integrate() {
}
integrate
exec "$APPDIR/usr/bin/solvecraft" "$@"
exec "$APPDIR/usr/bin/solidcraft" "$@"
@@ -3,12 +3,12 @@ Type=Application
Name=SolidCraft
GenericName=3D CAD
Comment=Parametric 3D CAD: sketch, constrain, extrude, and change a parameter to rebuild
Exec=solvecraft %f
TryExec=solvecraft
Exec=solidcraft %f
TryExec=solidcraft
Icon=ai.storyteller.solvecraft
Terminal=false
StartupNotify=true
StartupWMClass=solvecraft
StartupWMClass=solidcraft
Categories=Graphics;3DGraphics;Engineering;
Keywords=cad;3d;parametric;sketch;step;stl;modelling;modeling;
MimeType=application/x-solvecraft;model/step;model/iges;model/stl;model/3mf;
MimeType=application/x-solidcraft;model/step;model/iges;model/stl;model/3mf;
@@ -53,9 +53,9 @@
<control>pointing</control>
</supports>
<provides>
<binary>solvecraft</binary>
<binary>solvecraft-cli</binary>
<mediatype>application/x-solvecraft</mediatype>
<binary>solidcraft</binary>
<binary>solidcraft-cli</binary>
<mediatype>application/x-solidcraft</mediatype>
</provides>
<content_rating type="oars-1.1"/>
<releases>
@@ -2,10 +2,13 @@
<!-- shared-mime-info definitions for types the system database may not have.
Installed to /usr/share/mime/packages/ (update-mime-database runs on install). -->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-solvecraft">
<mime-type type="application/x-solidcraft">
<comment>SolidCraft design</comment>
<icon name="ai.storyteller.solvecraft"/>
<sub-class-of type="application/json"/>
<glob pattern="*.solidcraft"/>
<!-- Compatibility shim (docs/compatibility.md): designs saved before the rename. -->
<glob pattern="*.solvecraft"/>
<alias type="application/x-solvecraft"/>
</mime-type>
</mime-info>
+6 -6
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env bash
# Repackage a SolidCraft Linux tarball as a single-file Flatpak bundle:
#
# $DIST/solvecraft-<version>-linux-<arch>.flatpak
# $DIST/solidcraft-<version>-linux-<arch>.flatpak
#
# Usage: packaging/linux/flatpak-bundle.sh [--no-test] [TARBALL]
#
# TARBALL defaults to $DIST/solvecraft-<version>-linux-<arch>.tar.gz from package.sh. The bundle
# TARBALL defaults to $DIST/solidcraft-<version>-linux-<arch>.tar.gz from package.sh. The bundle
# is built for the host architecture (x86_64 or aarch64), which must match the tarball's.
# Needs flatpak, flatpak-builder and the SVG pixbuf loader (librsvg2-common) for the host's
# `appstreamcli compose`; the freedesktop runtime and SDK named in the manifest are
@@ -37,7 +37,7 @@ case "$ARCH" in
aarch64 | arm64) ARCH=aarch64 ;;
*) echo "unsupported architecture $ARCH" >&2; exit 2 ;;
esac
BASENAME="solvecraft-$VERSION-linux-$ARCH"
BASENAME="solidcraft-$VERSION-linux-$ARCH"
TARBALL="${TARBALL:-$DIST/$BASENAME.tar.gz}"
[ -f "$TARBALL" ] || { echo "error: $TARBALL not found (run packaging/linux/package.sh --formats tar first)" >&2; exit 1; }
for tool in flatpak flatpak-builder; do
@@ -50,7 +50,7 @@ WORK="$CARGO_TARGET_DIR/flatpak-bundle"
rm -rf "$WORK"
mkdir -p "$WORK/stage"
tar -xzf "$TARBALL" -C "$WORK/stage" --strip-components=1
[ -x "$WORK/stage/bin/solvecraft" ] || { echo "error: $TARBALL has no bin/solvecraft" >&2; exit 1; }
[ -x "$WORK/stage/bin/solidcraft" ] || { echo "error: $TARBALL has no bin/solidcraft" >&2; exit 1; }
cp "$HERE/flatpak/$APP_ID.bundle.yml" "$WORK/$APP_ID.yml"
flatpak remote-add --user --if-not-exists flathub "$FLATHUB"
@@ -66,8 +66,8 @@ echo "wrote $OUT"
if [ "$TEST" = 1 ]; then
flatpak install --user -y --noninteractive --reinstall "$OUT"
flatpak info --user "$APP_ID"
flatpak run --command=solvecraft-cli "$APP_ID" --version
flatpak run --command=sh "$APP_ID" -c 'ls /app/share/applications /app/share/metainfo /app/share/mime/packages /app/share/icons/hicolor/256x256/apps /app/share/doc/solvecraft'
flatpak run --command=solidcraft-cli "$APP_ID" --version
flatpak run --command=sh "$APP_ID" -c 'ls /app/share/applications /app/share/metainfo /app/share/mime/packages /app/share/icons/hicolor/256x256/apps /app/share/doc/solidcraft'
python3 "$HERE/flatpak-smoke.py" --backend both
fi
echo "==> done"
+4 -3
View File
@@ -28,7 +28,7 @@ work=$(mktemp -d)
export XDG_CONFIG_HOME="$work/config" XDG_DATA_HOME="$work/data" XDG_CACHE_HOME="$work/cache"
mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME"
unset WGPU_BACKEND WGPU_BACKENDS
solvecraft --control 18837 >"$work/app.log" 2>&1 &
solidcraft --control 18837 >"$work/app.log" 2>&1 &
app=$!
cleanup() {
kill "$app" 2>/dev/null || true
@@ -165,10 +165,10 @@ def frame_statistics(png):
def probe(gl_saved_four=False):
shell = SANDBOX_PROBE
if gl_saved_four:
shell = shell.replace('solvecraft --control',
shell = shell.replace('solidcraft --control',
'mkdir -p "$XDG_DATA_HOME/solvecraft"\n'
'printf \'{"msaa":4}\' >"$XDG_DATA_HOME/solvecraft/graphics.json"\n'
'export WGPU_BACKEND=gl\nsolvecraft --control')
'export WGPU_BACKEND=gl\nsolidcraft --control')
result = subprocess.run(["flatpak", "run", "--user", "--command=bash", APP_ID, "-c", shell],
text=True, capture_output=True, timeout=125)
if result.returncode:
@@ -201,6 +201,7 @@ def run_backend(backend):
env.pop("WAYLAND_DISPLAY", None)
env.pop("WAYLAND_SOCKET", None)
env.pop("DISPLAY", None)
env.pop("SOLIDCRAFT_WAYLAND", None)
env.pop("SOLVECRAFT_WAYLAND", None)
command = ["dbus-run-session", "--"]
compositor = None
@@ -1,17 +1,19 @@
# Flatpak manifest for the single-file bundle attached to every GitHub release
# (solvecraft-<version>-linux-<arch>.flatpak). Build it with packaging/linux/flatpak-bundle.sh.
# (solidcraft-<version>-linux-<arch>.flatpak). Build it with packaging/linux/flatpak-bundle.sh.
# Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
#
# It repackages the release's own Linux tarball (solvecraft-<version>-linux-<arch>.tar.gz, built
# It repackages the release's own Linux tarball (solidcraft-<version>-linux-<arch>.tar.gz, built
# on Ubuntu 22.04 by package.sh) instead of compiling Rust inside flatpak-builder: no network
# during the build, and the same binaries as the AppImage/deb/rpm. The binaries need glibc >= 2.35
# and load X11, Wayland, xkbcommon and Vulkan at runtime; the freedesktop runtime has all of
# them, and the GPU driver comes from its org.freedesktop.Platform.GL extension.
# Compatibility shim, see docs/compatibility.md: the Flatpak app id keeps its pre-rename value.
# A new id would be a different app to Flatpak, and existing installs would stop updating.
id: ai.storyteller.solvecraft
runtime: org.freedesktop.Platform
runtime-version: '26.08'
sdk: org.freedesktop.Sdk
command: solvecraft
command: solidcraft
finish-args:
# Display: Wayland first; X11 only when no Wayland compositor is running.
- --socket=wayland
@@ -28,16 +30,18 @@ finish-args:
# the sandbox; `flatpak override --user --share=network ai.storyteller.solvecraft` exposes it to
# host agents) and home/host filesystem access.
modules:
- name: solvecraft
- name: solidcraft
buildsystem: simple
build-commands:
- install -Dm755 bin/solvecraft /app/bin/solvecraft
- install -Dm755 bin/solidcraft /app/bin/solidcraft
- install -Dm755 bin/solidcraft-cli /app/bin/solidcraft-cli
# The old CLI name, forwarding to solidcraft-cli (compatibility shim, docs/compatibility.md).
- install -Dm755 bin/solvecraft-cli /app/bin/solvecraft-cli
- install -Dm644 share/applications/ai.storyteller.solvecraft.desktop /app/share/applications/ai.storyteller.solvecraft.desktop
- install -Dm644 share/mime/packages/ai.storyteller.solvecraft.xml /app/share/mime/packages/ai.storyteller.solvecraft.xml
# Already has the release's version and date substituted by package.sh.
- install -Dm644 share/metainfo/ai.storyteller.solvecraft.metainfo.xml /app/share/metainfo/ai.storyteller.solvecraft.metainfo.xml
- mkdir -p /app/share/icons /app/share/doc && cp -R share/icons/hicolor /app/share/icons/ && cp -R share/doc/solvecraft /app/share/doc/
- mkdir -p /app/share/icons /app/share/doc && cp -R share/icons/hicolor /app/share/icons/ && cp -R share/doc/solidcraft /app/share/doc/
sources:
- type: dir
path: stage
+20 -5
View File
@@ -1,7 +1,15 @@
# .deb and .rpm packages (nfpm: https://nfpm.goreleaser.com). Run by package.sh, which stages
# the file tree, substitutes its path for ${STAGE} (nfpm doesn't expand env vars in `src`) and
# exports VERSION and NFPM_ARCH. Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0).
name: solvecraft
name: solidcraft
# Compatibility shim (docs/compatibility.md): releases before the rename were packaged as `solvecraft`.
# Installing this package replaces that one (Debian Replaces/Conflicts, set under overrides.deb;
# nfpm writes `replaces` as RPM Obsoletes), so `apt install ./solidcraft-….deb` or `dnf install ./solidcraft-….rpm`
# upgrades an old install instead of failing on the shared desktop and icon files.
replaces:
- solvecraft
provides:
- solvecraft
arch: ${NFPM_ARCH}
platform: linux
version: ${VERSION}
@@ -10,14 +18,14 @@ version_schema: semver
release: 1
section: graphics
priority: optional
maintainer: ${SOLVECRAFT_MAINTAINER}
maintainer: ${SOLIDCRAFT_MAINTAINER}
vendor: SolidCraft contributors
homepage: https://bherbruck.github.io/solidcraft/
license: MIT OR Apache-2.0
description: |-
Open-source parametric 3D CAD in the style of Fusion
Sketches with a constraint solver, a parametric feature timeline, components and
joints, sheet metal; STEP, IGES, 3MF, STL and OBJ. Includes solvecraft-cli, a
joints, sheet metal; STEP, IGES, 3MF, STL and OBJ. Includes solidcraft-cli, a
headless command runner and MCP server for AI agents.
# The binaries link only glibc and libgcc_s directly. Windowing (X11/Wayland, xkbcommon) and the
@@ -25,6 +33,9 @@ description: |-
# the package tools can't find them: list them here.
overrides:
deb:
# Debian's rename pattern: Replaces + Conflicts + Provides (docs/compatibility.md).
conflicts:
- solvecraft
depends:
- libc6 (>= 2.35)
- libgcc-s1
@@ -59,9 +70,13 @@ overrides:
- xdg-desktop-portal
contents:
- src: ${STAGE}/usr/bin/solvecraft
dst: /usr/bin/solvecraft
- src: ${STAGE}/usr/bin/solidcraft
dst: /usr/bin/solidcraft
file_info: { mode: 0755 }
- src: ${STAGE}/usr/bin/solidcraft-cli
dst: /usr/bin/solidcraft-cli
file_info: { mode: 0755 }
# Compatibility shim (docs/compatibility.md): the old CLI name forwards to solidcraft-cli.
- src: ${STAGE}/usr/bin/solvecraft-cli
dst: /usr/bin/solvecraft-cli
file_info: { mode: 0755 }
+28 -19
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env bash
# Build and package SolidCraft for Linux (<arch> is x86_64 or aarch64):
#
# $DIST/solvecraft-<version>-linux-<arch>.AppImage any distro with glibc >= the build host's
# $DIST/solvecraft-<version>-linux-<arch>.AppImage.zsync delta updates (needs zsyncmake)
# $DIST/solvecraft-<version>-linux-<arch>.deb Debian, Ubuntu, Mint, Pop!_OS, ...
# $DIST/solvecraft-<version>-linux-<arch>.rpm Fedora, openSUSE, RHEL, ...
# $DIST/solvecraft-<version>-linux-<arch>.tar.gz plain FHS-style tree (bin/, share/)
# $DIST/solidcraft-<version>-linux-<arch>.AppImage any distro with glibc >= the build host's
# $DIST/solidcraft-<version>-linux-<arch>.AppImage.zsync delta updates (needs zsyncmake)
# $DIST/solvecraft-<version>-linux-<arch>.AppImage.zsync the same, for pre-rename AppImages
# $DIST/solidcraft-<version>-linux-<arch>.deb Debian, Ubuntu, Mint, Pop!_OS, ...
# $DIST/solidcraft-<version>-linux-<arch>.rpm Fedora, openSUSE, RHEL, ...
# $DIST/solidcraft-<version>-linux-<arch>.tar.gz plain FHS-style tree (bin/, share/)
#
# Usage: packaging/linux/package.sh [--skip-build] [--formats "appimage deb rpm tar"]
#
@@ -37,17 +38,17 @@ case "$ARCH" in
aarch64 | arm64) ARCH=aarch64; DEB_ARCH=arm64 ;;
*) echo "unsupported architecture $ARCH" >&2; exit 2 ;;
esac
export SOLVECRAFT_MAINTAINER="${SOLVECRAFT_MAINTAINER:-SolidCraft contributors <https://github.com/bherbruck/solidcraft>}"
BASENAME="solvecraft-$VERSION-linux-$ARCH"
export SOLIDCRAFT_MAINTAINER="${SOLIDCRAFT_MAINTAINER:-${SOLVECRAFT_MAINTAINER:-SolidCraft contributors <https://github.com/bherbruck/solidcraft>}}" # SOLVECRAFT_*: compatibility shim, see docs/compatibility.md
BASENAME="solidcraft-$VERSION-linux-$ARCH"
echo "==> SolidCraft $VERSION for Linux $ARCH ($FORMATS)"
if [ "$SKIP_BUILD" = 0 ]; then
if [ -n "${CROSS_TARGET:-}" ]; then
(cd "$ROOT" && cargo build --release --locked -p solvecraft -p solvecraft-cli --target "$CROSS_TARGET")
(cd "$ROOT" && cargo build --release --locked -p solidcraft -p solidcraft-cli --target "$CROSS_TARGET")
BIN="$CARGO_TARGET_DIR/$CROSS_TARGET/release"
else
(cd "$ROOT" && cargo build --release --locked -p solvecraft -p solvecraft-cli)
(cd "$ROOT" && cargo build --release --locked -p solidcraft -p solidcraft-cli)
BIN="$CARGO_TARGET_DIR/release"
fi
else
@@ -62,19 +63,21 @@ STAGE="$WORK/root"
rm -rf "$WORK"
# ---- stage an FHS tree (shared by every format) -------------------------------------------------
install -Dm755 "$BIN/solvecraft" "$STAGE/usr/bin/solvecraft"
install -Dm755 "$BIN/solidcraft" "$STAGE/usr/bin/solidcraft"
install -Dm755 "$BIN/solidcraft-cli" "$STAGE/usr/bin/solidcraft-cli"
# Compatibility shim (docs/compatibility.md): the old CLI name forwards to solidcraft-cli.
install -Dm755 "$BIN/solvecraft-cli" "$STAGE/usr/bin/solvecraft-cli"
strip "$STAGE/usr/bin/solvecraft" "$STAGE/usr/bin/solvecraft-cli" 2>/dev/null || true
strip "$STAGE/usr/bin/solidcraft" "$STAGE/usr/bin/solidcraft-cli" "$STAGE/usr/bin/solvecraft-cli" 2>/dev/null || true
install -Dm644 "$HERE/$APP_ID.desktop" "$STAGE/usr/share/applications/$APP_ID.desktop"
install -Dm644 "$HERE/$APP_ID.mime.xml" "$STAGE/usr/share/mime/packages/$APP_ID.xml"
mkdir -p "$STAGE/usr/share/metainfo"
sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$SOLVECRAFT_BUILD_DATE/g" \
sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$SOLIDCRAFT_BUILD_DATE/g" \
"$HERE/$APP_ID.metainfo.xml.in" >"$STAGE/usr/share/metainfo/$APP_ID.metainfo.xml"
make_icons "$STAGE/usr/bin/solvecraft-cli" "$WORK/icons"
make_icons "$STAGE/usr/bin/solidcraft-cli" "$WORK/icons"
mkdir -p "$STAGE/usr/share/icons"
cp -R "$WORK/icons/hicolor" "$STAGE/usr/share/icons/"
mkdir -p "$STAGE/usr/share/doc/solvecraft"
copy_docs "$STAGE/usr/share/doc/solvecraft"
mkdir -p "$STAGE/usr/share/doc/solidcraft"
copy_docs "$STAGE/usr/share/doc/solidcraft"
if command -v desktop-file-validate >/dev/null; then
desktop-file-validate "$STAGE/usr/share/applications/$APP_ID.desktop"
@@ -107,7 +110,7 @@ fi
# ---- AppImage -----------------------------------------------------------------------------------
if has appimage; then
APPDIR="$WORK/SolveCraft.AppDir"
APPDIR="$WORK/SolidCraft.AppDir"
cp -R "$STAGE" "$APPDIR"
mv "$APPDIR/usr/share/doc" "$WORK/doc-unused"
# AppRun is a script, not a symlink: it installs the .desktop entry and icons into
@@ -133,19 +136,25 @@ if has appimage; then
# Update information: AppImageUpdate and the like fetch only the changed blocks of a newer
# release through the .zsync published next to each AppImage. `latest` is the newest published
# release that is not a pre-release.
REPO="${GITHUB_REPOSITORY:-bherbruck/solvecraft}"
UPDATE_INFO="gh-releases-zsync|${REPO%%/*}|${REPO#*/}|latest|solvecraft-*-linux-$ARCH.AppImage.zsync"
REPO="${GITHUB_REPOSITORY:-bherbruck/solidcraft}"
UPDATE_INFO="gh-releases-zsync|${REPO%%/*}|${REPO#*/}|latest|solidcraft-*-linux-$ARCH.AppImage.zsync"
# Extract-and-run: works without FUSE (containers, CI). With zsyncmake on the host appimagetool
# also writes the .zsync, into its working directory, hence the cd.
(cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream -u "$UPDATE_INFO" "$APPDIR" "$OUT")
echo "wrote $OUT"
if [ -s "$OUT.zsync" ]; then
echo "wrote $OUT.zsync"
# Compatibility shim (docs/compatibility.md): AppImages from before the rename look for
# `solvecraft-*-linux-$ARCH.AppImage.zsync` in the latest release. The same .zsync under that
# name (its URL is the new AppImage's file name, relative to the release) updates them to
# SolidCraft.
cp "$OUT.zsync" "$DIST/solvecraft-$VERSION-linux-$ARCH.AppImage.zsync"
echo "wrote $DIST/solvecraft-$VERSION-linux-$ARCH.AppImage.zsync (legacy update alias)"
else
warn "zsyncmake not found, so $OUT.zsync was not written; AppImage delta updates need it"
fi
fi
"$STAGE/usr/bin/solvecraft-cli" --version
"$STAGE/usr/bin/solidcraft-cli" --version
echo "==> done"
ls -lh "$DIST"