Files
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

697 lines
21 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/zsh
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SCRIPT_DIR="$ROOT_DIR/scripts"
APP_VERSION_CONFIG="$ROOT_DIR/Configs/AppVersion.xcconfig"
PROJECT_FILE="$ROOT_DIR/MacTools.xcodeproj"
XCODEBUILD="${XCODEBUILD:-$SCRIPT_DIR/xcodebuild-filtered.sh}"
APP_NAME="MacTools"
APP_ENTITLEMENTS="$ROOT_DIR/Configs/MacTools.entitlements"
FINDER_SYNC_ENTITLEMENTS="$ROOT_DIR/Sources/Extensions/RightClickFinderSync/RightClickFinderSync.entitlements"
SCHEME="MacTools"
CONFIG_FILE="${RELEASE_CONFIG_FILE:-$SCRIPT_DIR/release.local.env}"
DEFAULT_GITHUB_REPOSITORY="ggbond268/MacTools"
VERSION=""
BUILD_NUMBER=""
TAG=""
NOTES_FILE=""
SPARKLE_NOTES_FILE=""
PUBLISH=0
PUBLISH_EXISTING=0
SKIP_BUILD=0
SKIP_SIGN=0
SKIP_NOTARIZE=0
FORCE=0
if [[ -f "$CONFIG_FILE" ]]; then
# shellcheck disable=SC1090
source "$CONFIG_FILE"
fi
function usage() {
cat <<'EOF'
Usage:
./scripts/release-local.sh [options]
Options:
--version <version> Release marketing version. Defaults to Configs/AppVersion.xcconfig.
--build-number <number> Release build number. Defaults to Configs/AppVersion.xcconfig.
--tag <tag> Git tag / release tag. Defaults to v<version>.
--notes-file <path> Release notes file for GitHub Release upload.
--publish Push the tag and sync the DMG to GitHub Releases.
--publish-existing Upload the existing DMG artifact without rebuilding.
--skip-build Reuse the existing Release app in build/DerivedData.
--skip-sign Skip Developer ID signing. Implies --skip-notarize.
--skip-notarize Skip notarization and stapling.
--force Recreate the local tag if it already points elsewhere.
-h, --help Show this help message.
Environment:
Copy scripts/release.local.env.sample to scripts/release.local.env
and set:
DEVELOPER_ID_APPLICATION
APPLE_NOTARY_PROFILE
GITHUB_REPOSITORY
DMG_SIGNING_IDENTIFIER (optional)
SPARKLE_KEYCHAIN_ACCOUNT (optional)
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--version)
VERSION="${2:-}"
shift 2
;;
--build-number)
BUILD_NUMBER="${2:-}"
shift 2
;;
--tag)
TAG="${2:-}"
shift 2
;;
--notes-file)
NOTES_FILE="${2:-}"
shift 2
;;
--publish)
PUBLISH=1
shift
;;
--publish-existing)
PUBLISH_EXISTING=1
shift
;;
--skip-build)
SKIP_BUILD=1
shift
;;
--skip-sign)
SKIP_SIGN=1
SKIP_NOTARIZE=1
shift
;;
--skip-notarize)
SKIP_NOTARIZE=1
shift
;;
--force)
FORCE=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
esac
done
function info() {
printf '[release] %s\n' "$1"
}
function fail() {
printf '[release] error: %s\n' "$1" >&2
exit 1
}
function require_command() {
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
}
function available_code_signing_identities() {
security find-identity -v -p codesigning 2>/dev/null || true
}
function require_release_signing_identity() {
local identities matched_identity
identities="$(available_code_signing_identities)"
[[ -n "$identities" ]] || fail "当前钥匙串里没有可用的代码签名身份。"
matched_identity="$(printf '%s\n' "$identities" | grep -F "\"$DEVELOPER_ID_APPLICATION\"" || true)"
if [[ -z "$matched_identity" ]]; then
fail "未找到签名身份 \"$DEVELOPER_ID_APPLICATION\"。
请在 scripts/release.local.env 中把 DEVELOPER_ID_APPLICATION 设置为钥匙串里的完整证书名称,例如:
DEVELOPER_ID_APPLICATION=\"Developer ID Application: Your Name (TEAMID)\"
当前可见的代码签名身份:
$identities"
fi
if [[ "$matched_identity" != *"Developer ID Application:"* ]]; then
fail "当前匹配到的签名身份不是 Developer ID Application 证书:
$matched_identity
本脚本用于外部正式分发,必须使用 Developer ID Application 证书,而不是 Apple Development 证书。"
fi
}
function read_version_setting() {
local key="$1"
awk -v key="$key" '$1 == key && $2 == "=" { print $3; exit }' "$APP_VERSION_CONFIG"
}
function git_repository() {
if [[ -n "${GITHUB_REPOSITORY:-}" ]]; then
printf '%s\n' "$GITHUB_REPOSITORY"
return 0
fi
local remote_url
remote_url="$(git -C "$ROOT_DIR" remote get-url origin 2>/dev/null || true)"
case "$remote_url" in
git@github.com:*)
remote_url="${remote_url#git@github.com:}"
printf '%s\n' "${remote_url%.git}"
;;
https://github.com/*)
remote_url="${remote_url#https://github.com/}"
printf '%s\n' "${remote_url%.git}"
;;
ssh://git@github.com/*)
remote_url="${remote_url#ssh://git@github.com/}"
printf '%s\n' "${remote_url%.git}"
;;
*)
printf '%s\n' "$DEFAULT_GITHUB_REPOSITORY"
;;
esac
}
function sparkle_bin_dir() {
local bin_dir="${SPARKLE_BIN_DIR:-$DERIVED_DATA/SourcePackages/artifacts/sparkle/Sparkle/bin}"
[[ -x "$bin_dir/sign_update" ]] || fail "未找到 Sparkle 工具链:$bin_dir
请先执行一次 xcodebuild / make build,让 Xcode 拉取 Sparkle 包依赖后再发布。"
printf '%s\n' "$bin_dir"
}
function sparkle_signature() {
local dmg_path="$1"
local bin_dir signature
bin_dir="$(sparkle_bin_dir)"
signature="$("$bin_dir/sign_update" --account "${SPARKLE_KEYCHAIN_ACCOUNT:-ed25519}" -p "$dmg_path")" \
|| fail "无法使用 Sparkle EdDSA 私钥为 DMG 签名。
请先运行:
$(sparkle_bin_dir)/generate_keys
然后允许 Sparkle 访问本机钥匙串中的更新签名密钥。"
[[ -n "$signature" ]] || fail "Sparkle EdDSA 签名结果为空。"
printf '%s\n' "$signature"
}
function release_download_url() {
local repository="$1"
printf 'https://github.com/%s/releases/download/%s/%s.dmg\n' "$repository" "$TAG" "$APP_NAME"
}
function release_notes_url() {
local repository="$1"
printf 'https://github.com/%s/releases/tag/%s\n' "$repository" "$TAG"
}
function write_appcast() {
local dmg_path="$1"
local repository download_url notes_url signature file_size pub_date minimum_system_version
repository="$(git_repository)"
download_url="$(release_download_url "$repository")"
notes_url="$(release_notes_url "$repository")"
signature="$(sparkle_signature "$dmg_path")"
file_size="$(stat -f '%z' "$dmg_path")"
pub_date="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
minimum_system_version="${SPARKLE_MINIMUM_SYSTEM_VERSION:-14.0}"
mkdir -p "$DOCS_DIR"
APP_NAME="$APP_NAME" \
VERSION="$VERSION" \
BUILD_NUMBER="$BUILD_NUMBER" \
DOWNLOAD_URL="$download_url" \
RELEASE_NOTES_URL="$notes_url" \
PUB_DATE="$pub_date" \
FILE_SIZE="$file_size" \
ED_SIGNATURE="$signature" \
MINIMUM_SYSTEM_VERSION="$minimum_system_version" \
SPARKLE_NOTES_FILE="$SPARKLE_NOTES_FILE" \
APPCAST_PATH="$APPCAST_PATH" \
python3 <<'PY_APPCAST'
import os
import xml.sax.saxutils as xml
from pathlib import Path
values = {
key: os.environ[key]
for key in [
"APP_NAME",
"VERSION",
"BUILD_NUMBER",
"DOWNLOAD_URL",
"RELEASE_NOTES_URL",
"PUB_DATE",
"FILE_SIZE",
"ED_SIGNATURE",
"MINIMUM_SYSTEM_VERSION",
]
}
release_notes = Path(os.environ["SPARKLE_NOTES_FILE"]).read_text(encoding="utf-8")
cdata_release_notes = release_notes.replace("]]>", "]]]]><![CDATA[>")
content = f'''<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>{xml.escape(values["APP_NAME"])} Releases</title>
<description>Latest release metadata for {xml.escape(values["APP_NAME"])}.</description>
<language>en</language>
<item>
<title>Version {xml.escape(values["VERSION"])}</title>
<link>{xml.escape(values["RELEASE_NOTES_URL"])}</link>
<sparkle:version>{xml.escape(values["BUILD_NUMBER"])}</sparkle:version>
<sparkle:shortVersionString>{xml.escape(values["VERSION"])}</sparkle:shortVersionString>
<description sparkle:format="markdown"><![CDATA[{cdata_release_notes}]]></description>
<sparkle:fullReleaseNotesLink>{xml.escape(values["RELEASE_NOTES_URL"])}</sparkle:fullReleaseNotesLink>
<pubDate>{xml.escape(values["PUB_DATE"])}</pubDate>
<enclosure url="{xml.escape(values["DOWNLOAD_URL"])}" length="{xml.escape(values["FILE_SIZE"])}" type="application/octet-stream" sparkle:edSignature="{xml.escape(values["ED_SIGNATURE"])}" />
<sparkle:minimumSystemVersion>{xml.escape(values["MINIMUM_SYSTEM_VERSION"])}</sparkle:minimumSystemVersion>
</item>
</channel>
</rss>
'''
Path(os.environ["APPCAST_PATH"]).write_text(content, encoding="utf-8")
PY_APPCAST
python3 -c 'import sys, xml.etree.ElementTree as ET; ET.parse(sys.argv[1])' "$APPCAST_PATH"
info "Appcast updated: $APPCAST_PATH"
}
function ensure_clean_git() {
local worktree_status
worktree_status="$(git -C "$ROOT_DIR" status --porcelain)"
[[ -z "$worktree_status" ]] || fail "Git 工作区必须干净后才能 --publish。"
}
function ensure_tag_ready() {
local head_commit existing_commit
head_commit="$(git -C "$ROOT_DIR" rev-parse HEAD)"
if git -C "$ROOT_DIR" rev-parse -q --verify "refs/tags/$TAG" >/dev/null 2>&1; then
existing_commit="$(git -C "$ROOT_DIR" rev-list -n 1 "$TAG")"
if [[ "$existing_commit" != "$head_commit" ]]; then
[[ "$FORCE" -eq 1 ]] || fail "标签 $TAG 已存在且不指向当前提交,添加 --force 才会重建。"
git -C "$ROOT_DIR" tag -d "$TAG" >/dev/null
fi
fi
if ! git -C "$ROOT_DIR" rev-parse -q --verify "refs/tags/$TAG" >/dev/null 2>&1; then
git -C "$ROOT_DIR" tag -a "$TAG" -m "Release $TAG"
fi
if [[ "$FORCE" -eq 1 ]]; then
git -C "$ROOT_DIR" push --force origin "refs/tags/$TAG"
else
git -C "$ROOT_DIR" push origin "refs/tags/$TAG"
fi
}
function sign_path() {
local path="$1"
/usr/bin/codesign \
--force \
--sign "$DEVELOPER_ID_APPLICATION" \
--options runtime \
--timestamp \
"$path"
}
function sign_path_preserving_entitlements() {
local path="$1"
/usr/bin/codesign \
--force \
--sign "$DEVELOPER_ID_APPLICATION" \
--options runtime \
--timestamp \
--preserve-metadata=entitlements \
"$path"
}
function sign_path_with_entitlements() {
local path="$1"
local entitlements="$2"
[[ -f "$entitlements" ]] || fail "未找到 entitlements$entitlements"
/usr/bin/codesign \
--force \
--sign "$DEVELOPER_ID_APPLICATION" \
--options runtime \
--timestamp \
--entitlements "$entitlements" \
"$path"
}
function sign_app_path() {
local path="$1"
[[ -f "$APP_ENTITLEMENTS" ]] || fail "未找到 app entitlements$APP_ENTITLEMENTS"
/usr/bin/codesign \
--force \
--sign "$DEVELOPER_ID_APPLICATION" \
--options runtime \
--timestamp \
--entitlements "$APP_ENTITLEMENTS" \
"$path"
}
function is_inside_sparkle_framework() {
local path="$1"
[[ "$path" == *"/Sparkle.framework" || "$path" == *"/Sparkle.framework/"* ]]
}
function is_right_click_finder_sync_extension() {
local path="$1"
[[ "$(basename "$path")" == "RightClickFinderSync.appex" ]]
}
function sign_sparkle_framework() {
local app_path="$1"
local sparkle_framework="$app_path/Contents/Frameworks/Sparkle.framework"
local sparkle_current="$sparkle_framework/Versions/Current"
[[ -d "$sparkle_framework" ]] || return 0
if [[ -d "$sparkle_current/XPCServices/Installer.xpc" ]]; then
sign_path "$sparkle_current/XPCServices/Installer.xpc"
fi
if [[ -d "$sparkle_current/XPCServices/Downloader.xpc" ]]; then
sign_path_preserving_entitlements "$sparkle_current/XPCServices/Downloader.xpc"
fi
if [[ -f "$sparkle_current/Autoupdate" ]]; then
sign_path "$sparkle_current/Autoupdate"
fi
if [[ -d "$sparkle_current/Updater.app" ]]; then
sign_path "$sparkle_current/Updater.app"
fi
sign_path "$sparkle_framework"
}
function validate_finder_sync_extension() {
local app_path="$1"
local extension_path="$app_path/Contents/PlugIns/RightClickFinderSync.appex"
local extension_point entitlements
[[ -d "$extension_path" ]] || fail "未找到 Finder Sync 扩展:$extension_path"
extension_point="$(/usr/libexec/PlistBuddy -c "Print :NSExtension:NSExtensionPointIdentifier" "$extension_path/Contents/Info.plist" 2>/dev/null || true)"
[[ "$extension_point" == "com.apple.FinderSync" ]] \
|| fail "Finder Sync 扩展 Info.plist 缺少正确的 NSExtensionPointIdentifier。"
entitlements="$(/usr/bin/codesign -d --entitlements :- "$extension_path" 2>/dev/null || true)"
[[ "$entitlements" == *"com.apple.security.app-sandbox"* ]] \
|| fail "Finder Sync 扩展签名缺少 com.apple.security.app-sandbox entitlement。"
[[ "$entitlements" == *"com.apple.security.temporary-exception.files.home-relative-path.read-only"* ]] \
|| fail "Finder Sync 扩展签名缺少读取右键配置文件所需的 temporary exception entitlement。"
}
function app_bundle_identifier() {
local app_path="$1"
/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$app_path/Contents/Info.plist" 2>/dev/null \
|| fail "无法从 $app_path 读取 CFBundleIdentifier。"
}
function dmg_signing_identifier() {
local app_path="$1"
if [[ -n "${DMG_SIGNING_IDENTIFIER:-}" ]]; then
printf '%s\n' "$DMG_SIGNING_IDENTIFIER"
return 0
fi
printf '%s\n' "$(app_bundle_identifier "$app_path").disk-image"
}
function sign_app_bundle() {
local app_path="$1"
if [[ -d "$app_path/Contents" ]]; then
while IFS= read -r binary; do
[[ -n "$binary" ]] || continue
sign_path "$binary"
done < <(find "$app_path/Contents" -type f \( -name "*.dylib" -o -name "*.so" \) -print)
while IFS= read -r bundle; do
[[ -n "$bundle" ]] || continue
if is_inside_sparkle_framework "$bundle"; then
continue
fi
if is_right_click_finder_sync_extension "$bundle"; then
sign_path_with_entitlements "$bundle" "$FINDER_SYNC_ENTITLEMENTS"
else
sign_path "$bundle"
fi
done < <(find "$app_path/Contents" -depth -type d \( -name "*.framework" -o -name "*.app" -o -name "*.xpc" -o -name "*.appex" \) -print)
sign_sparkle_framework "$app_path"
fi
sign_app_path "$app_path"
validate_finder_sync_extension "$app_path"
/usr/bin/codesign --verify --deep --strict --verbose=2 "$app_path"
}
function sign_disk_image() {
local dmg_path="$1"
local identifier="$2"
/usr/bin/codesign \
--force \
--sign "$DEVELOPER_ID_APPLICATION" \
--timestamp \
--identifier "$identifier" \
"$dmg_path"
/usr/bin/codesign --verify --verbose=2 "$dmg_path"
}
function build_release_app() {
require_command xcodegen
require_command "$XCODEBUILD"
info "Generating Xcode project"
(cd "$ROOT_DIR" && make generate >/dev/null)
info "Building Release app version=$VERSION build=$BUILD_NUMBER"
"$XCODEBUILD" \
-project "$PROJECT_FILE" \
-scheme "$SCHEME" \
-configuration Release \
-destination "generic/platform=macOS" \
-derivedDataPath "$DERIVED_DATA" \
MARKETING_VERSION="$VERSION" \
CURRENT_PROJECT_VERSION="$BUILD_NUMBER" \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY= \
build \
-quiet
}
function create_dmg() {
local app_path="$1"
local dmg_path="$2"
local volume_name="${DMG_VOLUME_NAME:-$APP_NAME}"
local stage_dir
stage_dir="$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/mactools-release-dmg.XXXXXX")"
ditto "$app_path" "$stage_dir/$APP_NAME.app"
ln -s /Applications "$stage_dir/Applications"
info "Creating DMG at $dmg_path"
hdiutil create \
-volname "$volume_name" \
-srcfolder "$stage_dir" \
-ov \
-format UDZO \
"$dmg_path" >/dev/null
/bin/rm -rf "$stage_dir"
}
function notarize_dmg() {
local dmg_path="$1"
[[ -n "${APPLE_NOTARY_PROFILE:-}" ]] || fail "缺少 APPLE_NOTARY_PROFILE,无法公证。"
info "Submitting DMG for notarization"
xcrun notarytool submit "$dmg_path" --keychain-profile "$APPLE_NOTARY_PROFILE" --wait
info "Stapling notarization ticket"
xcrun stapler staple -v "$dmg_path"
}
function validate_notarized_dmg() {
local dmg_path="$1"
local assessment_output
assessment_output="$(spctl -a -t open --context context:primary-signature -v "$dmg_path" 2>&1)" \
|| fail "Gatekeeper 未接受最终 DMG
$assessment_output"
info "Gatekeeper assessment passed"
printf '%s\n' "$assessment_output"
}
function require_existing_dmg() {
local dmg_path="$1"
[[ -f "$dmg_path" ]] || fail "未找到现有 DMG$dmg_path
如果你想先构建并公证新包,请直接运行:
./scripts/release-local.sh
如果你已经做过一次完整发布,请确认要上传的 DMG 仍在上面的路径。"
}
function publish_release() {
local dmg_path="$1"
local repository
repository="$(git_repository)" || fail "无法推断 GitHub 仓库,请在 scripts/release.local.env 中设置 GITHUB_REPOSITORY。"
require_command gh
gh auth status >/dev/null 2>&1 || fail "gh 尚未登录,请先运行 gh auth login。"
ensure_clean_git
ensure_tag_ready
if [[ -z "$NOTES_FILE" ]]; then
NOTES_FILE="$(mktemp "${TMPDIR:-/tmp}/mactools-release-notes.XXXXXX.md")"
python3 "$ROOT_DIR/scripts/changelog.py" extract --tag "$TAG" --output "$NOTES_FILE"
fi
SPARKLE_NOTES_FILE="$(mktemp "${TMPDIR:-/tmp}/mactools-sparkle-notes.XXXXXX.md")"
python3 "$ROOT_DIR/scripts/changelog.py" compose-sparkle \
--tag "$TAG" \
--app-notes "$NOTES_FILE" \
--output "$SPARKLE_NOTES_FILE"
info "Syncing asset to GitHub Release $TAG ($repository)"
if gh release view "$TAG" --repo "$repository" >/dev/null 2>&1; then
gh release upload "$TAG" "$dmg_path" --repo "$repository" --clobber
gh release edit "$TAG" --repo "$repository" --title "$APP_NAME $VERSION" --notes-file "$NOTES_FILE"
else
gh release create "$TAG" "$dmg_path" --repo "$repository" --title "$APP_NAME $VERSION" --notes-file "$NOTES_FILE"
fi
}
require_command ditto
require_command hdiutil
require_command shasum
require_command codesign
require_command git
VERSION="${VERSION:-$(read_version_setting MARKETING_VERSION)}"
BUILD_NUMBER="${BUILD_NUMBER:-$(read_version_setting CURRENT_PROJECT_VERSION)}"
TAG="${TAG:-v$VERSION}"
NOTES_FILE="${NOTES_FILE:-${GITHUB_RELEASE_NOTES_FILE:-}}"
[[ -n "$VERSION" ]] || fail "无法从 Configs/AppVersion.xcconfig 读取 MARKETING_VERSION。"
[[ -n "$BUILD_NUMBER" ]] || fail "无法从 Configs/AppVersion.xcconfig 读取 CURRENT_PROJECT_VERSION。"
if [[ -n "$NOTES_FILE" && ! -f "$NOTES_FILE" ]]; then
fail "Release notes 文件不存在:$NOTES_FILE"
fi
if [[ "$PUBLISH" -eq 1 && "$PUBLISH_EXISTING" -eq 1 ]]; then
fail "--publish 和 --publish-existing 不能同时使用。"
fi
ARTIFACT_DIR="$ROOT_DIR/build/release/$TAG"
DERIVED_DATA="$ROOT_DIR/build/DerivedData"
APP_PATH="$DERIVED_DATA/Build/Products/Release/$APP_NAME.app"
SIGNED_APP_PATH="$ARTIFACT_DIR/$APP_NAME.app"
DMG_PATH="$ARTIFACT_DIR/$APP_NAME.dmg"
DMG_IDENTIFIER=""
DOCS_DIR="$ROOT_DIR/docs"
APPCAST_PATH="$DOCS_DIR/appcast.xml"
mkdir -p "$ARTIFACT_DIR"
if [[ "$PUBLISH_EXISTING" -eq 1 ]]; then
require_existing_dmg "$DMG_PATH"
validate_notarized_dmg "$DMG_PATH"
else
if [[ "$SKIP_BUILD" -eq 0 ]]; then
build_release_app
fi
[[ -d "$APP_PATH" ]] || fail "未找到 Release app$APP_PATH"
info "Preparing artifact directory $ARTIFACT_DIR"
rm -rf "$SIGNED_APP_PATH" "$DMG_PATH"
ditto "$APP_PATH" "$SIGNED_APP_PATH"
if [[ "$SKIP_SIGN" -eq 0 ]]; then
[[ -n "${DEVELOPER_ID_APPLICATION:-}" ]] || fail "缺少 DEVELOPER_ID_APPLICATION,无法做正式签名。"
require_release_signing_identity
info "Signing app with Developer ID"
sign_app_bundle "$SIGNED_APP_PATH"
DMG_IDENTIFIER="$(dmg_signing_identifier "$SIGNED_APP_PATH")"
else
info "Skipping code signing"
fi
create_dmg "$SIGNED_APP_PATH" "$DMG_PATH"
if [[ "$SKIP_SIGN" -eq 0 ]]; then
info "Signing DMG with Developer ID identifier=$DMG_IDENTIFIER"
sign_disk_image "$DMG_PATH" "$DMG_IDENTIFIER"
fi
if [[ "$SKIP_NOTARIZE" -eq 0 ]]; then
require_command xcrun
notarize_dmg "$DMG_PATH"
validate_notarized_dmg "$DMG_PATH"
else
info "Skipping notarization"
fi
fi
DMG_SHA256="$(shasum -a 256 "$DMG_PATH" | awk '{print $1}')"
info "DMG ready: $DMG_PATH"
info "SHA256: $DMG_SHA256"
if [[ "$PUBLISH" -eq 1 ]]; then
publish_release "$DMG_PATH"
write_appcast "$DMG_PATH"
info "Appcast updated after GitHub Release publish: $APPCAST_PATH"
fi
if [[ "$PUBLISH_EXISTING" -eq 1 ]]; then
publish_release "$DMG_PATH"
write_appcast "$DMG_PATH"
info "Appcast updated after existing artifact publish: $APPCAST_PATH"
fi
if [[ "$PUBLISH" -eq 0 && "$PUBLISH_EXISTING" -eq 0 ]]; then
info "Skipping appcast update because this run did not publish a GitHub Release."
fi
info "Done"