diff --git a/.gitignore b/.gitignore index 999a052..baa40f8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,15 @@ frontend/node_modules/ frontend/dist/ build/bin/verstak-desktop +build/appicon.png +build/windows/ build/windows-amd64/ +build/linux-amd64/ +build/appimage/ +build/deb/ +build/downloads/ +build/tools/ +build/webview2-runtime/ smoke-platform plugins/ vendor/ diff --git a/README.md b/README.md index f473255..7a77c8d 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,7 @@ No server account or browser extension is needed for a local desktop vault. ## Build the desktop and official plugins -The current release scripts target Linux. Install Go 1.24+, Node.js 20+ with -npm, Python 3, the [Wails v2 build +To build from source, install Go 1.24+, Node.js 20+ with npm, Python 3, the [Wails v2 build prerequisites](https://wails.io/docs/gettingstarted/installation/), and your distribution's WebKitGTK development package. @@ -78,29 +77,45 @@ diagnostic logs: ./build/bin/verstak-desktop --debug ``` -## Windows test bundle from Linux +## Portable test artifacts -The current Windows path is intended for manual testing, not publication. On a -Linux host install MinGW, then run: +These commands make local alpha artifacts in `release/`. They do not create a +GitHub Release. ```bash -sudo apt install gcc-mingw-w64-x86-64 -./scripts/build-windows.sh +# Debian 13 / Ubuntu 24.04 or later. APT installs WebKitGTK dependencies. +./scripts/package-deb.sh v0.1.0-alpha.1 + +# Portable Linux x86_64 AppImage with bundled WebKitGTK runtime and plugins. +./scripts/package-appimage.sh v0.1.0-alpha.1 ``` -The script builds Windows amd64 plugin packages from the sibling -`verstak-official-plugins` checkout, cross-compiles the Wails host, and writes -a folder ready to copy to a Windows machine: +Install the Debian package with `sudo apt install ./release/verstak_*.deb`, +then launch `verstak`. Run the AppImage directly after making it executable: -```text -build/windows-amd64/ -├── verstak-desktop.exe -└── plugins/ +```bash +chmod +x release/verstak-linux-x86_64-v0.1.0-alpha.1.AppImage +./release/verstak-linux-x86_64-v0.1.0-alpha.1.AppImage ``` -Copy the whole directory to Windows and run `verstak-desktop.exe`. Windows -requires the Microsoft WebView2 Runtime; Windows 11 normally includes it. -No Windows GitHub Release is created by this command. +On distributions without FUSE support, use +`APPIMAGE_EXTRACT_AND_RUN=1 ./release/verstak-linux-x86_64-v0.1.0-alpha.1.AppImage`. + +Build the Windows portable ZIP on Linux with MinGW: + +```bash +sudo apt install gcc-mingw-w64-x86-64 cabextract zip +./scripts/package-windows-portable.sh v0.1.0-alpha.1 +``` + +The script downloads the current x64 Microsoft Fixed Version WebView2 Runtime, +bundles it next to `verstak-desktop.exe`, and writes +`release/verstak-windows-amd64-.zip`. On Windows, extract the ZIP to a +local disk (not a network share) and launch `Verstak.cmd`. No separate WebView2 +installation or download is required. To make a reproducible build from an +already downloaded CAB, set `VERSTAK_WEBVIEW2_CAB=/path/to/FixedVersionRuntime.x64.cab`. + +Each format is listed in `release/SHA256SUMS` after packaging. ## First local vault @@ -154,25 +169,26 @@ README](https://github.com/mirivlad/verstak-sync-server#readme). In the desktop application, open the Sync plugin, enter the server URL and user credentials, test the connection, then select **Connect**. Each vault is paired separately. -## Release artifacts +## Separate plugin archives -Maintainers can produce a self-contained Linux desktop archive after the SDK -and official plugins have been built: +The desktop artifacts already include matching official plugins. To distribute +plugins separately, build OS-specific archives in the sibling repository: ```bash -cd verstak -./scripts/release.sh v0.1.0-alpha.1 +cd ../verstak-official-plugins +sudo apt install gcc-mingw-w64-x86-64 zip +./scripts/package-portable.sh v0.1.0-alpha.1 ``` -It writes `release/verstak-desktop-linux-amd64-.tar.gz` and a matching -`SHA256SUMS` file. Unpack the archive and run the included executable; its -matching plugins are already in the archive. The browser extension, SDK, -official plugins and sync server have their own build or release instructions -in their repositories. +It writes a Linux `tar.gz` and a Windows ZIP. Both archives expand directly +into the `plugins/` directory beside the corresponding desktop executable. +Separate archives are required because `platform-test` includes a native +sidecar for its target OS. ## Publish a GitHub Release -After checking the local archive, publish the same assets to GitHub: +The current publisher still uses the legacy Linux tarball. It will be switched +to the portable artifacts after they have been manually checked: ```bash ./scripts/publish-github-release.sh v0.1.0-alpha.1 diff --git a/main.go b/main.go index 8bb4e8d..4777c5d 100644 --- a/main.go +++ b/main.go @@ -243,7 +243,7 @@ func main() { } // ─── Wails App ─────────────────────────────────────────── - err = wails.Run(&options.App{ + appOptions := &options.App{ Title: "Verstak", Width: 1200, Height: 800, @@ -257,7 +257,9 @@ func main() { Bind: []interface{}{ app, }, - }) + } + applyPlatformOptions(appOptions) + err = wails.Run(appOptions) if err != nil { log.Fatalf("Error: %v", err) diff --git a/packaging/deb/control b/packaging/deb/control new file mode 100644 index 0000000..92c096a --- /dev/null +++ b/packaging/deb/control @@ -0,0 +1,11 @@ +Package: verstak +Version: @VERSION@ +Section: utils +Priority: optional +Architecture: amd64 +Depends: libwebkit2gtk-4.1-0, libgtk-3-0t64 | libgtk-3-0 +Maintainer: Verstak contributors +Homepage: https://github.com/mirivlad/verstak +Description: Local-first workspace + Verstak is a local-first workspace for files, notes, browser captures, + activity and work-journal entries. diff --git a/packaging/deb/verstak b/packaging/deb/verstak new file mode 100755 index 0000000..07c9db7 --- /dev/null +++ b/packaging/deb/verstak @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +exec /opt/verstak/verstak-desktop "$@" diff --git a/packaging/linux/AppRun b/packaging/linux/AppRun new file mode 100755 index 0000000..80597d8 --- /dev/null +++ b/packaging/linux/AppRun @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +set -eu + +HERE="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +export APPDIR="$HERE" +export LD_LIBRARY_PATH="$HERE/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export GIO_EXTRA_MODULES="$HERE/usr/lib/gio/modules" +export GSETTINGS_SCHEMA_DIR="$HERE/usr/share/glib-2.0/schemas" +export GST_PLUGIN_SYSTEM_PATH_1_0="$HERE/usr/lib/gstreamer-1.0" +export WEBKIT_EXEC_PATH="$HERE/usr/lib/webkit2gtk-4.1" + +cd "$HERE/usr/bin" +exec "$HERE/usr/bin/verstak-desktop" "$@" diff --git a/packaging/linux/verstak.desktop b/packaging/linux/verstak.desktop new file mode 100644 index 0000000..4afe4cb --- /dev/null +++ b/packaging/linux/verstak.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Verstak +Comment=Local-first workspace +Exec=verstak-desktop %U +Icon=verstak +Terminal=false +Categories=Office;Utility; +StartupNotify=true diff --git a/packaging/linux/verstak.svg b/packaging/linux/verstak.svg new file mode 100644 index 0000000..23a9fe8 --- /dev/null +++ b/packaging/linux/verstak.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/windows/Verstak.cmd b/packaging/windows/Verstak.cmd new file mode 100644 index 0000000..a169559 --- /dev/null +++ b/packaging/windows/Verstak.cmd @@ -0,0 +1,13 @@ +@echo off +setlocal +set "ROOT=%~dp0" + +if not exist "%ROOT%webview2\msedgewebview2.exe" ( + echo The bundled WebView2 runtime is missing. + pause + exit /b 1 +) + +icacls "%ROOT%webview2" /grant *S-1-15-2-2:(OI)(CI)(RX) /T /C >nul 2>&1 +icacls "%ROOT%webview2" /grant *S-1-15-2-1:(OI)(CI)(RX) /T /C >nul 2>&1 +start "" "%ROOT%verstak-desktop.exe" %* diff --git a/platform_options_other.go b/platform_options_other.go new file mode 100644 index 0000000..d346049 --- /dev/null +++ b/platform_options_other.go @@ -0,0 +1,7 @@ +//go:build !windows + +package main + +import "github.com/wailsapp/wails/v2/pkg/options" + +func applyPlatformOptions(_ *options.App) {} diff --git a/platform_options_windows.go b/platform_options_windows.go new file mode 100644 index 0000000..279d9d6 --- /dev/null +++ b/platform_options_windows.go @@ -0,0 +1,35 @@ +//go:build windows + +package main + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +func applyPlatformOptions(app *options.App) { + executablePath, err := os.Executable() + if err != nil { + return + } + + runtimePath := bundledWebView2Path(executablePath) + if _, err := os.Stat(filepath.Join(runtimePath, "msedgewebview2.exe")); err != nil { + return + } + + // Fixed Version WebView2 runtimes from v120 need these permissions on + // unpackaged Windows 10 applications. The archive is extracted by the user, + // so the command can update permissions without an installer or download. + for _, sid := range []string{"*S-1-15-2-2", "*S-1-15-2-1"} { + _ = exec.Command("icacls", runtimePath, "/grant", sid+":(OI)(CI)(RX)", "/T", "/C").Run() + } + + app.Windows = &windows.Options{ + WebviewBrowserPath: runtimePath, + } +} diff --git a/runtime_path.go b/runtime_path.go new file mode 100644 index 0000000..3fa1ac3 --- /dev/null +++ b/runtime_path.go @@ -0,0 +1,7 @@ +package main + +import "path/filepath" + +func bundledWebView2Path(executablePath string) string { + return filepath.Join(filepath.Dir(executablePath), "webview2") +} diff --git a/runtime_path_test.go b/runtime_path_test.go new file mode 100644 index 0000000..d3b6192 --- /dev/null +++ b/runtime_path_test.go @@ -0,0 +1,11 @@ +package main + +import "testing" + +func TestBundledWebView2PathUsesExecutableDirectory(t *testing.T) { + got := bundledWebView2Path("/opt/verstak/verstak-desktop") + want := "/opt/verstak/webview2" + if got != want { + t.Fatalf("bundledWebView2Path() = %q, want %q", got, want) + } +} diff --git a/scripts/build-linux-bundle.sh b/scripts/build-linux-bundle.sh new file mode 100755 index 0000000..2eda918 --- /dev/null +++ b/scripts/build-linux-bundle.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +OFFICIAL_PLUGINS="${VERSTAK_OFFICIAL_PLUGINS_DIR:-$ROOT/../verstak-official-plugins}" +OUTPUT="${VERSTAK_LINUX_BUNDLE_DIR:-$ROOT/build/linux-amd64}" + +if [[ ! -d "$OFFICIAL_PLUGINS" ]]; then + echo "official plugins repository not found: $OFFICIAL_PLUGINS" >&2 + exit 1 +fi + +echo "=== verstak desktop Linux amd64 bundle ===" +(cd "$OFFICIAL_PLUGINS" && ./scripts/build.sh) +"$ROOT/scripts/install-dev-plugins.sh" +"$ROOT/scripts/build.sh" + +BINARY="$ROOT/build/bin/verstak-desktop" +if [[ ! -x "$BINARY" ]]; then + echo "desktop binary was not produced: $BINARY" >&2 + exit 1 +fi + +rm -rf "$OUTPUT" +mkdir -p "$OUTPUT" +install -m 755 "$BINARY" "$OUTPUT/verstak-desktop" +install -m 644 "$ROOT/README.md" "$ROOT/LICENSE" "$OUTPUT/" +cp -R "$ROOT/plugins" "$OUTPUT/plugins" +chmod -R a+rX "$OUTPUT/plugins" + +echo "Linux bundle: $OUTPUT" diff --git a/scripts/build-windows.sh b/scripts/build-windows.sh index 3ba4357..b631e5e 100755 --- a/scripts/build-windows.sh +++ b/scripts/build-windows.sh @@ -53,9 +53,12 @@ if [[ ! -d "$WINDOWS_PLUGIN_DIST" ]]; then exit 1 fi -# Wails generates Windows resources and invokes the supplied MinGW compiler. -# -clean resets build/bin, so copy the finished executable only afterwards. -"$WAILS" build -clean -platform windows/amd64 -compiler "$WINDOWS_CC" -o verstak-desktop.exe +# Wails' -compiler option selects a Go binary, not a C compiler. Cross-CGO +# therefore has to be supplied through the standard Go environment instead. +# The runtime is bundled by package-windows-portable.sh, so never compile an +# Evergreen downloader into the portable executable. +CC="$WINDOWS_CC" CGO_ENABLED=1 "$WAILS" build -clean -platform windows/amd64 \ + -webview2 error -o verstak-desktop.exe WINDOWS_BINARY="$ROOT/build/bin/verstak-desktop.exe" if [[ ! -f "$WINDOWS_BINARY" ]]; then diff --git a/scripts/package-appimage.sh b/scripts/package-appimage.sh new file mode 100755 index 0000000..c3e3d59 --- /dev/null +++ b/scripts/package-appimage.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +VERSION="${1:-}" +APPIMAGETOOL_URL="${APPIMAGETOOL_URL:-https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage}" + +if [[ -z "$VERSION" || ! "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then + echo "usage: $0 " >&2 + echo "example: $0 v0.1.0-alpha.1" >&2 + exit 2 +fi +if ! command -v ldd >/dev/null || ! command -v file >/dev/null; then + echo "ldd and file are required to bundle AppImage runtime libraries" >&2 + exit 1 +fi + +"$ROOT/scripts/build-linux-bundle.sh" + +APPIMAGETOOL="${APPIMAGETOOL_BIN:-$ROOT/build/tools/appimagetool-x86_64.AppImage}" +if [[ ! -x "$APPIMAGETOOL" ]]; then + if ! command -v curl >/dev/null; then + echo "appimagetool is missing; install curl or set APPIMAGETOOL_BIN" >&2 + exit 1 + fi + mkdir -p "$(dirname "$APPIMAGETOOL")" + curl --fail --location --show-error "$APPIMAGETOOL_URL" -o "$APPIMAGETOOL" + chmod +x "$APPIMAGETOOL" +fi + +BUNDLE="${VERSTAK_LINUX_BUNDLE_DIR:-$ROOT/build/linux-amd64}" +APPDIR="$ROOT/build/appimage/AppDir" +RELEASE_ROOT="$ROOT/release" +ARCHIVE="$RELEASE_ROOT/verstak-linux-x86_64-$VERSION.AppImage" +WEBKIT_RUNTIME_DIR="${VERSTAK_WEBKIT_RUNTIME_DIR:-/usr/lib/x86_64-linux-gnu/webkit2gtk-4.1}" + +if [[ ! -d "$WEBKIT_RUNTIME_DIR" ]] || [[ ! -x "$WEBKIT_RUNTIME_DIR/WebKitWebProcess" ]]; then + echo "WebKitWebProcess was not found at $WEBKIT_RUNTIME_DIR" >&2 + echo "Set VERSTAK_WEBKIT_RUNTIME_DIR to the WebKitGTK 4.1 runtime directory." >&2 + exit 1 +fi + +rm -rf "$APPDIR" "$ARCHIVE" +mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/lib" "$APPDIR/usr/share/applications" \ + "$APPDIR/usr/share/icons/hicolor/scalable/apps" +install -m 755 "$ROOT/packaging/linux/AppRun" "$APPDIR/AppRun" +install -m 755 "$BUNDLE/verstak-desktop" "$APPDIR/usr/bin/verstak-desktop" +install -m 644 "$ROOT/packaging/linux/verstak.desktop" "$APPDIR/verstak.desktop" +install -m 644 "$ROOT/packaging/linux/verstak.desktop" "$APPDIR/usr/share/applications/verstak.desktop" +install -m 644 "$ROOT/packaging/linux/verstak.svg" "$APPDIR/verstak.svg" +install -m 644 "$ROOT/packaging/linux/verstak.svg" "$APPDIR/usr/share/icons/hicolor/scalable/apps/verstak.svg" +cp -R "$BUNDLE/plugins" "$APPDIR/usr/bin/plugins" +cp -a "$WEBKIT_RUNTIME_DIR" "$APPDIR/usr/lib/webkit2gtk-4.1" + +copy_runtime_dir() { + local source="$1" + local target="$2" + if [[ -d "$source" ]]; then + mkdir -p "$target" + cp -a "$source/." "$target/" + fi +} + +copy_runtime_dir /usr/lib/x86_64-linux-gnu/gio/modules "$APPDIR/usr/lib/gio/modules" +copy_runtime_dir /usr/lib/x86_64-linux-gnu/gstreamer-1.0 "$APPDIR/usr/lib/gstreamer-1.0" +copy_runtime_dir /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0 "$APPDIR/usr/lib/gdk-pixbuf-2.0" +copy_runtime_dir /usr/share/glib-2.0/schemas "$APPDIR/usr/share/glib-2.0/schemas" + +declare -A queued=() +queue_elf() { + local candidate="$1" + [[ -f "$candidate" ]] || return + if [[ "$(file --brief "$candidate")" != *ELF* ]]; then + return + fi + if [[ -z "${queued[$candidate]:-}" ]]; then + queued[$candidate]=1 + printf '%s\n' "$candidate" >> "$APPDIR/.elf-queue" + fi +} + +queue_elf "$APPDIR/usr/bin/verstak-desktop" +while IFS= read -r candidate; do + queue_elf "$candidate" +done < <(find "$APPDIR/usr/lib/webkit2gtk-4.1" "$APPDIR/usr/lib/gio/modules" \ + "$APPDIR/usr/lib/gstreamer-1.0" "$APPDIR/usr/lib/gdk-pixbuf-2.0" -type f) + +while IFS= read -r candidate; do + if ldd "$candidate" | grep -q 'not found'; then + echo "unresolved shared library in $candidate" >&2 + exit 1 + fi + while IFS= read -r library; do + case "$(basename "$library")" in + libc.so.6|libm.so.6|libpthread.so.0|librt.so.1|libdl.so.2|ld-linux-x86-64.so.2) + continue + ;; + esac + bundled="$APPDIR/usr/lib/$(basename "$library")" + if [[ ! -e "$bundled" ]]; then + cp -aL "$library" "$bundled" + fi + queue_elf "$bundled" + done < <(ldd "$candidate" | awk '/=> \/[^ ]+/ { print $3 } /^\// { print $1 }') +done < "$APPDIR/.elf-queue" +rm -f "$APPDIR/.elf-queue" + +if command -v glib-compile-schemas >/dev/null; then + glib-compile-schemas "$APPDIR/usr/share/glib-2.0/schemas" +fi + +mkdir -p "$RELEASE_ROOT" +ARCH=x86_64 APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGETOOL" "$APPDIR" "$ARCHIVE" +(cd "$RELEASE_ROOT" && find . -maxdepth 1 -type f \( -name '*.deb' -o -name '*.AppImage' -o -name '*.zip' \) \ + -printf '%f\n' | LC_ALL=C sort | xargs -r sha256sum > SHA256SUMS) + +echo "AppImage: $ARCHIVE" +echo "checksums: $RELEASE_ROOT/SHA256SUMS" diff --git a/scripts/package-deb.sh b/scripts/package-deb.sh new file mode 100755 index 0000000..92aa425 --- /dev/null +++ b/scripts/package-deb.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +VERSION="${1:-}" + +if [[ -z "$VERSION" || ! "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then + echo "usage: $0 " >&2 + echo "example: $0 v0.1.0-alpha.1" >&2 + exit 2 +fi +if ! command -v dpkg-deb >/dev/null; then + echo "dpkg-deb is required to create a Debian package" >&2 + exit 1 +fi + +"$ROOT/scripts/build-linux-bundle.sh" + +PACKAGE_VERSION="${VERSION#v}" +BUNDLE="${VERSTAK_LINUX_BUNDLE_DIR:-$ROOT/build/linux-amd64}" +STAGING="$ROOT/build/deb/verstak_$PACKAGE_VERSION" +RELEASE_ROOT="$ROOT/release" +ARCHIVE="$RELEASE_ROOT/verstak_${PACKAGE_VERSION}_amd64.deb" + +rm -rf "$STAGING" "$ARCHIVE" +mkdir -p "$STAGING/DEBIAN" "$STAGING/opt/verstak" "$STAGING/usr/bin" \ + "$STAGING/usr/share/applications" "$STAGING/usr/share/icons/hicolor/scalable/apps" \ + "$STAGING/usr/share/doc/verstak" + +sed "s/@VERSION@/$PACKAGE_VERSION/g" "$ROOT/packaging/deb/control" > "$STAGING/DEBIAN/control" +install -m 755 "$ROOT/packaging/deb/verstak" "$STAGING/usr/bin/verstak" +install -m 755 "$BUNDLE/verstak-desktop" "$STAGING/opt/verstak/verstak-desktop" +install -m 644 "$BUNDLE/README.md" "$BUNDLE/LICENSE" "$STAGING/usr/share/doc/verstak/" +install -m 644 "$ROOT/packaging/linux/verstak.desktop" "$STAGING/usr/share/applications/verstak.desktop" +install -m 644 "$ROOT/packaging/linux/verstak.svg" "$STAGING/usr/share/icons/hicolor/scalable/apps/verstak.svg" +cp -R "$BUNDLE/plugins" "$STAGING/opt/verstak/plugins" + +mkdir -p "$RELEASE_ROOT" +dpkg-deb --root-owner-group --build "$STAGING" "$ARCHIVE" +(cd "$RELEASE_ROOT" && find . -maxdepth 1 -type f \( -name '*.deb' -o -name '*.AppImage' -o -name '*.zip' \) \ + -printf '%f\n' | LC_ALL=C sort | xargs -r sha256sum > SHA256SUMS) + +echo "Debian package: $ARCHIVE" +echo "checksums: $RELEASE_ROOT/SHA256SUMS" diff --git a/scripts/package-windows-portable.sh b/scripts/package-windows-portable.sh new file mode 100755 index 0000000..6536839 --- /dev/null +++ b/scripts/package-windows-portable.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +VERSION="${1:-}" +WEBVIEW2_CAB="${VERSTAK_WEBVIEW2_CAB:-}" +WEBVIEW2_URL="${VERSTAK_WEBVIEW2_URL:-}" + +if [[ -z "$VERSION" || ! "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then + echo "usage: $0 " >&2 + echo "example: $0 v0.1.0-alpha.1" >&2 + exit 2 +fi +for command in cabextract curl python3 zip; do + if ! command -v "$command" >/dev/null; then + echo "$command is required to create the Windows portable archive" >&2 + exit 1 + fi +done + +if [[ -z "$WEBVIEW2_CAB" ]]; then + if [[ -z "$WEBVIEW2_URL" ]]; then + WEBVIEW2_URL="$(python3 - <<'PY' +import re +import sys +import urllib.request + +with urllib.request.urlopen('https://developer.microsoft.com/en-us/microsoft-edge/webview2/', timeout=30) as response: + page = response.read().decode('utf-8') +page = page.replace(r'\u002F', '/') +match = re.search(r'"x64","(https://[^\"]+Microsoft\.WebView2\.FixedVersionRuntime[^\"]+\.x64\.cab)"', page) +if not match: + sys.exit('could not locate the x64 FixedVersionRuntime CAB on the Microsoft WebView2 download page') +print(match.group(1)) +PY +)" + fi + WEBVIEW2_CAB="$ROOT/build/downloads/$(basename "${WEBVIEW2_URL%%\?*}")" + mkdir -p "$(dirname "$WEBVIEW2_CAB")" + if [[ ! -f "$WEBVIEW2_CAB" ]]; then + curl --fail --location --show-error "$WEBVIEW2_URL" -o "$WEBVIEW2_CAB" + fi +fi + +if [[ ! -f "$WEBVIEW2_CAB" ]]; then + echo "FixedVersionRuntime CAB was not found: $WEBVIEW2_CAB" >&2 + exit 1 +fi + +"$ROOT/scripts/build-windows.sh" + +RUNTIME_EXTRACT="$ROOT/build/webview2-runtime" +rm -rf "$RUNTIME_EXTRACT" +mkdir -p "$RUNTIME_EXTRACT" +cabextract -q -d "$RUNTIME_EXTRACT" "$WEBVIEW2_CAB" +WEBVIEW2_RUNTIME_DIR="$(dirname "$(find "$RUNTIME_EXTRACT" -type f -iname msedgewebview2.exe -print -quit)")" +if [[ -z "$WEBVIEW2_RUNTIME_DIR" || ! -f "$WEBVIEW2_RUNTIME_DIR/msedgewebview2.exe" ]]; then + echo "FixedVersionRuntime CAB does not contain msedgewebview2.exe" >&2 + exit 1 +fi + +RELEASE_ROOT="$ROOT/release" +STAGING="$RELEASE_ROOT/verstak-windows-amd64-$VERSION" +ARCHIVE="$RELEASE_ROOT/verstak-windows-amd64-$VERSION.zip" +rm -rf "$STAGING" "$ARCHIVE" +mkdir -p "$STAGING" +cp -R "$ROOT/build/windows-amd64/." "$STAGING/" +install -m 644 "$ROOT/README.md" "$ROOT/LICENSE" "$STAGING/" +install -m 644 "$ROOT/packaging/windows/Verstak.cmd" "$STAGING/Verstak.cmd" +cp -R "$WEBVIEW2_RUNTIME_DIR" "$STAGING/webview2" + +(cd "$RELEASE_ROOT" && zip -qr "$(basename "$ARCHIVE")" "$(basename "$STAGING")") +(cd "$RELEASE_ROOT" && find . -maxdepth 1 -type f \( -name '*.deb' -o -name '*.AppImage' -o -name '*.zip' \) \ + -printf '%f\n' | LC_ALL=C sort | xargs -r sha256sum > SHA256SUMS) + +echo "Windows portable archive: $ARCHIVE" +echo "checksums: $RELEASE_ROOT/SHA256SUMS" diff --git a/scripts/test-build-windows.sh b/scripts/test-build-windows.sh index d712880..19af9af 100755 --- a/scripts/test-build-windows.sh +++ b/scripts/test-build-windows.sh @@ -11,7 +11,9 @@ fi bash -n "$BUILDER" grep -Fq -- '-platform windows/amd64' "$BUILDER" -grep -Fq -- '-compiler "$WINDOWS_CC"' "$BUILDER" +grep -Fq 'CC="$WINDOWS_CC"' "$BUILDER" +grep -Fq 'CGO_ENABLED=1' "$BUILDER" +grep -Fq -- '-webview2 error' "$BUILDER" grep -Fq 'dist-windows' "$BUILDER" grep -Fq 'verstak-desktop.exe' "$BUILDER" grep -Fq 'x86_64-w64-mingw32-gcc' "$BUILDER" diff --git a/scripts/test-package-formats.sh b/scripts/test-package-formats.sh new file mode 100755 index 0000000..126c497 --- /dev/null +++ b/scripts/test-package-formats.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +for script in build-linux-bundle.sh package-deb.sh package-appimage.sh package-windows-portable.sh; do + path="$ROOT/scripts/$script" + if [[ ! -x "$path" ]]; then + echo "packaging script is missing or not executable: $path" >&2 + exit 1 + fi + bash -n "$path" +done + +grep -Fq 'dpkg-deb' "$ROOT/scripts/package-deb.sh" +grep -Fq -- '--build' "$ROOT/scripts/package-deb.sh" +grep -Fq 'packaging/deb/control' "$ROOT/scripts/package-deb.sh" +grep -Fq 'libwebkit2gtk-4.1-0' "$ROOT/packaging/deb/control" +grep -Fq 'appimagetool' "$ROOT/scripts/package-appimage.sh" +grep -Fq 'WebKitWebProcess' "$ROOT/scripts/package-appimage.sh" +grep -Fq 'FixedVersionRuntime' "$ROOT/scripts/package-windows-portable.sh" +grep -Fq 'msedgewebview2.exe' "$ROOT/scripts/package-windows-portable.sh" +grep -Fq 'zip -qr' "$ROOT/scripts/package-windows-portable.sh" +grep -Fq 'WebviewBrowserPath' "$ROOT/platform_options_windows.go" +grep -Fq 'webview2' "$ROOT/platform_options_windows.go" +grep -Fq 'chmod -R a+rX' "$ROOT/scripts/build-linux-bundle.sh" + +echo "desktop package script contracts passed"