From 081a7d48d4fc5f82dd82354215d29ea5798099e4 Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sat, 31 Oct 2026 04:24:36 +1100 Subject: [PATCH 1/5] Build releases from this fork's own workflow and register under its own names --- .github/workflows/release.yml | 272 ++++++++++++++++-- ATTRIBUTION.md | 2 +- ATTRIBUTION.toml | 2 +- PATCHES.md | 7 + .../ai.storyteller.pdfcraft.metainfo.xml.in | 2 +- packaging/linux/package.sh | 2 +- packaging/windows/install-policy.json | 82 +++--- packaging/windows/pdfcraft.wxs | 96 +++---- packaging/windows/test-install.ps1 | 42 +++ packaging/windows/test-msi.ps1 | 6 +- 10 files changed, 392 insertions(+), 121 deletions(-) create mode 100644 packaging/windows/test-install.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 573b9f69f3..7d4e929bb8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,11 @@ name: Release +# Builds unsigned Windows, Linux and macOS packages. A push to `release` also drafts the GitHub +# release; publishing the draft is a person's decision. A push to `ryan/release-*` is a dry run: +# it builds and tests the same packages and drafts nothing. on: push: - branches: [release] + branches: [release, "ryan/release-*"] workflow_dispatch: inputs: version: @@ -11,33 +14,252 @@ on: default: "" permissions: - contents: write + contents: read concurrency: group: release-${{ github.ref }} cancel-in-progress: false +env: + CRAFT_FONTS_REF: b076417b5880bd4154bd22f00695b1d28c6bea83 + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + RUST_BACKTRACE: 1 + jobs: - release: - # Pinned: a mutable reference to another repository can change what CI runs. Move it deliberately. - uses: storytold/craft-common-actions/.github/workflows/release.yml@2b3fb9dc05036f73c77d18f753a3aa9da55fa386 # main, 31 October 2026 - with: - app-name: pdfcraft - app-title: PlutarchPages - craft-fonts-ref: b076417b5880bd4154bd22f00695b1d28c6bea83 - version: ${{ inputs.version }} - secrets: - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} - WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SIGNING_ENDPOINT: ${{ secrets.AZURE_SIGNING_ENDPOINT }} - AZURE_SIGNING_ACCOUNT: ${{ secrets.AZURE_SIGNING_ACCOUNT }} - AZURE_CERT_PROFILE: ${{ secrets.AZURE_CERT_PROFILE }} + version: + name: version + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + version: ${{ steps.resolve.outputs.version }} + date: ${{ steps.resolve.outputs.date }} + prerelease: ${{ steps.resolve.outputs.prerelease }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - id: resolve + shell: bash + env: + INPUT_VERSION: ${{ inputs.version }} + run: | + . packaging/env.sh + v="${INPUT_VERSION#v}" + v="${v:-$VERSION}" + semver='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$' + if [[ ! "$v" =~ $semver ]]; then + echo "::error::'$v' is not a version like 1.2.3 or 1.2.3-rc.1" + exit 1 + fi + case "$v" in *-*) pre=true ;; *) pre=false ;; esac + { + echo "version=$v" + echo "date=$(date -u +%Y-%m-%d)" + echo "prerelease=$pre" + } >>"$GITHUB_OUTPUT" + + windows: + name: Windows ${{ matrix.arch }} + needs: [version] + runs-on: windows-latest + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + include: + - { arch: x64, target: x86_64-pc-windows-msvc } + - { arch: x86, target: i686-pc-windows-msvc } + - { arch: arm64, target: aarch64-pc-windows-msvc } + env: + VERSION: ${{ needs.version.outputs.version }} + INPUT_VERSION: ${{ inputs.version }} + PDFCRAFT_VERSION: ${{ needs.version.outputs.version }} + PDFCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} + PDFCRAFT_BUILD_SHA: ${{ github.sha }} + CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts + CRAFT_FONTS_REQUIRED: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: storytold/craft-fonts + ref: ${{ env.CRAFT_FONTS_REF }} + path: craft-fonts + persist-credentials: false + - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 + with: + toolchain: stable + targets: ${{ matrix.target }} + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 + with: + key: release-windows-${{ matrix.arch }} + - name: Apply version override + if: env.INPUT_VERSION != '' + shell: bash + run: cargo xtask version set "$VERSION" + - name: Install WiX + shell: pwsh + run: | + dotnet tool install --global wix --version 5.0.2 + if ($LASTEXITCODE -ne 0) { throw 'WiX installation failed.' } + wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2 + if ($LASTEXITCODE -ne 0) { throw 'WiX extension installation failed.' } + - name: Build and package, unsigned + shell: pwsh + run: ./packaging/windows/package.ps1 -Arch ${{ matrix.arch }} + - name: Install, run and uninstall the MSI + if: matrix.arch == 'x64' + shell: pwsh + run: ./packaging/windows/test-install.ps1 -Path "dist/release/pdfcraft-$env:VERSION-windows-x64.msi" -ExpectedVersion $env:VERSION + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-windows-${{ matrix.arch }} + path: dist/release/* + if-no-files-found: error + + linux: + name: Linux ${{ matrix.arch }} + needs: [version] + runs-on: ${{ matrix.runner }} + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + include: + - { arch: x86_64, runner: ubuntu-22.04, deb: amd64, nfpm_sha256: 787af76e172dd5fc7cedf4d7c5a090a2f62be9001557a4db1103e270f90ddefd } + - { arch: aarch64, runner: ubuntu-22.04-arm, deb: arm64, nfpm_sha256: 8e9cd8b4ed372a5e8af555a997dfe5e111cb6c7d4d757fd0a0368a35ad84a9b0 } + env: + VERSION: ${{ needs.version.outputs.version }} + INPUT_VERSION: ${{ inputs.version }} + PDFCRAFT_VERSION: ${{ needs.version.outputs.version }} + PDFCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} + PDFCRAFT_BUILD_SHA: ${{ github.sha }} + CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts + CRAFT_FONTS_REQUIRED: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: storytold/craft-fonts + ref: ${{ env.CRAFT_FONTS_REF }} + path: craft-fonts + persist-credentials: false + - name: Install build and packaging tools + env: + DEB_ARCH: ${{ matrix.deb }} + NFPM_SHA256: ${{ matrix.nfpm_sha256 }} + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libxkbcommon-dev libwayland-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev \ + libasound2-dev pkg-config desktop-file-utils appstream python3-yaml zsync + curl --proto '=https' --proto-redir =https -fsSL -o "$RUNNER_TEMP/nfpm.deb" \ + "https://github.com/goreleaser/nfpm/releases/download/v2.48.0/nfpm_2.48.0_${DEB_ARCH}.deb" + echo "$NFPM_SHA256 $RUNNER_TEMP/nfpm.deb" | sha256sum -c - + sudo dpkg -i "$RUNNER_TEMP/nfpm.deb" + - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 + with: + toolchain: stable + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 + with: + key: release-linux-${{ matrix.arch }} + - name: Apply version override + if: env.INPUT_VERSION != '' + run: cargo xtask version set "$VERSION" + - name: Build and package (AppImage, deb, rpm, tar.gz) + run: packaging/linux/package.sh + - name: Check the packages + run: | + dpkg-deb --info dist/release/*.deb + dpkg-deb --contents dist/release/*.deb | grep -E 'usr/bin/|metainfo|applications' + chmod +x dist/release/*.AppImage + for appimage in dist/release/*.AppImage; do + APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version + done + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-linux-${{ matrix.arch }} + path: dist/release/* + if-no-files-found: error + + macos: + name: macOS universal + needs: [version] + runs-on: macos-15 + timeout-minutes: 120 + env: + VERSION: ${{ needs.version.outputs.version }} + INPUT_VERSION: ${{ inputs.version }} + PDFCRAFT_VERSION: ${{ needs.version.outputs.version }} + PDFCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} + PDFCRAFT_BUILD_SHA: ${{ github.sha }} + CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts + CRAFT_FONTS_REQUIRED: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: storytold/craft-fonts + ref: ${{ env.CRAFT_FONTS_REF }} + path: craft-fonts + persist-credentials: false + - uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1 + with: + toolchain: stable + targets: aarch64-apple-darwin, x86_64-apple-darwin + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3 + with: + key: release-macos-universal + - name: Apply version override + if: env.INPUT_VERSION != '' + run: cargo xtask version set "$VERSION" + - name: Build and package, ad-hoc signed + run: packaging/macos/package.sh --arch universal + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: release-macos + path: dist/release/* + if-no-files-found: error + + draft: + name: Draft GitHub release + if: github.ref == 'refs/heads/release' + needs: [version, windows, linux, macos] + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: release-* + path: artifacts + merge-multiple: true + - name: Create or replace the draft + working-directory: artifacts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + VERSION: ${{ needs.version.outputs.version }} + PRERELEASE: ${{ needs.version.outputs.prerelease }} + run: | + sha256sum -- * | tee SHA256SUMS.txt + tag="v$VERSION" + if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then + if [ "$draft" != "true" ]; then + echo "::error::$tag is already published. Bump the version before pushing to release." + exit 1 + fi + gh release delete "$tag" --yes + fi + pre=() + if [ "$PRERELEASE" = "true" ]; then pre=(--prerelease); fi + gh release create "$tag" --draft "${pre[@]}" --target "$GITHUB_SHA" \ + --title "PlutarchPages v$VERSION" --generate-notes -- * diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index b8281a9c8a..f318bf0170 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -238,7 +238,7 @@ Every asset PdfCraft includes, bundles or uses to build its published material, | `assets/app-icon/pdfcraft.icns` | PdfCraft app icon (macOS .icns) | PdfCraft contributors (drawn by the project owner in ArtCraft; vectorised with craftrules vectorize_tile.py) | MIT OR Apache-2.0 | Contributor-original (see assets/app-icon/README.md) | macOS app bundle icon | | `assets/app-icon/pdfcraft.ico` | PdfCraft app icon (Windows .ico, 16-256 px) | PdfCraft contributors (drawn by the project owner in ArtCraft; vectorised with craftrules vectorize_tile.py) | MIT OR Apache-2.0 | Contributor-original (see assets/app-icon/README.md) | Embedded in pdfcraft.exe by apps/pdfcraft/build.rs (Explorer, taskbar, Start menu, Alt-Tab) | | `assets/app-icon/pdfcraft-document.svg` | PdfCraft PDF document icon (master vector) | Lucide Icons and Contributors (document outline); PdfCraft contributors (PDF badge, lettering and composition) | ISC AND (MIT OR Apache-2.0) | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/file-text.svg); contributor-original composition (assets/app-icon/README.md) | Master artwork for the Windows PDF file type icon; rendered by packaging/icons.sh, without fonts or external images | -| `assets/app-icon/pdfcraft-document.ico` | PdfCraft PDF document icon (Windows .ico, 16-256 px) | Lucide Icons and Contributors (document outline); PdfCraft contributors (PDF badge, lettering and composition) | ISC AND (MIT OR Apache-2.0) | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/file-text.svg); contributor-original composition (assets/app-icon/README.md) | Embedded in pdfcraft.exe as resource 2; the Windows installer uses it for PdfCraft.Document and Applications/pdfcraft.exe PDF file associations | +| `assets/app-icon/pdfcraft-document.ico` | PdfCraft PDF document icon (Windows .ico, 16-256 px) | Lucide Icons and Contributors (document outline); PdfCraft contributors (PDF badge, lettering and composition) | ISC AND (MIT OR Apache-2.0) | https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icons/file-text.svg); contributor-original composition (assets/app-icon/README.md) | Embedded in pdfcraft.exe as resource 2; the Windows installer uses it for PlutarchPages.Document and Applications/pdfcraft.exe PDF file associations | | `assets/app-icon/hicolor/16x16/apps/ai.storyteller.pdfcraft.png` | PdfCraft app icon, 16 px | PdfCraft contributors (drawn by the project owner in ArtCraft; vectorised with craftrules vectorize_tile.py) | MIT OR Apache-2.0 | Contributor-original (see assets/app-icon/README.md) | Linux hicolor theme icon (packaging/linux/ai.storyteller.pdfcraft.desktop) | | `assets/app-icon/hicolor/24x24/apps/ai.storyteller.pdfcraft.png` | PdfCraft app icon, 24 px | PdfCraft contributors (drawn by the project owner in ArtCraft; vectorised with craftrules vectorize_tile.py) | MIT OR Apache-2.0 | Contributor-original (see assets/app-icon/README.md) | Linux hicolor theme icon (packaging/linux/ai.storyteller.pdfcraft.desktop) | | `assets/app-icon/hicolor/32x32/apps/ai.storyteller.pdfcraft.png` | PdfCraft app icon, 32 px | PdfCraft contributors (drawn by the project owner in ArtCraft; vectorised with craftrules vectorize_tile.py) | MIT OR Apache-2.0 | Contributor-original (see assets/app-icon/README.md) | Linux hicolor theme icon (packaging/linux/ai.storyteller.pdfcraft.desktop) | diff --git a/ATTRIBUTION.toml b/ATTRIBUTION.toml index a735efea83..6568720fe6 100644 --- a/ATTRIBUTION.toml +++ b/ATTRIBUTION.toml @@ -2856,7 +2856,7 @@ source = "https://github.com/lucide-icons/lucide (npm lucide-static 1.49.0, icon licence = "ISC AND (MIT OR Apache-2.0)" licence_file = "assets/app-icon/LICENSE-document.txt" kind = "icon" -usage = "Embedded in pdfcraft.exe as resource 2; the Windows installer uses it for PdfCraft.Document and Applications/pdfcraft.exe PDF file associations" +usage = "Embedded in pdfcraft.exe as resource 2; the Windows installer uses it for PlutarchPages.Document and Applications/pdfcraft.exe PDF file associations" sha256 = "614a86c0cedbc663c15150c2e14ce1d4d5ee0b37952ebbbe5c8b8dbb1b6781a5" [[asset]] diff --git a/PATCHES.md b/PATCHES.md index c84872c3aa..0f5ee4e41e 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -29,6 +29,13 @@ Upstream's tests for these were changed to assert the fork's behaviour. Keep the - Update check and repository links point at EquityBot. - Shared CI: workflows from `storytold/craft-common-actions` are pinned to a commit, and SonarCloud receives only `SONAR_TOKEN`. Upstream merges restore `@main` and `secrets: inherit`, so reapply both. +- Releases: `.github/workflows/release.yml` is this fork's own and uses nothing from + `craft-common-actions`. It builds unsigned packages, tests an installed MSI with + `packaging/windows/test-install.ps1` and only drafts the release. Keep it over upstream's. Flatpak, + Arch, FreeBSD, riscv64 and web packages aren't built yet. +- Windows registration: `PlutarchPages.*` names and its own context-menu component GUIDs, so it doesn't + share registry state with an upstream install. `pdfcraft.exe` stays, so `App Paths\pdfcraft.exe` and + `Applications\pdfcraft.exe` are still shared if both are installed. ## Merging upstream diff --git a/packaging/linux/ai.storyteller.pdfcraft.metainfo.xml.in b/packaging/linux/ai.storyteller.pdfcraft.metainfo.xml.in index df5efce4fe..23ddac452b 100644 --- a/packaging/linux/ai.storyteller.pdfcraft.metainfo.xml.in +++ b/packaging/linux/ai.storyteller.pdfcraft.metainfo.xml.in @@ -35,7 +35,7 @@ A PDF open with its threaded comments - https://raw.githubusercontent.com/storytold/pdfcraft/main/docs/images/pdfcraft-viewer.png + https://raw.githubusercontent.com/EquityBot/PlutarchPages/main/docs/images/pdfcraft-viewer.png diff --git a/packaging/linux/package.sh b/packaging/linux/package.sh index 36edf51128..82f186468c 100755 --- a/packaging/linux/package.sh +++ b/packaging/linux/package.sh @@ -196,7 +196,7 @@ if has appimage; then # fetch only the blocks that changed in a newer release, through the .zsync published next to # each AppImage on GitHub Releases. `latest` is the newest published release that is not a # pre-release. A fork's builds point at its own releases through GITHUB_REPOSITORY. - REPO="${GITHUB_REPOSITORY:-storytold/pdfcraft}" + REPO="${GITHUB_REPOSITORY:-EquityBot/PlutarchPages}" UPDATE_INFO="gh-releases-zsync|${REPO%%/*}|${REPO#*/}|latest|pdfcraft-*-linux-$ARCH.AppImage.zsync" # Extract-and-run: works without FUSE (containers, CI). The output embeds the static runtime, # so users don't need libfuse2 either. With zsyncmake on the host (CI installs the zsync diff --git a/packaging/windows/install-policy.json b/packaging/windows/install-policy.json index 1c180d59dd..7366dbc43a 100644 --- a/packaging/windows/install-policy.json +++ b/packaging/windows/install-policy.json @@ -26,32 +26,32 @@ ], [ "-1", - "Software\\Classes\\PdfCraft.Document", + "Software\\Classes\\PlutarchPages.Document", "", "PDF Document" ], [ "-1", - "Software\\Classes\\PdfCraft.Document\\DefaultIcon", + "Software\\Classes\\PlutarchPages.Document\\DefaultIcon", "", "\"[#PdfcraftExe]\",-2" ], [ "-1", - "Software\\Classes\\PdfCraft.Document\\shell\\open\\command", + "Software\\Classes\\PlutarchPages.Document\\shell\\open\\command", "", "\"[#PdfcraftExe]\" \"%1\"" ], [ "-1", - "Software\\Classes\\PdfCraft.Document", + "Software\\Classes\\PlutarchPages.Document", "TypeOverlay", "" ], [ "-1", "Software\\Classes\\.pdf\\OpenWithProgids", - "PdfCraft.Document", + "PlutarchPages.Document", "" ], [ @@ -100,12 +100,12 @@ "-1", "Software\\PlutarchPages\\Capabilities\\FileAssociations", ".pdf", - "PdfCraft.Document" + "PlutarchPages.Document" ], [ "-1", "Software\\RegisteredApplications", - "PdfCraft", + "PlutarchPages", "Software\\PlutarchPages\\Capabilities" ], [ @@ -122,205 +122,205 @@ ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PdfCraft.Edit", + "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PlutarchPages.Edit", "", "Edit with PlutarchPages" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PdfCraft.Edit", + "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PlutarchPages.Edit", "Icon", "\"[#PdfcraftExe]\",0" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PdfCraft.Edit", + "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PlutarchPages.Edit", "MultiSelectModel", "Document" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PdfCraft.Edit\\command", + "Software\\Classes\\SystemFileAssociations\\.pdf\\shell\\PlutarchPages.Edit\\command", "", "\"[#PdfcraftExe]\" \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.png\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.jpg\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.jpeg\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.tif\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.tiff\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.gif\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.bmp\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.jp2\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.j2k\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PlutarchPages.CreatePdf", "", "Create PDF with PlutarchPages\u2026" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PdfCraft.CreatePdf", + "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PlutarchPages.CreatePdf", "MultiSelectModel", "Single" ], [ "-1", - "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PdfCraft.CreatePdf\\command", + "Software\\Classes\\SystemFileAssociations\\.jpx\\shell\\PlutarchPages.CreatePdf\\command", "", "\"[#PdfcraftExe]\" --create-images \"%1\"" ] diff --git a/packaging/windows/pdfcraft.wxs b/packaging/windows/pdfcraft.wxs index 05d54af776..a692373209 100644 --- a/packaging/windows/pdfcraft.wxs +++ b/packaging/windows/pdfcraft.wxs @@ -80,10 +80,10 @@ - - - - + + + + @@ -94,7 +94,7 @@ @@ -107,17 +107,17 @@ .pdf's own default value is left alone, and the user chooses. Without this, the only PlutarchPages entry to pick was the advertised Start Menu shortcut, which Windows can't run as a file handler ("This app can't run on your PC"). --> - + - - + + - - + + @@ -125,8 +125,8 @@ - - + + @@ -152,11 +152,11 @@ Windows 11 the entry is under "Show more options": the first-level menu only takes packaged apps' (IExplorerCommand) entries. Its own component with an HKLM key path, so INSTALLPDFCONTEXTMENU=0 leaves it out and uninstall removes it. --> - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/windows/test-install.ps1 b/packaging/windows/test-install.ps1 new file mode 100644 index 0000000000..7719bf3aae --- /dev/null +++ b/packaging/windows/test-install.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Install an MSI for the current account, run it, uninstall it and check nothing is left. Changes the + machine: for disposable CI runners. +.EXAMPLE + pwsh packaging/windows/test-install.ps1 -Path dist/release/pdfcraft-0.15.0-windows-x64.msi -ExpectedVersion 0.15.0 +#> +param( + [Parameter(Mandatory)] [string] $Path, + [Parameter(Mandatory)] [string] $ExpectedVersion +) +$ErrorActionPreference = 'Stop' +$msi = (Resolve-Path -LiteralPath $Path).Path +$key = 'HKCU:\Software\PlutarchPages' +$folder = Join-Path $env:LOCALAPPDATA 'Programs\PlutarchPages' + +function Invoke-Msi([string[]] $Arguments) { + $process = Start-Process msiexec.exe -ArgumentList $Arguments -Wait -PassThru + if ($process.ExitCode -ne 0) { throw "msiexec $($Arguments -join ' ') exited with $($process.ExitCode)" } +} + +Invoke-Msi @('/i', "`"$msi`"", '/qn', '/norestart') +$installed = (Get-ItemProperty -LiteralPath $key -Name InstallDir).InstallDir.TrimEnd('\') +if ($installed -ne $folder) { throw "InstallDir is '$installed', expected '$folder'" } + +$cli = Join-Path $folder 'pdfcraft-cli.exe' +$version = & $cli --version +if ($LASTEXITCODE -ne 0) { throw "pdfcraft-cli --version exited with $LASTEXITCODE" } +if ($version -notmatch [regex]::Escape($ExpectedVersion)) { throw "pdfcraft-cli reported '$version'" } + +$app = Start-Process (Join-Path $folder 'pdfcraft.exe') -PassThru +Start-Sleep -Seconds 15 +if ($app.HasExited) { throw "pdfcraft.exe exited with $($app.ExitCode) within 15 seconds" } +Stop-Process -Id $app.Id -Force +$app.WaitForExit() + +Invoke-Msi @('/x', "`"$msi`"", '/qn', '/norestart') +if (Test-Path -LiteralPath (Join-Path $folder 'pdfcraft.exe')) { throw 'pdfcraft.exe survived the uninstall.' } +if ($null -ne (Get-ItemProperty -LiteralPath $key -Name InstallDir -ErrorAction SilentlyContinue)) { + throw 'The InstallDir record survived the uninstall.' +} +Write-Output "PASS installed, ran $version and uninstalled." diff --git a/packaging/windows/test-msi.ps1 b/packaging/windows/test-msi.ps1 index aa0a725bfa..619cbf1287 100644 --- a/packaging/windows/test-msi.ps1 +++ b/packaging/windows/test-msi.ps1 @@ -187,9 +187,9 @@ if ([int] $remove[0] -le [int] $validate[0] -or [int] $remove[0] -ge [int] $init # Image context menu (#190) opens the DPI chooser. Its own component, gated by INSTALLIMAGECONTEXTMENU # (on for a first install, kept on update only while present, secure), with the .png label as key # path so uninstall removes every row. No image default association is changed. -$imageKey = 'Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf' +$imageKey = 'Software\Classes\SystemFileAssociations\.png\shell\PlutarchPages.CreatePdf' foreach ($ext in @('png', 'jpg', 'jpeg', 'tif', 'tiff', 'gif', 'bmp', 'jp2', 'j2k', 'jpx')) { - $key = 'Software\Classes\SystemFileAssociations\.' + $ext + '\shell\PdfCraft.CreatePdf' + $key = 'Software\Classes\SystemFileAssociations\.' + $ext + '\shell\PlutarchPages.CreatePdf' $menu = Read-Row ('SELECT `Value`, `Component_`, `Root` FROM `Registry` WHERE `Key` = ''' + $key + ''' AND `Name` IS NULL') 3 Assert-Equal $menu[0] 'Create PDF with PlutarchPages…' "$ext context menu label" Assert-Equal $menu[1] 'PdfcraftImageContextMenu' "$ext context menu component" @@ -222,7 +222,7 @@ if (($secure[0] -split ';') -notcontains 'INSTALLIMAGECONTEXTMENU') { throw "INS # checkbox), with its menu value as key path so uninstall removes it. Several selected PDFs each start pdfcraft.exe "%1" (Document), which the # single-instance hand-off opens as tabs. The .pdf default is never set, so the user's default app # stays. -$pdfKey = 'Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit' +$pdfKey = 'Software\Classes\SystemFileAssociations\.pdf\shell\PlutarchPages.Edit' $pdfMenu = Read-Row ('SELECT `Registry`, `Value`, `Component_`, `Root` FROM `Registry` WHERE `Key` = ''' + $pdfKey + ''' AND `Name` IS NULL') 4 Assert-Equal $pdfMenu[1] 'Edit with PlutarchPages' 'PDF context menu label' Assert-Equal $pdfMenu[2] 'PdfcraftPdfContextMenu' 'PDF context menu component' From adfacfe7ceff0fead9d498e9bd8adf8b63121e44 Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sat, 31 Oct 2026 05:20:40 +1100 Subject: [PATCH 2/5] Read the CLI's whole version output in the install test --- packaging/windows/test-install.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/windows/test-install.ps1 b/packaging/windows/test-install.ps1 index 7719bf3aae..859e9582fc 100644 --- a/packaging/windows/test-install.ps1 +++ b/packaging/windows/test-install.ps1 @@ -24,7 +24,7 @@ $installed = (Get-ItemProperty -LiteralPath $key -Name InstallDir).InstallDir.Tr if ($installed -ne $folder) { throw "InstallDir is '$installed', expected '$folder'" } $cli = Join-Path $folder 'pdfcraft-cli.exe' -$version = & $cli --version +$version = (& $cli --version) -join "`n" if ($LASTEXITCODE -ne 0) { throw "pdfcraft-cli --version exited with $LASTEXITCODE" } if ($version -notmatch [regex]::Escape($ExpectedVersion)) { throw "pdfcraft-cli reported '$version'" } @@ -39,4 +39,4 @@ if (Test-Path -LiteralPath (Join-Path $folder 'pdfcraft.exe')) { throw 'pdfcraft if ($null -ne (Get-ItemProperty -LiteralPath $key -Name InstallDir -ErrorAction SilentlyContinue)) { throw 'The InstallDir record survived the uninstall.' } -Write-Output "PASS installed, ran $version and uninstalled." +Write-Output "PASS installed, ran $ExpectedVersion and uninstalled." From 33f9d788b2ebd9c288ac686c311c9b58defea03c Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sat, 31 Oct 2026 05:59:49 +1100 Subject: [PATCH 3/5] Draft only from a push, refuse a moved tag, and check the installed window and uninstall --- .github/workflows/release.yml | 8 +++++++- PATCHES.md | 2 +- packaging/windows/test-install.ps1 | 30 +++++++++++++++++++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d4e929bb8..be5a9cdc27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -230,7 +230,7 @@ jobs: draft: name: Draft GitHub release - if: github.ref == 'refs/heads/release' + if: github.event_name == 'push' && github.ref == 'refs/heads/release' needs: [version, windows, linux, macos] runs-on: ubuntu-latest timeout-minutes: 10 @@ -252,6 +252,12 @@ jobs: run: | sha256sum -- * | tee SHA256SUMS.txt tag="v$VERSION" + # GitHub keeps an existing tag's commit and ignores --target, so the release would point + # at other source than these packages were built from. + if tagged="$(gh api "repos/$GH_REPO/commits/refs/tags/$tag" --jq .sha 2>/dev/null)" && [ "$tagged" != "$GITHUB_SHA" ]; then + echo "::error::Tag $tag is at $tagged, not this commit. Bump the version before pushing to release." + exit 1 + fi if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then if [ "$draft" != "true" ]; then echo "::error::$tag is already published. Bump the version before pushing to release." diff --git a/PATCHES.md b/PATCHES.md index 0f5ee4e41e..f071875829 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -35,7 +35,7 @@ Upstream's tests for these were changed to assert the fork's behaviour. Keep the Arch, FreeBSD, riscv64 and web packages aren't built yet. - Windows registration: `PlutarchPages.*` names and its own context-menu component GUIDs, so it doesn't share registry state with an upstream install. `pdfcraft.exe` stays, so `App Paths\pdfcraft.exe` and - `Applications\pdfcraft.exe` are still shared if both are installed. + `Applications\pdfcraft.exe` are still shared: installing it alongside upstream isn't supported. ## Merging upstream diff --git a/packaging/windows/test-install.ps1 b/packaging/windows/test-install.ps1 index 859e9582fc..9473c6a355 100644 --- a/packaging/windows/test-install.ps1 +++ b/packaging/windows/test-install.ps1 @@ -1,7 +1,7 @@ <# .SYNOPSIS Install an MSI for the current account, run it, uninstall it and check nothing is left. Changes the - machine: for disposable CI runners. + machine: for disposable CI runners. Upgrades need two releases, so this doesn't cover them. .EXAMPLE pwsh packaging/windows/test-install.ps1 -Path dist/release/pdfcraft-0.15.0-windows-x64.msi -ExpectedVersion 0.15.0 #> @@ -13,6 +13,14 @@ $ErrorActionPreference = 'Stop' $msi = (Resolve-Path -LiteralPath $Path).Path $key = 'HKCU:\Software\PlutarchPages' $folder = Join-Path $env:LOCALAPPDATA 'Programs\PlutarchPages' +$owned = @( + $folder, + $key, + 'HKCU:\Software\Classes\PlutarchPages.Document', + 'HKCU:\Software\Classes\SystemFileAssociations\.pdf\shell\PlutarchPages.Edit', + 'HKCU:\Software\Classes\SystemFileAssociations\.png\shell\PlutarchPages.CreatePdf', + (Join-Path ([Environment]::GetFolderPath('Programs')) 'PlutarchPages.lnk') +) function Invoke-Msi([string[]] $Arguments) { $process = Start-Process msiexec.exe -ArgumentList $Arguments -Wait -PassThru @@ -20,6 +28,9 @@ function Invoke-Msi([string[]] $Arguments) { } Invoke-Msi @('/i', "`"$msi`"", '/qn', '/norestart') +foreach ($path in $owned) { + if (-not (Test-Path -LiteralPath $path)) { throw "The install didn't create $path" } +} $installed = (Get-ItemProperty -LiteralPath $key -Name InstallDir).InstallDir.TrimEnd('\') if ($installed -ne $folder) { throw "InstallDir is '$installed', expected '$folder'" } @@ -29,14 +40,19 @@ if ($LASTEXITCODE -ne 0) { throw "pdfcraft-cli --version exited with $LASTEXITCO if ($version -notmatch [regex]::Escape($ExpectedVersion)) { throw "pdfcraft-cli reported '$version'" } $app = Start-Process (Join-Path $folder 'pdfcraft.exe') -PassThru -Start-Sleep -Seconds 15 -if ($app.HasExited) { throw "pdfcraft.exe exited with $($app.ExitCode) within 15 seconds" } +$deadline = (Get-Date).AddSeconds(60) +do { + Start-Sleep -Seconds 2 + $app.Refresh() + if ($app.HasExited) { throw "pdfcraft.exe exited with $($app.ExitCode) before showing a window" } +} until ($app.MainWindowHandle -ne 0 -or (Get-Date) -gt $deadline) +if ($app.MainWindowHandle -eq 0) { throw 'pdfcraft.exe showed no window within 60 seconds' } +$title = $app.MainWindowTitle Stop-Process -Id $app.Id -Force $app.WaitForExit() Invoke-Msi @('/x', "`"$msi`"", '/qn', '/norestart') -if (Test-Path -LiteralPath (Join-Path $folder 'pdfcraft.exe')) { throw 'pdfcraft.exe survived the uninstall.' } -if ($null -ne (Get-ItemProperty -LiteralPath $key -Name InstallDir -ErrorAction SilentlyContinue)) { - throw 'The InstallDir record survived the uninstall.' +foreach ($path in $owned) { + if (Test-Path -LiteralPath $path) { throw "$path survived the uninstall" } } -Write-Output "PASS installed, ran $ExpectedVersion and uninstalled." +Write-Output "PASS installed, ran $ExpectedVersion, showed the window '$title' and uninstalled." From 606c7ace1658d1af8902c5343ea5a7ce021861da Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sat, 31 Oct 2026 06:28:23 +1100 Subject: [PATCH 4/5] Fail the draft when the tag lookup fails --- .github/workflows/release.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be5a9cdc27..0fbcaa4d7d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -254,9 +254,14 @@ jobs: tag="v$VERSION" # GitHub keeps an existing tag's commit and ignores --target, so the release would point # at other source than these packages were built from. - if tagged="$(gh api "repos/$GH_REPO/commits/refs/tags/$tag" --jq .sha 2>/dev/null)" && [ "$tagged" != "$GITHUB_SHA" ]; then - echo "::error::Tag $tag is at $tagged, not this commit. Bump the version before pushing to release." - exit 1 + # A failed lookup stops the job here: only a listing without the tag means it's absent. + found="$(gh api "repos/$GH_REPO/git/matching-refs/tags/$tag" --jq "map(select(.ref == \"refs/tags/$tag\")) | length")" + if [ "$found" != 0 ]; then + tagged="$(gh api "repos/$GH_REPO/commits/refs/tags/$tag" --jq .sha)" + if [ "$tagged" != "$GITHUB_SHA" ]; then + echo "::error::Tag $tag is at $tagged, not this commit. Bump the version before pushing to release." + exit 1 + fi fi if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then if [ "$draft" != "true" ]; then From 10d0d24dae7d02ef35d2f626a98e70a7bf506bec Mon Sep 17 00:00:00 2001 From: Ryan Duguid Date: Sat, 31 Oct 2026 07:28:24 +1100 Subject: [PATCH 5/5] Point the workspace's repository link at this fork --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bf8a8fba87..61ed0380db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ version = "0.15.0" edition = "2024" license = "MIT OR Apache-2.0" rust-version = "1.95" -repository = "https://github.com/storytold/pdfcraft" +repository = "https://github.com/EquityBot/PlutarchPages" [workspace.dependencies] # Shared frontend components; engines remain independent of egui.