From 00952d19962a7240e5d6b7b78b4591d5828d3dae Mon Sep 17 00:00:00 2001 From: Nozzit <150452728+Nozzit@users.noreply.github.com> Date: Thu, 30 Jul 2026 14:24:41 +0200 Subject: [PATCH] feat(snap): Snap Store packaging for Open CAD Studio Repackages the released Linux AppImage (--appimage-extract) instead of rebuilding from source, triggered on completion of the "Release" workflow. Publishing to the Snap Store only happens once SNAPCRAFT_STORE_CREDENTIALS is set as a repo secret. Different from the other OpenAEC snaps in one important way: OCS uses winit + wgpu (Vulkan/GL), not GTK/WebKit, so this uses explicit opengl/wayland/x11 plugs and staged Mesa/X11/Wayland runtime libraries instead of the `gnome` extension. Higher confidence in the packaging mechanics (build succeeds, produces a valid .snap) than in the actual runtime GPU/windowing behavior under strict confinement, which needs real hands-on testing on a Linux desktop before publishing to stable -- see the PR description. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snap.yml | 169 +++++++++++++++++++++++++++++++++++++ snap/snapcraft.yaml | 84 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 .github/workflows/snap.yml create mode 100644 snap/snapcraft.yaml diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml new file mode 100644 index 00000000..b142dc64 --- /dev/null +++ b/.github/workflows/snap.yml @@ -0,0 +1,169 @@ +name: Build Snap Package + +on: + # workflow_run werkt alleen als dit workflowbestand op de DEFAULT branch staat. + # De workflow wordt getriggerd zodra "Release" afrondt (die triggert zelf op + # `release: published` + workflow_dispatch, niet op elke main-push). + workflow_run: + workflows: ["Release"] + types: [completed] + workflow_dispatch: + inputs: + version: + description: 'Bestaande release-tag om tegen te bouwen (test)' + required: true + default: 'v0.9.0' + +# Minimale default; de build-snap-job zet zelf `contents: write` omdat hij de +# snap als asset aan de release hangt. +permissions: + contents: read + +# Eén globale groep, NIET annuleren: twee releases kort na elkaar publiceren +# allebei naar het `stable`-kanaal van de Snap Store, en een Snap-release is +# niet in te trekken. +concurrency: + group: snap + cancel-in-progress: false + +jobs: + build-snap: + timeout-minutes: 75 + runs-on: ubuntu-22.04 + # `types: [completed]` vuurt OOK als de release-workflow faalde of werd + # geannuleerd; zonder deze gate zou een mislukte release alsnog een snap + # bouwen. Geen extra head_branch-guard nodig zoals bij open-calc-studio: + # "Release" triggert uitsluitend op `release: published` + workflow_dispatch, + # nooit op een gewone main-push, dus er is geen vervuilingsscenario. + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + permissions: + contents: write + env: + # Job-level zodat de gated publish-stap er in `if:` naar kan verwijzen. + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + + steps: + - uses: actions/checkout@v5 + with: + # Bij workflow_run checkt checkout standaard de DEFAULT BRANCH uit, + # niet de tag/commit die de release-workflow bouwde — fout, want + # snap/snapcraft.yaml moet van de gebouwde commit komen. Daarom + # expliciet de head_sha van die workflow; voor workflow_dispatch + # (geen workflow_run-event) valt die weg naar github.ref (de branch + # waarop handmatig werd gedispatcht). + ref: ${{ github.event.workflow_run.head_sha || github.ref }} + + - name: Get version + id: get-version + env: + # Niet-vertrouwde invoer via env, NOOIT rechtstreeks ${} in de shell. + # GITHUB_*-namen zelf NIET zetten: gereserveerd en al aanwezig in de runner. + EVENT_NAME: ${{ github.event_name }} + DISPATCH_VERSION: ${{ github.event.inputs.version }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + VERSION="$DISPATCH_VERSION" + elif [ "$EVENT_NAME" = "workflow_run" ]; then + # head_branch is voor een `release`-getriggerde workflow_run niet + # betrouwbaar gegarandeerd de tagnaam (kan de default branch zijn) — + # daarom altijd de laatste gepubliceerde release opzoeken i.p.v. op + # head_branch te vertrouwen. + if printf '%s' "$HEAD_BRANCH" | grep -qE '^v[0-9]'; then + VERSION="$HEAD_BRANCH" + else + VERSION="$(gh release list --exclude-drafts --limit 1 --json tagName --jq '.[0].tagName // empty')" + fi + else + VERSION="" + fi + if [ -z "$VERSION" ]; then + echo "::error::Kon geen versie afleiden (event=$EVENT_NAME, head_branch=$HEAD_BRANCH)" + exit 1 + fi + # `version` en `version_number` gaan verderop een `sed`-VERVANGINGSPATROON + # in, en een dispatch-input is vrije tekst — een '/' of '&' herschrijft dat + # patroon, een nieuwe regel schrijft een tweede regel in $GITHUB_OUTPUT + # (output-injectie). Whitelist: de `case` dekt de hele waarde (dus ook + # nieuwe regels), de regex daarna de structuur. + case "$VERSION" in + *[!v0-9.]*) + echo "::error::Versie bevat tekens buiten 'v', cijfers en punten." >&2 + exit 1 + ;; + esac + if ! printf '%s' "$VERSION" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Versie '$VERSION' heeft niet de vorm vX.Y.Z." >&2 + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "version_number=${VERSION#v}" >> "$GITHUB_OUTPUT" + + # We bouwen de Rust-app NIET opnieuw: de snap herverpakt de AppImage van de + # gepubliceerde GitHub-release (--appimage-extract i.p.v. dpkg-deb -x, want + # dit project levert geen .deb). + - name: Wait for release AppImage and download it + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.get-version.outputs.version }} + run: | + echo "Wachten op AppImage-asset van release $VERSION..." + found="" + for i in $(seq 1 10); do + if gh release view "$VERSION" --json assets --jq '.assets[].name' 2>/dev/null | grep -q '\.AppImage$'; then + found=1 + echo "AppImage-asset gevonden" + break + fi + echo "nog geen AppImage-asset... (poging $i/10)" + sleep 15 + done + if [ -z "$found" ]; then + echo "::error::AppImage-asset voor $VERSION verscheen niet op tijd" + exit 1 + fi + gh release download "$VERSION" -p '*.AppImage' --clobber --dir . + appimage=$(ls *.AppImage | head -1) + cp "$appimage" open-cad-studio.AppImage + echo "Gebruik $appimage -> open-cad-studio.AppImage" + + - name: Set version in snapcraft.yaml + env: + VERSION_NUMBER: ${{ steps.get-version.outputs.version_number }} + run: | + sed -i "s/^version: .*/version: '$VERSION_NUMBER'/" snap/snapcraft.yaml + grep '^version:' snap/snapcraft.yaml + + - name: Build snap + uses: snapcore/action-build@v1 + id: snapcraft + + # Echte release (via workflow_run): snap als asset aan de release hangen. + - name: Upload snap to GitHub release + if: github.event_name == 'workflow_run' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.get-version.outputs.version }} + SNAP: ${{ steps.snapcraft.outputs.snap }} + run: | + gh release upload "$VERSION" "$SNAP" --clobber + + # Handmatige test: snap als CI-artifact (de release NIET aanraken). + - name: Upload snap as workflow artifact + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: open-cad-studio-snap + path: ${{ steps.snapcraft.outputs.snap }} + if-no-files-found: error + + # Live publiceren naar de Snap Store: alleen op een echte release én pas + # zodra het store-credentials-secret bestaat (eigenaar-stap) — én pas + # zodra de naam `open-cad-studio` op snapcraft.io geregistreerd is. + - name: Publish to Snap Store + if: github.event_name == 'workflow_run' && env.SNAPCRAFT_STORE_CREDENTIALS != '' + uses: snapcore/action-publish@v1 + with: + snap: ${{ steps.snapcraft.outputs.snap }} + release: stable diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 00000000..d6ae6258 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,84 @@ +name: open-cad-studio +base: core22 +# Versie wordt in CI overschreven met het release-versienummer (zie .github/workflows/snap.yml). +version: '0.0.0' +title: Open CAD Studio +summary: 2D drafting and 3D modeling with native DWG/DXF support +description: | + Open CAD Studio (OCS) is a CAD application for 2D drafting and 3D + modeling, built in Rust. Reads and writes DWG and DXF files natively + (R13 through R2018). + + File formats: + - DWG and DXF read/write (R13-R2018) + - STL export, STEP AP203 export, OBJ import + - PDF export (plot layouts) + - WBLOCK, and XREF (attach/reload/auto-resolve external references) + + 2D drafting: + - Lines, polylines, rectangles, polygons, circles, arcs, ellipses, + splines + - Hatch fills with pattern, scale, and angle editing + - Offset, trim, extend, fillet, break, stretch, lengthen + - Array, mirror, move, copy, rotate, scale, explode + - Double-click text editing, and area/perimeter/centroid measurement + + 3D modeling: + - Solid primitives (box, sphere, cylinder), extrude, revolve, loft, + sweep, and 3D array + - ACIS tessellation for rendering 3DSOLID/REGION/BODY entities + + Open CAD Studio is open source, part of the OpenAEC Foundation family + of applications, and rendered with wgpu for GPU-accelerated 2D/3D + drawing. + +grade: stable +confinement: strict + +apps: + open-cad-studio: + command: usr/bin/OpenCADStudio + desktop: usr/share/applications/io.github.HakanSeven12.OpenCadStudio.desktop + plugs: + - home # tekeningen openen/opslaan/exporteren in de home-map + - removable-media # idem op externe schijven/USB + - network # licentie-/Patreon-check + - opengl # GPU-toegang voor wgpu (Vulkan/GL) + - wayland + - x11 + - desktop + - desktop-legacy + +parts: + open-cad-studio: + plugin: nil + # De CI-stap kopieert de gepubliceerde release-AppImage naar de project-root + # als `open-cad-studio.AppImage`; we pakken die uit i.p.v. opnieuw te bouwen + # (dezelfde herverpak-aanpak als de .deb-gebaseerde OpenAEC-snaps, maar dan + # voor een AppImage: `--appimage-extract` levert een kant-en-klare AppDir). + override-build: | + set -eu + chmod +x "${CRAFT_PROJECT_DIR}/open-cad-studio.AppImage" + "${CRAFT_PROJECT_DIR}/open-cad-studio.AppImage" --appimage-extract + mkdir -p "${CRAFT_PART_INSTALL}/usr" + cp -r squashfs-root/usr/. "${CRAFT_PART_INSTALL}/usr/" + # OCS gebruikt winit (X11/Wayland) + wgpu (Vulkan met GL-fallback), geen + # GTK/WebKit — dus GEEN `extensions: [gnome]` (dat is voor de Tauri-apps). + # libvulkan1/mesa-vulkan-drivers zijn een fallback; echte hardware-versnelling + # loopt via de `opengl`-plug naar de host-driver. + stage-packages: + - libvulkan1 + - mesa-vulkan-drivers + - libgl1 + - libx11-6 + - libxcursor1 + - libxi6 + - libxrandr2 + - libxinerama1 + - libxkbcommon0 + - libxkbcommon-x11-0 + - libwayland-client0 + - libwayland-cursor0 + - libwayland-egl1 + - libfontconfig1 + - libfreetype6