Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Every release of Substratal Hub for macOS. The Hub help center's What's new page
### Build and release

- `scripts/run-local.sh`: build and launch the app for testing, against the production API or a local devserver.
- `scripts/dist-local.sh` (`make dist`): build a distributable `.pkg` on your Mac, signed and notarized with the Developer ID certificates and `notarytool` profile in your Keychain, with signature and Gatekeeper checks.
- `scripts/build-pkg.sh` and the **Release** workflow: build the `.pkg` installer and publish it as a GitHub Release asset. Signed and notarized when the Developer ID secrets are configured; an unsigned pre-release otherwise.

### Platform API
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ run-local: ## Build and launch for testing; API=… to choose the API (default p
pkg: ## Build the installer into build/pkg (signs/notarizes when APP_IDENTITY etc. are set)
scripts/build-pkg.sh

dist: ## Build a distributable .pkg on this Mac, signing/notarizing with what's in your Keychain
scripts/dist-local.sh --open

test: ## SubstratalKit unit tests
cd Packages/SubstratalKit && swift test

Expand All @@ -44,4 +47,4 @@ help-check: ## Check the app's Help links against a fresh build of the Hub help

check: test build help-check ## Everything CI runs

.PHONY: help project build run run-local pkg test ui-test sync-api sync-screens help-check check
.PHONY: help project build run run-local pkg dist test ui-test sync-api sync-screens help-check check
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ or choose **Settings → Connection → Local devserver** in the app. Each envir

### Building the installer (.pkg)

On your Mac, for distribution:

```bash
scripts/dist-local.sh # or: make dist (also shows it in Finder)
scripts/dist-local.sh 0.2.0 # a specific version
```

It finds the **Developer ID Application** and **Developer ID Installer** certificates in your Keychain and signs with them. With a `notarytool` profile (create it once: `xcrun notarytool store-credentials substratal-notary --team-id <TEAMID> --apple-id <you@example.com>`) it also notarizes and staples, so the `.pkg` opens on any Mac without warnings. Then it checks the signature and Gatekeeper and prints the path and SHA-256. Without certificates it builds an unsigned test package and says so.

The underlying build, used by CI as well:

```bash
scripts/build-pkg.sh # → build/pkg/SubstratalHub-<version>.pkg (+ .sha256)
VERSION=0.2.0 BUILD_NUMBER=7 scripts/build-pkg.sh
Expand Down
15 changes: 12 additions & 3 deletions scripts/build-pkg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# Unset → ad-hoc signed: runs locally, Gatekeeper warns elsewhere.
# INSTALLER_IDENTITY "Developer ID Installer: …" signs the .pkg. Unset → unsigned .pkg.
# NOTARY_KEY_PATH, NOTARY_KEY_ID, NOTARY_ISSUER_ID
# App Store Connect API key → notarize and staple the .pkg.
# Needs both identities. Unset → not notarized.
# App Store Connect API key → notarize and staple the .pkg (CI).
# NOTARY_PROFILE Or a notarytool Keychain profile (on a Mac:
# xcrun notarytool store-credentials <name>).
# Either needs both identities. Neither → not notarized.
#
# Prints the .pkg path last, and writes <pkg>.sha256 next to it.
set -euo pipefail
Expand Down Expand Up @@ -88,10 +90,17 @@ sign=()
[[ -n "$INSTALLER_IDENTITY" ]] && sign=(--sign "$INSTALLER_IDENTITY" --timestamp)
productbuild --distribution "$dist" --package-path "$OUT" ${sign[@]+"${sign[@]}"} "$PKG" >/dev/null

notary=()
if [[ -n "${NOTARY_KEY_PATH:-}" && -n "${NOTARY_KEY_ID:-}" && -n "${NOTARY_ISSUER_ID:-}" ]]; then
notary=(--key "$NOTARY_KEY_PATH" --key-id "$NOTARY_KEY_ID" --issuer "$NOTARY_ISSUER_ID")
elif [[ -n "${NOTARY_PROFILE:-}" ]]; then
notary=(--keychain-profile "$NOTARY_PROFILE")
fi

if [[ ${#notary[@]} -gt 0 ]]; then
[[ -n "$APP_IDENTITY" && -n "$INSTALLER_IDENTITY" ]] || { echo "build-pkg: notarizing needs APP_IDENTITY and INSTALLER_IDENTITY" >&2; exit 1; }
step "Notarizing (this waits for Apple)"
xcrun notarytool submit "$PKG" --key "$NOTARY_KEY_PATH" --key-id "$NOTARY_KEY_ID" --issuer "$NOTARY_ISSUER_ID" --wait
xcrun notarytool submit "$PKG" "${notary[@]}" --wait
xcrun stapler staple "$PKG"
spctl --assess --type install --verbose "$PKG"
status="signed, notarized and stapled"
Expand Down
87 changes: 87 additions & 0 deletions scripts/dist-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
# Builds a distributable Substratal Hub installer on this Mac.
#
# scripts/dist-local.sh # version from project.yml
# scripts/dist-local.sh 0.2.0 # a specific version (build number: BUILD_NUMBER or a timestamp)
# scripts/dist-local.sh --unsigned # skip signing even if certificates are installed
# scripts/dist-local.sh --open # show the .pkg in Finder when done
#
# Finds your signing setup and uses as much of it as is there:
# - "Developer ID Application" and "Developer ID Installer" certificates in your
# Keychain sign the app and the .pkg. Override with APP_IDENTITY / INSTALLER_IDENTITY.
# - A notarytool Keychain profile notarizes and staples it, so it opens on any Mac
# without warnings. Create one once with:
# xcrun notarytool store-credentials substratal-notary --team-id <TEAMID> --apple-id <you@example.com>
# The profile name defaults to substratal-notary; override with NOTARY_PROFILE.
# With none of that, you get an unsigned .pkg that's fine for testing on Macs you control.
#
# The build itself is scripts/build-pkg.sh; this wraps it for a person at a Mac.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

unsigned=false
reveal=false
version=""
for arg in "$@"; do
case "$arg" in
--unsigned) unsigned=true ;;
--open) reveal=true ;;
-h|--help) sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
-*) echo "dist-local: unknown option $arg (try --help)" >&2; exit 1 ;;
*) version="$arg" ;;
esac
done

command -v xcodegen >/dev/null || { echo "dist-local: XcodeGen is required: brew install xcodegen" >&2; exit 1; }

[[ -n "$version" ]] && export VERSION="$version"
export BUILD_NUMBER="${BUILD_NUMBER:-$(date +%Y%m%d%H%M)}"

find_identity() { # $1 = certificate kind, e.g. "Developer ID Application"
security find-identity -v 2>/dev/null | sed -n "s/.*\"\($1: .*\)\"/\1/p" | head -1
}

if $unsigned; then
export APP_IDENTITY="" INSTALLER_IDENTITY=""
else
export APP_IDENTITY="${APP_IDENTITY:-$(find_identity "Developer ID Application")}"
export INSTALLER_IDENTITY="${INSTALLER_IDENTITY:-$(find_identity "Developer ID Installer")}"
fi

profile="${NOTARY_PROFILE:-substratal-notary}"
unset NOTARY_PROFILE
if [[ -n "$APP_IDENTITY" && -n "$INSTALLER_IDENTITY" ]]; then
# Only notarize with a profile that actually exists.
if xcrun notarytool history --keychain-profile "$profile" >/dev/null 2>&1; then
export NOTARY_PROFILE="$profile"
fi
fi

echo "Substratal Hub installer"
echo " App signing: ${APP_IDENTITY:-none (ad-hoc)}"
echo " Installer signing: ${INSTALLER_IDENTITY:-none (unsigned .pkg)}"
echo " Notarization: ${NOTARY_PROFILE:-none}"
if [[ -z "$APP_IDENTITY" || -z "$INSTALLER_IDENTITY" ]] && ! $unsigned; then
echo " (No Developer ID certificates found in your Keychain, so this build is for testing only.)"
fi
echo

pkg="$(scripts/build-pkg.sh | tee /dev/stderr | tail -1)"

echo
echo "Checks"
echo " Package signature: $(pkgutil --check-signature "$pkg" 2>/dev/null | sed -n 's/^ Status: //p' | head -1 || true)"
if spctl --assess --type install "$pkg" >/dev/null 2>&1; then
echo " Gatekeeper: accepted (opens on any Mac)"
else
echo " Gatekeeper: not accepted; people must right-click → Open, or allow it in System Settings → Privacy & Security"
fi
echo " SHA-256: $(cut -d' ' -f1 "$pkg.sha256")"
echo
echo "Installer: $pkg"
echo "Install: sudo installer -pkg \"$pkg\" -target / (or double-click it)"

$reveal && open -R "$pkg"
exit 0
Loading