Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
026144e
M10: add multilingual OCR and automatic scan correction
lukashold Oct 11, 2026
74ea39c
M10: crop scanner borders at detected paper edges
lukashold Oct 11, 2026
78a3456
M10: add Italian OCR and accented-text regression coverage
lukashold Oct 11, 2026
5ea94ba
M10: add Latin OCR languages and system-language defaults
lukashold Oct 11, 2026
f807821
M10: add optional cross-platform Hot Folder processing service
lukashold Oct 11, 2026
486653b
M10: version Hot Folder service path dependencies
lukashold Oct 11, 2026
26b989c
Harden Hot Folder supervision, durable recovery and Windows service u…
lukashold Oct 11, 2026
391a74d
Localize Windows and macOS installers using the primary system language
lukashold Oct 11, 2026
1802d2f
M10: keep installer language enumeration compatible with macOS Bash 3.2
lukashold Oct 11, 2026
98010e7
M10: preserve existing SCM account during Hot Folder MSI upgrades
lukashold Oct 11, 2026
9734d99
M10: integrate account-preserving service upgrades into localized ins…
lukashold Oct 11, 2026
e6198b6
M10: avoid Foundation property names in native installer language test
lukashold Oct 11, 2026
b112cd8
Keep single-command server acceptance scripts as JSON arrays
lukashold Oct 11, 2026
dc10839
Merge the server acceptance JSON regression fix
lukashold Oct 11, 2026
5ccb499
Gate server acceptance on unchanged source bytes
lukashold Oct 11, 2026
527bd81
Merge source-preservation gates and release installer checksums
lukashold Oct 11, 2026
db10f61
M14: expand installer translations and qualify native Unicode dialogs
lukashold Oct 11, 2026
8adf4d3
M10: accept independently quoted MSI service paths
lukashold Oct 11, 2026
c324294
M10: include the MSI service-path quoting regression fix
lukashold Oct 11, 2026
8dd3956
M14: diagnose native installer preview failures with stage and window…
lukashold Oct 11, 2026
3b33bb9
M14: keep native MSI preview dialogs visible in the guarded CI fixture
lukashold Oct 11, 2026
93b3cec
M14: record MSI native control identity and accessibility patterns in CI
lukashold Oct 11, 2026
67108c6
test(installer): verify native MSI control identity and visible text
lukashold Oct 11, 2026
a0c6fee
M10: release MSI upgrade-fixture handles and preserve primary test fa…
lukashold Oct 11, 2026
b931cc7
M10: include corrected artifact-backed MSI upgrade acceptance
lukashold Oct 11, 2026
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
1 change: 1 addition & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
self-hosted-runner:
labels:
- macos-release
- pdfcraft-server-2019-disposable
16 changes: 15 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
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 libgtk-3-dev
libgl1-mesa-dev libgtk-3-dev libdbus-1-dev

- uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -67,6 +67,8 @@ jobs:

- name: Test
if: runner.os != 'Linux'
env:
PDFCRAFT_REQUIRE_SYMLINK_TESTS: "1"
run: cargo test --workspace

- name: Test (with craft-fonts)
Expand All @@ -79,6 +81,18 @@ jobs:
- name: Layering
run: cargo xtask layers

- name: Verify administrative credential provisioning (Linux)
if: runner.os == 'Linux'
shell: bash
env:
CRAFT_FONTS_DIR: ${{ github.workspace }}/craft-fonts
CRAFT_FONTS_REQUIRED: "1"
run: |
cargo test -p pdfcraft-engine --lib --no-run --message-format=json > "$RUNNER_TEMP/engine-tests.json"
test_binary="$(jq -rs '[.[] | select(.reason == "compiler-artifact" and .profile.test == true and .target.name == "pdfcraft_engine") | .executable | select(. != null)] | last // empty' "$RUNNER_TEMP/engine-tests.json")"
test -n "$test_binary"
sudo "$test_binary" --exact hot_folder_sources::credentials::tests::unix_admin_provisioning_preserves_service_uid_gid_and_private_modes --ignored

- name: Assets (licences and attribution)
if: runner.os == 'Linux'
run: cargo xtask assets
Expand Down
142 changes: 142 additions & 0 deletions .github/workflows/hot-folder-server.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Hot Folder Windows Server acceptance

# Installing services and creating an SMB share belongs on disposable hosts only.
# In particular, no pull_request/pull_request_target or workflow_run trigger may
# route unreviewed code to the opt-in self-hosted Server 2019 runner.
on:
# The existing installer workflow can call the reviewed branch before this
# new file reaches the default branch. Its parent event must still be manual.
workflow_call:
inputs:
installer_run_id:
required: true
type: string
expected_sha:
required: true
type: string
artifact_name:
default: pdfcraft-preview-windows-x64
type: string
include_server_2019:
default: false
type: boolean
server_2019_confirmation:
default: ''
type: string
workflow_dispatch:
inputs:
installer_run_id:
description: Successful installer workflow run from this repository
required: true
type: string
expected_sha:
description: Full 40-character source SHA (must equal this workflow ref)
required: true
type: string
artifact_name:
description: x64 MSI artifact name in that run
required: true
default: pdfcraft-preview-windows-x64
type: string
include_server_2019:
description: Also use the dedicated disposable self-hosted Server 2019 runner
default: false
type: boolean
server_2019_confirmation:
description: For 2019 enter exactly DISPOSABLE SERVER 2019
default: ''
type: string

permissions:
contents: read
actions: read

concurrency:
group: hot-folder-server-${{ github.ref }}
cancel-in-progress: false

jobs:
provenance:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
artifact_id: ${{ steps.verify.outputs.artifact_id }}
steps:
- uses: actions/checkout@v4
- name: Require matching successful trusted installer run
id: verify
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
INSTALLER_RUN_ID: ${{ inputs.installer_run_id }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
ARTIFACT_NAME: ${{ inputs.artifact_name }}
INCLUDE_2019: ${{ inputs.include_server_2019 }}
CONFIRM_2019: ${{ inputs.server_2019_confirmation }}
run: ./packaging/server-tests/verify-provenance.ps1

hosted:
if: github.event_name == 'workflow_dispatch'
needs: provenance
strategy:
fail-fast: false
matrix:
include:
- { os: windows-2022, build: 20348 }
- { os: windows-2025, build: 26100 }
runs-on: ${{ matrix.os }}
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
artifact-ids: ${{ needs.provenance.outputs.artifact_id }}
run-id: ${{ inputs.installer_run_id }}
github-token: ${{ github.token }}
path: installer
merge-multiple: true
- name: Install and exercise the actual OCR service
shell: pwsh
env:
EXPECTED_SHA: ${{ inputs.expected_sha }}
INSTALLER_RUN_ID: ${{ inputs.installer_run_id }}
EXPECTED_BUILD: ${{ matrix.build }}
run: ./packaging/server-tests/acceptance.ps1 -InstallerDirectory "$PWD/installer" -ExpectedCommit $env:EXPECTED_SHA -ExpectedServerBuild $env:EXPECTED_BUILD -DisposableHost
- uses: actions/upload-artifact@v4
if: always()
with:
name: hot-folder-server-${{ matrix.os }}
path: server-test-results
if-no-files-found: error

server-2019:
if: github.event_name == 'workflow_dispatch' && inputs.include_server_2019 && inputs.server_2019_confirmation == 'DISPOSABLE SERVER 2019'
needs: provenance
# Configure this environment with required reviewers before enabling the job.
environment: hot-folder-server-2019
runs-on: [self-hosted, Windows, X64, pdfcraft-server-2019-disposable]
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
with:
artifact-ids: ${{ needs.provenance.outputs.artifact_id }}
run-id: ${{ inputs.installer_run_id }}
github-token: ${{ github.token }}
path: installer
merge-multiple: true
- name: Verify explicitly approved Server 2019 host
shell: pwsh
env:
EXPECTED_SHA: ${{ inputs.expected_sha }}
INSTALLER_RUN_ID: ${{ inputs.installer_run_id }}
run: ./packaging/server-tests/acceptance.ps1 -InstallerDirectory "$PWD/installer" -ExpectedCommit $env:EXPECTED_SHA -ExpectedServerBuild 17763 -DisposableHost
- uses: actions/upload-artifact@v4
if: always()
with:
name: hot-folder-server-2019
path: server-test-results
if-no-files-found: error
62 changes: 62 additions & 0 deletions .github/workflows/hot-folder-services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Hot Folder system services
on:
workflow_dispatch:
push:
branches: [codex/hot-folder]
paths:
- 'crates/hot-folder-service/**'
- 'packaging/service-tests/**'
- 'packaging/windows/**'
- 'packaging/macos/**'
- 'packaging/linux/**'
- '.github/workflows/hot-folder-services.yml'
pull_request:
paths:
- 'crates/hot-folder-service/**'
- 'packaging/service-tests/**'
- 'packaging/windows/**'
- 'packaging/macos/**'
- 'packaging/linux/**'
- '.github/workflows/hot-folder-services.yml'
permissions:
contents: read
jobs:
lifecycle:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2022, windows-2025, macos-15]
runs-on: ${{ matrix.os }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- run: cargo fmt -p pdfcraft-hot-folder-service --check
- run: cargo clippy -p pdfcraft-hot-folder-service --all-targets -- -D warnings
- run: cargo test -p pdfcraft-hot-folder-service --all-targets
- run: cargo build -p pdfcraft-hot-folder-service --example service_probe
- if: runner.os != 'Windows'
run: bash packaging/service-tests/unix.sh
- if: runner.os == 'Windows'
shell: pwsh
run: packaging/service-tests/windows.ps1
- if: runner.os == 'Windows'
run: dotnet tool install --global wix --version 5.0.2
- if: runner.os == 'Windows'
shell: pwsh
run: packaging/service-tests/windows-msi.ps1
- if: runner.os == 'macOS'
run: bash packaging/service-tests/macos-pkg.sh
- if: runner.os == 'Linux'
run: |
curl -fsSL -o "$RUNNER_TEMP/nfpm.deb" https://github.com/goreleaser/nfpm/releases/download/v2.47.0/nfpm_2.47.0_amd64.deb
sudo dpkg -i "$RUNNER_TEMP/nfpm.deb"
bash packaging/service-tests/linux-deb.sh
- if: always() && runner.os == 'Windows'
uses: actions/upload-artifact@v4
with:
name: hot-folder-msi-lifecycle-logs-${{ matrix.os }}
path: ${{ runner.temp }}/hot-folder-msi/*.log
if-no-files-found: ignore
73 changes: 73 additions & 0 deletions .github/workflows/installer-languages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Installer languages
on:
workflow_dispatch:
pull_request:
paths:
- 'packaging/**'
- '.github/workflows/installer-languages.yml'
push:
branches: [main, codex/installer-languages]
paths:
- 'packaging/**'
- '.github/workflows/installer-languages.yml'
permissions:
contents: read
jobs:
windows:
name: Offline Setup and MSI (${{ matrix.arch }})
runs-on: windows-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
arch: [x64, arm64, x86]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Install pinned WiX build tool
run: dotnet tool install --global wix --version 5.0.2
- name: Catalog parity and codepages
run: python packaging/test-installer-languages.py
- name: Build and inspect all installer languages (no installation)
shell: pwsh
run: ./packaging/windows/test-language-build.ps1 -Arch ${{ matrix.arch }} -Work "$env:RUNNER_TEMP/installer-language-fixture"
windows-native-ui:
name: Native MSI Unicode dialogs (Hindi, Bengali, Urdu)
runs-on: windows-2025
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Install pinned WiX build tool
run: dotnet tool install --global wix --version 5.0.2
- name: Build inert MSI preview fixture (no installation)
shell: pwsh
run: ./packaging/windows/test-language-build.ps1 -Arch x64 -Work "$env:RUNNER_TEMP/native-language-fixture"
- name: Inspect actual native dialog controls (no installation)
shell: pwsh
run: |
& "$env:WINDIR/System32/WindowsPowerShell/v1.0/powershell.exe" -NoProfile -NonInteractive -MTA -File ./packaging/windows/test-native-dialogs.ps1 -Msi "$env:RUNNER_TEMP/native-language-fixture/out/PdfCraft.msi" -Work "$env:RUNNER_TEMP/native-dialog-results"
if ($LASTEXITCODE -ne 0) { throw 'Native MSI UI qualification failed; inspect the text report. No visual pass is claimed.' }
- name: Retain native control-text evidence (no screenshots or payloads)
if: always()
uses: actions/upload-artifact@v4
with:
name: native-msi-unicode-dialog-results
if-no-files-found: warn
path: |
${{ runner.temp }}/native-dialog-results/*.json
${{ runner.temp }}/native-dialog-results/*.log
macos:
name: Native localized PKG
runs-on: macos-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Catalog parity and codepages
run: python3 packaging/test-installer-languages.py
- name: Build PKG and inspect native locale resolution (no installation)
run: bash packaging/macos/test-localized-pkg.sh "$RUNNER_TEMP/installer-language-fixture"
5 changes: 4 additions & 1 deletion .github/workflows/packaging-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
run: |
"$RUNNER_TEMP/actionlint" -color
- name: shellcheck
run: shellcheck packaging/*.sh packaging/*/*.sh
run: shellcheck packaging/*.sh packaging/*/*.sh packaging/macos/hot-folder-scripts/*
- name: PowerShell
shell: pwsh
run: |
Expand All @@ -44,6 +44,7 @@ jobs:
if ($errors) { $errors | ForEach-Object { Write-Output "::error file=$($f.Name)::$_" }; exit 1 }
Write-Output "ok $($f.Name)"
}
./packaging/server-tests/check.ps1
- name: XML, desktop entry, AppStream, Flatpak manifest
run: |
xmllint --noout packaging/windows/*.wxs packaging/macos/*.plist* packaging/linux/*.xml*
Expand All @@ -52,6 +53,8 @@ jobs:
echo "::error file=packaging/windows/pdfcraft.wxs::Icon Id must end in .ico or .exe"; exit 1
fi
desktop-file-validate packaging/linux/ai.storyteller.pdfcraft.desktop
desktop-file-validate packaging/linux/hot-folder.desktop
xmllint --noout packaging/macos/Distribution.xml.in
sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.pdfcraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.pdfcraft.metainfo.xml"
appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.pdfcraft.metainfo.xml"
python3 - <<'EOF'
Expand Down
Loading