Snap package: strict core24 snap on every release, optional Snap Store upload (#413)
* Snap package: build, test and attach a strict snap to every release packaging/linux/snap/snapcraft.yaml (core24, strict, gnome extension) repackages the release's Linux tarball like the Flatpak does. The release workflow builds it for amd64 and arm64, installs it with --dangerous, models a box in the confined app on headless X11 and Wayland, and attaches solidcraft-<version>-linux-<arch>.snap plus the version-less copy. When SNAPCRAFT_STORE_CREDENTIALS is set, nightlies go to edge and release drafts to candidate, and snap-store.yml promotes candidate to stable when a release is published. The README gets a Snap badge and install line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT * snap smoke: stop the app's whole process group, refuse a busy control port Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT * snap smoke: quit the app cleanly between backends Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cddbfedb28
commit
daed2686bc
10 files changed
+484
-4
No files matched your search
@@ -32,7 +32,8 @@ old-name Pages addresses are not permanent aliases.
|
|||||||
- **macOS 11+** (Apple silicon and Intel): open the `.dmg` and drag `SolidCraft.app` onto Applications.
|
- **macOS 11+** (Apple silicon and Intel): open the `.dmg` and drag `SolidCraft.app` onto Applications.
|
||||||
The CLI is in `solidcraft-cli-{version}-macos-universal.zip`.
|
The CLI is in `solidcraft-cli-{version}-macos-universal.zip`.
|
||||||
- **Linux** (x86_64 and aarch64, glibc 2.35+): the `.AppImage` (`chmod +x`, then run it), the
|
- **Linux** (x86_64 and aarch64, glibc 2.35+): the `.AppImage` (`chmod +x`, then run it), the
|
||||||
`.deb` or `.rpm`, the `.flatpak` (`flatpak install --user <file>`), or the `.tar.gz`.
|
`.deb` or `.rpm`, the `.flatpak` (`flatpak install --user <file>`), the `.snap`
|
||||||
|
(`sudo snap install --dangerous <file>`, or `sudo snap install solidcraft` from the Snap Store), or the `.tar.gz`.
|
||||||
- **FreeBSD:** `tar -xzf solidcraft-{version}-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local`.
|
- **FreeBSD:** `tar -xzf solidcraft-{version}-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local`.
|
||||||
- **Web:** `solidcraft-web-{version}.zip` is a static site to host yourself.
|
- **Web:** `solidcraft-web-{version}.zip` is a static site to host yourself.
|
||||||
- `SHA256SUMS.txt`: `sha256sum -c SHA256SUMS.txt --ignore-missing`.
|
- `SHA256SUMS.txt`: `sha256sum -c SHA256SUMS.txt --ignore-missing`.
|
||||||
|
|||||||
@@ -57,3 +57,5 @@ jobs:
|
|||||||
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"
|
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"
|
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'"
|
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'"
|
||||||
|
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
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# At midnight Eastern, changed main commits build and publish a nightly pre-release.
|
# At midnight Eastern, changed main commits build and publish a nightly pre-release.
|
||||||
# A version with a suffix (0.2.0-rc.1) is a pre-release. Nothing is signed yet.
|
# A version with a suffix (0.2.0-rc.1) is a pre-release. Nothing is signed yet.
|
||||||
#
|
#
|
||||||
# Linux x86_64, aarch64 AppImage (+ .zsync), .deb, .rpm, .flatpak, .tar.gz packaging/linux
|
# Linux x86_64, aarch64 AppImage (+ .zsync), .deb, .rpm, .flatpak, .snap, .tar.gz packaging/linux
|
||||||
# FreeBSD x86_64 .tar.gz (built in a FreeBSD VM) packaging/freebsd
|
# FreeBSD x86_64 .tar.gz (built in a FreeBSD VM) packaging/freebsd
|
||||||
# macOS universal .dmg, and the CLI as a .zip packaging/macos
|
# macOS universal .dmg, and the CLI as a .zip packaging/macos
|
||||||
# Windows x64, x86, arm64 .msi and a portable .zip; x64 also the plain .exe packaging/windows
|
# Windows x64, x86, arm64 .msi and a portable .zip; x64 also the plain .exe packaging/windows
|
||||||
@@ -199,6 +199,59 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
snap:
|
||||||
|
name: Snap ${{ matrix.arch }}
|
||||||
|
needs: [version, linux]
|
||||||
|
if: needs.version.outputs.build == 'true'
|
||||||
|
# Repackages the Linux job's tarball, like the Flatpak. On 24.04 because the core24 base is
|
||||||
|
# Ubuntu 24.04, which lets snapcraft build on the runner itself (--destructive-mode).
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { arch: x86_64, runner: ubuntu-24.04 }
|
||||||
|
- { arch: aarch64, runner: ubuntu-24.04-arm }
|
||||||
|
env:
|
||||||
|
SOLIDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||||||
|
SOLIDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||||
|
with:
|
||||||
|
name: release-linux-${{ matrix.arch }}
|
||||||
|
path: dist/linux
|
||||||
|
- name: Install snapcraft and the smoke-test tools
|
||||||
|
run: |
|
||||||
|
sudo snap install snapcraft --classic
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends xvfb xauth weston dbus-x11
|
||||||
|
- name: Build the snap
|
||||||
|
run: |
|
||||||
|
sudo --preserve-env=SOLIDCRAFT_VERSION,SOLIDCRAFT_BUILD_DATE env "PATH=$PATH" \
|
||||||
|
packaging/linux/snap-pack.sh --destructive-mode "dist/linux/solidcraft-$SOLIDCRAFT_VERSION-linux-${{ matrix.arch }}.tar.gz"
|
||||||
|
sudo chown -R "$(id -u):$(id -g)" dist target
|
||||||
|
- name: Install it and start the app on headless X11 and Wayland
|
||||||
|
run: |
|
||||||
|
snap_file="dist/release/solidcraft-$SOLIDCRAFT_VERSION-linux-${{ matrix.arch }}.snap"
|
||||||
|
sudo snap install --dangerous "$snap_file"
|
||||||
|
snap info --verbose "$snap_file" | head -n 12
|
||||||
|
snap connections solidcraft
|
||||||
|
solidcraft.cli --version
|
||||||
|
# Snaps expect the login session's runtime directory, which runners don't create.
|
||||||
|
sudo install -d -m 700 -o "$(id -u)" -g "$(id -g)" "/run/user/$(id -u)"
|
||||||
|
python3 packaging/linux/snap-smoke.py --out snap-smoke
|
||||||
|
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
|
with:
|
||||||
|
name: snap-smoke-${{ matrix.arch }}
|
||||||
|
path: snap-smoke/*.png
|
||||||
|
if-no-files-found: error
|
||||||
|
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||||
|
with:
|
||||||
|
name: release-snap-${{ matrix.arch }}
|
||||||
|
path: dist/release/*.snap
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
freebsd:
|
freebsd:
|
||||||
name: FreeBSD x86_64
|
name: FreeBSD x86_64
|
||||||
needs: version
|
needs: version
|
||||||
@@ -337,7 +390,7 @@ jobs:
|
|||||||
|
|
||||||
release:
|
release:
|
||||||
name: GitHub Release or dry-run artifacts
|
name: GitHub Release or dry-run artifacts
|
||||||
needs: [version, linux, flatpak, freebsd, macos, windows, web]
|
needs: [version, linux, flatpak, snap, freebsd, macos, windows, web]
|
||||||
if: needs.version.outputs.build == 'true'
|
if: needs.version.outputs.build == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -425,6 +478,35 @@ jobs:
|
|||||||
echo
|
echo
|
||||||
sed 's/^/ /' SHA256SUMS.txt
|
sed 's/^/ /' SHA256SUMS.txt
|
||||||
} >>"$GITHUB_STEP_SUMMARY"
|
} >>"$GITHUB_STEP_SUMMARY"
|
||||||
|
# Snap Store: nightlies go to the edge channel and release drafts to candidate, only after
|
||||||
|
# every package built and the GitHub release was staged. Publishing the GitHub release
|
||||||
|
# promotes candidate to stable (snap-store.yml). Dry runs never upload. Skipped until the
|
||||||
|
# owner adds the SNAPCRAFT_STORE_CREDENTIALS secret (docs/releasing.md).
|
||||||
|
- id: snapstore
|
||||||
|
name: Check for Snap Store credentials
|
||||||
|
env:
|
||||||
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$SNAPCRAFT_STORE_CREDENTIALS" ]; then
|
||||||
|
echo "configured=true" >>"$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "No SNAPCRAFT_STORE_CREDENTIALS secret: skipping the Snap Store upload."
|
||||||
|
fi
|
||||||
|
- name: Upload the snaps to the Snap Store
|
||||||
|
if: >-
|
||||||
|
steps.snapstore.outputs.configured == 'true' && (github.event_name == 'schedule' ||
|
||||||
|
(github.ref == 'refs/heads/release' && (github.event_name == 'push' || inputs.draft)))
|
||||||
|
env:
|
||||||
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||||
|
working-directory: artifacts
|
||||||
|
run: |
|
||||||
|
sudo snap install snapcraft --classic
|
||||||
|
channel=candidate
|
||||||
|
if [ "$GITHUB_EVENT_NAME" = schedule ]; then channel=edge; fi
|
||||||
|
for arch in x86_64 aarch64; do
|
||||||
|
snapcraft upload --release="$channel" "solidcraft-$VERSION-linux-$arch.snap"
|
||||||
|
done
|
||||||
|
echo "Uploaded the snaps to the Snap Store's $channel channel." >>"$GITHUB_STEP_SUMMARY"
|
||||||
- name: Keep the files (dry run)
|
- name: Keep the files (dry run)
|
||||||
if: github.event_name == 'workflow_dispatch' && !inputs.draft
|
if: github.event_name == 'workflow_dispatch' && !inputs.draft
|
||||||
run: cp notes.md artifacts/release-notes.md
|
run: cp notes.md artifacts/release-notes.md
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Publishing a GitHub release (not a prerelease) promotes the snaps that release.yml uploaded to
|
||||||
|
# the Snap Store's candidate channel to stable, so `sudo snap install solidcraft` gets it.
|
||||||
|
# Skipped until the owner adds the SNAPCRAFT_STORE_CREDENTIALS secret; see docs/releasing.md.
|
||||||
|
name: Snap Store
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [released]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: snap-store
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
promote:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- id: creds
|
||||||
|
name: Check for Snap Store credentials
|
||||||
|
env:
|
||||||
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$SNAPCRAFT_STORE_CREDENTIALS" ]; then
|
||||||
|
echo "configured=true" >>"$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "No SNAPCRAFT_STORE_CREDENTIALS secret: nothing to promote."
|
||||||
|
fi
|
||||||
|
- name: Promote candidate to stable
|
||||||
|
if: steps.creds.outputs.configured == 'true'
|
||||||
|
env:
|
||||||
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||||
|
run: |
|
||||||
|
sudo snap install snapcraft --classic
|
||||||
|
snapcraft status solidcraft
|
||||||
|
snapcraft promote solidcraft --from-channel=latest/candidate --to-channel=latest/stable --yes
|
||||||
|
snapcraft status solidcraft
|
||||||
@@ -15,6 +15,7 @@ model through the built-in MCP server.
|
|||||||
[![Download for Windows][badge-windows]][windows-msi]
|
[![Download for Windows][badge-windows]][windows-msi]
|
||||||
[![Download for macOS][badge-macos]][macos-dmg]
|
[![Download for macOS][badge-macos]][macos-dmg]
|
||||||
[![Download for Linux][badge-linux]][linux-appimage]
|
[![Download for Linux][badge-linux]][linux-appimage]
|
||||||
|
[![Get it from the Snap Store][badge-snap]][snap-store]
|
||||||
[![Try it in your browser][badge-web]][web-app]
|
[![Try it in your browser][badge-web]][web-app]
|
||||||
|
|
||||||
- **Windows:** download and run the [installer][windows-msi].
|
- **Windows:** download and run the [installer][windows-msi].
|
||||||
@@ -25,6 +26,7 @@ model through the built-in MCP server.
|
|||||||
- **Linux:** download the [AppImage][linux-appimage], allow it to run as a program and open it.
|
- **Linux:** download the [AppImage][linux-appimage], allow it to run as a program and open it.
|
||||||
Or install the [.deb][linux-deb] (Ubuntu, Debian, Mint) or [.rpm][linux-rpm] (Fedora,
|
Or install the [.deb][linux-deb] (Ubuntu, Debian, Mint) or [.rpm][linux-rpm] (Fedora,
|
||||||
openSUSE), or use [Flatpak](https://bherbruck.github.io/solidcraft/guide/getting-started.html#linux-flatpak).
|
openSUSE), or use [Flatpak](https://bherbruck.github.io/solidcraft/guide/getting-started.html#linux-flatpak).
|
||||||
|
With Snap: `sudo snap install solidcraft` ([Snap Store][snap-store], or the [.snap file][linux-snap]).
|
||||||
For ARM64: [AppImage][linux-arm64-appimage], [.deb][linux-arm64-deb], [.rpm][linux-arm64-rpm].
|
For ARM64: [AppImage][linux-arm64-appimage], [.deb][linux-arm64-deb], [.rpm][linux-arm64-rpm].
|
||||||
- **Web:** [try it in your browser][web-app], no install.
|
- **Web:** [try it in your browser][web-app], no install.
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ Install details, upgrading from earlier builds and building from source are in
|
|||||||
[badge-windows]: https://img.shields.io/badge/Windows-Download-0078D6?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0yIDJoOS41djkuNUgyek0xMi41IDJIMjJ2OS41aC05LjV6TTIgMTIuNWg5LjVWMjJIMnpNMTIuNSAxMi41SDIyVjIyaC05LjV6Ii8%2BPC9zdmc%2B
|
[badge-windows]: https://img.shields.io/badge/Windows-Download-0078D6?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0id2hpdGUiPjxwYXRoIGQ9Ik0yIDJoOS41djkuNUgyek0xMi41IDJIMjJ2OS41aC05LjV6TTIgMTIuNWg5LjVWMjJIMnpNMTIuNSAxMi41SDIyVjIyaC05LjV6Ii8%2BPC9zdmc%2B
|
||||||
[badge-macos]: https://img.shields.io/badge/macOS-Download-333333?style=for-the-badge&logo=apple&logoColor=white
|
[badge-macos]: https://img.shields.io/badge/macOS-Download-333333?style=for-the-badge&logo=apple&logoColor=white
|
||||||
[badge-linux]: https://img.shields.io/badge/Linux-AppImage-E95420?style=for-the-badge&logo=linux&logoColor=white
|
[badge-linux]: https://img.shields.io/badge/Linux-AppImage-E95420?style=for-the-badge&logo=linux&logoColor=white
|
||||||
|
[badge-snap]: https://img.shields.io/badge/Snap-Snap_Store-82BEA0?style=for-the-badge&logo=snapcraft&logoColor=white
|
||||||
[badge-web]: https://img.shields.io/badge/Web-Try_in_browser-2EA44F?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjxwYXRoIGQ9Ik0yIDEyaDIwTTEyIDJhMTUgMTUgMCAwIDEgMCAyME0xMiAyYTE1IDE1IDAgMCAwIDAgMjAiLz48L3N2Zz4%3D
|
[badge-web]: https://img.shields.io/badge/Web-Try_in_browser-2EA44F?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjxwYXRoIGQ9Ik0yIDEyaDIwTTEyIDJhMTUgMTUgMCAwIDEgMCAyME0xMiAyYTE1IDE1IDAgMCAwIDAgMjAiLz48L3N2Zz4%3D
|
||||||
[windows-msi]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-windows-x64.msi
|
[windows-msi]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-windows-x64.msi
|
||||||
[windows-arm64-msi]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-windows-arm64.msi
|
[windows-arm64-msi]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-windows-arm64.msi
|
||||||
@@ -50,6 +53,8 @@ Install details, upgrading from earlier builds and building from source are in
|
|||||||
[linux-arm64-appimage]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.AppImage
|
[linux-arm64-appimage]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.AppImage
|
||||||
[linux-arm64-deb]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.deb
|
[linux-arm64-deb]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.deb
|
||||||
[linux-arm64-rpm]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.rpm
|
[linux-arm64-rpm]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-aarch64.rpm
|
||||||
|
[linux-snap]: https://github.com/bherbruck/solidcraft/releases/latest/download/solidcraft-linux-x86_64.snap
|
||||||
|
[snap-store]: https://snapcraft.io/solidcraft
|
||||||
[web-app]: https://bherbruck.github.io/solidcraft/app/
|
[web-app]: https://bherbruck.github.io/solidcraft/app/
|
||||||
|
|
||||||
**Documentation:** the [SolidCraft book](https://bherbruck.github.io/solidcraft/) (user guide,
|
**Documentation:** the [SolidCraft book](https://bherbruck.github.io/solidcraft/) (user guide,
|
||||||
|
|||||||
@@ -51,6 +51,22 @@ missing runtime. Launch with `flatpak run ai.storyteller.solvecraft`, rather tha
|
|||||||
a host `solidcraft` binary. Files chosen through Open/Save use the desktop file-chooser portal;
|
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.
|
command-line paths outside Documents may need a specific additional filesystem permission.
|
||||||
|
|
||||||
|
### Linux Snap
|
||||||
|
|
||||||
|
On Ubuntu and other distributions with snapd:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo snap install solidcraft
|
||||||
|
solidcraft # or open SolidCraft from the app menu
|
||||||
|
solidcraft.cli --version # the CLI, for scripts and `solidcraft.cli mcp`
|
||||||
|
```
|
||||||
|
|
||||||
|
The snap can open designs in your home folder (not hidden folders) and, through the file
|
||||||
|
chooser, anywhere else. For USB drives and other mounted disks, run
|
||||||
|
`sudo snap connect solidcraft:removable-media` once. A release's `.snap` file installs without
|
||||||
|
the store with `sudo snap install --dangerous ./solidcraft-VERSION-linux-ARCH.snap`; it then
|
||||||
|
doesn't update automatically.
|
||||||
|
|
||||||
## Web
|
## Web
|
||||||
|
|
||||||
The same app runs in the browser: [open SolidCraft on the web](../app/) (`?sample` opens the sample
|
The same app runs in the browser: [open SolidCraft on the web](../app/) (`?sample` opens the sample
|
||||||
|
|||||||
+50
-1
@@ -15,7 +15,7 @@ Every release asset, binary, installer product name and package uses the SolidCr
|
|||||||
copies, the deprecated old-name CLI alias); keep them until that document says they can go. Do not
|
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.
|
rewrite published assets, checksums, tags or historical release notes of earlier versions.
|
||||||
|
|
||||||
The main installers (`.msi`, portable `.zip`, `.dmg`, `.AppImage`, `.deb`, `.rpm`, `.flatpak`) are
|
The main installers (`.msi`, portable `.zip`, `.dmg`, `.AppImage`, `.deb`, `.rpm`, `.flatpak`, `.snap`) are
|
||||||
also attached without the version in their names, for example `solidcraft-windows-x64.msi` and
|
also attached without the version in their names, for example `solidcraft-windows-x64.msi` and
|
||||||
`solidcraft-linux-x86_64.AppImage`, and listed in `SHA256SUMS.txt`. The README's Download section
|
`solidcraft-linux-x86_64.AppImage`, and listed in `SHA256SUMS.txt`. The README's Download section
|
||||||
links to `https://github.com/bherbruck/solidcraft/releases/latest/download/<name>`, which follows
|
links to `https://github.com/bherbruck/solidcraft/releases/latest/download/<name>`, which follows
|
||||||
@@ -145,6 +145,55 @@ docs updates include the latest stable Flatpak release too. Verify the resulting
|
|||||||
not only the dispatch job, so a release cannot erase the docs or app and docs updates cannot
|
not only the dispatch job, so a release cannot erase the docs or app and docs updates cannot
|
||||||
erase the package repository.
|
erase the package repository.
|
||||||
|
|
||||||
|
## Snap package and Snap Store
|
||||||
|
|
||||||
|
Every release and nightly attaches `solidcraft-<version>-linux-<arch>.snap` for x86_64 and
|
||||||
|
aarch64 (`packaging/linux/snap-pack.sh`, manifest `packaging/linux/snap/snapcraft.yaml`). Like the
|
||||||
|
Flatpak, it repackages the Linux job's tarball. The `snap` job installs it with `--dangerous`,
|
||||||
|
starts the confined app on headless X11 and Wayland (`packaging/linux/snap-smoke.py`), models a
|
||||||
|
box over the control channel and keeps the captured frames as the `snap-smoke-<arch>` artifact.
|
||||||
|
|
||||||
|
The Snap Store steps run only when the `SNAPCRAFT_STORE_CREDENTIALS` repository secret exists;
|
||||||
|
without it they are skipped and the `.snap` files are still attached to the release:
|
||||||
|
|
||||||
|
- nightlies are uploaded to `latest/edge`;
|
||||||
|
- release drafts (pushes to `release`) are uploaded to `latest/candidate`, after every package
|
||||||
|
built and the draft was staged;
|
||||||
|
- publishing a non-prerelease GitHub release runs `snap-store.yml`, which promotes
|
||||||
|
`latest/candidate` to `latest/stable`. As with the Flatpak repository, a publication made with
|
||||||
|
`GITHUB_TOKEN` doesn't trigger it: then run `gh workflow run snap-store.yml`.
|
||||||
|
|
||||||
|
To enable the Snap Store once (owner):
|
||||||
|
|
||||||
|
1. Sign in at <https://snapcraft.io> with an Ubuntu One account and register the name at
|
||||||
|
<https://snapcraft.io/register-snap> (or `snapcraft login` then `snapcraft register solidcraft`).
|
||||||
|
2. On a machine with snapcraft (`sudo snap install snapcraft --classic`), export upload-only
|
||||||
|
credentials for this snap:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
snapcraft export-login --snaps=solidcraft \
|
||||||
|
--channels=edge,candidate,stable \
|
||||||
|
--acls=package_access,package_push,package_update,package_release \
|
||||||
|
--expires=2027-10-01 solidcraft-store-credentials.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Store them as the repository secret and delete the file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gh secret set SNAPCRAFT_STORE_CREDENTIALS -R bherbruck/solidcraft < solidcraft-store-credentials.txt
|
||||||
|
rm solidcraft-store-credentials.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Optional: fill in the store listing at <https://snapcraft.io/solidcraft/listing> (the icon,
|
||||||
|
summary and description come from the snap; add screenshots and the category "Art and design").
|
||||||
|
5. The next nightly fills `edge` and the next release fills `candidate`, then `stable` on
|
||||||
|
publication. To fill `stable` from an already published release right away, download its
|
||||||
|
`.snap` files and run `snapcraft upload --release=stable <file>` for each.
|
||||||
|
|
||||||
|
Renew the credentials before `--expires` by repeating steps 2 and 3. The snap uses strict
|
||||||
|
confinement and only plugs that connect automatically, apart from `removable-media`, which users
|
||||||
|
connect with `sudo snap connect solidcraft:removable-media` (getting-started.md).
|
||||||
|
|
||||||
## Repository setup
|
## Repository setup
|
||||||
|
|
||||||
The `release` branch is advanced only for intentional release batches. Restrict its writers to
|
The `release` branch is advanced only for intentional release batches. Restrict its writers to
|
||||||
|
|||||||
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Repackage a SolidCraft Linux tarball as a snap:
|
||||||
|
#
|
||||||
|
# $DIST/solidcraft-<version>-linux-<arch>.snap
|
||||||
|
#
|
||||||
|
# Usage: packaging/linux/snap-pack.sh [--destructive-mode] [TARBALL]
|
||||||
|
#
|
||||||
|
# TARBALL defaults to $DIST/solidcraft-<version>-linux-<arch>.tar.gz from package.sh. The snap is
|
||||||
|
# built for the host architecture (x86_64 or aarch64), which must match the tarball's. Needs
|
||||||
|
# snapcraft (`sudo snap install snapcraft --classic`) and, without --destructive-mode, LXD.
|
||||||
|
# --destructive-mode builds on the host itself, which must be Ubuntu 24.04 (the core24 base),
|
||||||
|
# as in CI. Install and smoke-test the result with:
|
||||||
|
# sudo snap install --dangerous dist/release/solidcraft-<version>-linux-<arch>.snap
|
||||||
|
# python3 packaging/linux/snap-smoke.py
|
||||||
|
# Manifest: packaging/linux/snap/snapcraft.yaml.
|
||||||
|
set -euo pipefail
|
||||||
|
# shellcheck source=../env.sh
|
||||||
|
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||||
|
HERE="$ROOT/packaging/linux"
|
||||||
|
|
||||||
|
MODE=(--use-lxd)
|
||||||
|
TARBALL=""
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--destructive-mode) MODE=(--destructive-mode); shift ;;
|
||||||
|
-h | --help) sed -n '2,15p' "$0"; exit 0 ;;
|
||||||
|
-*) echo "unknown argument: $1" >&2; exit 2 ;;
|
||||||
|
*) TARBALL="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64) SNAP_ARCH=amd64 ;;
|
||||||
|
aarch64 | arm64) ARCH=aarch64; SNAP_ARCH=arm64 ;;
|
||||||
|
*) echo "unsupported architecture $ARCH" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
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; }
|
||||||
|
command -v snapcraft >/dev/null || { echo "error: snapcraft not found (sudo snap install snapcraft --classic)" >&2; exit 1; }
|
||||||
|
# Snap versions: at most 32 characters of [A-Za-z0-9.+~-].
|
||||||
|
if ! printf '%s' "$VERSION" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9.+~-]{0,31}$'; then
|
||||||
|
echo "error: version $VERSION is not a valid snap version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> SolidCraft $VERSION snap for $SNAP_ARCH from $(basename "$TARBALL")"
|
||||||
|
|
||||||
|
WORK="$CARGO_TARGET_DIR/snap-pack"
|
||||||
|
rm -rf "$WORK"
|
||||||
|
mkdir -p "$WORK/snap" "$WORK/stage"
|
||||||
|
tar -xzf "$TARBALL" -C "$WORK/stage" --strip-components=1
|
||||||
|
[ -x "$WORK/stage/bin/solidcraft" ] || { echo "error: $TARBALL has no bin/solidcraft" >&2; exit 1; }
|
||||||
|
cp "$HERE/snap/snapcraft.yaml" "$WORK/snap/snapcraft.yaml"
|
||||||
|
printf '%s\n' "$VERSION" >"$WORK/version.txt"
|
||||||
|
|
||||||
|
(cd "$WORK" && snapcraft pack "${MODE[@]}" --build-for="$SNAP_ARCH" --output="$WORK/out.snap")
|
||||||
|
|
||||||
|
OUT="$DIST/$BASENAME.snap"
|
||||||
|
mv "$WORK/out.snap" "$OUT"
|
||||||
|
echo "wrote $OUT"
|
||||||
|
ls -lh "$OUT"
|
||||||
Executable
+141
@@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Start the installed SolidCraft snap on headless X11 and Wayland and model a box in it.
|
||||||
|
|
||||||
|
Runs the confined app (`snap install --dangerous` first) with its normal plugs, drives it over
|
||||||
|
the control channel from the host (snaps share the host's network namespace), checks a
|
||||||
|
30x20x10 box measures 6000 mm³ and that the GPU drew a non-blank frame, and copies each frame
|
||||||
|
to --out (default: the current directory) as snap-<backend>.png. Needs xvfb-run and weston.
|
||||||
|
Reuses the frame checks of flatpak-smoke.py.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
_spec = importlib.util.spec_from_file_location("flatpak_smoke", HERE / "flatpak-smoke.py")
|
||||||
|
flatpak_smoke = importlib.util.module_from_spec(_spec)
|
||||||
|
_spec.loader.exec_module(flatpak_smoke)
|
||||||
|
|
||||||
|
PORTS = {"x11": 18839, "wayland": 18840}
|
||||||
|
REQUESTS = [
|
||||||
|
{"id": 1, "method": "ui.inspect", "params": {}},
|
||||||
|
{"id": 2, "method": "engine.execute",
|
||||||
|
"params": {"command": "solid.box", "params": {"length": 30, "width": 20, "height": 10}}},
|
||||||
|
{"id": 3, "method": "document.inspect", "params": {"measure": True}},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def drive(app, port, frame):
|
||||||
|
"""Connect to the app's control port, model the box and capture a frame."""
|
||||||
|
deadline = time.monotonic() + 60
|
||||||
|
while True:
|
||||||
|
if app.poll() is not None:
|
||||||
|
raise RuntimeError(f"the app exited with {app.returncode} before listening")
|
||||||
|
try:
|
||||||
|
conn = socket.create_connection(("127.0.0.1", port), timeout=60)
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
if time.monotonic() >= deadline:
|
||||||
|
raise RuntimeError("the control port never opened") from None
|
||||||
|
time.sleep(.2)
|
||||||
|
with conn, conn.makefile("rw") as io:
|
||||||
|
replies = []
|
||||||
|
for request in REQUESTS + [{"id": 4, "method": "ui.screenshot", "params": {"path": str(frame)}},
|
||||||
|
{"id": 5, "method": "app.quit", "params": {"force": True}}]:
|
||||||
|
io.write(json.dumps(request) + "\n")
|
||||||
|
io.flush()
|
||||||
|
replies.append(io.readline())
|
||||||
|
renderer = flatpak_smoke.validate_replies("".join(replies[:3]))
|
||||||
|
capture = json.loads(replies[3])
|
||||||
|
if any(json.loads(reply).get("ok") is not True for reply in replies[4:]):
|
||||||
|
raise ValueError(f"app.quit failed: {replies[4:]}")
|
||||||
|
if capture.get("ok") is not True or renderer != "gpu":
|
||||||
|
raise ValueError(f"GPU frame capture failed: renderer={renderer}, {capture}")
|
||||||
|
return renderer, flatpak_smoke.frame_statistics(frame.read_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
def run_backend(backend, out):
|
||||||
|
runtime = Path(f"/run/user/{os.getuid()}")
|
||||||
|
if not runtime.is_dir():
|
||||||
|
raise RuntimeError(f"{runtime} is missing: snaps expect the login session's runtime directory")
|
||||||
|
env = dict(os.environ, XDG_RUNTIME_DIR=str(runtime))
|
||||||
|
for name in ("WAYLAND_DISPLAY", "WAYLAND_SOCKET", "DISPLAY", "SOLIDCRAFT_WAYLAND", "SOLVECRAFT_WAYLAND"):
|
||||||
|
env.pop(name, None)
|
||||||
|
# The snap can write to the (non-hidden) home folder, not to the host's /tmp.
|
||||||
|
work = Path(tempfile.mkdtemp(prefix="solidcraft-snap-smoke-", dir=Path.home()))
|
||||||
|
compositor = None
|
||||||
|
log = (work / "app.log").open("wb")
|
||||||
|
try:
|
||||||
|
# No private dbus-run-session bus: snapd moves the app into its own systemd scope through
|
||||||
|
# the session's systemd, which a private bus doesn't have.
|
||||||
|
prefix = []
|
||||||
|
if backend == "x11":
|
||||||
|
prefix += ["xvfb-run", "-a", "-s", "-screen 0 1600x1000x24 -nolisten tcp"]
|
||||||
|
else:
|
||||||
|
# The wayland plug only allows sockets named wayland-N in the runtime directory.
|
||||||
|
env["WAYLAND_DISPLAY"] = "wayland-77"
|
||||||
|
compositor = subprocess.Popen(["weston", "--backend=headless", "--renderer=pixman", "--no-config",
|
||||||
|
"--socket=wayland-77", "--width=1600", "--height=1000"],
|
||||||
|
env=env, stdout=log, stderr=subprocess.STDOUT)
|
||||||
|
deadline = time.monotonic() + 20
|
||||||
|
while not (runtime / "wayland-77").exists():
|
||||||
|
if compositor.poll() is not None or time.monotonic() >= deadline:
|
||||||
|
raise RuntimeError("headless Weston failed")
|
||||||
|
time.sleep(.1)
|
||||||
|
with socket.socket() as probe:
|
||||||
|
if probe.connect_ex(("127.0.0.1", PORTS[backend])) == 0:
|
||||||
|
raise RuntimeError(f"port {PORTS[backend]} is already in use (another SolidCraft?)")
|
||||||
|
# Its own process group, so stopping it also stops the app under xvfb-run.
|
||||||
|
app = subprocess.Popen(prefix + ["/snap/bin/solidcraft", "--control", str(PORTS[backend])],
|
||||||
|
env=env, stdout=log, stderr=subprocess.STDOUT, start_new_session=True)
|
||||||
|
try:
|
||||||
|
renderer, stats = drive(app, PORTS[backend], work / "frame.png")
|
||||||
|
# app.quit exits cleanly, so the next run doesn't report a crash.
|
||||||
|
app.wait(timeout=30)
|
||||||
|
finally:
|
||||||
|
if app.poll() is None:
|
||||||
|
os.killpg(app.pid, signal.SIGTERM)
|
||||||
|
try:
|
||||||
|
app.wait(timeout=15)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
os.killpg(app.pid, signal.SIGKILL)
|
||||||
|
app.wait()
|
||||||
|
shutil.copy(work / "frame.png", out / f"snap-{backend}.png")
|
||||||
|
print(f"snap {backend}: PASS renderer={renderer}, box=6000 mm³, frame={stats}")
|
||||||
|
except BaseException:
|
||||||
|
log.flush()
|
||||||
|
sys.stderr.write((work / "app.log").read_text(errors="replace")[-8000:])
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
log.close()
|
||||||
|
if compositor:
|
||||||
|
compositor.terminate()
|
||||||
|
compositor.wait(timeout=10)
|
||||||
|
shutil.rmtree(work, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--backend", choices=["x11", "wayland", "both"], default="both")
|
||||||
|
parser.add_argument("--out", type=Path, default=Path.cwd())
|
||||||
|
args = parser.parse_args()
|
||||||
|
args.out.mkdir(parents=True, exist_ok=True)
|
||||||
|
for backend in (["x11", "wayland"] if args.backend == "both" else [args.backend]):
|
||||||
|
run_backend(backend, args.out.resolve())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except (OSError, ValueError, KeyError, TypeError, RuntimeError, subprocess.SubprocessError) as error:
|
||||||
|
print(f"snap desktop smoke failed: {error}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# Snap package (solidcraft-<version>-linux-<arch>.snap on every GitHub release, and the Snap
|
||||||
|
# Store when its credentials are configured). Build it with packaging/linux/snap-pack.sh.
|
||||||
|
#
|
||||||
|
# Like the Flatpak, it repackages the release's own Linux tarball (built on Ubuntu 22.04 by
|
||||||
|
# package.sh) instead of compiling Rust: the same binaries as the AppImage/deb/rpm. snap-pack.sh
|
||||||
|
# unpacks the tarball into stage/ next to snap/ and writes the package version to version.txt.
|
||||||
|
# The binaries need glibc >= 2.35 (core24 has 2.39) and load X11, Wayland, xkbcommon and Vulkan
|
||||||
|
# at runtime: the gnome extension supplies the desktop libraries and the GPU userspace (mesa,
|
||||||
|
# through the gpu-2404 content interface) for the wgpu viewport.
|
||||||
|
name: solidcraft
|
||||||
|
title: SolidCraft
|
||||||
|
summary: Open-source parametric 3D CAD
|
||||||
|
description: |
|
||||||
|
SolidCraft is an open-source parametric 3D CAD application written in Rust. Sketch on a
|
||||||
|
plane, constrain and dimension the sketch, turn profiles into solids on a feature timeline,
|
||||||
|
and change a parameter to watch the timeline rebuild.
|
||||||
|
|
||||||
|
- Sketches with a constraint solver and parameter-driven dimensions
|
||||||
|
- Extrude, revolve, sweep, loft, fillet, chamfer, shell, holes and patterns
|
||||||
|
- Components and joints, sheet metal, and a plastic-enclosure subset
|
||||||
|
- STEP and IGES import and export; 3MF, STL and OBJ
|
||||||
|
- Every action is a command, scriptable from a CLI, a JSON control channel and an MCP server
|
||||||
|
|
||||||
|
The command-line tool is `solidcraft.cli` (for example `solidcraft.cli mcp`).
|
||||||
|
adopt-info: solidcraft
|
||||||
|
license: MIT OR Apache-2.0
|
||||||
|
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
|
||||||
|
base: core24
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
# lzo starts faster than the default xz, at a somewhat larger download.
|
||||||
|
compression: lzo
|
||||||
|
platforms:
|
||||||
|
amd64:
|
||||||
|
arm64:
|
||||||
|
|
||||||
|
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
|
||||||
|
extensions: [gnome]
|
||||||
|
plugs:
|
||||||
|
- desktop
|
||||||
|
- desktop-legacy
|
||||||
|
- wayland
|
||||||
|
- x11
|
||||||
|
- opengl
|
||||||
|
# Designs anywhere in the home folder (not hidden files) and on USB drives; Open/Save
|
||||||
|
# also work through the desktop file-chooser portal.
|
||||||
|
- home
|
||||||
|
- removable-media
|
||||||
|
# Opening links (Create Issue, documentation); the optional --control server listens
|
||||||
|
# on 127.0.0.1 for agents and tests.
|
||||||
|
- network
|
||||||
|
- network-bind
|
||||||
|
cli:
|
||||||
|
command: usr/bin/solidcraft-cli
|
||||||
|
plugs: [home, removable-media, network, network-bind]
|
||||||
|
|
||||||
|
parts:
|
||||||
|
solidcraft:
|
||||||
|
plugin: dump
|
||||||
|
source: stage
|
||||||
|
organize:
|
||||||
|
bin/: usr/bin/
|
||||||
|
share/: usr/share/
|
||||||
|
override-pull: |
|
||||||
|
craftctl default
|
||||||
|
craftctl set version="$(cat "$CRAFT_PROJECT_DIR/version.txt")"
|
||||||
|
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
|
||||||
Reference in new issue
Block a user