Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,13 @@ jobs:
Copy-Item -Path "dist\OpenReader\*" -Destination $stageDir -Recurse -Force

# Copy and patch manifest
# PowerShell -replace is case-insensitive; use -creplace to avoid
# matching version="1.0" in the XML declaration.
# Use XML DOM patching (not regex) to avoid corrupting MinVersion/MaxVersionTested
Copy-Item -Path "packaging\msix\AppxManifest.xml" -Destination "$stageDir\AppxManifest.xml" -Force
$manifestContent = [System.IO.File]::ReadAllText("$stageDir\AppxManifest.xml")
$manifestContent = $manifestContent -creplace 'Version="[^"]+"', "Version=`"$msixVersion`""
[System.IO.File]::WriteAllText("$stageDir\AppxManifest.xml", $manifestContent, [System.Text.UTF8Encoding]$false)
$manifestXml = New-Object System.Xml.XmlDocument
$manifestXml.PreserveWhitespace = $true
$manifestXml.Load("$stageDir\AppxManifest.xml")
$manifestXml.Package.Identity.Version = $msixVersion
$manifestXml.Save("$stageDir\AppxManifest.xml")

# Generate MSIX asset placeholders using Python
Write-Host "=== Generating MSIX asset placeholders ==="
Expand Down
34 changes: 21 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,25 +131,27 @@ jobs:
Copy-Item -Path "dist\OpenReader\*" -Destination $stageDir -Recurse -Force

# Copy and patch manifest
# CRITICAL: PowerShell -replace is CASE-INSENSITIVE by default.
# Regex 'Version="[^"]+"' matches version="1.0" in XML declaration too!
# Use -creplace (case-sensitive) to only match Identity element's Version.
# Use XML DOM patching (not regex) to avoid corrupting MinVersion/MaxVersionTested
Copy-Item -Path "packaging\msix\AppxManifest.xml" -Destination "$stageDir\AppxManifest.xml" -Force
$manifestPath = "$stageDir\AppxManifest.xml"
$content = [System.IO.File]::ReadAllText($manifestPath)
$content = $content -creplace 'Version="[^"]+"', "Version=`"$msixVersion`""
[System.IO.File]::WriteAllText($manifestPath, $content, [System.Text.UTF8Encoding]$false)
Write-Host "Manifest patched: Version -> $msixVersion"
$manifestXml = New-Object System.Xml.XmlDocument
$manifestXml.PreserveWhitespace = $true
$manifestXml.Load($manifestPath)
$manifestXml.Package.Identity.Version = $msixVersion
$manifestXml.Save($manifestPath)
Write-Host "Manifest patched: Identity Version -> $msixVersion"
Write-Host ""

# Validate XML before MakeAppx
Write-Host "=== XML Validation ==="
try {
$xml = [xml](Get-Content $manifestPath -Raw)
Write-Host "XML parsed successfully."
Write-Host "Identity: $($xml.Package.Identity.Name)"
Write-Host "Version: $($xml.Package.Identity.Version)"
Write-Host "Publisher: $($xml.Package.Identity.Publisher)"
Write-Host "Identity: $($xml.Package.Identity.Name)"
Write-Host "Version: $($xml.Package.Identity.Version)"
Write-Host "Publisher: $($xml.Package.Identity.Publisher)"
Write-Host "MinVersion: $($xml.Package.TargetDeviceFamily.MinVersion)"
Write-Host "MaxVersion: $($xml.Package.TargetDeviceFamily.MaxVersionTested)"
} catch {
Write-Host "XML PARSE ERROR: $_"
Write-Host "First 20 lines of manifest:"
Expand Down Expand Up @@ -213,9 +215,15 @@ jobs:
Write-Host "=== MSIX built: $msixName (unsigned) ==="

# Generate AppInstaller
$installerContent = [System.IO.File]::ReadAllText("packaging\msix\AppInstaller.xml")
$installerContent = $installerContent -creplace 'Version="[^"]+"', "Version=`"$msixVersion`""
[System.IO.File]::WriteAllText("$pwd\OpenReader.appinstaller", $installerContent, [System.Text.UTF8Encoding]$false)
# Use XML DOM patching to only update MainPackage version (not AppInstaller schema version)
$installerXml = New-Object System.Xml.XmlDocument
$installerXml.PreserveWhitespace = $true
$installerXml.Load("packaging\msix\AppInstaller.xml")
$nsm = New-Object System.Xml.XmlNamespaceManager($installerXml.NameTable)
$nsm.AddNamespace("ai", "http://schemas.microsoft.com/appx/appinstaller/2021")
$mainPkg = $installerXml.SelectSingleNode("//ai:MainPackage", $nsm)
if ($mainPkg) { $mainPkg.Version = $msixVersion }
$installerXml.Save("$pwd\OpenReader.appinstaller")

# Clean up
Remove-Item -Recurse -Force $stageDir
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## v1.2.2 — Store Submission Fix — 2026-06-18

- **Version:** Bumped `__version__` to `1.2.2`, MSIX version to `1.2.2.0`.
- **Fixed:** Partner Center package validation rejection (`MinVersion <= 10.0.17134.0`).
- **Root cause:** MSIX manifest patching used regex `-creplace 'Version="[^"]+"'` which matched inside `MinVersion` and `MaxVersionTested` attributes, overwriting them with the app version number.
- **Fix:** Replaced regex-based version injection with proper XML DOM patching in:
- `release.yml` (CI release build)
- `build-windows.yml` (CI dev build)
- `build-msix.ps1` (local build script)
- **Result:** Generated MSIX now correctly preserves `TargetDeviceFamily MinVersion="10.0.17763.0"`.
- **Validation added:** CI now logs `MinVersion` and `MaxVersionTested` during XML validation step.

## v1.2.1 — First Public Microsoft Store Release Candidate — 2026-06-18

- **Version:** Bumped `__version__` to `1.2.1`.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ OpenReader is a **stable, local-first desktop PDF utility** built with Python, P

The app is intentionally local-first: PDFs are opened, rendered, searched, merged, split, annotated, and compressed on your computer — no uploads, no accounts, no telemetry.

**v1.2.1** (current release) is the first public Microsoft Store release candidate. Windows distribution uses MSIX/App Installer with Windows-native updates — the app never replaces itself. See the [changelog](CHANGELOG.md) and [roadmap](ROADMAP.md) for what's new and what's next.
**v1.2.2** (current release) fixes the MSIX manifest for Microsoft Store acceptance. Windows distribution uses MSIX/App Installer with Windows-native updates — the app never replaces itself. See the [changelog](CHANGELOG.md) and [roadmap](ROADMAP.md) for what's new and what's next.

## Download

Expand Down
10 changes: 5 additions & 5 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ OpenReader uses semantic version tags to publish packaged builds.
## Version Source of Truth

- `__version__` in `main.py` is the canonical source. Set it to the next release version.
- Tags must use the format `vMAJOR.MINOR.PATCH`, for example `v1.2.1`.
- The injected runtime version removes the leading `v`, so `v1.2.1` becomes `__version__ = "1.2.1"` in packaged builds.
- Tags must use the format `vMAJOR.MINOR.PATCH`, for example `v1.2.2`.
- The injected runtime version removes the leading `v`, so `v1.2.2` becomes `__version__ = "1.2.1"` in packaged builds.
- CI injects the tag version for release builds via `scripts/inject_version.py`.

## Release Architecture (v1.2.0+)
Expand Down Expand Up @@ -45,8 +45,8 @@ no separate code-signing certificate is needed.
4. Create and push a semantic version tag:

```bash
git tag v1.2.1
git push origin v1.2.1
git tag v1.2.2
git push origin v1.2.2
```

5. GitHub Actions runs `.github/workflows/release.yml`.
Expand Down Expand Up @@ -98,7 +98,7 @@ curl https://api.github.com/repos/sparshsam/pdfreader-by-sparsh/releases/latest
The MSIX package is currently unsigned. The distribution plan is:

1. **Microsoft Store** — Submit the unsigned MSIX to the Microsoft Store. The Store
signs the package automatically with its Store identity. **v1.2.1 is the first
signs the package automatically with its Store identity. **v1.2.2 is the first
Store release candidate.**
2. **Sideloading** — Unsigned MSIX from GitHub Releases requires Windows
Developer Mode. Local test-signing scripts are in `packaging/msix/`.
Expand Down
1 change: 1 addition & 0 deletions VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ MSIX packages use a 4-part version: `major.minor.patch.build`.
| `v1.2.0-rc.1` | `1.2.0.2` |
| `v1.2.0` | `1.2.0.0` |
| `v1.2.1` | `1.2.1.0` |
| `v1.2.2` | `1.2.2.0` |

The build number is the prerelease index minus 1 (beta.1 → 0, beta.2 → 1, etc.).
Stable releases use build 0. The CI workflow automatically extracts the correct
Expand Down
15 changes: 8 additions & 7 deletions docs/store-submission-checklist.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Microsoft Store Submission Checklist — OpenReader

**Target version:** v1.2.1 stable (MSIX version `1.2.1.0`)
**Target version:** v1.2.2 stable (MSIX version `1.2.2.0`)
**Store ID:** `9MXDVW2645LL`
**PFN:** `SparshSam.OpenReader_yh0byntbzd2qw`
**Status:** 🔜 Ready for submission (privacy policy published)
**Privacy policy URL:** https://sparshsam.github.io/pdfreader-by-sparsh/privacy/
**Upload artifact:** `OpenReader.msix` from v1.2.1 GitHub Release (built by release.yml workflow)
**Upload artifact:** `OpenReader.msix` from v1.2.2 GitHub Release (built by release.yml workflow)

---

Expand All @@ -24,7 +24,7 @@ Get-AppxPackage SparshSam.OpenReader | Select Name, Version, PackageFamilyName
```text
Name Version PackageFamilyName
---- ------- -----------------
SparshSam.OpenReader 1.2.1.0 SparshSam.OpenReader_yh0byntbzd2qw
SparshSam.OpenReader 1.2.2.0 SparshSam.OpenReader_yh0byntbzd2qw
```

### 1.2 Manifest Audit
Expand All @@ -44,10 +44,11 @@ Select-Xml -Path .\msix-check\AppxManifest.xml -XPath "//*[local-name()='Identit

- [ ] `<Identity Name="SparshSam.OpenReader">`
- [ ] `<Publisher="CN=E6186421-BF8A-47E0-A89C-0F513DFF91C0">`
- [ ] Version is `1.2.1.0`
- [ ] Version is `1.2.2.0`
- [ ] `<DisplayName>OpenReader</DisplayName>`
- [ ] `<PublisherDisplayName>Sparsh Sam</PublisherDisplayName>`
- [ ] Executable is `OpenReader.exe`
- [ ] `TargetDeviceFamily MinVersion="10.0.17763.0"` (must be > 10.0.17134.0 for Store)
- [ ] No placeholder or draft values remain

### 1.3 Capability Review
Expand Down Expand Up @@ -111,7 +112,7 @@ Start-Process "OpenReader"
1. Navigate to **Partner Center** → OpenReader → **Packages**
2. Upload the **unsigned** `OpenReader.msix` from the GitHub Release
3. The Store will automatically sign the package with its Store identity
4. Set `1.2.1.0` as the version in Partner Center (must match manifest)
4. Set `1.2.2.0` as the version in Partner Center (must match manifest)
5. Submit for certification

> **ℹ️** Upload the MSIX produced by the GitHub Actions release workflow directly.
Expand Down Expand Up @@ -150,7 +151,7 @@ Start-Process "OpenReader"
| **`runFullTrust` capability** | Store may ask why a desktop app needs full trust | Expected for Win32 desktop bridge apps. Document in submission notes: *"Desktop PDF reader using PySide6 — requires full trust for file system access and window management."* |
| **App description claims** | Store may reject if claims are unrealistic | Keep description factual and shipping-feature-only. Remove roadmap items from Store description. |
| **Unsplash/mock screenshots** | Store requires real app screenshots | Use actual app screenshots from `assets/` |
| **Version mismatch** | Upload rejected if manifest version ≠ Partner Center version | Verify `1.2.1.0` matches everywhere |
| **Version mismatch** | Upload rejected if manifest version ≠ Partner Center version | Verify `1.2.2.0` matches everywhere |
| **Store ID reuse** | Cannot reuse Store ID for a different app | Reserved ID `9MXDVW2645LL` is tied to OpenReader — do not reassign |

### 4.2 Certification Notes for Submission
Expand Down Expand Up @@ -203,7 +204,7 @@ Get-AppxPackage SparshSam.OpenReader | Select Name, Version, PackageFamilyName
```text
Name Version PackageFamilyName
---- ------- -----------------
SparshSam.OpenReader 1.2.1.0 SparshSam.OpenReader_yh0byntbzd2qw
SparshSam.OpenReader 1.2.2.0 SparshSam.OpenReader_yh0byntbzd2qw
```

### 5.3 Functional Smoke Test
Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
)


__version__ = "1.2.1"
__version__ = "1.2.2"
GITHUB_REPO = "sparshsam/pdfreader-by-sparsh"
IPC_SERVER_NAME = "OpenReader-IPC"
RECENT_FILES_MAX = 10
Expand Down
2 changes: 1 addition & 1 deletion packaging/msix/AppInstaller.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<MainPackage
Name="SparshSam.OpenReader"
Publisher="CN=E6186421-BF8A-47E0-A89C-0F513DFF91C0"
Version="1.2.1.0"
Version="1.2.2.0"
ProcessorArchitecture="x64"
Uri="https://downloads.openreader.app/stable/OpenReader.msix" />

Expand Down
2 changes: 1 addition & 1 deletion packaging/msix/AppxManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<Identity
Name="SparshSam.OpenReader"
Publisher="CN=E6186421-BF8A-47E0-A89C-0F513DFF91C0"
Version="1.2.1.0" />
Version="1.2.2.0" />

<Properties>
<DisplayName>OpenReader</DisplayName>
Expand Down
1 change: 1 addition & 0 deletions packaging/msix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ MSIX follows a `major.minor.patch.build` version scheme.
|---------|--------------|-------------|
| `v1.2.0` | `1.2.0.0` | Initial MSIX release |
| `v1.2.1` | `1.2.1.0` | First Microsoft Store release candidate |
| `v1.2.2` | `1.2.2.0` | Store submission fix (MinVersion patch bug) |

The CI workflow automatically extracts the version from the Git tag and injects it
into the manifest as `{tag}.0` (padded to 4 parts).
Expand Down
9 changes: 6 additions & 3 deletions packaging/msix/build-msix.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,15 @@ Write-Host "Copying application files..." -ForegroundColor Cyan
Copy-Item -Path "$ExeDir\*" -Destination $StageDir -Recurse -Force

# Copy AppxManifest and update version
# Use XML DOM patching (not regex) to avoid corrupting MinVersion/MaxVersionTested
$ManifestPath = Join-Path $ScriptDir "AppxManifest.xml"
$ManifestDest = Join-Path $StageDir "AppxManifest.xml"
Copy-Item -Path $ManifestPath -Destination $ManifestDest -Force
$content = [System.IO.File]::ReadAllText("$ManifestDest")
$content = $content -creplace 'Version="[^"]+"', 'Version="' + "$Version" + '"'
[System.IO.File]::WriteAllText("$ManifestDest", $content, [System.Text.UTF8Encoding]$false)
$manifestXml = New-Object System.Xml.XmlDocument
$manifestXml.PreserveWhitespace = $true
$manifestXml.Load($ManifestDest)
$manifestXml.Package.Identity.Version = $Version
$manifestXml.Save($ManifestDest)

# Generate MSIX asset placeholders using Python
Write-Host "Generating MSIX asset placeholders..." -ForegroundColor Cyan
Expand Down