Skip to content

feat(windows): make every conversion work in a per-user Windows installer - #6

Merged
leoisadev1 merged 21 commits into
mainfrom
leoplayz317/cnv-10-make-every-conversion-work-in-the-windows-build
Oct 7, 2026
Merged

leoisadev1 merged 21 commits into
mainfrom
leoplayz317/cnv-10-make-every-conversion-work-in-the-windows-build

Conversation

@leoisadev1

@leoisadev1 leoisadev1 commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

On Windows, HEIC and Office files failed with no_route: libheif wasn't shipped, and the document pack refused to install because its ownership checks only understood Unix permissions. There was also no installer, because build.ps1 stopped on an empty input lock. This branch pins every native input, builds a per-user MSI, and gets every conversion working from the installed app.

 packaging/windows/
+├── build-native.ps1          # builds libde265, x265, aom and libheif with MSVC
+├── build-document-pack.py    # repackages the LibreOffice MSI as the document pack
+├── document-launcher.rs      # soffice.exe that loads Office DLLs from its own folder only
 ├── inputs.lock.json          # was empty; now 12 pinned, hashed inputs
 ├── build.ps1                 # builds the payload instead of refusing
 ├── installer.ps1 + convt.wxs # per-user MSI, no admin prompt
 crates/convt-engines/src/
+├── windows_acl.rs            # owner, DACL, integrity label and reparse-point checks
 ├── packs.rs                  # uses windows_acl on Windows; Unix path unchanged
 ├── heic.rs, office.rs        # find libheif and the pack launcher beside convt.exe

Evidence

All of this was run on a Windows 11 PC (leopc), against the MSI built from this branch, with nothing on PATH except the installed app.

  • Before (origin/main): heic -> jpg and docx -> pdf exited 1 with no_route; build.ps1 threw Windows release blocked; installer.ps1 threw Build audited payload first.
  • After:
    • msiexec /i (per-user, no elevation): exit 0. convt engines lists image, svg, libheif, ffmpeg, pdfium, libreoffice.
    • cargo test -p convt-engines --test matrix full_matrix against the installed CLI: 421 of 421 cases, 0 errors (161 image, 192 video, 42 audio, 13 SVG, 13 PDF), including HEIC in and out, multi-page PDF, and transparent PNG to JPG on white.
    • Office through the installed document pack: .doc, .odt, .docx, .pptx and .xlsx each produced a valid %PDF- file.
    • convt targets --menu: docx: pdf txt, pptx: pdf, xlsx: pdf csv.
    • msiexec /x: exit 0. The CLI, app, Start menu shortcut and registry key are gone; user data and the document pack are kept.
    • Windows unit tests: windows_acl::tests 6/6 (including a new test that marks a file low-integrity with icacls /setintegritylevel low) and packs:: 20/20.
  • Linux: cargo test -p convt-core -p convt-engines -p convt-cli passes and bun run rs:check is clean.

A GPT-6.1 Sol review found one blocker: pack files labelled below medium integrity were accepted, so a sandboxed low-integrity process could swap a DLL that LibreOffice then loads. It's fixed in 26100c7. The real installed pack still passes the check and converts DOCX to PDF.

Not tested:

  • The desktop app window on Windows. The PC was in use, so no GUI input was sent.
  • Windows CI. GitHub-hosted runners are blocked org-wide by a billing/spending-limit error, and Tenki has no Windows runners.

Merge Danger

Door: two-way. Nothing is published or signed; reverting removes the Windows packaging.

Blast radius: Windows builds and the document-pack checks. packs.rs is shared, but its Unix path is unchanged and the Linux pack tests pass. The FFmpeg input is gyan.dev's GPLv3 "essentials" build, so the release licence and source-closure work tracked separately still has to clear it before public distribution. The MSI is unsigned.

Fixes CNV-10

Created with Claude Opus 5.5 in Claude Code.


Devin Review

@linear-code

linear-code Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
CNV-10 Make every conversion work in the Windows build

Problem

On Windows 11 the CLI builds and most conversions work: PNG to JPG, PNG to WebP, JPG to AVIF, SVG to PNG, MP4 and MOV to WebM, MP4 to MP3, WAV to FLAC, and a two-page PDF to PNG. Two kinds fail with no_route:

  • HEIC (HEIC to JPG): libheif isn't bundled or found on Windows.
  • Office documents (DOCX to PDF): no LibreOffice, and the document-pack ownership check rejects Windows outright (crates/convt-engines/src/packs.rs, around line 437).

The Windows release payload can't be built either. packaging/windows/build.ps1 stops with "Windows release blocked" because its native-input manifest (packaging/windows/inputs.lock.json) is empty. installer.ps1 then refuses with "Build audited payload first", and convt.wxs installs per-machine.

Goal

A Windows build where every conversion the Mac and Linux builds offer works, packaged as an installer.

Acceptance criteria

  • HEIC input converts on Windows: libheif is bundled and pinned, or FFmpeg decodes it.
  • The LibreOffice document pack installs and runs on Windows, and DOCX, PPTX and XLSX convert to PDF.
  • inputs.lock.json pins FFmpeg, PDFium and libheif with hashes, and build.ps1 produces the payload.
  • installer.ps1 produces an MSI that installs and uninstalls cleanly, per-user if possible.
  • The installed app's CLI passes the same conversion matrix as Linux, with no no_route for formats the menu offers.

How to verify

On a Windows 11 machine, build with packaging/windows/build.ps1 and installer.ps1, install the MSI, and run convt engines and the matrix (HEIC to JPG, DOCX to PDF, PDF to PNG, MOV to MP4, PNG to JPG). Then open the desktop app and convert a file from it.

Review in Linear

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread crates/convt-engines/src/windows_acl.rs Resolved
Comment thread crates/convt-engines/src/packs.rs Resolved

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.


Graphify review — findings

Enables executable document packs on Windows by replacing the "not supported" bail with fail-closed ACL ownership checks (windows_acl::trusted) on the pack root, its ancestors, and the parent of any missing component; a bundle:documents.tar.gz source now resolves to a pack shipped next to the executable. Restricts libheif's dependent DLL search to its own directory plus System32, so the working directory and PATH are never consulted. Passes LibreOffice a proper file URL for its private profile and an absolute input path, so concurrent jobs can't fall back to the shared default profile; release dry runs call build.ps1 -VerificationOnly.

Worth a look

  • Bundled document-pack URL built by string formatting instead of a proper file URL — crates/convt-engines/src/packs.rs:31 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TrustedInstaller owner exception is not applied to ACL allow ACEs — crates/convt-engines/src/windows_acl.rs:184 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review

Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 285 functions depend on the 277 functions this change touches.

Health — this change adds coupling hotspots:

  • new: install_at_with() — 5 callers, 14 callees
  • new: validate() — 5 callers, 6 callees
  • new: remove_at() — 4 callers, 7 callees
  • new: encoded_fixture() — 6 callers, 3 callees
  • new: encode_depth() — 2 callers, 7 callees
  • new: rgb_to_srgb_matrix() — 3 callers, 4 callees
  • new: pack() — 1 callers, 6 callees
  • new: full_matrix() — 0 callers, 10 callees
  • …and 6 more — each is listed as a finding

Verification — 285 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 285 function(s) in the blast radius were not formally verified this run

Risky patterns (advisory)

  • medium no-stderr-to-stdout in packaging/windows/build.ps1:31: stderr redirected to stdout: $BuildConfig = & "$Out/ffmpeg.exe" -buildconf 2>&1

Docs that may be stale (advisory)

· 14 more finding(s) on lines outside this diff (see the check run).

@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 3/5

[High risk] Adds Windows installer build and native codec compilation.

The latest test change is sound, but the previously reported CMake 4 and Python 3.10 build failures still block merging.

Fix All in Claude CodeFindings

  1. P1 CMake 4 breaks Windows builds ▶
  2. P1 Python 3.10 stops the build ▶
  3. P2 Python 2 breaks optional tests ▶

Summary

This PR adds pinned Windows dependencies, a bundled LibreOffice document pack, Windows permission checks, and a per-user MSI.

  • The latest change limits the unsupported-platform test to platforms other than Unix and Windows.
  • No new findings were accepted.
  • leoisadev1 explicitly defers public distribution until signing and the matching source inventory are complete. Windows desktop-window checks remain untested.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Inputs[Hashed native inputs] --> Build[Build codecs and extract LibreOffice]
  Build --> Payload[Windows payload and document archive]
  Payload --> MSI[Per-user MSI]
  MSI --> App[Installed CLI and desktop app]
  App --> Install[User explicitly installs document pack]
  Install --> Checks[Check archive hash and Windows permissions]
  Checks --> Pack[Private document pack]
  Pack --> Office[Office conversion with private profile]
Loading

Reviews (6) · Last reviewed commit: "test(packs): keep the unsupported-owners..."

Comment thread packaging/windows/build.ps1 Resolved
Comment thread crates/convt-engines/src/windows_acl.rs Resolved
Comment thread packaging/windows/installer.ps1 Resolved
Comment thread crates/convt-engines/src/heic.rs Resolved
Comment thread crates/convt-engines/src/windows_acl.rs Resolved
leoisadev1 and others added 18 commits October 6, 2026 19:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A low-integrity process can write to an object labelled low even when its
DACL names only our user, so the pack check now reads the mandatory label
too and refuses anything below medium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@leoisadev1
leoisadev1 force-pushed the leoplayz317/cnv-10-make-every-conversion-work-in-the-windows-build branch from d2f41c7 to bb5dd6b Compare October 6, 2026 23:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +42 to +43
& $CMake -S (Join-Path $Source $Subdir) -B $Build -G $Generator -A x64 "-DCMAKE_INSTALL_PREFIX=$Prefix" "-DCMAKE_PREFIX_PATH=$Prefix" -DCMAKE_INSTALL_LIBDIR=lib @Options
if ($LASTEXITCODE -ne 0) { throw "Configure $Name" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 CMake 4 breaks Windows builds

If cmake.exe on PATH is CMake 4, this configure step fails on the pinned x265 3.5 source. It selects old policies that CMake 4 removed. The Linux and macOS recipes already fix those policies, but this recipe omits that fix and CMAKE_POLICY_VERSION_MINIMUM. The build stops before producing the payload or MSI.

Apply the same compatibility changes, or explicitly require CMake 3 before starting the build. Certainty: 4/5.

Fix in Claude Code

archive.addfile(info, stream)
else:
archive.addfile(info)
print(hashlib.file_digest(output.open('rb'), 'sha256').hexdigest())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Python 3.10 stops the build

If the builder runs with Python 3.10, hashlib.file_digest raises AttributeError after writing the archive. That function requires Python 3.11, but the Windows instructions only require Python 3. build.ps1 then stops without compiling the app or producing an MSI.

Use a digest loop that works on the supported Python versions, or require and check Python 3.11 before starting the build. Certainty: 5/5.

Suggested change
print(hashlib.file_digest(output.open('rb'), 'sha256').hexdigest())
digest = hashlib.sha256()
with output.open('rb') as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(chunk)
print(digest.hexdigest())

Fix in Claude Code

Comment on lines +25 to +29
.or_else(|| {
(name == "python3")
.then(|| which::which("python").ok())
.flatten()
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Python 2 breaks optional tests

If a host has only Python 2 under the name python, the new fallback makes tool("python3") succeed instead of letting the matrix skip its Python-dependent checks. The fixture's f-strings fail to parse, and native_validators then panics during discovery.

This is a non-blocking test issue. Check the fallback's Python version before returning it so hosts without Python 3 still skip these checks. Certainty: 5/5.

Suggested change
.or_else(|| {
(name == "python3")
.then(|| which::which("python").ok())
.flatten()
})
.or_else(|| {
(name == "python3")
.then(|| which::which("python").ok())
.flatten()
.filter(|path| {
Command::new(path)
.args([
"-c",
"import sys; sys.exit(0 if sys.version_info >= (3, 6) else 1)",
])
.status()
.is_ok_and(|status| status.success())
})
})

Fix in Claude Code

leoisadev1 and others added 2 commits October 6, 2026 20:14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow checks ACLs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@leoisadev1
leoisadev1 merged commit 5b4bb4a into main Oct 7, 2026
7 of 8 checks passed
@leoisadev1
leoisadev1 deleted the leoplayz317/cnv-10-make-every-conversion-work-in-the-windows-build branch October 7, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant