diff --git a/BuildTools/packaging/homebrew/README.md b/BuildTools/packaging/homebrew/README.md new file mode 100644 index 000000000..ec53f4108 --- /dev/null +++ b/BuildTools/packaging/homebrew/README.md @@ -0,0 +1,26 @@ +# Homebrew cask distribution + +ILSpy is distributed for macOS via a Homebrew *tap*: a separate repository named +`icsharpcode/homebrew-ilspy` containing nothing but the cask file at `Casks/ilspy.rb`. +Users then install with: + +``` +brew install --cask icsharpcode/ilspy/ilspy +``` + +The tap holds no secrets and runs no CI. Signing and notarization happen offline +(see `../macos/README.md`); the cask merely points at the signed dmg attached to the +GitHub release. + +## Per-release update (manual) + +1. Create the dmg and attach it to the GitHub release (`../macos/README.md`). +2. In the tap repo, edit `Casks/ilspy.rb`: + - `version` — the release version (must match the dmg file name and release tag, + see the `url` line). + - `sha256` — printed by `create-dmg.ps1`, or `shasum -a 256 ILSpy-.dmg`. +3. Commit and push. `brew upgrade --cask ilspy` picks it up immediately; there is no + review process for casks in your own tap. + +`ilspy.rb` in this directory is the template for the tap's cask file. If the release +tag or dmg naming scheme ever changes, adjust the `url` line accordingly. diff --git a/BuildTools/packaging/homebrew/ilspy.rb b/BuildTools/packaging/homebrew/ilspy.rb new file mode 100644 index 000000000..aadead876 --- /dev/null +++ b/BuildTools/packaging/homebrew/ilspy.rb @@ -0,0 +1,16 @@ +# Template for the ILSpy Homebrew cask. The live copy belongs in the tap repository +# (icsharpcode/homebrew-ilspy) at Casks/ilspy.rb; see README.md next to this file. +cask "ilspy" do + version "REPLACE_ME" # e.g. 11.0.0.8948 + sha256 "REPLACE_ME" # printed by create-dmg.ps1, or: shasum -a 256 ILSpy-.dmg + + url "https://github.com/icsharpcode/ILSpy/releases/download/v#{version}/ILSpy-#{version}.dmg" + name "ILSpy" + desc ".NET assembly browser and decompiler" + homepage "https://github.com/icsharpcode/ILSpy" + + depends_on macos: ">= :big_sur" + depends_on arch: :arm64 + + app "ILSpy.app" +end diff --git a/BuildTools/packaging/macos/README.md b/BuildTools/packaging/macos/README.md new file mode 100644 index 000000000..adf9fd1b6 --- /dev/null +++ b/BuildTools/packaging/macos/README.md @@ -0,0 +1,27 @@ +# macOS release flow + +CI (the `macOS` job in `build-ilspy.yml`) produces an **unsigned** zipped `ILSpy.app` +artifact. All signing happens offline on the release manager's machine; no signing +secrets exist in any CI. + +Users who run the unsigned zip directly must clear the quarantine flag once: +`xattr -dr com.apple.quarantine ILSpy.app`. The dmg below is the signed distribution +channel that avoids this. + +## Creating the release dmg + +1. Download the `ILSpy macOS arm64 (Release)` artifact from the build run on + the release commit and unzip it. +2. Build, sign, and notarize the dmg (see `create-dmg.ps1` for parameter details): + + ``` + ./create-dmg.ps1 -AppPath ./ILSpy.app -Version ` + -SigningIdentity 'Developer ID Application: ()' ` + -Notarize -NotaryProfile + ``` + + Omit `-SigningIdentity`/`-Notarize` to produce an unsigned dmg for local testing. +3. Attach `ILSpy-.dmg` to the manually created GitHub release. The dmg file + name must match the URL pattern in the Homebrew cask (`../homebrew/ilspy.rb`). +4. Update the Homebrew cask with the new version and the sha256 the script printed + (see `../homebrew/README.md`). diff --git a/BuildTools/packaging/macos/create-dmg.ps1 b/BuildTools/packaging/macos/create-dmg.ps1 new file mode 100644 index 000000000..8bed02a5f --- /dev/null +++ b/BuildTools/packaging/macos/create-dmg.ps1 @@ -0,0 +1,65 @@ +#!/usr/bin/env pwsh +# Build (and optionally sign + notarize) the ILSpy dmg for distribution. +# +# This is an OFFLINE release-manager tool: it is never called from CI, because no signing +# happens in CI. Typical flow: +# 1. Download the 'ILSpy macOS arm64 ...' artifact from the build workflow and unzip it. +# 2. ./create-dmg.ps1 -AppPath ./ILSpy.app -Version 11.0.0.8948 ` +# -SigningIdentity 'Developer ID Application: ...' -Notarize -NotaryProfile ilspy +# (omit -SigningIdentity for an unsigned smoke-test dmg) +# 3. Attach the dmg to the GitHub release; use the printed sha256 for the Homebrew cask +# (see ../homebrew/README.md). +# +# -NotaryProfile names credentials stored once via: +# xcrun notarytool store-credentials --apple-id --team-id +param( + [Parameter(Mandatory)] + [string]$AppPath, + [Parameter(Mandatory)] + [string]$Version, + [string]$OutputPath, + [string]$SigningIdentity, + [switch]$Notarize, + [string]$NotaryProfile +) +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + +if (-not $IsMacOS) { + throw 'This script needs macOS (codesign, hdiutil, notarytool).' +} +if ($Notarize -and -not $NotaryProfile) { + throw '-Notarize requires -NotaryProfile (a notarytool keychain profile name).' +} +$AppPath = (Resolve-Path $AppPath).Path +if (-not (Test-Path "$AppPath/Contents/Info.plist")) { + throw "$AppPath does not look like an .app bundle." +} +if (-not $OutputPath) { + $OutputPath = "ILSpy-$Version.dmg" +} + +if ($SigningIdentity) { + codesign --force --deep --options runtime --timestamp --sign $SigningIdentity $AppPath +} + +$stagingDir = Join-Path ([IO.Path]::GetTempPath()) 'ilspy-dmg' +Remove-Item -Recurse -Force $stagingDir -ErrorAction Ignore +New-Item -ItemType Directory $stagingDir | Out-Null +Copy-Item $AppPath $stagingDir -Recurse +ln -s /Applications (Join-Path $stagingDir 'Applications') + +Remove-Item $OutputPath -ErrorAction Ignore +hdiutil create -volname "ILSpy $Version" -srcfolder $stagingDir -ov -format UDZO $OutputPath + +if ($SigningIdentity) { + codesign --sign $SigningIdentity $OutputPath +} +if ($Notarize) { + xcrun notarytool submit $OutputPath --keychain-profile $NotaryProfile --wait + xcrun stapler staple $OutputPath +} + +Write-Host "created $OutputPath" +Write-Host "sha256 (for the Homebrew cask):" +shasum -a 256 $OutputPath