Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
161 lines (142 loc) · 7.47 KB
/
Copy pathMakefile
File metadata and controls
161 lines (142 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Only if the caller hasn't already chosen a toolchain (`$DEVELOPER_DIR`, or
# `sudo xcode-select -s`) and the standard path actually exists — exporting a
# path that isn't there breaks every target with `xcrun: missing DEVELOPER_DIR`
# on a machine that only has the Command Line Tools installed.
ifeq (,$(DEVELOPER_DIR))
ifneq (,$(wildcard /Applications/Xcode.app/Contents/Developer))
export DEVELOPER_DIR := /Applications/Xcode.app/Contents/Developer
endif
endif
PROJECT := Codenotch.xcodeproj
SCHEME := Codenotch
DEST := platform=macOS,arch=arm64
# Debug ad-hoc signs itself when the maintainer's Developer ID certificate
# isn't in the keychain, which is every machine but the maintainer's — so a
# contributor can `make build`/`make test`/`make run` with no Apple account at
# all, per CONTRIBUTING.md. On the maintainer's own machine this is empty and
# changes nothing: project.yml's stable identity is what keeps a keychain
# "Always Allow" grant alive across rebuilds, and forcing ad-hoc there would
# throw that away and bring the prompt back on every `make run`.
ifeq (,$(shell security find-identity -v -p codesigning 2>/dev/null | grep -c "Developer ID Application"))
DEV_SIGN := CODE_SIGN_IDENTITY="-" DEVELOPMENT_TEAM="" CODE_SIGN_STYLE=Automatic
endif
.PHONY: gen build test run clean
gen:
xcodegen generate
build: gen
xcodebuild -project $(PROJECT) -scheme $(SCHEME) -destination '$(DEST)' \
-configuration Debug $(DEV_SIGN) build
test: gen
xcodebuild -project $(PROJECT) -scheme $(SCHEME) -destination '$(DEST)' \
-configuration Debug $(DEV_SIGN) test
run: build
@APP=$$(xcodebuild -project $(PROJECT) -scheme $(SCHEME) -destination '$(DEST)' \
-configuration Debug -showBuildSettings 2>/dev/null \
| awk -F' = ' '/ BUILT_PRODUCTS_DIR/ {print $$2; exit}')/Codenotch.app; \
pkill -x Codenotch || true; \
open "$$APP"
clean:
rm -rf build DerivedData $(PROJECT)
# --- Release -----------------------------------------------------------------
# The path to a notarized .dmg. Run `make release` for the whole thing, or the
# steps one at a time while something is going wrong.
#
# One-time setup, which you have to run yourself because it takes a password:
#
# xcrun notarytool store-credentials UsageNotch \
# --apple-id <your-apple-id> --team-id 6WFPL8B9FB --password <app-specific-password>
#
# The app-specific password comes from appleid.apple.com → Sign-In and Security
# → App-Specific Passwords. Not your Apple ID password.
RELEASE_DIR := build/release
APP_NAME := Codenotch
# The label of the stored notarytool credential in the login keychain, not
# anything to do with the app's name — it was created before the rename and
# renaming the variable is what broke `make release` after it. Recreating it
# needs an app-specific password, so the label simply stays as it is.
NOTARY_PROFILE := UsageNotch
DMG := $(RELEASE_DIR)/$(APP_NAME).dmg
.PHONY: archive dmg notarize release verify-release
# Release configuration, exported with the Developer ID identity. `xcodebuild
# archive` + `-exportArchive` rather than a plain build: it re-signs the bundle
# as a distributable, which a Debug build is not.
archive: gen
rm -rf $(RELEASE_DIR)
mkdir -p $(RELEASE_DIR)
@# Spotlight indexes build output as installed applications, so every
@# release leaves extra "Codenotch" entries in app search next to the
@# real one in /Applications. This stops the whole tree being indexed.
@touch build/.metadata_never_index
xcodebuild -project $(PROJECT) -scheme $(SCHEME) -destination '$(DEST)' \
-configuration Release -archivePath $(RELEASE_DIR)/$(APP_NAME).xcarchive archive
printf '%s\n' \
'<?xml version="1.0" encoding="UTF-8"?>' \
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' \
'<plist version="1.0"><dict>' \
'<key>method</key><string>developer-id</string>' \
'<key>teamID</key><string>6WFPL8B9FB</string>' \
'<key>signingStyle</key><string>manual</string>' \
'<key>signingCertificate</key><string>Developer ID Application</string>' \
'</dict></plist>' > $(RELEASE_DIR)/ExportOptions.plist
xcodebuild -exportArchive \
-archivePath $(RELEASE_DIR)/$(APP_NAME).xcarchive \
-exportOptionsPlist $(RELEASE_DIR)/ExportOptions.plist \
-exportPath $(RELEASE_DIR)
# A plain drag-to-Applications disk image. `hdiutil` writes it read-only and
# compressed, which is what notarization expects.
dmg: archive
rm -f $(DMG)
rm -rf $(RELEASE_DIR)/stage
mkdir -p $(RELEASE_DIR)/stage
cp -R $(RELEASE_DIR)/$(APP_NAME).app $(RELEASE_DIR)/stage/
ln -s /Applications $(RELEASE_DIR)/stage/Applications
hdiutil create -volname "$(APP_NAME)" -srcfolder $(RELEASE_DIR)/stage \
-ov -format UDZO $(DMG)
codesign --force --sign "Developer ID Application" --timestamp $(DMG)
@# The app is inside the dmg now. Leaving the loose copies around is how
@# three spare "Codenotch" entries end up in Spotlight; everything
@# downstream (notarize, verify, appcast) works from the dmg alone.
rm -rf $(RELEASE_DIR)/stage $(RELEASE_DIR)/$(APP_NAME).app
# Submits and waits. `--wait` blocks until Apple answers, which is usually a
# couple of minutes; on rejection, the log says which binary failed and why.
notarize: dmg
xcrun notarytool submit $(DMG) --keychain-profile $(NOTARY_PROFILE) --wait
xcrun stapler staple $(DMG)
# Sparkle ships its tools inside the resolved package artifacts.
SPARKLE_BIN = $(shell dirname $$(find $$HOME/Library/Developer/Xcode/DerivedData/Codenotch-*/SourcePackages/artifacts/sparkle -name generate_appcast 2>/dev/null | head -1))
# The feed customers' copies poll. Signs each update with the EdDSA private key
# in the login keychain — Sparkle installs nothing that key did not sign, so a
# compromised host cannot push code.
#
# Writes into docs/, which GitHub Pages serves. The dmg goes there too, so the
# URL the appcast advertises is the one the file actually sits at — a mismatch
# is the usual reason an update downloads and then fails to verify.
# NOT docs/ — that holds the design frames and specs, and GitHub Pages serves
# whatever it is pointed at. Publishing from there would put the whole design
# history on the public web alongside the download.
PAGES_DIR := site
# Where the dmg actually sits. The enclosure URL the appcast advertises has to
# match it exactly, or an update downloads and then fails to verify.
DOWNLOAD_PREFIX := https://hivinz.com/
appcast: $(DMG)
@test -n "$(SPARKLE_BIN)" || (echo "Sparkle tools not found — run make build first" && exit 1)
mkdir -p $(PAGES_DIR)
@# Rebuilt from what is actually in the folder, never merged into the old
@# one. The dmg keeps a constant name, so only one build can exist at a
@# time — but generate_appcast preserves entries it already knows, and left
@# the previous version advertised at a URL now serving a different file,
@# with a signature that could never verify.
rm -f $(PAGES_DIR)/appcast.xml
cp $(DMG) $(PAGES_DIR)/
$(SPARKLE_BIN)/generate_appcast $(PAGES_DIR) --download-url-prefix $(DOWNLOAD_PREFIX)
@echo "Publish by committing $(PAGES_DIR)/ and pushing."
release: notarize verify-release appcast
@echo "Notarized: $(DMG)"
# What Gatekeeper on a customer's Mac will check. `spctl` accepting the app is
# the actual proof that the download will open without a right-click.
verify-release:
xcrun stapler validate $(DMG)
hdiutil attach $(DMG) -nobrowse -mountpoint $(RELEASE_DIR)/mnt
codesign --verify --deep --strict --verbose=2 $(RELEASE_DIR)/mnt/$(APP_NAME).app
spctl --assess --type execute --verbose=4 $(RELEASE_DIR)/mnt/$(APP_NAME).app
hdiutil detach $(RELEASE_DIR)/mnt