diff --git a/doc/articles/toc.yml b/doc/articles/toc.yml index 481d4237870c..0d7eb6b6559f 100644 --- a/doc/articles/toc.yml +++ b/doc/articles/toc.yml @@ -268,7 +268,16 @@ - name: Overview href: xref:uno.publishing.overview - name: Publishing for Desktop - href: xref:uno.publishing.desktop + topicHref: xref:uno.publishing.desktop + items: + - name: Publishing Your App for Desktop + href: xref:uno.publishing.desktop + - name: Publishing Your App for macOS + href: xref:uno.publishing.desktop.macos + - name: Publishing Your App for macOS - Advanced Topics + href: xref:uno.publishing.desktop.macos.advanced + - name: Publishing Your App for Linux + href: xref:uno.publishing.desktop.linux - name: Publishing for WebAssembly href: xref:uno.publishing.webassembly - name: Publishing for Windows App SDK diff --git a/doc/articles/uno-publishing-desktop-macos-advanced.md b/doc/articles/uno-publishing-desktop-macos-advanced.md new file mode 100644 index 000000000000..07334d7a1a0e --- /dev/null +++ b/doc/articles/uno-publishing-desktop-macos-advanced.md @@ -0,0 +1,175 @@ +--- +uid: uno.publishing.desktop.macos.advanced +--- + +# Publishing Your App for macOS - Advanced Topics + +## App Bundle (.app) Customization + +### Custom `Info.plist` + +If your application requires extraneous permissions from macOS to execute some operations, e.g. using the camera, then you need to customize the `Info.plist` file of your app bundle. + +You can create a basic `Info.plist` file yourself, using any text editor. The content should be like: + +```xml + + + + + + CFBundleDevelopmentRegion + + + + CFBundleDisplayName + + + + CFBundleExecutable + + + + CFBundleIconFile + + + + CFBundleIdentifier + + + + CFBundleName + + + + CFBundleShortVersionString + + + + CFBundleVersion + + + + + +``` + +You can edit the `Info.plist` file, add any required entries (for permissions), and let other fields empty. The basic, empty fields will be filled automatically by the `msbuild` task based on your project. + +Then from the CLI run: + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSCustomInfoPlist=path/to/Info.plist +``` + +### Hardened Runtime + +[Hardened Runtime](https://developer.apple.com/documentation/security/hardened-runtime) is `true` by default as it is **required** for [notarization](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution). + +If needed you can turn it off by providing `-p:UnoMacOSHardenedRuntime=false` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSHardenedRuntime=false +``` + +### Custom Entitlements + +[Entitlements](https://developer.apple.com/documentation/bundleresources/entitlements) grants permission to your executable. If nothing is specified the default entitlements required for a notarization-compatible dotnet-based application will be included automatically. + +```xml + + + + + com.apple.security.cs.allow-jit + + + +``` + +You can provide your own entitlement file if needed using `-p:UnoMacOSEntitlements=/path/to/entitlements.plist` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSEntitlements=/path/to/entitlements.plist +``` + +### Trimming + +App bundles that are distributed should be self-contained applications that depends only on the OS to execute. However bundling the dotnet runtime, base class libraries and Uno Platform libraries produce a rather large application size. + +To reduce the size of the app bundle you can enable dotnet's [trimming](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options#enable-trimming) when publishing the app, using `-p:PublishTrimmed=true`. The full command from the CLI would be: + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:PublishTrimmed=true +``` + +> [!IMPORTANT] +> Your code and dependencies needs to be [trimmer-aware](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/prepare-libraries-for-trimming) and the trimmed app bundle should be carefully tested to ensure the code removed by the trimmer does not affect its functionality. + +### Optional files + +`dotnet publish` include several files that are not strictly required to execute your application. To reduce the app bundle size most of those files are **not** included, by default, inside the app bundles. + +#### Including dotnet `createdump` tool + +Useful for debugging, the `createdump` executable is rarely used by the consumers of the application and, by default, is not included in the app bundle. + +If you wish to include `createdump` inside your app bundle add the `-p:UnoMacOSIncludeCreateDump=true` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSIncludeCreateDump=true +``` + +#### Including `libclrgc.dylib` extraneous GC + +An alternate garbage collection (GC) library is included by `dotnet publish`. It is, by default, removed from the app bundle since it would not be used at runtime. + +If you wish to include this extra GC library inside your app bundle add the `-p:UnoMacOSIncludeExtraClrGC=true` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSIncludeExtraClrGC=true +``` + +#### Including `libmscordaccore.dylib` and `libmscordbi.dylib` debugging support + +Extraneous debugging support libraries are included by `dotnet publish`. They are, by default, removed from the app bundle since it unlikely to be used for debugging. + +If you wish to include the extra debugging libraries inside your app bundle add the `-p:UnoMacOSIncludeNativeDebugging=true` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSIncludeNativeDebugging=true +``` + +#### Including assemblies debugging symbols (.pdb) files + +dotnet debugging symbols (`.pdb`) are generally included in released applications since it helps to provide better stack trace and help developers resolving issues. As such they are, by default, included inside the app bundle. + +If you wish to remove them anyway you can do so by adding the `-p:UnoMacOSIncludeDebugSymbols=false` on the CLI. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=app -p:UnoMacOSIncludeDebugSymbols=false +``` + +## Disk Image (.dmg) Customization + +### Symlink to /Applications + +By default the produced disk image will contain a symlink to `/Applications` so users can drag-and-drop the app bundle inside it. If you do not want the symlink to be present inside the disk image you can add `-p:UnoMacOSIncludeSymlinkToApplications=false` on the command line. + +```bash +dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=dmg -p:UnoMacOSIncludeSymlinkToApplications=false -p:CodesignKey={{identity}} -p:DiskImageSigningKey={{identity}} +``` + +### Additional Customization + +Further disk image customization is possible but can be tricky since it requires modification to the `.DS_Store` binary file inside the disk image (a lot of trials and errors). If more control is required (e.g. icon positioning, background image...) we recommend the use of 3rd party tools created specifically for this purpose. Some free/open source examples are: + +- [create-dmg](https://github.com/sindresorhus/create-dmg) +- [dmgbuild](https://dmgbuild.readthedocs.io/en/latest/) diff --git a/doc/articles/uno-publishing-desktop-macos.md b/doc/articles/uno-publishing-desktop-macos.md new file mode 100644 index 000000000000..573f8be59299 --- /dev/null +++ b/doc/articles/uno-publishing-desktop-macos.md @@ -0,0 +1,219 @@ +--- +uid: uno.publishing.desktop.macos +--- + +# Publishing Your App for macOS + +There are several options to publish your macOS application to your customers. They all start with creating an [app bundle](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html) (.app). + +> [!IMPORTANT] +> Publishing for macOS is only supported on macOS + +## Create an app bundle (.app) + +The most basic app bundle can be created with: + +```bash +dotnet publish -f net8.0-desktop -p:PackageFormat=app +``` + +However this bundle would depend on the correct version of dotnet, `net8.0` in this case, to be installed on the Mac computer. In practice macOS end-users expect app bundles to be self-contained and not require anything extraneous to execute on their Mac computer. + +You can create such a self-contained app bundle with: + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=app +``` + +Where `{{RID}}` is either `osx-x64` or `osx-arm64`. + +The resulting app bundle, which is a directory, will be located at `bin/Release/net8.0-desktop/{{RID}}/publish/{{APPNAME}}.app`. + +### Code Signing + +> [!IMPORTANT] +> The code signing process requires access to the Internet to retrieve a secure timestamp. + +To ensure the integrity of the app bundle Apple requires you to digitally sign your code. The key difference to produce a signed app bundle is to add `-p:CodesignKey={{identity}}` to specify which identity should be used to produce the signature. + +```bash +dotnet publish -f net8.0-desktop -r osx-arm64 -p:SelfContained=true -p:PackageFormat=app -p:CodesignKey={{identity}} +``` + +You can use the special identity `-` to produce an adhoc signature. This basically tells macOS's [Gatekeeper](https://support.apple.com/en-us/102445) that the file is safe to use **locally**, however this does not help for distributing the app bundle. + +> [!NOTE] +> Beside needed access to the Internet the code signing process slows down the builds. For local testing of your app you do not need to sign the app bundle. + +#### How to find your identity + +If you have not already you need to create your [developer certificates](https://developer.apple.com/help/account/create-certificates/create-developer-id-certificates/). Once you have created on your Mac computer them you can find your identities, from the CLI, by running: + +```bash +security find-identity -v +``` + +This will show you every **valid** identities available on your Mac. + +```text + 1) 8C8D47A2A6F7428971A8AA5C6D8F7A30D344E93C "Apple Development: John Appleby (XXXXXXXXXX)" + 2) F84D25AAF30BAFA988D8B4CE8A0BA3BE891199D8 "Developer ID Installer: John Appleby (XXXXXXXXXX)" + 3) 0357503C3CF78B093A764EA382BF10E7D3AEDA9A "Apple Distribution: John Appleby (XXXXXXXXXX)" + 4) A148697E815F6090DE9698F8E2602773296E2689 "Developer ID Application: John Appleby (XXXXXXXXXX)" + 4 valid identities found +``` + +To properly sign an app bundle for publishing you need to use the `"Developer ID Application: *"` or its thumbprint (long hex number) entry as the identity. Both + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=app -p:CodesignKey="Developer ID Application: John Appleby (XXXXXXXXXX)" +``` + +and + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=app -p:CodesignKey=A148697E815F6090DE9698F8E2602773296E2689 +``` + +are functionally identical and will produce a signed app bundle. + +## Distributing the app bundle + +An app bundle is a directory and, as such, is not easy to distribute. Most macOS applications are distributed using one of the following methods. + +### Installer (.pkg) + +You can easily create an installer package for your app bundle. This will produce a single, compressed executable file that can be shared (if signed and notarized) with anyone using a Mac computer. + +From the CLI run: + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=pkg -p:CodesignKey={{identity}} -p:PackageSigningKey={{installer_identity}} +``` + +Where the following changes to the previous command are + +- modifying `PackageFormat` to `pkg` to produce the package. This package will include the the app bundle inside it, so the `CodesignKey` argument is still required; +- adding `-p:PackageSigningKey={{installer_identity}}` to specify which identity should be used to sign the package. Unlike app bundles the signing step requires the use of a `Developer ID Installer: *` identity. + +The resulting installer will be located at `bin/Release/net8.0-desktop/{{RID}}/publish/{{APPNAME}}.pkg`. + +> [!IMPORTANT] +> The installer can behave weirdly locally (or on CI) since the app bundle name is known to macOS and it will try to update the application, where it was built or copied, instead of installing a copy of it under the `/Applications/` directory. Ensure you are testing your package installer on a different Mac or inside a clean virtual machine (VM). + +#### Notarize the package + +Having both the app bundle (.app) and installer (.pkg) signed is not quite enough. As the package is a binary that you'll share with customers it needs to be notarized by Apple. + +The first step is the store your Apple Account credentials inside the key store. This makes all the further commands (and notarization) much simpler. From the CLI run: + +```bash +xcrun notarytool store-credentials {{notarytool-credentials}} --apple-id john.appleby@platform.uno --team-id XXXXXXXXXX --password aaaa-bbbb-cccc-dddd +``` + +where + +- `{{notarytool-credentials}}` is the name of your credentials inside the key store. +- `--apple-id` provides the email address used for your [Apple Account](https://developer.apple.com/account). +- `--team-id` provides your team id, a 10 character code. [How to find it](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id). +- `--password` is an [app specific password](https://support.apple.com/en-us/102654) created specifically for `notarytool` + +> [!NOTE] +> Since Apple Accounts requires two factors authentication (2FA) you will need to [create an app-specific password](https://support.apple.com/en-us/102654) for `notarytool`. + +```text +This process stores your credentials securely in the Keychain. You reference these credentials later using a profile name. + +Validating your credentials... +Success. Credentials validated. +Credentials saved to Keychain. +To use them, specify `--keychain-profile "notarytool-credentials"` +``` + +Once this (one time) setup is done you can notarize the disk image while building the app. From the CLI run: + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=dmg -p:CodesignKey={{identity}} -p:PackageSigningKey={{installer_identity}} -p:UnoMacOSNotarizeKeychainProfile={{notarytool-credentials}} -bl +``` + +where + +- `{{notarytool-credentials}}` is the name of your credentials inside the key store +- `-bl` will create a binary log of your build. This will include information about the notarization process. + +> [!NOTE] +> Running this command might [take a while](https://developer.apple.com/documentation/security/customizing-the-notarization-workflow) as it will wait for the notarization process to complete on Apple servers. + +Once completed you can distribute the package installer. + +### Disk Image (.dmg) + +Another common way to distribute your macOS software is to create a disk image (.dmg). This will produce a single, compressed disk image that can be shared (if signed and notarized) with anyone using a Mac computer. + +To create a disk image from the CLI run: + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=dmg -p:CodesignKey={{identity}} -p:DiskImageSigningKey={{identity}} +``` + +Where the following changes to the original command are + +- modifying `PackageFormat` to `dmg` to produce the disk image. This image will include the the app bundle inside it, so the `CodesignKey` argument is still required; +- adding `-p:DiskImageSigningKey={{identity}}` to specify which identity should be used to sign the package. Like app bundles the signing step requires the use of a `Developer ID Application: *` identity. + +The resulting disk image will be located at `bin/Release/net8.0-desktop/{{RID}}/publish/{{APPNAME}}.dmg`. + +#### Notarize the disk image + +Like an installer (.pkg) a disk image is the outermost container that you'll share with customers and, as such, needs to be notarized by Apple. + +The first step is the store your Apple Account credentials inside the key store. This makes all the further commands (and notarization) much simpler. From the CLI run: + +```bash +xcrun notarytool store-credentials {{notarytool-credentials}} --apple-id john.appleby@platform.uno --team-id XXXXXXXXXX --password aaaa-bbbb-cccc-dddd +``` + +where + +- `{{notarytool-credentials}}` is the name of your credentials inside the key store. +- `--apple-id` provides the email address used for your [Apple Account](https://developer.apple.com/account). +- `--team-id` provides your team id, a 10 character code. [How to find it](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id). +- `--password` is an [app specific password](https://support.apple.com/en-us/102654) created specifically for `notarytool` + +> [!NOTE] +> Since Apple Accounts requires two factors authentication (2FA) you will need to [create an app-specific password](https://support.apple.com/en-us/102654) for `notarytool`. + +```text +This process stores your credentials securely in the Keychain. You reference these credentials later using a profile name. + +Validating your credentials... +Success. Credentials validated. +Credentials saved to Keychain. +To use them, specify `--keychain-profile "notarytool-credentials"` +``` + +Once this (one time) setup is done you can notarize the disk image while building the app. From the CLI run: + +```bash +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=dmg -p:CodesignKey={{identity}} -p:DiskImageSigningKey={{identity}} -p:UnoMacOSNotarizeKeychainProfile={{notarytool-credentials}} -bl +``` + +where + +- `{{notarytool-credentials}}` is the name of your credentials inside the key store +- `-bl` will create a binary log of your build. This will include information about the notarization process. + +> [!NOTE] +> Running this command might [take a while](https://developer.apple.com/documentation/security/customizing-the-notarization-workflow) as it will wait for the notarization process to complete on Apple servers. + +Once completed you can distribute the notarized disk image. + +### Mac App Store + +> [!IMPORTANT] +> Application distributed on the Mac App Store are required to execute under a [sandbox](https://developer.apple.com/documentation/security/app-sandbox?language=objc) which impose additional limits on how applications can interact with the computer. + +An app bundle (.app) can be submitted to Apple's [App Store](https://www.apple.com/app-store/) using the [transporter app](https://developer.apple.com/help/app-store-connect/manage-builds/upload-builds) from a computer running macOS. + +> [!NOTE] +> Notarization of the app bundle is **not** required as the Apple App Store will be taking care of your app binary distribution. diff --git a/doc/articles/uno-publishing-desktop.linux.md b/doc/articles/uno-publishing-desktop.linux.md new file mode 100644 index 000000000000..3ad18f09f6db --- /dev/null +++ b/doc/articles/uno-publishing-desktop.linux.md @@ -0,0 +1,65 @@ +--- +uid: uno.publishing.desktop.linux +--- + +# Publishing Your App for Linux + +## Snap Packages + +We support creating .snap packages on **Ubuntu 20.04** or later. + +### Requirements + +The following must be installed and configured: + +- snapd +- snaps (with `snap install`): + - core20 on Ubuntu 20.04 + - core22 on Ubuntu 22.04 + - core24 on Ubuntu 24.04 + - multipass + - lxd + - current user must be part of the `lxd` group + - `lxd init --minimal` or similar should be run + - snapcraft + +> [!NOTE] +> Docker may interfere with Lxd causing network connectivity issues, for solutions see: https://documentation.ubuntu.com/lxd/en/stable-5.0/howto/network_bridge_firewalld/#prevent-connectivity-issues-with-lxd-and-docker + +### Publishing A Snap + +To publish a snap, run: + +```shell +dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=snap +``` + +Where `{{RID}}` is either `linux-x64` or `linux-arm64`. + +We generate snap manifests in classic confinement mode and a .desktop file by default. + +If you wish to customize your snap manifest, you will need to pass the following MSBuild properties: + +- SnapManifest +- DesktopFile + +The `.desktop` filename MUST conform to the Desktop File spec. + +If you wish, you can generate a default snap manifest and desktop file by running the command above, then tweak them. + +> [!NOTE] +> .NET 9 publishing and cross-publishing are not supported as of Uno 5.5, we will support .NET 9 publishing soon. + +## Limitations + +- NativeAOT is not supported +- R2R is not supported +- Single file publish is not supported + +> [!NOTE] +> Publishing is a work in progress + +## Links + +- [Snapcraft.yaml schema](https://snapcraft.io/docs/snapcraft-yaml-schema) +- [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest) diff --git a/doc/articles/uno-publishing-desktop.md b/doc/articles/uno-publishing-desktop.md index c55ef9b25c31..85be4eebc075 100644 --- a/doc/articles/uno-publishing-desktop.md +++ b/doc/articles/uno-publishing-desktop.md @@ -136,93 +136,3 @@ Depending on your deployment settings, you can run the `Setup.exe` file to insta > [!IMPORTANT] > At this time, publishing with the Visual Studio Publishing Wizard is not supported for > multi-targeted projects. Using the command line above is required. - -### macOS App Bundles - -We now support generating `.app` bundles on macOS machines. From the CLI run: - -```shell -dotnet publish -f net8.0-desktop -p:PackageFormat=app -``` - -You can also do a self-contained publish with: - -```shell -dotnet publish -f net8.0-desktop -r {{RID}} -p:SelfContained=true -p:PackageFormat=app -``` - -Where `{{RID}}` is either `osx-x64` or `osx-arm64`. - -> [!NOTE] -> Code signing is planned but not supported yet. - -### Snap Packages - -We support creating .snap packages on **Ubuntu 20.04** or later. - -#### Requirements - -The following must be installed and configured: - -```bash -sudo apt-get install -y snapd -sudo snap install core22 -sudo snap install multipass -sudo snap install lxd -sudo snap install snapcraft -lxd init --minimal -sudo usermod --append --groups lxd $USER # In order for the current user to use LXD -``` - -> [!NOTE] -> In the above script, replace `core22` with `core20` if building on Ubuntu 20.04, or `core24` if building on Ubuntu 24.04. -> [!NOTE] -> Docker may interfere with Lxd causing network connectivity issues, for solutions see: https://documentation.ubuntu.com/lxd/en/stable-5.0/howto/network_bridge_firewalld/#prevent-connectivity-issues-with-lxd-and-docker - -#### Generate a Snap file - -To generate a snap file, run the following: - -```shell -dotnet publish -f net8.0-desktop -p:SelfContained=true -p:PackageFormat=snap -``` - -The generated snap file is located in the `bin/Release/netX.0-desktop/linux-[x64|arm64]/publish` folder. - -Uno Platform generates snap manifests in classic confinement mode and a `.desktop` file by default. - -If you wish to customize your snap manifest, you will need to pass the following MSBuild properties: - -- `SnapManifest` -- `DesktopFile` - -The `.desktop` filename MUST conform to the [Desktop File](https://specifications.freedesktop.org/desktop-entry-spec/latest) spec. - -If you wish, you can generate a default snap manifest and desktop file by running the command above, then tweak them. - -> [!NOTE] -> .NET 9 publishing and cross-publishing are not supported as of Uno 5.5, we will support .NET 9 publishing soon. - -#### Publish your Snap Package - -You can install your app on your machine using the following: - -```bash -sudo snap install MyApp_1.0_amd64.snap --dangerous --classic -``` - -You can also publish your app to the [Snap store](https://snapcraft.io/store). - -## Limitations - -- NativeAOT is not yet supported -- R2R is not yet supported -- Single file publish is not yet supported - -> [!NOTE] -> Publishing is a [work in progress](https://github.com/unoplatform/uno/issues/16440) - -## Links - -- [Snapcraft.yaml schema](https://snapcraft.io/docs/snapcraft-yaml-schema) -- [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest)