diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..aabce0f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +permissions: + contents: write + +env: + APPIMAGE_EXTRACT_AND_RUN: '1' + +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - name: Checkout Reminduck + uses: actions/checkout@v4 + with: + path: reminduck + + - name: Checkout sqgi + uses: actions/checkout@v4 + with: + repository: supercamel/sqgi + path: sqgi + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential cmake ninja-build pkg-config git file ca-certificates wget \ + meson valac gobject-introspection gettext desktop-file-utils appstream \ + libglib2.0-dev libgirepository1.0-dev libffi-dev libcairo2-dev \ + libgtk-4-dev libgranite-7-dev libgee-0.8-dev libsqlite3-dev libportal-dev \ + librsvg2-dev librsvg2-common adwaita-icon-theme hicolor-icon-theme shared-mime-info \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user-static binfmt-support \ + mingw-w64 nsis squashfs-tools + + - name: Build and install sqgi + run: | + cmake -S sqgi -B sqgi/build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSQ_ENABLE_JIT=ON + cmake --build sqgi/build + sudo cmake --install sqgi/build --prefix /usr/local + sudo ldconfig + + - name: Build Linux x86_64 AppImage + working-directory: reminduck + run: sqgipkg --target appimage --appimage-arch x86_64 --sqgi-source-dir "$GITHUB_WORKSPACE/sqgi" + + - name: Build Linux aarch64 AppImage + working-directory: reminduck + run: sqgipkg --target appimage --appimage-arch aarch64 --sqgi-source-dir "$GITHUB_WORKSPACE/sqgi" + + - name: Build Windows installer + working-directory: reminduck + run: sqgipkg --target win-nsis --sqgi-source-dir "$GITHUB_WORKSPACE/sqgi" + + - name: Collect artifacts + working-directory: reminduck + run: | + mkdir -p artifacts + cp dist-linux-x86_64/Reminduck.AppImage artifacts/Reminduck-x86_64.AppImage + cp dist-linux-aarch64/Reminduck.AppImage artifacts/Reminduck-aarch64.AppImage + cp dist-windows-x86_64/Reminduck-Setup.exe artifacts/Reminduck-Setup.exe + ls -lh artifacts + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: reminduck-builds + path: reminduck/artifacts/* + + - name: Publish release assets + if: startsWith(github.ref, 'refs/tags/') + working-directory: reminduck + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "$GITHUB_REF_NAME" artifacts/* \ + --repo "$GITHUB_REPOSITORY" \ + --title "$GITHUB_REF_NAME" \ + --generate-notes \ + || gh release upload "$GITHUB_REF_NAME" artifacts/* \ + --repo "$GITHUB_REPOSITORY" --clobber diff --git a/.gitignore b/.gitignore index 92a1469..f799a2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ builddir +build-linux-*/ +build-windows-*/ +dist-linux-*/ +dist-windows-*/ +.sqgipkg/ .flatpak-builder repo _build -settings.json \ No newline at end of file +settings.json diff --git a/data/meson.build b/data/meson.build index f385411..0f188a3 100644 --- a/data/meson.build +++ b/data/meson.build @@ -8,12 +8,41 @@ gresource_file = configure_file( configuration: config_data, ) -gresource = gnome.compile_resources( - 'gresource', - gresource_file, - source_dir: '.' +glib_compile_resources = find_program('glib-compile-resources', native: true) + +gresource_c = custom_target( + 'gresource_c', + input: gresource_file, + output: 'gresource.c', + command: [ + glib_compile_resources, + '@INPUT@', + '--sourcedir', meson.current_source_dir(), + '--generate-source', + '--target', '@OUTPUT@', + '--dependency-file', '@DEPFILE@', + ], + depfile: 'gresource.c.d', ) +gresource_h = custom_target( + 'gresource_h', + input: gresource_file, + output: 'gresource.h', + command: [ + glib_compile_resources, + '@INPUT@', + '--sourcedir', meson.current_source_dir(), + '--generate-header', + '--target', '@OUTPUT@', + ], +) + +gresource = [ + gresource_c, + gresource_h, +] + #======================== # GSCHEMA @@ -28,7 +57,7 @@ install_data( install_dir: get_option('datadir') / 'glib-2.0' / 'schemas', ) -compile_schemas = find_program('glib-compile-schemas', required: false) +compile_schemas = find_program('glib-compile-schemas', native: true, required: false) if compile_schemas.found() test('Validate schema file', compile_schemas, args: ['--strict', '--dry-run', meson.current_source_dir()]) endif @@ -92,4 +121,4 @@ if not windows_build test('Validate appstream file', appstreamcli, args: ['validate', '--pedantic', '--no-net', '--explain', appstream_file]) endif -endif \ No newline at end of file +endif diff --git a/meson.build b/meson.build index 584b81b..842aaf9 100644 --- a/meson.build +++ b/meson.build @@ -70,8 +70,10 @@ subdir('data') subdir('src') -gnome.post_install( - glib_compile_schemas: true, - gtk_update_icon_cache: true, - update_desktop_database: true - ) +if not windows_build + gnome.post_install( + glib_compile_schemas: true, + gtk_update_icon_cache: true, + update_desktop_database: true + ) +endif diff --git a/sqgipkg.json b/sqgipkg.json new file mode 100644 index 0000000..af7e2c7 --- /dev/null +++ b/sqgipkg.json @@ -0,0 +1,186 @@ +{ + "name": "Reminduck", + "entry": { + "type": "native", + "linux": "build-linux-x86_64/src/io.github.elly_code.reminduck", + "windows": "build-windows-x86_64/src/io.github.elly_code.reminduck.exe" + }, + "linux": { + "arches": [ + { + "arch": "x86_64", + "build_dir": "build-linux-x86_64", + "entry_linux": "build-linux-x86_64/src/io.github.elly_code.reminduck", + "deb": { + "download": true, + "packages": [ + "libgtk-4-dev", + "gir1.2-gtk-4.0", + "libgranite-7-dev", + "gir1.2-granite-7.0", + "libgee-0.8-dev", + "libglib2.0-dev", + "libsqlite3-dev", + "libportal-dev", + "gobject-introspection", + "libgirepository1.0-dev", + "adwaita-icon-theme", + "hicolor-icon-theme", + "librsvg2-common", + "shared-mime-info", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good" + ] + }, + "inherit_native_projects": false, + "native_projects": [ + { + "name": "reminduck", + "dir": ".", + "build": [ + "meson setup \"$SQGI_LINUX_BUILD_DIR\" --wipe --prefix /usr --buildtype=release -Ddevelopment=false -Dlegacy-rdnn=false ${SQGI_LINUX_MESON_CROSS_FILE:+--cross-file \"$SQGI_LINUX_MESON_CROSS_FILE\"} || meson setup \"$SQGI_LINUX_BUILD_DIR\" --prefix /usr --buildtype=release -Ddevelopment=false -Dlegacy-rdnn=false ${SQGI_LINUX_MESON_CROSS_FILE:+--cross-file \"$SQGI_LINUX_MESON_CROSS_FILE\"}", + "meson compile -C \"$SQGI_LINUX_BUILD_DIR\"" + ], + "files": [ + "build-linux-x86_64/data/io.github.elly_code.reminduck.gschema.xml=usr/share/glib-2.0/schemas/io.github.elly_code.reminduck.gschema.xml", + "build-linux-x86_64/data/io.github.elly_code.reminduck.desktop=usr/share/applications/io.github.elly_code.reminduck.desktop", + "build-linux-x86_64/data/io.github.elly_code.reminduck.metainfo.xml=usr/share/metainfo/io.github.elly_code.reminduck.metainfo.xml", + "data/icons/hicolor/16.png=usr/share/icons/hicolor/16x16/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/24.png=usr/share/icons/hicolor/24x24/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/32.png=usr/share/icons/hicolor/32x32/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/48.png=usr/share/icons/hicolor/48x48/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/64.png=usr/share/icons/hicolor/64x64/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/128.png=usr/share/icons/hicolor/128x128/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/16@2.png=usr/share/icons/hicolor/16x16@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/24@2.png=usr/share/icons/hicolor/24x24@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/32@2.png=usr/share/icons/hicolor/32x32@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/48@2.png=usr/share/icons/hicolor/48x48@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/64@2.png=usr/share/icons/hicolor/64x64@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/128@2.png=usr/share/icons/hicolor/128x128@2/apps/io.github.elly_code.reminduck.png", + "data/icons/scalable.svg=usr/share/icons/hicolor/scalable/apps/io.github.elly_code.reminduck.svg" + ] + } + ] + }, + { + "arch": "aarch64", + "build_dir": "build-linux-aarch64", + "entry_linux": "build-linux-aarch64/src/io.github.elly_code.reminduck", + "deb": { + "download": true, + "packages": [ + "libgtk-4-dev", + "gir1.2-gtk-4.0", + "libgranite-7-dev", + "gir1.2-granite-7.0", + "libgee-0.8-dev", + "libglib2.0-dev", + "libsqlite3-dev", + "libportal-dev", + "gobject-introspection", + "libgirepository1.0-dev", + "adwaita-icon-theme", + "hicolor-icon-theme", + "librsvg2-common", + "shared-mime-info", + "gstreamer1.0-plugins-base", + "gstreamer1.0-plugins-good" + ] + }, + "inherit_native_projects": false, + "native_projects": [ + { + "name": "reminduck", + "dir": ".", + "build": [ + "meson setup \"$SQGI_LINUX_BUILD_DIR\" --wipe --prefix /usr --buildtype=release -Ddevelopment=false -Dlegacy-rdnn=false ${SQGI_LINUX_MESON_CROSS_FILE:+--cross-file \"$SQGI_LINUX_MESON_CROSS_FILE\"} || meson setup \"$SQGI_LINUX_BUILD_DIR\" --prefix /usr --buildtype=release -Ddevelopment=false -Dlegacy-rdnn=false ${SQGI_LINUX_MESON_CROSS_FILE:+--cross-file \"$SQGI_LINUX_MESON_CROSS_FILE\"}", + "meson compile -C \"$SQGI_LINUX_BUILD_DIR\"" + ], + "files": [ + "build-linux-aarch64/data/io.github.elly_code.reminduck.gschema.xml=usr/share/glib-2.0/schemas/io.github.elly_code.reminduck.gschema.xml", + "build-linux-aarch64/data/io.github.elly_code.reminduck.desktop=usr/share/applications/io.github.elly_code.reminduck.desktop", + "build-linux-aarch64/data/io.github.elly_code.reminduck.metainfo.xml=usr/share/metainfo/io.github.elly_code.reminduck.metainfo.xml", + "data/icons/hicolor/16.png=usr/share/icons/hicolor/16x16/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/24.png=usr/share/icons/hicolor/24x24/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/32.png=usr/share/icons/hicolor/32x32/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/48.png=usr/share/icons/hicolor/48x48/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/64.png=usr/share/icons/hicolor/64x64/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/128.png=usr/share/icons/hicolor/128x128/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/16@2.png=usr/share/icons/hicolor/16x16@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/24@2.png=usr/share/icons/hicolor/24x24@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/32@2.png=usr/share/icons/hicolor/32x32@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/48@2.png=usr/share/icons/hicolor/48x48@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/64@2.png=usr/share/icons/hicolor/64x64@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/128@2.png=usr/share/icons/hicolor/128x128@2/apps/io.github.elly_code.reminduck.png", + "data/icons/scalable.svg=usr/share/icons/hicolor/scalable/apps/io.github.elly_code.reminduck.svg" + ] + } + ] + } + ] + }, + "windows": { + "msys2_prefix": "mingw64", + "gdk_backend": "win32", + "gtk_theme": "io.elementary.stylesheet.banana", + "gtk_icon_theme": "Adwaita", + "gtk_font_name": "Inter Variable Text 9", + "build_dir": "build-windows-x86_64", + "packages": [ + "mingw-w64-x86_64-vala", + "mingw-w64-x86_64-gtk4", + "mingw-w64-x86_64-granite7", + "mingw-w64-x86_64-libgee", + "mingw-w64-x86_64-sqlite3", + "mingw-w64-x86_64-gdk-pixbuf2", + "mingw-w64-x86_64-librsvg", + "mingw-w64-x86_64-gstreamer", + "mingw-w64-x86_64-gst-plugins-base", + "mingw-w64-x86_64-gst-plugins-good", + "mingw-w64-x86_64-gsettings-desktop-schemas", + "mingw-w64-x86_64-hicolor-icon-theme", + "mingw-w64-x86_64-adwaita-icon-theme", + "mingw-w64-x86_64-adwaita-icon-theme-legacy", + "mingw-w64-x86_64-elementary-icon-theme", + "mingw-w64-x86_64-gtk-elementary-theme" + ], + "native_projects": [ + { + "name": "reminduck", + "dir": ".", + "build": [ + "meson setup \"$SQGI_WINDOWS_BUILD_DIR\" --wipe --prefix \"$SQGI_WINDOWS_PREFIX\" --buildtype=release -Dwindows_build=true -Ddevelopment=false -Dlegacy-rdnn=false --cross-file \"$SQGI_MESON_CROSS_FILE\" || meson setup \"$SQGI_WINDOWS_BUILD_DIR\" --prefix \"$SQGI_WINDOWS_PREFIX\" --buildtype=release -Dwindows_build=true -Ddevelopment=false -Dlegacy-rdnn=false --cross-file \"$SQGI_MESON_CROSS_FILE\"", + "meson compile -C \"$SQGI_WINDOWS_BUILD_DIR\"" + ], + "files": [ + "build-windows-x86_64/data/io.github.elly_code.reminduck.gschema.xml=share/glib-2.0/schemas/io.github.elly_code.reminduck.gschema.xml", + "data/icons/hicolor/16.png=share/icons/hicolor/16x16/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/24.png=share/icons/hicolor/24x24/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/32.png=share/icons/hicolor/32x32/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/48.png=share/icons/hicolor/48x48/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/64.png=share/icons/hicolor/64x64/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/128.png=share/icons/hicolor/128x128/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor/256.png=share/icons/hicolor/256x256/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/16@2.png=share/icons/hicolor/16x16@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/24@2.png=share/icons/hicolor/24x24@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/32@2.png=share/icons/hicolor/32x32@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/48@2.png=share/icons/hicolor/48x48@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/64@2.png=share/icons/hicolor/64x64@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/128@2.png=share/icons/hicolor/128x128@2/apps/io.github.elly_code.reminduck.png", + "data/icons/hicolor@2/256@2.png=share/icons/hicolor/256x256@2/apps/io.github.elly_code.reminduck.png", + "data/icons/scalable.svg=share/icons/hicolor/scalable/apps/io.github.elly_code.reminduck.svg" + ] + } + ], + "nsis_options": { + "installer_name": "Reminduck-Setup.exe", + "install_dir": "$LOCALAPPDATA\\Programs\\io.github.elly_code.reminduck", + "request_execution_level": "user", + "icon": "windows/icons/install.ico", + "desktop_shortcut": true, + "start_menu_shortcut": true, + "start_menu_folder": "Reminduck", + "uninstall_registry": true + } + } +} diff --git a/src/Services/Utils.vala b/src/Services/Utils.vala index 256e7b1..5d9cbeb 100644 --- a/src/Services/Utils.vala +++ b/src/Services/Utils.vala @@ -7,7 +7,65 @@ namespace Reminduck.Utils { +#if WINDOWS + private static string? get_windows_startup_path () { + unowned string? appdata = Environment.get_variable ("APPDATA"); + if (appdata == null || appdata == "") { + return null; + } + + return Path.build_filename ( + appdata, + "Microsoft", + "Windows", + "Start Menu", + "Programs", + "Startup", + "Reminduck.vbs" + ); + } + + private static string get_windows_launcher_path () { + string install_dir = Win32.get_package_installation_directory_of_module (null); + string launcher = Path.build_filename (install_dir, "Reminduck.bat"); + + if (FileUtils.test (launcher, FileTest.EXISTS)) { + return launcher; + } + + var parent = File.new_for_path (install_dir).get_parent (); + if (parent != null) { + launcher = Path.build_filename (parent.get_path (), "Reminduck.bat"); + } + + return launcher; + } + + private static string escape_vbs_string (string value) { + return value.replace ("\"", "\"\""); + } +#endif + private static void request_autostart () { +#if WINDOWS + string? startup_path = get_windows_startup_path (); + if (startup_path == null) { + warning ("Unable to find Windows startup folder"); + return; + } + + string launcher = escape_vbs_string (get_windows_launcher_path ()); + string contents = "Set WshShell = CreateObject(\"WScript.Shell\")\r\n" + + "WshShell.Run \"\"\"" + launcher + "\"\" --headless\", 0, False\r\n"; + + try { + DirUtils.create_with_parents (Path.get_dirname (startup_path), 0755); + FileUtils.set_contents (startup_path, contents); + stdout.printf ("\nRequested autostart"); + } catch (Error e) { + warning ("Unable to request Windows autostart: %s", e.message); + } +#else Xdp.Portal portal = new Xdp.Portal (); GenericArray cmd = new GenericArray (); cmd.add ("io.github.elly_code.reminduck"); @@ -21,9 +79,23 @@ namespace Reminduck.Utils { null); stdout.printf ("\nšŸš€ Requested autostart"); +#endif } private static void remove_autostart () { +#if WINDOWS + string? startup_path = get_windows_startup_path (); + if (startup_path == null) { + warning ("Unable to find Windows startup folder"); + return; + } + + if (FileUtils.test (startup_path, FileTest.EXISTS)) { + FileUtils.remove (startup_path); + } + + stdout.printf ("\nRemoved autostart"); +#else Xdp.Portal portal = new Xdp.Portal (); GenericArray cmd = new GenericArray (); cmd.add ("io.github.elly_code.reminduck"); @@ -37,5 +109,6 @@ namespace Reminduck.Utils { null); stdout.printf ("\nšŸš€ Removed autostart"); +#endif } } diff --git a/src/meson.build b/src/meson.build index 4cb025d..d0cd290 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,7 +17,7 @@ config_file = configure_file( dependencies = [ dependency('gtk4'), - dependency('granite-7', version: '>=7.7.0'), + dependency('granite-7', version: '>=7.4.0'), dependency('gee-0.8'), dependency('sqlite3') ]