NetDesk 1.5.0 — source release

This commit is contained in:
NetDesk committed 2026-10-18 00:10:03 +00:00
commit 37aa876908
227 files changed
+80694

No files matched your search

+293
View File
@@ -0,0 +1,293 @@
# Building the NetDesk desktop app for macOS
This builds the **same** egui viewer/host-panel binary the Windows editions
ship (crate `netdesk-viewer`) as a signed, notarized **universal** macOS app
(`NetDesk.app`) and a distributable `.dmg`.
- One binary, two roles: the viewer window **and** the host panel, identical to
the Windows build — only the packaging here is Mac-specific.
- **Decode path:** on macOS the host H.264 (Annex-B) stream is decoded on the
Apple video engine through **VideoToolbox** — `core/src/decode_vt.rs`
(`VTDecompressionSession`, 32-bit BGRA output repacked to RGB8), gated
`#[cfg(any(target_os = "macos", target_os = "ios"))]`. It falls back to the
portable openh264 software decoder if the hardware path can't initialize. See
[`cross-platform-decode.md`](./cross-platform-decode.md). No VideoToolbox-
specific flags are needed at build time: the CoreMedia / CoreVideo /
VideoToolbox framework `#[link]`s resolve against the active SDK.
> **Where this runs:** on the Mac (the M4), **not** the Windows dev box. Cross-
> compiling a macOS `.app` from Windows is not supported — codesign, notarytool,
> iconutil, lipo and hdiutil are all macOS-only tools.
The script is [`packaging/build-macos.sh`](../packaging/build-macos.sh). All
outputs land in `packaging/dist/`.
---
## TL;DR — the Mac build command
Once the one-time Apple Developer setup below is done (Developer ID cert in the
keychain + a stored `notarytool` profile named `netdesk`), the whole signed +
notarized build is:
```bash
# on the Mac, from the repo root:
export PATH="$HOME/.cargo/bin:$PATH"
bash packaging/build-macos.sh --notary-profile netdesk
```
That produces, in `packaging/dist/`:
| Artifact | What it is |
|----------------------------------------|---------------------------------------------|
| `NetDesk.app` | signed + notarized + **stapled** universal app |
| `NetDesk-macOS-universal.dmg` | signed + notarized + stapled disk image (ship this) |
Useful flags: `--skip-build` (reuse the per-target binaries), `--no-sign`
(local unsigned test build), `--no-notarize` (sign but skip the Apple round-
trip), `--identity "…"`, `--icon <png>`, `--version <x.y.z>`, `--entitlements
<plist>`. Run `bash packaging/build-macos.sh --help` for the full list.
---
## Prerequisites (on the Mac)
1. **Xcode command-line tools** (gives `clang`, the SDKs, `lipo`, `iconutil`,
`sips`, `hdiutil`, `codesign`, `xcrun`):
```bash
xcode-select --install
```
A full Xcode install from the App Store also works.
2. **Rust** via rustup. The script adds the two Apple-silicon/Intel std targets
for you, but you can pre-add them:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add aarch64-apple-darwin x86_64-apple-darwin
```
3. An **Apple Developer Program** membership (needed only for signing +
notarization — a `--no-sign` build needs none). Set that up once, below.
---
## One-time Apple Developer setup
You need a **Developer ID Application** certificate (this is the identity that
lets an app run on *any* Mac after notarization — distinct from the "Apple
Development" / "Apple Distribution" / Mac App Store certs) and a way for
`notarytool` to authenticate.
### 1. Enrol in the Apple Developer Program
<https://developer.apple.com/programs/> — \$99/yr. You must be the **Account
Holder** (or have the Admin role) to create a Developer ID certificate.
### 2. Create the "Developer ID Application" certificate
Easiest, via Xcode:
1. Xcode → **Settings… → Accounts**, sign in with the Apple ID on the team.
2. Select the team → **Manage Certificates…**
3. Click **+** → **Developer ID Application**.
4. Xcode creates the private key + cert and installs both into your **login
keychain**. (Back up the private key — it cannot be re-downloaded.)
Or manually at <https://developer.apple.com/account/resources/certificates> →
**+** → *Developer ID → Developer ID Application*, upload a CSR made with
**Keychain Access → Certificate Assistant → Request a Certificate from a
Certificate Authority**, then double-click the downloaded `.cer` to install it.
Verify it is present and usable for signing:
```bash
security find-identity -v -p codesigning
# look for a line like:
# 1) ABCD… "Developer ID Application: Akronlogic LLC (TEAMID1234)"
```
The script auto-detects this identity; override with `--identity "Developer ID
Application: Akronlogic LLC (TEAMID1234)"` if you have more than one.
### 3. Find your Team ID
<https://developer.apple.com/account> → **Membership details** → *Team ID* (a
10-character string, also shown in the parentheses of the identity name above).
### 4. Create credentials for notarytool
Two supported options — pick one:
**A. App-specific password (simplest).** At <https://appleid.apple.com> →
*Sign-In and Security → App-Specific Passwords* → **+**, label it e.g.
`netdesk-notarytool`. Then store it once in the keychain as a named profile:
```bash
xcrun notarytool store-credentials netdesk \
--apple-id "you@akronlogic.com" \
--team-id "TEAMID1234" \
--password "abcd-efgh-ijkl-mnop" # the app-specific password
```
`netdesk` is the profile name you pass to `--notary-profile netdesk`.
**B. App Store Connect API key.** Create a key at *App Store Connect → Users and
Access → Integrations → App Store Connect API* (role: *Developer*), download the
`AuthKey_XXXX.p8`, and store it:
```bash
xcrun notarytool store-credentials netdesk \
--key "/path/AuthKey_XXXXXXXX.p8" \
--key-id "XXXXXXXXXX" \
--issuer "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
```
> Instead of a stored profile you can pass credentials inline each run:
> `--apple-id you@akronlogic.com --team-id TEAMID1234` **plus** the app-specific
> password in the environment: `export NETDESK_NOTARY_PASSWORD='abcd-efgh-…'`.
---
## What the script does (step by step)
`packaging/build-macos.sh`:
1. **rustup target add** `aarch64-apple-darwin` `x86_64-apple-darwin`.
2. **cargo build --release -p netdesk-viewer** once per target — producing
`target/aarch64-apple-darwin/release/netdesk-viewer` and the `x86_64` one.
(Cross-building the Intel slice on an Apple-silicon Mac uses the same SDK and
needs no extra tooling.)
3. **lipo -create** the two into one universal Mach-O at
`NetDesk.app/Contents/MacOS/NetDesk` (`lipo -info` prints `arm64 x86_64`).
4. **iconutil** builds `NetDesk.icns` from the largest PNG master it finds
(`assets/icon-1024.png` → `icon-512.png` → `icon.png`, or `--icon <png>`):
`sips` resizes the master into a complete `.iconset` (16→1024 px, @1x/@2x),
then `iconutil -c icns` packs it into `Contents/Resources/NetDesk.icns`.
5. **Assembles `NetDesk.app`** — writes `Contents/Info.plist` with bundle id
**`com.akronlogic.netdesk`**, `CFBundleVersion` / `CFBundleShortVersionString`
= the workspace version (read from `Cargo.toml`, currently **0.1.0**),
`CFBundleExecutable`/`CFBundleIconFile` = `NetDesk`, `LSMinimumSystemVersion`
11.0, `NSHighResolutionCapable`, plus a `PkgInfo` (`APPL????`).
6. **codesign** the executable and the bundle with the Developer ID Application
identity, **`--options runtime`** (hardened runtime, required to notarize) and
**`--timestamp`** (secure timestamp), then `codesign --verify --deep --strict`.
7. **Notarize + staple the `.app`**: `ditto -c -k --keepParent` it into a zip,
`xcrun notarytool submit … --wait`, then `xcrun stapler staple NetDesk.app`
so the ticket travels with the app offline. (Skip with `--no-staple-app`.)
8. **Build the `.dmg`** (`hdiutil create`, UDZO, with an `/Applications`
symlink for drag-install), **sign** it, **notarize** it
(`notarytool submit … --wait`), and **staple** it.
If no Developer ID identity is found (or `--no-sign`), it still produces an
**unsigned** `.app` + `.dmg` and tells you so — handy for a local smoke test.
If signing works but no notarization credentials are present, it signs and
prints exactly how to add them, like the Windows installer script does when
NSIS is missing.
---
## Exact commands (equivalent to what the script runs)
If you want to drive it by hand, this is the full sequence (`VERSION=0.1.0`,
`ID="Developer ID Application: Akronlogic LLC (TEAMID1234)"`):
```bash
export PATH="$HOME/.cargo/bin:$PATH"
# 1-2. targets + release builds
rustup target add aarch64-apple-darwin x86_64-apple-darwin
cargo build --release -p netdesk-viewer --target aarch64-apple-darwin
cargo build --release -p netdesk-viewer --target x86_64-apple-darwin
# 3. universal binary
mkdir -p packaging/dist/NetDesk.app/Contents/MacOS packaging/dist/NetDesk.app/Contents/Resources
lipo -create -output packaging/dist/NetDesk.app/Contents/MacOS/NetDesk \
target/aarch64-apple-darwin/release/netdesk-viewer \
target/x86_64-apple-darwin/release/netdesk-viewer
# 4. icon (ICONSET fully populated 16..1024 as in the script)
iconutil -c icns NetDesk.iconset -o packaging/dist/NetDesk.app/Contents/Resources/NetDesk.icns
# 5. Info.plist (the script writes com.akronlogic.netdesk + version 0.1.0)
# 6. sign (hardened runtime + timestamp)
codesign --force --timestamp --options runtime --sign "$ID" packaging/dist/NetDesk.app/Contents/MacOS/NetDesk
codesign --force --timestamp --options runtime --sign "$ID" packaging/dist/NetDesk.app
codesign --verify --deep --strict --verbose=2 packaging/dist/NetDesk.app
# 7. notarize + staple the app
ditto -c -k --keepParent packaging/dist/NetDesk.app packaging/dist/NetDesk.zip
xcrun notarytool submit packaging/dist/NetDesk.zip --keychain-profile netdesk --wait
xcrun stapler staple packaging/dist/NetDesk.app
# 8. dmg -> sign -> notarize -> staple
hdiutil create -volname NetDesk -srcfolder <stage-with-app+Applications-symlink> \
-fs HFS+ -format UDZO -ov packaging/dist/NetDesk-macOS-universal.dmg
codesign --force --timestamp --sign "$ID" packaging/dist/NetDesk-macOS-universal.dmg
xcrun notarytool submit packaging/dist/NetDesk-macOS-universal.dmg --keychain-profile netdesk --wait
xcrun stapler staple packaging/dist/NetDesk-macOS-universal.dmg
```
The script wraps all of this (with the full `.iconset` generation, the plist,
auto-detection, and graceful fallbacks) — prefer it over the manual steps.
---
## Verify a finished build
```bash
# signature valid + hardened runtime + Developer-ID authority
codesign --verify --deep --strict --verbose=2 packaging/dist/NetDesk.app
codesign -dv --verbose=4 packaging/dist/NetDesk.app 2>&1 | grep -E 'Authority|Timestamp|flags|Identifier'
# Gatekeeper will accept it (Developer ID + notarized)
spctl -a -vvv --type exec packaging/dist/NetDesk.app
# notarization ticket is stapled (works offline)
xcrun stapler validate packaging/dist/NetDesk.app
xcrun stapler validate packaging/dist/NetDesk-macOS-universal.dmg
```
`spctl` should print `source=Notarized Developer ID` and `accepted`.
---
## Runtime permissions (host mode)
Signing/notarization are **not** the macOS capture permissions. When the binary
runs as the **host** and first tries to capture the screen or inject input,
macOS prompts the user (TCC) to grant, under **System Settings → Privacy &
Security**:
- **Screen Recording** — to capture the remote screen.
- **Accessibility** — to inject mouse/keyboard input (CGEvent).
These are user-granted at runtime and are **not** code-signing entitlements, so
the default build needs no entitlements file. (If a future host feature drives
*other* apps via Apple Events, add an entitlements plist with the matching keys
and pass it via `--entitlements`.)
---
## Troubleshooting
- **`no identity found` / unsigned output** — the Developer ID Application cert
(and its private key) must be in the login keychain of the user running the
build; confirm with `security find-identity -v -p codesigning`.
- **Notarization `Invalid` / rejected** — fetch the log:
`xcrun notarytool log <submission-id> --keychain-profile netdesk`. Almost
always a missing `--options runtime` (hardened runtime) or a missing secure
`--timestamp`; the script sets both.
- **`The binary is not signed with a valid Developer ID certificate`** — an
*Apple Development* cert was used instead of *Developer ID Application*; only
the latter notarizes for distribution outside the App Store.
- **openh264 fails to cross-compile the x86_64 slice** — it builds C++ via the
`cc` crate; the Xcode CLT clang handles `-arch x86_64` on an arm Mac, so a
full `xcode-select --install` (or Xcode) is enough. If it still fails, build
only the native slice with `--target aarch64-apple-darwin` and drop the lipo
step (the app is then arm64-only).
- **Icon looks soft** — add a 1024×1024 `assets/icon-1024.png` master (the repo
currently ships 256 and 512); the script prefers the largest it finds.