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
79 changes: 76 additions & 3 deletions .github/workflows/desktop-release-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,36 @@ jobs:
node -e 'if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(process.argv[1])) process.exit(1)' "$version"
echo "version=$version" >> "$GITHUB_OUTPUT"

renderer-axe-boundary:
name: Prove stock Chromium existing-target axe boundary
if: github.event_name == 'pull_request'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout pull-request validation source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- name: Set up Node.js
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version-file: '.nvmrc'
cache: npm
cache-dependency-path: package-lock.json

- name: Install locked dependencies and real Chromium
run: |
npm ci
npx playwright install --with-deps chromium

- name: Run focused stock Chromium existing-target axe regressions
env:
PROPR_DESKTOP_REAL_CHROMIUM_BOUNDARY: '1'
run: node --test --test-name-pattern='packaged acceptance renderer variants' apps/desktop/scripts/packaged-acceptance-renderer.test.mjs

package:
name: Validate unsigned ${{ matrix.platform }}-${{ matrix.arch }} package
if: github.event_name == 'pull_request'
needs: validation-version
needs: [validation-version, renderer-axe-boundary]
runs-on: ${{ matrix.runner }}
continue-on-error: ${{ matrix.release_target == false }}
timeout-minutes: 60
Expand Down Expand Up @@ -209,7 +235,7 @@ jobs:
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install --yes cpio dbus-x11 desktop-file-utils fakeroot gnome-keyring libglib2.0-bin libsecret-1-0 rpm unzip xdg-utils xvfb zip
sudo apt-get install --yes cpio dbus-x11 desktop-file-utils fakeroot fonts-liberation gnome-keyring libglib2.0-bin libsecret-1-0 rpm tesseract-ocr unzip xdg-utils xvfb zip

- name: Package desktop app from clean checkout
shell: bash
Expand Down Expand Up @@ -294,6 +320,30 @@ jobs:
xvfb-run --auto-servernum npm run desktop:smoke
' bash "$keyring_root"

- name: Capture packaged Linux x64 visual and accessibility acceptance evidence
if: matrix.platform == 'linux' && matrix.arch == 'x64'
shell: bash
env:
PROPR_DESKTOP_ACCEPTANCE_OUTPUT: ${{ github.workspace }}/desktop-acceptance-artifacts
run: |
keyring_root="$(mktemp -d)"
trap 'rm -rf -- "$keyring_root"' EXIT
dbus-run-session -- bash -euo pipefail -c '
export XDG_DATA_HOME="$1"
eval "$(printf "%s\n" "propr-packaged-acceptance" | gnome-keyring-daemon --unlock --components=secrets)"
xvfb-run --auto-servernum npm run desktop:acceptance
' bash "$keyring_root"
npm run desktop:acceptance:verify

- name: Publish mandatory packaged desktop acceptance evidence
if: always() && matrix.platform == 'linux' && matrix.arch == 'x64'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: propr-desktop-acceptance-linux-x64-${{ github.run_id }}-${{ github.run_attempt }}
path: desktop-acceptance-artifacts
if-no-files-found: error
retention-days: 14

- name: Launch packaged Windows application and exercise MVP desktop flows
if: matrix.platform == 'win32'
shell: bash
Expand Down Expand Up @@ -549,7 +599,7 @@ jobs:
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get install --yes cpio dbus-x11 fakeroot gnome-keyring libsecret-1-0 rpm zip
sudo apt-get install --yes cpio dbus-x11 fakeroot fonts-liberation gnome-keyring libsecret-1-0 rpm tesseract-ocr zip

- name: Configure required macOS signing and notarization
if: matrix.platform == 'darwin'
Expand Down Expand Up @@ -651,6 +701,29 @@ jobs:
xvfb-run --auto-servernum npm run desktop:smoke
' bash "$keyring_root"

- name: Capture signed packaged Linux x64 visual and accessibility acceptance evidence
if: matrix.platform == 'linux' && matrix.arch == 'x64'
shell: bash
env:
PROPR_DESKTOP_ACCEPTANCE_OUTPUT: ${{ github.workspace }}/desktop-acceptance-artifacts
run: |
keyring_root="$(mktemp -d)"
trap 'rm -rf -- "$keyring_root"' EXIT
dbus-run-session -- bash -euo pipefail -c '
export XDG_DATA_HOME="$1"
eval "$(printf "%s\n" "propr-packaged-acceptance" | gnome-keyring-daemon --unlock --components=secrets)"
xvfb-run --auto-servernum npm run desktop:acceptance
' bash "$keyring_root"
npm run desktop:acceptance:verify

- name: Publish mandatory signed packaged desktop acceptance evidence
if: always() && matrix.platform == 'linux' && matrix.arch == 'x64'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: propr-desktop-acceptance-signed-linux-x64-${{ github.run_id }}-${{ github.run_attempt }}
path: desktop-acceptance-artifacts
if-no-files-found: error
retention-days: 14
- name: Inspect signed and notarized macOS application
if: matrix.platform == 'darwin'
shell: bash
Expand Down
25 changes: 25 additions & 0 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ npm run desktop:typecheck
npm run desktop:test
npm run desktop:package
npm run desktop:smoke # Run under xvfb-run on a headless Linux host.
npm run desktop:acceptance # Linux x64 package; run under Xvfb in a D-Bus/keyring session.
npm run desktop:make
npm run desktop:audit
# On Linux hosts with the corresponding native packaging tools installed:
Expand Down Expand Up @@ -44,6 +45,30 @@ executable and fuse inspection without launching a window. Release CI launches b
inspects macOS and Windows packages on their native runners, validates DMG/ZIP/DEB/RPM/MSI packages, and validates
configured OS signatures.

## Packaged visual and accessibility acceptance

Linux x64 is the canonical visual runtime. `desktop:acceptance` launches the real packaged executable with a fresh
private Electron profile and drives it over Chromium's debugging protocol with Playwright. A separately authorized
acceptance mode supplies deterministic local API, Socket.IO, browser-pairing, ProPR Connect, and local-setup fixtures
while preserving the production main/preload/renderer boundary, renderer sandbox, context isolation, navigation
policy, and credential service. It requires independent command-line and environment triggers, accepts only a
packaged Linux binary, and refuses the default Electron profile.

The mandatory artifact contains screenshots for first run, endpoint and Connect confirmation, pairing, local setup
prerequisites/progress/recovery/completion, dashboard profile management, offline, revoked, and incompatible states.
Every state is captured at standard, narrow, high-DPI, 200% zoom, and reduced-motion configurations. The manifest
records fixed locale/time/theme inputs, dimensions, hashes, and native coverage. The 200% variant uses an
acceptance-only, main-authorized preload bridge to Electron `webFrame` zoom and records its read-back alongside raw
CDP/renderer viewports, independently measured geometry, DPR, and physical PNG dimensions. Accessibility evidence
fails for any serious or critical axe finding or missing keyboard order, visible focus, dialog trap/restore,
accessible name, or live announcement proof. Finalization rejects missing, duplicate, unexpected, incorrectly sized,
or secret-bearing output. Sentinel coverage includes renderer DOM, process output, URLs, local/session storage,
persisted profile/config data, screenshots and metadata, and every decompressed Playwright trace entry.

The existing six-target package matrix is unchanged. Linux x64 produces the visual/accessibility runtime evidence.
Linux arm64, macOS x64/arm64, and Windows x64/arm64 retain native package inspection and platform runtime smoke
coverage; their acceptance classification is structural/runtime-only.

Darwin packaged Connect acceptance first inspects the normal unsigned package, then generates a one-run self-signed
CA:false code-signing leaf in an isolated default keychain and signs only that smoke artifact. The signature uses an
explicit certificate-bound designated requirement that is verified before the pair process and again after the
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"smoke:package": "node scripts/smoke-packaged.mjs",
"smoke:connect-package": "node scripts/smoke-packaged-connect.mjs",
"smoke:inspect": "node scripts/smoke-packaged.mjs --inspect-only",
"acceptance:package": "node scripts/run-packaged-acceptance.mjs",
"acceptance:verify": "node -e \"import('./scripts/acceptance-artifacts.mjs').then(m => m.verifyAcceptanceArtifacts(process.env.PROPR_DESKTOP_ACCEPTANCE_OUTPUT || m.defaultAcceptanceOutputDirectory()))\"",
"premake": "npm run prepare:renderer",
"make": "electron-forge make",
"make:dmg": "node scripts/make-dmg.mjs",
Expand All @@ -52,7 +54,9 @@
"@electron/fuses": "^2.1.3",
"@types/node": "^22.10.0",
"@vitejs/plugin-react": "^4.6.0",
"axe-core": "4.10.3",
"electron": "^44.0.0",
"playwright": "^1.62.1",
"socket.io": "^4.8.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
Expand Down
Loading
Loading