Linux app id: ai.storyteller.solidcraft, with an end-of-life rebase from the old id (#619)

The Flatpak app id, desktop/metainfo/MIME file names, icon name and the window's Wayland app_id / X11 class are now ai.storyteller.solidcraft, the id the ArtCraft launcher expects.

Existing Flatpak installs move over: the Pages repo marks app/ai.storyteller.solvecraft end-of-life with a rebase to the new id, so flatpak update offers the switch and Flatpak carries ~/.var/app along. A new install next to the old one copies settings and autosaves on first launch through a read-only grant on the old data folder. The metainfo declares <replaces>/<provides>, the desktop entry X-Flatpak-RenamedFrom, and AppImages drop the menu entry an older AppImage installed. deb/rpm upgrades remove the old-named files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-13 05:20:58 -04:00
1 parent bcbfa50eac
commit e67cc0c747
21 files changed
+194 -68

No files matched your search

+5 -2
View File
@@ -73,10 +73,13 @@ installing SolidCraft; keep both remotes in the same user installation:
```sh
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists solidcraft https://bherbruck.github.io/solidcraft/flatpak/solidcraft.flatpakrepo
flatpak install --user solidcraft ai.storyteller.solvecraft
flatpak run ai.storyteller.solvecraft
flatpak install --user solidcraft ai.storyteller.solidcraft
flatpak run ai.storyteller.solidcraft
```
Flatpak installs from 0.7 or earlier use the old ID `ai.storyteller.solvecraft`: `flatpak update --user`
offers to replace it with `ai.storyteller.solidcraft`, and settings move along.
Single-file `.flatpak` bundles include the runtime repository address; missing runtimes
still need an internet connection. [Linux installation details](https://bherbruck.github.io/solidcraft/guide/getting-started.html#linux-flatpak).
+8 -4
View File
@@ -53,9 +53,13 @@ jobs:
- name: XML, desktop entry, AppStream, Flatpak manifest
run: |
xmllint --noout packaging/windows/solidcraft.wxs packaging/windows/solidcraft.en-us.wxl packaging/macos/*.plist* packaging/linux/*.xml*
desktop-file-validate packaging/linux/ai.storyteller.solvecraft.desktop
sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.solvecraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.solvecraft.metainfo.xml"
appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.solvecraft.metainfo.xml"
python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.solvecraft.bundle.yml')); assert m['id'] == 'ai.storyteller.solvecraft' and m['command'] == 'solidcraft'"
desktop-file-validate packaging/linux/ai.storyteller.solidcraft.desktop
# The window's X11 class is its app id; the old id stays only as the rename shims.
grep -qx 'StartupWMClass=ai.storyteller.solidcraft' packaging/linux/ai.storyteller.solidcraft.desktop
grep -q '<id>ai.storyteller.solvecraft</id>' packaging/linux/ai.storyteller.solidcraft.metainfo.xml.in
grep -qF -e '--end-of-life-rebase=' packaging/flatpak-site.sh
sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.solidcraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.solidcraft.metainfo.xml"
appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.solidcraft.metainfo.xml"
python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/flatpak/ai.storyteller.solidcraft.bundle.yml')); assert m['id'] == 'ai.storyteller.solidcraft' and m['command'] == 'solidcraft'"
python3 -c "import yaml; m = yaml.safe_load(open('packaging/linux/snap/snapcraft.yaml')); assert m['name'] == 'solidcraft' and m['confinement'] == 'strict' and m['apps']['solidcraft']['command'] == 'usr/bin/solidcraft'"
python3 -m py_compile packaging/linux/snap-smoke.py
+19 -9
View File
@@ -141,17 +141,28 @@ fn graphics_path() -> Option<std::path::PathBuf> {
/// Compatibility shim (docs/compatibility.md): on the first launch after the rename, copy the
/// settings of earlier releases into the new folder. They kept `graphics.json` (and, on Linux
/// and macOS, the recovery folder) under the old product name, and eframe's `app.ron` under the
/// window's app id. The old folders are never changed.
/// window's old app id. In the Flatpak sandbox, releases before 0.8 kept everything under the
/// old app id's `~/.var/app` folder; that copy comes first, as it is the newest. The old folders
/// are never changed.
fn adopt_legacy_settings() {
let (Some(current), Some(legacy)) = (settings_dir(), eframe::storage_dir("SolveCraft")) else { return };
let legacy_eframe = eframe::storage_dir(APP_ID);
if let Err(e) = adopt_settings(&current, &legacy, legacy_eframe.as_deref()) {
let legacy_eframe = eframe::storage_dir(solidcraft_engine::io::LEGACY_APP_ID);
let old_flatpak = solidcraft_engine::io::legacy_flatpak_path(&current);
if let Err(e) = adopt_settings(&current, old_flatpak.as_deref(), &legacy, legacy_eframe.as_deref()) {
log::warn!("settings: could not copy the previous release's settings into {}: {e}", current.display());
}
}
fn adopt_settings(current: &std::path::Path, legacy: &std::path::Path, legacy_eframe: Option<&std::path::Path>) -> std::io::Result<()> {
fn adopt_settings(
current: &std::path::Path,
old_flatpak: Option<&std::path::Path>,
legacy: &std::path::Path,
legacy_eframe: Option<&std::path::Path>,
) -> std::io::Result<()> {
let fresh = !current.exists();
if let Some(old) = old_flatpak {
solidcraft_engine::io::adopt_legacy_dir(current, old)?;
}
solidcraft_engine::io::adopt_legacy_dir(current, legacy)?;
if let Some(old) = legacy_eframe.map(|d| d.join("app.ron")).filter(|p| fresh && p.is_file()) {
std::fs::create_dir_all(current)?;
@@ -203,9 +214,8 @@ fn app_viewport() -> Result<egui::ViewportBuilder, eframe::Error> {
.with_app_id(APP_ID))
}
/// The window's app id: it names the desktop entry, so it stays the packages' reverse-DNS id,
/// which is kept from before the rename (compatibility shim, docs/compatibility.md).
const APP_ID: &str = "ai.storyteller.solvecraft";
/// The window's app id: it names the desktop entry, so it is the packages' reverse-DNS id.
const APP_ID: &str = solidcraft_engine::io::APP_ID;
fn main() -> eframe::Result {
adopt_legacy_settings();
@@ -408,14 +418,14 @@ mod msaa_tests {
std::fs::create_dir_all(&legacy_eframe).unwrap();
std::fs::write(legacy.join("graphics.json"), r#"{"msaa":8}"#).unwrap();
std::fs::write(legacy_eframe.join("app.ron"), "(prefs)").unwrap();
super::adopt_settings(&current, &legacy, Some(&legacy_eframe)).unwrap();
super::adopt_settings(&current, None, &legacy, Some(&legacy_eframe)).unwrap();
assert_eq!(std::fs::read_to_string(current.join("graphics.json")).unwrap(), r#"{"msaa":8}"#);
assert_eq!(std::fs::read_to_string(current.join("app.ron")).unwrap(), "(prefs)");
assert!(legacy.join("graphics.json").is_file() && legacy_eframe.join("app.ron").is_file());
// Later launches leave the current settings alone.
std::fs::write(current.join("app.ron"), "(mine)").unwrap();
std::fs::remove_file(current.join("graphics.json")).unwrap();
super::adopt_settings(&current, &legacy, Some(&legacy_eframe)).unwrap();
super::adopt_settings(&current, None, &legacy, Some(&legacy_eframe)).unwrap();
assert_eq!(std::fs::read_to_string(current.join("app.ron")).unwrap(), "(mine)");
assert!(!current.join("graphics.json").exists());
let _ = std::fs::remove_dir_all(&d);
+7 -4
View File
@@ -35,10 +35,13 @@ pub fn default_dir() -> Option<PathBuf> {
env("XDG_DATA_HOME").or_else(|| env("HOME").map(|h| h.join(".local/share")))?
};
let (dir, legacy) = dirs_under(&root, cfg!(windows) || cfg!(target_os = "macos"));
// Compatibility shim (docs/compatibility.md): autosaves made before the rename are copied
// to the new folder on first launch; the old folder is left as it was.
if let Err(e) = solidcraft_io::adopt_legacy_dir(&dir, &legacy) {
log::warn!("recovery: could not copy {} to {}: {e}", legacy.display(), dir.display());
// Compatibility shim (docs/compatibility.md): autosaves made before the rename (or, in the
// Flatpak sandbox, under the old app id) are copied to the new folder on first launch; the
// old folder is left as it was.
for old in solidcraft_io::legacy_flatpak_path(&dir).into_iter().chain([legacy]) {
if let Err(e) = solidcraft_io::adopt_legacy_dir(&dir, &old) {
log::warn!("recovery: could not copy {} to {}: {e}", old.display(), dir.display());
}
}
Some(dir)
}
+36 -1
View File
@@ -3,7 +3,12 @@
//! the legacy folder is copied over. The legacy folder is never changed or deleted, so an older
//! release installed side by side keeps working.
use std::path::Path;
use std::path::{Path, PathBuf};
/// The Linux app id (Flatpak, desktop entry, AppStream, Wayland `app_id`).
pub const APP_ID: &str = "ai.storyteller.solidcraft";
/// The app id of releases before 0.8 (compatibility shim, docs/compatibility.md).
pub const LEGACY_APP_ID: &str = "ai.storyteller.solvecraft";
/// Most files and nesting copied from a legacy folder (it holds preferences, autosaves and
/// small caches; anything bigger is not ours).
@@ -22,6 +27,25 @@ pub fn adopt_legacy_dir(current: &Path, legacy: &Path) -> std::io::Result<bool>
Ok(true)
}
/// In the Flatpak sandbox, the same `path` under the pre-rename app's data folder: Flatpak keeps
/// each app's XDG folders under `~/.var/app/<app id>`, so after the app id changed, settings and
/// autosaves of earlier Flatpak releases sit under the old id. `None` outside the sandbox or for
/// paths outside our own `~/.var/app` folder. Flatpak moves that folder itself when an install
/// follows the end-of-life rebase; this covers installs made next to the old app, which reach it
/// read-only through the manifest's `--filesystem` grant.
pub fn legacy_flatpak_path(path: &Path) -> Option<PathBuf> {
flatpak_twin(path, &std::env::var_os("FLATPAK_ID")?, Path::new(&std::env::var_os("HOME")?))
}
fn flatpak_twin(path: &Path, flatpak_id: &std::ffi::OsStr, home: &Path) -> Option<PathBuf> {
if flatpak_id != APP_ID {
return None;
}
let apps = home.join(".var").join("app");
let rel = path.strip_prefix(apps.join(APP_ID)).ok()?;
Some(apps.join(LEGACY_APP_ID).join(rel))
}
fn copy_tree(from: &Path, to: &Path, depth: usize, budget: &mut usize) -> std::io::Result<()> {
std::fs::create_dir_all(to)?;
if depth >= MAX_DEPTH {
@@ -75,6 +99,17 @@ mod tests {
let _ = std::fs::remove_dir_all(&d);
}
#[test]
fn flatpak_twin_maps_our_sandbox_data_to_the_old_app_id() {
let home = Path::new("/home/u");
let ours = home.join(".var/app/ai.storyteller.solidcraft/data/solidcraft");
let id = std::ffi::OsStr::new(APP_ID);
assert_eq!(flatpak_twin(&ours, id, home), Some(home.join(".var/app/ai.storyteller.solvecraft/data/solidcraft")));
// Outside our sandbox folder, or in another app's sandbox, there is nothing to map.
assert_eq!(flatpak_twin(&home.join(".local/share/solidcraft"), id, home), None);
assert_eq!(flatpak_twin(&ours, std::ffi::OsStr::new("org.example.Other"), home), None);
}
#[test]
fn nothing_to_adopt() {
let d = scratch("none");
+1 -1
View File
@@ -23,7 +23,7 @@ mod threemf;
pub mod vfs;
pub use dxf::{read_dxf, write_dxf};
pub use legacy_dir::adopt_legacy_dir;
pub use legacy_dir::{APP_ID, LEGACY_APP_ID, adopt_legacy_dir, legacy_flatpak_path};
pub use safe_write::{backup_path, write_atomic};
pub use sketch2d::{Geom2, MAX_DRAWING_BYTES};
pub use svg::read_svg;
+9 -6
View File
@@ -36,18 +36,21 @@ to the same user installation:
```sh
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists solidcraft https://bherbruck.github.io/solidcraft/flatpak/solidcraft.flatpakrepo
flatpak install --user solidcraft ai.storyteller.solvecraft
flatpak run ai.storyteller.solvecraft
flatpak install --user solidcraft ai.storyteller.solidcraft
flatpak run ai.storyteller.solidcraft
```
The application ID is unchanged, so existing installations keep
receiving updates under whatever remote name they were added with (see the
[compatibility notes](https://github.com/bherbruck/solidcraft/blob/main/docs/compatibility.md#package-ids)).
Releases up to 0.7 used the application ID `ai.storyteller.solvecraft`. If you installed one
of those from the SolidCraft remote, `flatpak update --user` reports it as end-of-life and offers
to replace it with `ai.storyteller.solidcraft`; accept, and your settings and autosaves move with
it. If you installed a `.flatpak` bundle, install the new bundle, launch it once (it copies your
settings), then run `flatpak uninstall --user ai.storyteller.solvecraft` (see the
[compatibility notes](https://github.com/bherbruck/solidcraft/blob/main/docs/compatibility.md#linux-app-id)).
Alternatively, download the release's `.flatpak` file for your architecture and run
`flatpak install --user ./solidcraft-VERSION-linux-ARCH.flatpak`. The bundle supplies the
Flathub runtime repository address; installation still needs internet access to fetch any
missing runtime. Launch with `flatpak run ai.storyteller.solvecraft`, rather than looking for
missing runtime. Launch with `flatpak run ai.storyteller.solidcraft`, rather than looking for
a host `solidcraft` binary. Files chosen through Open/Save use the desktop file-chooser portal;
command-line paths outside Documents may need a specific additional filesystem permission.
+35 -2
View File
@@ -22,7 +22,7 @@ New designs are saved as `.solidcraft`. Files with the old extension still open
| Browser file picker | `apps/solidcraft-web/src/files.rs` (`accept`) |
| Windows | `packaging/windows/solidcraft.wxs`: the `.solvecraft` extension on the design ProgId and in `SupportedTypes` |
| macOS | `packaging/macos/Info.plist.in`: `solvecraft` in the design document type's extensions |
| Linux / FreeBSD / Flatpak | `packaging/linux/ai.storyteller.solvecraft.mime.xml`: `*.solvecraft` glob and the `application/x-solvecraft` alias |
| Linux / FreeBSD / Flatpak | `packaging/linux/ai.storyteller.solidcraft.mime.xml`: `*.solvecraft` glob and the `application/x-solvecraft` alias |
Recent-files entries that point at a `.solvecraft` file keep working: they are plain paths and
open through the same detection. The browser app also lists designs it stored under the old
@@ -68,6 +68,8 @@ it is missing (desktop and browser, `solidcraft_ui_egui::prefs::stored`). The br
reads files and the autosave it kept under the old names in local storage and OPFS
(`apps/solidcraft-web/src/files.rs`). A 3DxWare profile the driver made for the old executable
name is read until it makes a new one (`apps/solidcraft/src/space_mouse.rs`).
In the Flatpak sandbox these folders live under `~/.var/app/<app id>/data`; for data kept under
the app id of releases before 0.8, see [Linux app ID](#linux-app-id).
The thumbnail cache is not migrated; it is rebuilt. The copy helper is
`solidcraft_io::adopt_legacy_dir`, used by the desktop app (`adopt_legacy_settings`) and
`solidcraft_engine::recovery::default_dir`. Tests: `legacy_dir` (copy once, never overwrite,
@@ -109,13 +111,44 @@ test `legacy_design_files_and_resource_uris`).
| Package | Decision | Why |
|---|---|---|
| Flatpak app ID / Linux desktop ID `ai.storyteller.solvecraft` | **kept** | A new Flatpak ID is a different app: existing installs would stop receiving updates. The `.desktop`, metainfo and MIME file names and the Wayland `app_id` must match it, so they keep it too. |
| Flatpak app ID / Linux desktop ID | changed in 0.8 from `ai.storyteller.solvecraft` to `ai.storyteller.solidcraft` | The ID the ArtCraft launcher looks for (`ai.storyteller.<name>`). The `.desktop`, metainfo and MIME file names, the icon name, and the Wayland `app_id` / X11 class follow it. Existing installs are moved over; see [Linux app ID](#linux-app-id). |
| Flatpak remote file | `solidcraft.flatpakrepo`, plus a legacy `solvecraft.flatpakrepo` copy | Old links keep working; existing remotes point at the repo URL, which is unchanged. |
| macOS bundle ID | changed to `ai.storyteller.solidcraft` | No updater depends on it; the app is drag-installed. macOS may ask again on first launch (unsigned builds). |
| MSI `UpgradeCode` | **kept** | So the new MSI upgrades the old install in place. Product name is SolidCraft; the default folder becomes `Program Files\SolidCraft` (the major upgrade removes the old folder). A custom install folder isn't remembered across this one upgrade. |
| deb / rpm package name | renamed to `solidcraft`; deb `Replaces`/`Conflicts`/`Provides: solvecraft`, rpm `Obsoletes`/`Provides: solvecraft` | Installing the new package replaces the old one instead of installing side by side. |
| AppImage | `solidcraft-<version>-linux-<arch>.AppImage`; the release also carries a copy of its `.zsync` under the old `solvecraft-…` name | AppImages already installed look for the old-named `.zsync`, so AppImageUpdate keeps finding updates. |
## Linux app ID
Releases up to 0.7 used `ai.storyteller.solvecraft`; 0.8 uses `ai.storyteller.solidcraft`
everywhere (Flatpak, deb, rpm, AppImage, Snap, FreeBSD, Nix). The shims:
| Shim | Where | What it does |
|---|---|---|
| End-of-life rebase in the Flatpak repository | `packaging/flatpak-site.sh` | Each release's Pages deploy keeps the old `app/ai.storyteller.solvecraft/<arch>/stable` refs (mirrored from the previous deploy) and marks them once with `flatpak build-commit-from --end-of-life-rebase=ai.storyteller.solvecraft=ai.storyteller.solidcraft`. `flatpak update` then says the old app is end-of-life and offers to replace it with the new one; accepting installs `ai.storyteller.solidcraft`, uninstalls the old app, and Flatpak moves `~/.var/app/ai.storyteller.solvecraft` to the new id's folder (leaving a symlink at the old path) on the first launch, so settings and autosaves come along unchanged. |
| AppStream `<replaces>` / `<provides>` | `packaging/linux/ai.storyteller.solidcraft.metainfo.xml.in` | Software centres treat the new id as the successor of the old one and of `ai.storyteller.solvecraft.desktop`. |
| `X-Flatpak-RenamedFrom` | `packaging/linux/ai.storyteller.solidcraft.desktop` | Desktops that honour it (GNOME Shell) keep favourites and pinned launchers pointing at the renamed entry. |
| Read-only access to the old Flatpak data folder | `--filesystem=~/.var/app/ai.storyteller.solvecraft:ro` in `packaging/linux/flatpak/ai.storyteller.solidcraft.bundle.yml`; `solidcraft_io::legacy_flatpak_path`, used by `adopt_legacy_settings` and `recovery::default_dir` | When the new app was installed next to the old one (for example from a `.flatpak` bundle, which has no update feed) instead of through the rebase, its first launch copies settings and autosaves from the old app's folder, as in section 4. The old folder is not changed. |
| The old window `app_id` as eframe's legacy storage key | `adopt_legacy_settings` (`LEGACY_APP_ID`) | Window state that releases before the settings rename kept under `~/.local/share/ai.storyteller.solvecraft` is still copied (section 4). |
| Old AppImage menu entries | `packaging/linux/AppRun` | An AppImage's first run removes the `ai.storyteller.solvecraft.desktop` entry and icons that an AppImage up to 0.7 installed in `~/.local/share`, so the menu doesn't show the app twice. |
deb and rpm need no shim: the package (`solidcraft`) is unchanged, and an upgrade removes the old
`/usr/share/applications/ai.storyteller.solvecraft.desktop`, metainfo, MIME and icon files
because the new version doesn't ship them; the post-install script refreshes the desktop, MIME
and icon caches, so no stale menu entry is left. The Snap is unaffected apart from its
`common-id`: snapd names its menu entry after the snap.
Flatpak installed from a downloaded `.flatpak` bundle (no update feed): install the new bundle,
launch it once (settings and autosaves are copied), then
`flatpak uninstall --user ai.storyteller.solvecraft`.
**Removal:** keep the end-of-life refs in the Flatpak repository for at least six months after
0.8.0 (until 2027-04) and at least two minor releases; removing them strands any install that
hasn't run `flatpak update` since. The read-only filesystem grant and `legacy_flatpak_path` can go
at the same time. `<replaces>`, `<provides>` and `X-Flatpak-RenamedFrom` are harmless and can stay.
Tests: `flatpak_twin_maps_our_sandbox_data_to_the_old_app_id`,
`first_launch_adopts_the_previous_release_settings`.
## macOS
Releases before 0.4 were installed as `SolveCraft.app`. To update, quit the app, drag
+1 -1
View File
@@ -11,7 +11,7 @@ version bump and no patch release for every merge.
Every release asset, binary, installer product name and package uses the SolidCraft name
(`solidcraft-<version>-linux-<arch>.AppImage`, `SolidCraft-<version>-windows-x64.exe`,
`solidcraft-cli` archives, …). The old name survives only in the shims listed in
[compatibility.md](compatibility.md) (the Linux app ID, the legacy `.zsync` and `.flatpakrepo`
[compatibility.md](compatibility.md) (the end-of-life Flatpak refs of the old Linux app ID, the legacy `.zsync` and `.flatpakrepo`
copies, the deprecated old-name CLI alias); keep them until that document says they can go. Do not
rewrite published assets, checksums, tags or historical release notes of earlier versions.
+5 -5
View File
@@ -135,12 +135,12 @@
postInstall = ''
${pkgs.lib.optionalString pkgs.stdenv.isLinux ''
install -Dm644 packaging/linux/ai.storyteller.solvecraft.desktop \
$out/share/applications/ai.storyteller.solvecraft.desktop
install -Dm644 packaging/linux/ai.storyteller.solvecraft.mime.xml \
$out/share/mime/packages/ai.storyteller.solvecraft.xml
install -Dm644 packaging/linux/ai.storyteller.solidcraft.desktop \
$out/share/applications/ai.storyteller.solidcraft.desktop
install -Dm644 packaging/linux/ai.storyteller.solidcraft.mime.xml \
$out/share/mime/packages/ai.storyteller.solidcraft.xml
for size in 16 24 32 48 64 128 256 512; do
icon=$out/share/icons/hicolor/''${size}x''${size}/apps/ai.storyteller.solvecraft.png
icon=$out/share/icons/hicolor/''${size}x''${size}/apps/ai.storyteller.solidcraft.png
mkdir -p "$(dirname "$icon")"
$out/bin/solidcraft-cli icon --size "$size" --out "$icon" >/dev/null
done
+4 -4
View File
@@ -37,10 +37,10 @@ export DIST
export SOLIDCRAFT_BUILD_DATE="${SOLIDCRAFT_BUILD_DATE:-${SOLVECRAFT_BUILD_DATE:-$(date -u +%Y-%m-%d)}}"
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target}"
# Compatibility shim, see docs/compatibility.md: the reverse-DNS id (Flatpak app id, desktop,
# metainfo and MIME file names, icon name, Wayland app_id) keeps its pre-rename value so Flatpak
# installs keep updating and desktop integration keeps working.
APP_ID=ai.storyteller.solvecraft
# The reverse-DNS id: Flatpak app id, desktop, metainfo and MIME file names, icon name and the
# window's Wayland app_id / X11 class. Releases before 0.8 used ai.storyteller.solvecraft; the
# Flatpak repository rebases that id to this one (packaging/flatpak-site.sh, docs/compatibility.md).
APP_ID=ai.storyteller.solidcraft
export APP_ID
# Emit a GitHub Actions warning (plain stderr outside Actions).
+16 -2
View File
@@ -5,8 +5,10 @@ set -euo pipefail
site="${1:?site directory required}"
tag="${RELEASE_TAG:-}"
# Compatibility shim (docs/compatibility.md): the Flatpak app id keeps its pre-rename value.
app_id=ai.storyteller.solvecraft
app_id=ai.storyteller.solidcraft
# Compatibility shim (docs/compatibility.md): releases before 0.8 used this app id. Its refs stay
# in the repository, marked end-of-life with a rebase to $app_id.
legacy_id=ai.storyteller.solvecraft
if [ -z "$tag" ]; then
tag="$(gh api "repos/$GITHUB_REPOSITORY/releases?per_page=100" \
--jq '[.[] | select(.draft == false and .prerelease == false)][0].tag_name // empty')"
@@ -50,6 +52,18 @@ for arch in x86_64 aarch64; do
ostree --repo="$validation_repo" refs | grep -Fxq "app/$app_id/$arch/stable"
flatpak build-import-bundle "$repo_dir" "$bundles/$asset"
done
# `flatpak update` then offers existing installs of the old id the move to the new one, and
# Flatpak moves their ~/.var/app data folder along. The mark is a new commit on the old ref (the
# same app, so a skipped update changes nothing); it is made once and mirrored by later runs.
for arch in x86_64 aarch64; do
legacy_ref="app/$legacy_id/$arch/stable"
ostree --repo="$repo_dir" rev-parse "$legacy_ref" >/dev/null 2>&1 || continue
if ! ostree --repo="$repo_dir" show --print-metadata-key=ostree.endoflife-rebase "$legacy_ref" >/dev/null 2>&1; then
flatpak build-commit-from --no-update-summary --end-of-life-rebase="$legacy_id=$app_id" \
--src-ref="$legacy_ref" "$repo_dir" "$legacy_ref"
fi
test "$(ostree --repo="$repo_dir" show --print-metadata-key=ostree.endoflife-rebase "$legacy_ref")" = "'app/$app_id/$arch/stable'"
done
flatpak build-update-repo --title="SolidCraft" --comment='Official SolidCraft releases' \
--description='Official Flatpak repository for SolidCraft' \
--homepage="https://github.com/$GITHUB_REPOSITORY" --default-branch=stable "$repo_dir"
+12 -3
View File
@@ -3,7 +3,7 @@
# 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, kept as a compatibility shim) and looking up an installed .desktop file.
# app_id (ai.storyteller.solidcraft) 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
@@ -14,8 +14,9 @@
set -u
APPDIR="$(dirname "$(readlink -f "$0")")"
# Compatibility shim (docs/compatibility.md): the pre-rename reverse-DNS id.
DESK_ID=ai.storyteller.solvecraft
DESK_ID=ai.storyteller.solidcraft
# Compatibility shim (docs/compatibility.md): the desktop id of releases before 0.8.
OLD_DESK_ID=ai.storyteller.solvecraft
integrate() {
case "${SOLIDCRAFT_NO_DESKTOP_INTEGRATION:-${SOLVECRAFT_NO_DESKTOP_INTEGRATION:-}}" in
@@ -59,6 +60,14 @@ integrate() {
return 0
fi
# An entry and icons that an AppImage before 0.8 installed under the old id would show a
# second menu entry; remove them (only an entry this script wrote: it points Exec at a path).
old="$apps/$OLD_DESK_ID.desktop"
if [ -f "$old" ] && grep -q "^Icon=$OLD_DESK_ID\$" "$old" && grep -q '^Exec="' "$old"; then
rm -f "$old" "$data"/icons/hicolor/*/apps/"$OLD_DESK_ID".png 2>/dev/null
command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$apps" >/dev/null 2>&1
fi
# Icons: per-file compare so an unchanged install costs a handful of cmp calls.
src_icons="$APPDIR/usr/share/icons/hicolor"
[ -d "$src_icons" ] || return 0
@@ -5,10 +5,12 @@ GenericName=3D CAD
Comment=Parametric 3D CAD: sketch, constrain, extrude, and change a parameter to rebuild
Exec=solidcraft %f
TryExec=solidcraft
Icon=ai.storyteller.solvecraft
Icon=ai.storyteller.solidcraft
Terminal=false
StartupNotify=true
StartupWMClass=solidcraft
StartupWMClass=ai.storyteller.solidcraft
Categories=Graphics;3DGraphics;Engineering;
Keywords=cad;3d;parametric;sketch;step;stl;modelling;modeling;
MimeType=application/x-solidcraft;model/step;model/iges;model/stl;model/3mf;
# Releases before 0.8 used this desktop id; launchers keep pinned entries across the rename.
X-Flatpak-RenamedFrom=ai.storyteller.solvecraft.desktop;
@@ -2,7 +2,7 @@
<!-- AppStream metadata (software centres, Flathub). Template: package.sh substitutes @VERSION@
and @DATE@ from Cargo.toml and the build date. -->
<component type="desktop-application">
<id>ai.storyteller.solvecraft</id>
<id>ai.storyteller.solidcraft</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT OR Apache-2.0</project_license>
<name>SolidCraft</name>
@@ -24,8 +24,8 @@
<li>Every action is a command, scriptable from a CLI, a JSON control channel and an MCP server</li>
</ul>
</description>
<launchable type="desktop-id">ai.storyteller.solvecraft.desktop</launchable>
<icon type="stock">ai.storyteller.solvecraft</icon>
<launchable type="desktop-id">ai.storyteller.solidcraft.desktop</launchable>
<icon type="stock">ai.storyteller.solidcraft</icon>
<url type="homepage">https://bherbruck.github.io/solidcraft/</url>
<url type="bugtracker">https://github.com/bherbruck/solidcraft/issues</url>
<url type="help">https://bherbruck.github.io/solidcraft/</url>
@@ -56,7 +56,13 @@
<binary>solidcraft</binary>
<binary>solidcraft-cli</binary>
<mediatype>application/x-solidcraft</mediatype>
<!-- The desktop id of releases before 0.8 (docs/compatibility.md). -->
<id>ai.storyteller.solvecraft.desktop</id>
</provides>
<!-- Releases before 0.8 used the app id ai.storyteller.solvecraft (docs/compatibility.md). -->
<replaces>
<id>ai.storyteller.solvecraft</id>
</replaces>
<content_rating type="oars-1.1"/>
<releases>
<release version="@VERSION@" date="@DATE@"/>
@@ -4,7 +4,7 @@
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-solidcraft">
<comment>SolidCraft design</comment>
<icon name="ai.storyteller.solvecraft"/>
<icon name="ai.storyteller.solidcraft"/>
<sub-class-of type="application/json"/>
<glob pattern="*.solidcraft"/>
<!-- Compatibility shim (docs/compatibility.md): designs saved before the rename. -->
+1 -1
View File
@@ -12,7 +12,7 @@
# installed per-user from Flathub. Unless --no-test, the bundle is installed per-user, the CLI
# is checked, and the desktop creates/measures a box on headless X11 and Wayland. The desktop
# check also needs python3, dbus-x11, xvfb, xauth and weston; no screenshots are produced.
# Manifest: packaging/linux/flatpak/ai.storyteller.solvecraft.bundle.yml.
# Manifest: packaging/linux/flatpak/ai.storyteller.solidcraft.bundle.yml.
# Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
set -euo pipefail
# shellcheck source=../env.sh
+1 -1
View File
@@ -19,7 +19,7 @@ import tempfile
import time
import zlib
APP_ID = "ai.storyteller.solvecraft"
APP_ID = "ai.storyteller.solidcraft"
MAX_PIXELS = 16_000_000
MAX_PNG_BYTES = 70_000_000
SANDBOX_PROBE = r'''
@@ -7,9 +7,10 @@
# 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
# Releases before 0.8 used the id ai.storyteller.solvecraft. The Flatpak repository marks that
# id end-of-life with a rebase to this one, so `flatpak update` moves existing installs over
# (packaging/flatpak-site.sh, docs/compatibility.md).
id: ai.storyteller.solidcraft
runtime: org.freedesktop.Platform
runtime-version: '26.08'
sdk: org.freedesktop.Sdk
@@ -26,8 +27,12 @@ finish-args:
# files work anywhere without broad filesystem access. Documents is granted so files passed by
# path (the command line, recent designs) work in the usual place.
- --filesystem=xdg-documents
# Compatibility shim (docs/compatibility.md): settings and autosaves of releases before 0.8,
# kept under the old app id, read once on first launch when this app was installed next to
# the old one instead of replacing it through `flatpak update`.
- --filesystem=~/.var/app/ai.storyteller.solvecraft:ro
# Deliberately not granted: network (the optional --control server listens on 127.0.0.1 in
# the sandbox; `flatpak override --user --share=network ai.storyteller.solvecraft` exposes it to
# the sandbox; `flatpak override --user --share=network ai.storyteller.solidcraft` exposes it to
# host agents) and home/host filesystem access.
modules:
- name: solidcraft
@@ -37,10 +42,10 @@ modules:
- 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
- install -Dm644 share/applications/ai.storyteller.solidcraft.desktop /app/share/applications/ai.storyteller.solidcraft.desktop
- install -Dm644 share/mime/packages/ai.storyteller.solidcraft.xml /app/share/mime/packages/ai.storyteller.solidcraft.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
- install -Dm644 share/metainfo/ai.storyteller.solidcraft.metainfo.xml /app/share/metainfo/ai.storyteller.solidcraft.metainfo.xml
- 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
+7 -7
View File
@@ -28,7 +28,7 @@ contact: https://github.com/bherbruck/solidcraft/issues
website: https://bherbruck.github.io/solidcraft/
source-code: https://github.com/bherbruck/solidcraft
issues: https://github.com/bherbruck/solidcraft/issues
icon: stage/share/icons/hicolor/512x512/apps/ai.storyteller.solvecraft.png
icon: stage/share/icons/hicolor/512x512/apps/ai.storyteller.solidcraft.png
base: core24
grade: stable
confinement: strict
@@ -41,10 +41,10 @@ platforms:
apps:
solidcraft:
command: usr/bin/solidcraft
desktop: usr/share/applications/ai.storyteller.solvecraft.desktop
# Ties the snap to the AppStream id (ai.storyteller.solvecraft, a compatibility shim kept by
# every package; see docs/compatibility.md) so software centres treat it as the same app.
common-id: ai.storyteller.solvecraft
desktop: usr/share/applications/ai.storyteller.solidcraft.desktop
# Ties the snap to the AppStream id (ai.storyteller.solidcraft, the same in every package) so
# software centres treat it as the same app.
common-id: ai.storyteller.solidcraft
extensions: [gnome]
plugs:
- desktop
@@ -77,5 +77,5 @@ parts:
override-prime: |
craftctl default
# Point the menu entry at the packaged icon (the hicolor name isn't on the host's path).
sed -i 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/ai.storyteller.solvecraft.png|' \
usr/share/applications/ai.storyteller.solvecraft.desktop
sed -i 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/ai.storyteller.solidcraft.png|' \
usr/share/applications/ai.storyteller.solidcraft.desktop
+1 -2
View File
@@ -13,8 +13,7 @@ set -euo pipefail
# shellcheck source=../env.sh
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
HERE="$ROOT/packaging/macos"
# macOS has no Flatpak-style update channel keyed by this id, so it follows the new name
# (the Linux reverse-DNS APP_ID stays as a compatibility shim; docs/compatibility.md).
# The same reverse-DNS id as the Linux APP_ID.
MACOS_BUNDLE_ID=ai.storyteller.solidcraft
ARCH=universal