From 77b28e97a443944c77ca624a6f9664e67830610b Mon Sep 17 00:00:00 2001 From: Youwes09 Date: Wed, 10 Jun 2026 12:49:48 -0500 Subject: [PATCH] Feat: Bug Reporter (Github Issue Templates) --- .github/ISSUE_TEMPLATE/bug_report.yml | 78 +++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 47 ++ .github/read_versions.sh | 18 + .github/workflows/build-linux.yml | 126 ++--- .github/workflows/build-macos.yml | 184 ++---- .github/workflows/build-windows.yml | 128 ++--- build-windows-local.ps1 | 99 ++++ nix/scripts.nix | 28 +- nix/versions.nix | 10 +- .../components/settings/BugReporter.svelte | 535 ++++++++++++++++++ src/lib/components/settings/Settings.svelte | 35 +- src/lib/components/settings/lib/bugReport.ts | 119 ++++ 13 files changed, 1096 insertions(+), 316 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/read_versions.sh create mode 100644 build-windows-local.ps1 create mode 100644 src/lib/components/settings/BugReporter.svelte create mode 100644 src/lib/components/settings/lib/bugReport.ts diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..15c3948 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,78 @@ +name: Bug Report +description: Something isn't working as expected +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. The more detail you include, the faster it gets fixed. + You can use the **Report a Bug** button in **Settings → About** to pre-fill most of this automatically. + + - type: textarea + id: description + attributes: + label: Description + description: What's broken? A clear, concise summary. + placeholder: "e.g. Library card stats don't appear even with 'Always show' enabled" + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Exact steps to trigger the bug. + placeholder: | + 1. Open Settings → Library + 2. Enable "Always show card stats" + 3. Return to Library + 4. Unread counts are not visible + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + placeholder: "Unread and download counts should be permanently visible on manga cards" + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + placeholder: "Counts only appear on hover, or not at all" + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment + description: Copy this from Settings → About → Report a Bug, or fill in manually. + placeholder: | + - Moku Version: v0.9.4 + - Platform: Windows / macOS / Linux / Web + - OS Version: Windows 11 24H2 + - Server: Suwayomi v2.2.2196 + - Server URL: localhost:4567 + validations: + required: true + + - type: textarea + id: settings + attributes: + label: Relevant Settings + description: Settings related to the bug (auto-filled by the in-app reporter, or paste manually). + placeholder: | + libraryStatsAlways: true + libraryCropCovers: true + libraryPageSize: 48 + render: yaml + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Screenshots, screen recordings, console errors, anything else helpful. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..52563c2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discussions (Questions & Support) + url: https://github.com/moku-project/Moku/discussions + about: Not a bug? Ask questions and get help here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..3eeead3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,47 @@ +name: Feature Request +description: Suggest an improvement or new feature +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Got an idea? Describe what you want and why it would be useful. + + - type: textarea + id: problem + attributes: + label: Problem / Motivation + description: What's the gap or frustration this would address? + placeholder: "e.g. There's no way to bulk-mark chapters as read without opening each series" + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: What would you like to see? + placeholder: "A 'Mark all read' option in the series long-press context menu" + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Any workarounds you've tried, or other ways this could be solved. + + - type: textarea + id: environment + attributes: + label: Environment + description: Optional — useful if this is platform-specific. + placeholder: | + - Moku Version: v0.9.4 + - Platform: Windows / macOS / Linux / Web + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Mockups, references, examples from other apps, etc. \ No newline at end of file diff --git a/.github/read_versions.sh b/.github/read_versions.sh new file mode 100644 index 0000000..913c89e --- /dev/null +++ b/.github/read_versions.sh @@ -0,0 +1,18 @@ +# Sourced by CI jobs that need versions from nix/versions.nix. +# Usage: source .github/read_versions.sh +# Exports: MOKU_VERSION SUWA_VERSION SUWA_HASH_LINUX SUWA_HASH_MACOS_ARM64 SUWA_HASH_MACOS_X64 SUWA_HASH_WINDOWS + +_nix="$( cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd )/nix/versions.nix" +_t=$(cat "$_nix") + +_pick() { echo "$_t" | grep -oP "${1}\s*=\s*\"\K[^\"]+"; } + +export MOKU_VERSION=$(_pick "moku") +export SUWA_VERSION=$(_pick "version") +export SUWA_HASH_WINDOWS=$(_pick "windowsHash") +export SUWA_HASH_LINUX=$(_pick "linuxHash") +export SUWA_HASH_MACOS_ARM64=$(_pick "macosArm64Hash") +export SUWA_HASH_MACOS_X64=$(_pick "macosX64Hash") + +unset _nix _t +unset -f _pick diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 06b9c7f..b7fb699 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -16,24 +16,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Upload dist - uses: actions/upload-artifact@v4 + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile + - run: pnpm build:static + - uses: actions/upload-artifact@v4 with: name: frontend-dist-linux path: dist/ @@ -43,77 +32,54 @@ jobs: name: Tauri (Linux x64) needs: frontend runs-on: ubuntu-22.04 - steps: - uses: actions/checkout@v4 - - name: Download frontend dist - uses: actions/download-artifact@v4 - with: - name: frontend-dist-linux - path: dist/ + - uses: actions/download-artifact@v4 + with: { name: frontend-dist-linux, path: dist/ } + + - name: Read versions + run: | + source .github/read_versions.sh + echo "MOKU_VERSION=$MOKU_VERSION" >> $GITHUB_ENV + echo "SUWA_VERSION=$SUWA_VERSION" >> $GITHUB_ENV + echo "SUWA_HASH=$SUWA_HASH_LINUX" >> $GITHUB_ENV - name: Install system dependencies run: | sudo apt-get update sudo apt-get install -y \ - libwebkit2gtk-4.1-dev \ - libappindicator3-dev \ - librsvg2-dev \ - patchelf \ - libfuse2 + libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libfuse2 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-unknown-linux-gnu + - uses: dtolnay/rust-toolchain@stable + with: { targets: x86_64-unknown-linux-gnu } - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - workspaces: src-tauri + - uses: Swatinem/rust-cache@v2 + with: { workspaces: src-tauri } - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install JS dependencies - run: pnpm install --frozen-lockfile + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile - name: Download Suwayomi (Linux x64) run: | curl -fsSL \ - "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v2.2.2196/Suwayomi-Server-v2.2.2196-linux-x64.tar.gz" \ + "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VERSION}/Suwayomi-Server-v${SUWA_VERSION}-linux-x64.tar.gz" \ -o suwayomi-linux.tar.gz - - echo "e13d63ceb7e2b15e83d0a78281e8c1c04ac4a833caa73e5a2b68fbaf0cb20c1f suwayomi-linux.tar.gz" | sha256sum -c - - + echo "${SUWA_HASH} suwayomi-linux.tar.gz" | sha256sum -c - mkdir -p suwayomi-extracted tar -xzf suwayomi-linux.tar.gz -C suwayomi-extracted --strip-components=1 - name: Stage Suwayomi bundle run: | mkdir -p src-tauri/binaries - - JAR="suwayomi-extracted/bin/Suwayomi-Server.jar" - JAVA="suwayomi-extracted/jre/bin/java" - CATCH="suwayomi-extracted/bin/catch_abort.so" - - for f in "$JAR" "$JAVA" "$CATCH"; do - if [ ! -e "$f" ]; then - echo "ERROR: expected file not found: $f" - find suwayomi-extracted -type f | head -40 - exit 1 - fi + for f in suwayomi-extracted/bin/Suwayomi-Server.jar \ + suwayomi-extracted/jre/bin/java \ + suwayomi-extracted/bin/catch_abort.so; do + [ -e "$f" ] || { echo "ERROR: missing $f"; find suwayomi-extracted -type f | head -40; exit 1; } done - - echo "JAR=$JAR JAVA=$JAVA CATCH=$CATCH" - cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle chmod +x src-tauri/binaries/suwayomi-bundle/jre/bin/java @@ -124,48 +90,34 @@ jobs: chmod +x src-tauri/binaries/suwayomi-launcher-linux-x86_64-unknown-linux-gnu - name: Patch tauri.conf.json for CI - run: | - sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json + run: sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json - name: Build Tauri app run: pnpm tauri build --target x86_64-unknown-linux-gnu --config src-tauri/tauri.linux.conf.json --verbose - env: - NO_STRIP: "true" + env: { NO_STRIP: "true" } - name: Upload Linux artifacts to release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ github.event.inputs.version }} run: | for i in $(seq 1 12); do RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ "https://api.github.com/repos/moku-project/Moku/releases" \ - | jq -r '.[] | select(.tag_name == "v'"$VERSION"'") | .id' | head -1) - if [ -n "$RELEASE_ID" ]; then break; fi - echo "Waiting for release to exist... attempt $i" - sleep 15 + | jq -r '.[] | select(.tag_name == "v${{ github.event.inputs.version }}") | .id' | head -1) + [ -n "$RELEASE_ID" ] && break + echo "Waiting for release... attempt $i"; sleep 15 done + [ -z "$RELEASE_ID" ] && { echo "ERROR: release not found"; exit 1; } - if [ -z "$RELEASE_ID" ]; then - echo "ERROR: Could not find release for v$VERSION after waiting" - exit 1 - fi - - echo "Found release ID: $RELEASE_ID" - - upload_asset() { - local file="$1" - local name="$2" - echo "Uploading $name..." + upload() { curl -s -X POST \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "Content-Type: application/octet-stream" \ - --data-binary @"$file" \ - "https://uploads.github.com/repos/moku-project/Moku/releases/$RELEASE_ID/assets?name=$name" + --data-binary @"$1" \ + "https://uploads.github.com/repos/moku-project/Moku/releases/$RELEASE_ID/assets?name=$2" } APPIMAGE=$(find src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage -name "*.AppImage" | head -1) DEB=$(find src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb -name "*.deb" | head -1) - - [ -n "$APPIMAGE" ] && upload_asset "$APPIMAGE" "moku-linux-x64-${VERSION}.AppImage" - [ -n "$DEB" ] && upload_asset "$DEB" "moku-linux-x64-${VERSION}.deb" \ No newline at end of file + [ -n "$APPIMAGE" ] && upload "$APPIMAGE" "moku-linux-x64-${{ github.event.inputs.version }}.AppImage" + [ -n "$DEB" ] && upload "$DEB" "moku-linux-x64-${{ github.event.inputs.version }}.deb" diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 2e0d621..8769f06 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: version: - description: "Version to build (e.g. 0.4.0)" + description: "Version to build (e.g. 0.9.0)" required: true permissions: @@ -16,28 +16,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Upload dist - uses: actions/upload-artifact@v4 - with: - name: frontend-dist - path: dist/ - retention-days: 1 + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile + - run: pnpm build:static + - uses: actions/upload-artifact@v4 + with: { name: frontend-dist, path: dist/, retention-days: 1 } tauri: name: Tauri (macOS) @@ -46,149 +32,99 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Download frontend dist - uses: actions/download-artifact@v4 - with: - name: frontend-dist - path: dist/ + - uses: actions/download-artifact@v4 + with: { name: frontend-dist, path: dist/ } - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: aarch64-apple-darwin,x86_64-apple-darwin + - name: Read versions + run: | + source .github/read_versions.sh + echo "SUWA_VERSION=$SUWA_VERSION" >> $GITHUB_ENV + echo "SUWA_HASH_ARM64=$SUWA_HASH_MACOS_ARM64" >> $GITHUB_ENV + echo "SUWA_HASH_X64=$SUWA_HASH_MACOS_X64" >> $GITHUB_ENV - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - workspaces: src-tauri + - uses: dtolnay/rust-toolchain@stable + with: { targets: aarch64-apple-darwin,x86_64-apple-darwin } + + - uses: Swatinem/rust-cache@v2 + with: { workspaces: src-tauri } - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install JS dependencies - run: pnpm install --frozen-lockfile + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile - name: Download Suwayomi binaries run: | - download_suwayomi() { + dl() { local asset="$1" sha="$2" outdir="$3" curl -fsSL \ - "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v2.2.2196/${asset}" \ + "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VERSION}/${asset}" \ -o "${outdir}.tar.gz" echo "${sha} ${outdir}.tar.gz" | shasum -a 256 -c - mkdir -p "${outdir}" tar -xzf "${outdir}.tar.gz" -C "${outdir}" --strip-components=1 } - - download_suwayomi \ - "Suwayomi-Server-v2.2.2196-macOS-arm64.tar.gz" \ - "9e3dbebc7475707e8d11c56a473385c00b09bde0103d013bc1cb3d06c89e5c43" \ - "suwayomi-arm64" - - download_suwayomi \ - "Suwayomi-Server-v2.2.2196-macOS-x64.tar.gz" \ - "eadee02060b780a5febfb8dada2f89c7bd7db5905cfd20d47eaca02fcde8c9c5" \ - "suwayomi-x64" + dl "Suwayomi-Server-v${SUWA_VERSION}-macOS-arm64.tar.gz" "$SUWA_HASH_ARM64" suwayomi-arm64 + dl "Suwayomi-Server-v${SUWA_VERSION}-macOS-x64.tar.gz" "$SUWA_HASH_X64" suwayomi-x64 - name: Stage Suwayomi sidecars run: | mkdir -p src-tauri/binaries - - stage_arch() { - local srcdir="$1" - local arch="$2" - local sidecar="src-tauri/binaries/suwayomi-server-${arch}" - local bundle_dest="src-tauri/binaries/suwayomi-bundle-${arch}" - + stage() { + local srcdir="$1" arch="$2" JAR=$(find "$srcdir" -name "Suwayomi-Server.jar" | head -1) JAVA=$(find "$srcdir" -path "*/jre/bin/java" | head -1) - - if [ -z "$JAR" ]; then - echo "ERROR: Suwayomi-Server.jar not found in $srcdir" - find "$srcdir" -type f | head -30 - exit 1 - fi - if [ -z "$JAVA" ]; then - echo "ERROR: jre/bin/java not found in $srcdir" - find "$srcdir" -type f | head -30 - exit 1 - fi - - echo "${arch}: jar=${JAR} java=${JAVA}" - - cp -r "$srcdir" "$bundle_dest" - - # The launcher script is committed at src-tauri/binaries/suwayomi-launcher.sh - # to avoid embedding a heredoc in YAML (which breaks GitHub Actions parsing). - cp src-tauri/binaries/suwayomi-launcher.sh "$sidecar" - chmod +x "$sidecar" - echo "Staged sidecar: $sidecar" + [ -z "$JAR" ] && { echo "ERROR: jar not found in $srcdir"; find "$srcdir" -type f | head -30; exit 1; } + [ -z "$JAVA" ] && { echo "ERROR: java not found in $srcdir"; find "$srcdir" -type f | head -30; exit 1; } + cp -r "$srcdir" "src-tauri/binaries/suwayomi-bundle-${arch}" + cp src-tauri/binaries/suwayomi-launcher.sh "src-tauri/binaries/suwayomi-server-${arch}" + chmod +x "src-tauri/binaries/suwayomi-server-${arch}" } - - stage_arch suwayomi-arm64 aarch64-apple-darwin - stage_arch suwayomi-x64 x86_64-apple-darwin + stage suwayomi-arm64 aarch64-apple-darwin + stage suwayomi-x64 x86_64-apple-darwin - name: Patch tauri.conf.json for CI - run: | - sed -i '' 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json - - - name: Swap bundle for aarch64 - run: | - rm -rf src-tauri/binaries/suwayomi-bundle - cp -r src-tauri/binaries/suwayomi-bundle-aarch64-apple-darwin \ - src-tauri/binaries/suwayomi-bundle + run: sed -i '' 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json - name: Build Tauri app (aarch64) - run: pnpm tauri build --target aarch64-apple-darwin --config src-tauri/tauri.macos.conf.json + run: | + rm -rf src-tauri/binaries/suwayomi-bundle + cp -r src-tauri/binaries/suwayomi-bundle-aarch64-apple-darwin src-tauri/binaries/suwayomi-bundle + pnpm tauri build --target aarch64-apple-darwin --config src-tauri/tauri.macos.conf.json env: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY || '-' }} - - name: Swap bundle for x86_64 + - name: Build Tauri app (x86_64) run: | rm -rf src-tauri/binaries/suwayomi-bundle - cp -r src-tauri/binaries/suwayomi-bundle-x86_64-apple-darwin \ - src-tauri/binaries/suwayomi-bundle - - - name: Build Tauri app (x86_64) - run: pnpm tauri build --target x86_64-apple-darwin --config src-tauri/tauri.macos.conf.json + cp -r src-tauri/binaries/suwayomi-bundle-x86_64-apple-darwin src-tauri/binaries/suwayomi-bundle + pnpm tauri build --target x86_64-apple-darwin --config src-tauri/tauri.macos.conf.json env: APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY || '-' }} - name: Upload macOS artifacts to release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ github.event.inputs.version }} run: | - # Wait for the Windows workflow to have created the draft release for i in $(seq 1 12); do - RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" "https://api.github.com/repos/moku-project/Moku/releases" | jq -r '.[] | select(.tag_name == "v'"$VERSION"'") | .id' | head -1) - if [ -n "$RELEASE_ID" ]; then break; fi - echo "Waiting for release to exist... attempt $i" - sleep 15 + RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + "https://api.github.com/repos/moku-project/Moku/releases" \ + | jq -r '.[] | select(.tag_name == "v${{ github.event.inputs.version }}") | .id' | head -1) + [ -n "$RELEASE_ID" ] && break + echo "Waiting for release... attempt $i"; sleep 15 done + [ -z "$RELEASE_ID" ] && { echo "ERROR: release not found"; exit 1; } - if [ -z "$RELEASE_ID" ]; then - echo "ERROR: Could not find release for v$VERSION after waiting" - exit 1 - fi - - echo "Found release ID: $RELEASE_ID" - - upload_asset() { - local file="$1" - local name="$2" - echo "Uploading $name..." - curl -s -X POST -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" --data-binary @"$file" "https://uploads.github.com/repos/moku-project/Moku/releases/$RELEASE_ID/assets?name=$name" + upload() { + curl -s -X POST \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$1" \ + "https://uploads.github.com/repos/moku-project/Moku/releases/$RELEASE_ID/assets?name=$2" } - ARM64_DMG=$(find src-tauri/target/aarch64-apple-darwin/release/bundle/dmg -name "*.dmg" | head -1) - X64_DMG=$(find src-tauri/target/x86_64-apple-darwin/release/bundle/dmg -name "*.dmg" | head -1) - - [ -n "$ARM64_DMG" ] && upload_asset "$ARM64_DMG" "moku-macos-arm64-${VERSION}.dmg" - [ -n "$X64_DMG" ] && upload_asset "$X64_DMG" "moku-macos-x64-${VERSION}.dmg" \ No newline at end of file + ARM64=$(find src-tauri/target/aarch64-apple-darwin/release/bundle/dmg -name "*.dmg" | head -1) + X64=$(find src-tauri/target/x86_64-apple-darwin/release/bundle/dmg -name "*.dmg" | head -1) + [ -n "$ARM64" ] && upload "$ARM64" "moku-macos-arm64-${{ github.event.inputs.version }}.dmg" + [ -n "$X64" ] && upload "$X64" "moku-macos-x64-${{ github.event.inputs.version }}.dmg" diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index fd40f80..0dcb52a 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -16,152 +16,102 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm build - - - name: Upload dist - uses: actions/upload-artifact@v4 - with: - name: frontend-dist-windows - path: dist/ - retention-days: 1 + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile + - run: pnpm build:static + - uses: actions/upload-artifact@v4 + with: { name: frontend-dist-windows, path: dist/, retention-days: 1 } tauri: name: Tauri (Windows x64) needs: frontend runs-on: windows-latest - steps: - uses: actions/checkout@v4 - - name: Download frontend dist - uses: actions/download-artifact@v4 - with: - name: frontend-dist-windows - path: dist/ + - uses: actions/download-artifact@v4 + with: { name: frontend-dist-windows, path: dist/ } - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-pc-windows-msvc + - name: Read versions + shell: bash + run: | + source .github/read_versions.sh + echo "SUWA_VERSION=$SUWA_VERSION" >> $GITHUB_ENV + echo "SUWA_HASH=$SUWA_HASH_WINDOWS" >> $GITHUB_ENV - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - workspaces: src-tauri + - uses: dtolnay/rust-toolchain@stable + with: { targets: x86_64-pc-windows-msvc } + + - uses: Swatinem/rust-cache@v2 + with: { workspaces: src-tauri } - uses: pnpm/action-setup@v4 - with: - version: latest - + with: { version: latest } - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - - name: Install JS dependencies - run: pnpm install --frozen-lockfile + with: { node-version: 22, cache: pnpm } + - run: pnpm install --frozen-lockfile - name: Download Suwayomi (Windows x64) shell: bash run: | curl -fsSL \ - "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v2.2.2196/Suwayomi-Server-v2.2.2196-windows-x64.zip" \ + "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VERSION}/Suwayomi-Server-v${SUWA_VERSION}-windows-x64.zip" \ -o suwayomi-windows.zip - echo "457ca4a64a57e0d274a87203d25e962103bcb456ee30ada3ea47328a3093329d suwayomi-windows.zip" | sha256sum -c - + echo "${SUWA_HASH} suwayomi-windows.zip" | sha256sum -c - unzip -q suwayomi-windows.zip -d suwayomi-raw - - name: Extract Suwayomi bundle + - name: Stage Suwayomi bundle shell: bash run: | mkdir -p suwayomi-extracted TOP_DIRS=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d | wc -l) TOP_FILES=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type f | wc -l) if [ "$TOP_DIRS" -eq 1 ] && [ "$TOP_FILES" -eq 0 ]; then - INNER=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d | head -1) - cp -r "$INNER"/. suwayomi-extracted/ + cp -r "$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d | head -1)"/. suwayomi-extracted/ else cp -r suwayomi-raw/. suwayomi-extracted/ fi - - - name: Stage Suwayomi bundle - shell: bash - run: | mkdir -p src-tauri/binaries - JAVA=$(find suwayomi-extracted -path "*/jre/bin/java.exe" | head -1) - JAR=$(find suwayomi-extracted -name "Suwayomi-Server.jar" | head -1) - if [ -z "$JAVA" ]; then - echo "ERROR: jre/bin/java.exe not found" - find suwayomi-extracted -type f | head -50 - exit 1 - fi - if [ -z "$JAR" ]; then - echo "ERROR: Suwayomi-Server.jar not found" - find suwayomi-extracted -type f | head -50 - exit 1 - fi + find suwayomi-extracted -path "*/jre/bin/java.exe" | grep -q . \ + || { echo "ERROR: java.exe not found"; find suwayomi-extracted -type f | head -50; exit 1; } + find suwayomi-extracted -name "Suwayomi-Server.jar" | grep -q . \ + || { echo "ERROR: Suwayomi-Server.jar not found"; find suwayomi-extracted -type f | head -50; exit 1; } cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle - - name: Validate staging - shell: bash - run: | - find src-tauri/binaries/suwayomi-bundle -path "*/jre/bin/java.exe" \ - | grep -q . || (echo "ERROR: jre/bin/java.exe missing" && exit 1) - find src-tauri/binaries/suwayomi-bundle -name "Suwayomi-Server.jar" \ - | grep -q . || (echo "ERROR: Suwayomi-Server.jar missing" && exit 1) - echo "Staging OK" - - name: Patch tauri.conf.json for CI shell: bash - run: | - sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json + run: sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json - - name: Delete existing draft release if present + - name: Delete existing draft release shell: bash - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" } run: | RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ "https://api.github.com/repos/moku-project/Moku/releases" \ | jq -r '.[] | select(.tag_name == "v${{ github.event.inputs.version }}" and .draft == true) | .id') if [ -n "$RELEASE_ID" ]; then - echo "Deleting existing draft release $RELEASE_ID" curl -s -X DELETE -H "Authorization: Bearer $GITHUB_TOKEN" \ "https://api.github.com/repos/moku-project/Moku/releases/$RELEASE_ID" curl -s -X DELETE -H "Authorization: Bearer $GITHUB_TOKEN" \ "https://api.github.com/repos/moku-project/Moku/git/refs/tags/v${{ github.event.inputs.version }}" - echo "Deleted draft release and tag" - else - echo "No existing draft release found" fi - name: Build Tauri app + create draft release uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" } with: tagName: v${{ github.event.inputs.version }} releaseName: Moku v${{ github.event.inputs.version }} releaseBody: | Moku v${{ github.event.inputs.version }} - **Windows:** Download `Moku_${{ github.event.inputs.version }}_x64-setup.exe` - **macOS arm64:** Download `moku-macos-arm64-${{ github.event.inputs.version }}.dmg` - **macOS x64:** Download `moku-macos-x64-${{ github.event.inputs.version }}.dmg` - **Linux:** Download `moku.flatpak` + **Windows:** `Moku_${{ github.event.inputs.version }}_x64-setup.exe` + **macOS arm64:** `moku-macos-arm64-${{ github.event.inputs.version }}.dmg` + **macOS x64:** `moku-macos-x64-${{ github.event.inputs.version }}.dmg` + **Linux:** `moku.flatpak` releaseDraft: true prerelease: false - args: --target x86_64-pc-windows-msvc --config src-tauri/tauri.windows.conf.json \ No newline at end of file + args: --target x86_64-pc-windows-msvc --config src-tauri/tauri.windows.conf.json diff --git a/build-windows-local.ps1 b/build-windows-local.ps1 new file mode 100644 index 0000000..0b2154f --- /dev/null +++ b/build-windows-local.ps1 @@ -0,0 +1,99 @@ +#Requires -Version 7 +param( + [switch]$SkipFrontend, + [switch]$SkipSuwayomi +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +function Step($msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan } +function Need($cmd) { + if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) { + Write-Error "Required tool not found: $cmd" + } +} + +$Root = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $Root + +Step "Reading nix/versions.nix" +$nix = Get-Content "$Root\nix\versions.nix" -Raw +$MOKU_VERSION = if ($nix -match 'moku\s*=\s*"([^"]+)"') { $Matches[1] } else { Write-Error "moku version not found" } +$SUWA_VERSION = if ($nix -match 'version\s*=\s*"([^"]+)"') { $Matches[1] } else { Write-Error "suwayomi version not found" } +$SUWA_HASH = if ($nix -match 'windowsHash\s*=\s*"([^"]+)"') { $Matches[1] } else { Write-Error "windowsHash not found" } +Write-Host " moku=$MOKU_VERSION suwayomi=$SUWA_VERSION" + +Need "pnpm"; Need "cargo"; Need "node" + +if (-not $SkipFrontend) { + Step "pnpm install" + pnpm install --frozen-lockfile + if ($LASTEXITCODE -ne 0) { Write-Error "pnpm install failed" } + + Step "Frontend build" + $env:MOKU_TARGET = "static" + pnpm build:static + if ($LASTEXITCODE -ne 0) { Write-Error "Frontend build failed" } +} + +$BundleDir = "$Root\src-tauri\binaries\suwayomi-bundle" +$ZipPath = "$env:TEMP\suwayomi-windows-$SUWA_VERSION.zip" +$ExtractDir = "$env:TEMP\suwayomi-extracted-$SUWA_VERSION" + +if (-not $SkipSuwayomi) { + Step "Downloading Suwayomi v$SUWA_VERSION" + $ZipUrl = "https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VERSION}/Suwayomi-Server-v${SUWA_VERSION}-windows-x64.zip" + + if (-not (Test-Path $ZipPath)) { + Invoke-WebRequest -Uri $ZipUrl -OutFile $ZipPath -UseBasicParsing + } + + $actual = (Get-FileHash $ZipPath -Algorithm SHA256).Hash.ToLower() + if ($actual -ne $SUWA_HASH.ToLower()) { + Write-Error "Hash mismatch`n expected: $SUWA_HASH`n got: $actual" + } + + Step "Staging bundle" + if (Test-Path $ExtractDir) { Remove-Item $ExtractDir -Recurse -Force } + Expand-Archive -Path $ZipPath -DestinationPath $ExtractDir + + $topDirs = @(Get-ChildItem $ExtractDir -Directory) + $topFiles = @(Get-ChildItem $ExtractDir -File) + $SrcDir = if ($topDirs.Count -eq 1 -and $topFiles.Count -eq 0) { $topDirs[0].FullName } else { $ExtractDir } + + if (Test-Path $BundleDir) { Remove-Item $BundleDir -Recurse -Force } + Copy-Item $SrcDir $BundleDir -Recurse + + $java = Get-ChildItem $BundleDir -Recurse -Filter "java.exe" | Where-Object { $_.FullName -match "jre.bin" } | Select-Object -First 1 + $jar = Get-ChildItem $BundleDir -Recurse -Filter "Suwayomi-Server.jar" | Select-Object -First 1 + if (-not $java) { Write-Error "java.exe not found in staged bundle" } + if (-not $jar) { Write-Error "Suwayomi-Server.jar not found in staged bundle" } + Write-Host " java: $($java.FullName)" + Write-Host " jar: $($jar.FullName)" +} elseif (-not (Test-Path $BundleDir)) { + Write-Error "Bundle dir missing at $BundleDir — run without -SkipSuwayomi first" +} + +Step "Patching tauri.conf.json" +$tauriConf = "$Root\src-tauri\tauri.conf.json" +$original = Get-Content $tauriConf -Raw +Set-Content $tauriConf ($original -replace '"beforeBuildCommand":\s*"pnpm build"', '"beforeBuildCommand": ""') -NoNewline + +Step "Tauri build" +$env:TAURI_SKIP_DEVSERVER_CHECK = "true" +pnpm tauri build --target x86_64-pc-windows-msvc --config src-tauri/tauri.windows.conf.json +$buildExit = $LASTEXITCODE + +Set-Content $tauriConf $original -NoNewline + +if ($buildExit -ne 0) { Write-Error "Tauri build failed (exit $buildExit)" } + +Step "Artifacts" +$out = "$Root\src-tauri\target\x86_64-pc-windows-msvc\release\bundle" +$msi = Get-ChildItem "$out\msi" -Filter "*.msi" -ErrorAction SilentlyContinue | Select-Object -First 1 +$exe = Get-ChildItem "$out\nsis" -Filter "*.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 +Write-Host "`nDone — Moku $MOKU_VERSION" -ForegroundColor Green +if ($msi) { Write-Host " MSI: $($msi.FullName)" -ForegroundColor Yellow } +if ($exe) { Write-Host " EXE: $($exe.FullName)" -ForegroundColor Yellow } +if (-not $msi -and -not $exe) { Write-Host " No artifacts found in $out" -ForegroundColor Red } diff --git a/nix/scripts.nix b/nix/scripts.nix index 3f366e2..d9804f1 100644 --- a/nix/scripts.nix +++ b/nix/scripts.nix @@ -85,17 +85,31 @@ PYEOF if [[ $# -ge 2 ]]; then SUWA_VER="$2" - JAR_URL="https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VER}/Suwayomi-Server-v${SUWA_VER}.jar" + BASE="https://github.com/Suwayomi/Suwayomi-Server-preview/releases/download/v${SUWA_VER}" - SUWA_SHA_HEX=$(curl -fsSL "$JAR_URL" | sha256sum | awk '{print $1}') - SUWA_SHA_SRI=$(echo "$SUWA_SHA_HEX" | xxd -r -p | base64 -w0 | sed 's/^/sha256-/') + echo "Fetching Suwayomi v${SUWA_VER} hashes (5 downloads)..." - sed -i "s/version = \"[^\"]*\"/version = \"$SUWA_VER\"/" "$VERSIONS" - sed -i "s|hash = \"sha256-[^\"]*\"|hash = \"$SUWA_SHA_SRI\"|" "$VERSIONS" + sha_of() { curl -fsSL "$1" | sha256sum | awk '{print $1}'; } + to_sri() { echo "$1" | xxd -r -p | base64 -w0 | sed 's/^/sha256-/'; } + + JAR_SHA=$(sha_of "${BASE}/Suwayomi-Server-v${SUWA_VER}.jar") + WIN_SHA=$(sha_of "${BASE}/Suwayomi-Server-v${SUWA_VER}-windows-x64.zip") + LINUX_SHA=$(sha_of "${BASE}/Suwayomi-Server-v${SUWA_VER}-linux-x64.tar.gz") + ARM64_SHA=$(sha_of "${BASE}/Suwayomi-Server-v${SUWA_VER}-macOS-arm64.tar.gz") + X64_SHA=$(sha_of "${BASE}/Suwayomi-Server-v${SUWA_VER}-macOS-x64.tar.gz") + + JAR_SRI=$(to_sri "$JAR_SHA") + + sed -i "s/version = \"[^\"]*\"/version = \"${SUWA_VER}\"/" "$VERSIONS" + sed -i "s|hash = \"sha256-[^\"]*\"|hash = \"${JAR_SRI}\"|" "$VERSIONS" + sed -i "s|windowsHash = \"[^\"]*\"|windowsHash = \"${WIN_SHA}\"|" "$VERSIONS" + sed -i "s|linuxHash = \"[^\"]*\"|linuxHash = \"${LINUX_SHA}\"|" "$VERSIONS" + sed -i "s|macosArm64Hash = \"[^\"]*\"|macosArm64Hash = \"${ARM64_SHA}\"|" "$VERSIONS" + sed -i "s|macosX64Hash = \"[^\"]*\"|macosX64Hash = \"${X64_SHA}\"|" "$VERSIONS" sed -i "s|Suwayomi-Server-preview/releases/download/v[^/]*/|Suwayomi-Server-preview/releases/download/v${SUWA_VER}/|" "$MANIFEST" sed -i "s|Suwayomi-Server-v[0-9.]*\.jar|Suwayomi-Server-v${SUWA_VER}.jar|g" "$MANIFEST" - python3 - "$MANIFEST" "$SUWA_SHA_HEX" <<'PYEOF' + python3 - "$MANIFEST" "$JAR_SHA" <<'PYEOF' import re, sys path, sha = sys.argv[1], sys.argv[2] text = open(path).read() @@ -106,6 +120,8 @@ if n == 0: sys.exit("ERROR: could not find Suwayomi jar sha256 in manifest") open(path, 'w').write(updated) PYEOF + + echo "Suwayomi hashes written." fi echo "Done — versions.nix, flatpak manifest, and PKGBUILD patched for v$VERSION" diff --git a/nix/versions.nix b/nix/versions.nix index c84a271..a3f4abf 100644 --- a/nix/versions.nix +++ b/nix/versions.nix @@ -4,11 +4,15 @@ suwayomi = { version = "2.2.2196"; hash = "sha256-jnJEwmlFZmGodwX3RvDYcnV3Cql2urfGkg5NUT6Xw/Y="; + windowsHash = "457ca4a64a57e0d274a87203d25e962103bcb456ee30ada3ea47328a3093329d"; + linuxHash = "e13d63ceb7e2b15e83d0a78281e8c1c04ac4a833caa73e5a2b68fbaf0cb20c1f"; + macosArm64Hash = "9e3dbebc7475707e8d11c56a473385c00b09bde0103d013bc1cb3d06c89e5c43"; + macosX64Hash = "eadee02060b780a5febfb8dada2f89c7bd7db5905cfd20d47eaca02fcde8c9c5"; }; frontend = { - pnpmHash = "sha256-18twdFhprV9v9hzvqxuVDHD6Tm4zHNDJs7s6l/7ClBo="; - distHash = "7db288b4b54277aa82b6ec5b21fc31a1e71f8246c50a74777500083b806c1fa5"; + pnpmHash = "sha256-18twdFhprV9v9hzvqxuVDHD6Tm4zHNDJs7s6l/7ClBo="; + distHash = "7db288b4b54277aa82b6ec5b21fc31a1e71f8246c50a74777500083b806c1fa5"; distHashSri = "sha256-fbiiu0tCd6qCtu+SIfw+aR8Yj2bFCnR3dQAIO4BvwfM="; }; @@ -16,6 +20,6 @@ tauri-plugin-discord-rpc = "sha256-xq0qyK2NrwSAFDhXo0vbvcygRD2/7uqBaLpqfpfxkrc="; }; - gitCommit = "239960683b6c7f1347e1798b0e179a8a46628728"; + gitCommit = "239960683b6c7f1347e1798b0e179a8a46628728"; tarballHash = ""; } diff --git a/src/lib/components/settings/BugReporter.svelte b/src/lib/components/settings/BugReporter.svelte new file mode 100644 index 0000000..e4d1d95 --- /dev/null +++ b/src/lib/components/settings/BugReporter.svelte @@ -0,0 +1,535 @@ + + + + +
{ if (e.target === e.currentTarget) onClose() }} + onkeydown={(e) => e.key === 'Escape' && onClose()}> + + +
+ + \ No newline at end of file diff --git a/src/lib/components/settings/Settings.svelte b/src/lib/components/settings/Settings.svelte index edc1851..aee48d3 100644 --- a/src/lib/components/settings/Settings.svelte +++ b/src/lib/components/settings/Settings.svelte @@ -1,6 +1,6 @@