Skip to content
Merged
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ xcuserdata/
## Build output
build/
DerivedData/
dist/

## Obj-C/Swift specific
*.hmap
Expand Down
18 changes: 18 additions & 0 deletions Macstify.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,11 @@
15111ED6B6AF4060A89A5695 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 97A8B2WE2P;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Sources/Info.plist;
INSTALL_PATH = "$(HOME)/Library/Screen Savers";
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -255,6 +259,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = jp.winebarrel.Macstify;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = saver;
};
Expand All @@ -263,21 +268,29 @@
3C31C29A14694C2FACC94A7E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 97A8B2WE2P;
INFOPLIST_FILE = Preview/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = jp.winebarrel.MacstifyPreview;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
};
name = Debug;
};
3DE8C5B086DB4FFFB2ED726E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 97A8B2WE2P;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = Sources/Info.plist;
INSTALL_PATH = "$(HOME)/Library/Screen Savers";
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -287,6 +300,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = jp.winebarrel.Macstify;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = saver;
};
Expand Down Expand Up @@ -397,14 +411,18 @@
EDF8207572564CBAB1873417 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 97A8B2WE2P;
INFOPLIST_FILE = Preview/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = jp.winebarrel.MacstifyPreview;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
};
name = Release;
};
Expand Down
80 changes: 80 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
PROJECT := Macstify.xcodeproj
SCHEME := Macstify
PREVIEW_SCHEME := MacstifyPreview
CONFIGURATION := Release
DERIVED_DATA := build
PRODUCTS := $(DERIVED_DATA)/Build/Products/$(CONFIGURATION)
SAVER := $(PRODUCTS)/Macstify.saver
PREVIEW := $(PRODUCTS)/MacstifyPreview.app/Contents/MacOS/MacstifyPreview
INSTALL_DIR := $(HOME)/Library/Screen Savers
DIST_DIR := dist
DIST := $(DIST_DIR)/Macstify.zip
CHECKSUM := $(DIST_DIR)/checksum.txt
SUBMISSION := $(DERIVED_DATA)/submission.zip

# codesign resolves this by prefix, and there is only one such identity.
CODESIGN_IDENTITY ?= Developer ID Application
NOTARY_PROFILE ?= macstify

XCODEBUILD := xcodebuild -project $(PROJECT) -configuration $(CONFIGURATION) -derivedDataPath $(DERIVED_DATA)

.DEFAULT_GOAL := build

.PHONY: build
build:
$(XCODEBUILD) -scheme $(SCHEME) build

.PHONY: install
install: build
mkdir -p "$(INSTALL_DIR)"
rm -rf "$(INSTALL_DIR)/Macstify.saver"
cp -R $(SAVER) "$(INSTALL_DIR)/"
@echo
@echo 'Installed. macOS caches the loaded bundle, so if an older build is'
@echo 'still running: killall legacyScreenSaver, then reopen System Settings.'

.PHONY: uninstall
uninstall:
rm -rf "$(INSTALL_DIR)/Macstify.saver"

.PHONY: preview
preview:
$(XCODEBUILD) -scheme $(PREVIEW_SCHEME) build
$(PREVIEW)

.PHONY: lint
lint:
swiftlint --strict

.PHONY: format
format:
swiftformat Sources Preview

# Signs with Developer ID rather than the ad-hoc signature a plain build
# produces, then notarizes. Gatekeeper rejects anything less once the bundle
# reaches another Mac and picks up a quarantine flag. Stapling attaches the
# ticket to the bundle so it validates without a network round trip.
.PHONY: release
release:
$(XCODEBUILD) -scheme $(SCHEME) \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$(CODESIGN_IDENTITY)" \
OTHER_CODE_SIGN_FLAGS="--timestamp" \
build
codesign --verify --strict --verbose=2 $(SAVER)
rm -f $(SUBMISSION)
ditto -c -k --keepParent $(SAVER) $(SUBMISSION)
xcrun notarytool submit $(SUBMISSION) --keychain-profile "$(NOTARY_PROFILE)" --wait
xcrun stapler staple $(SAVER)
xcrun stapler validate $(SAVER)
mkdir -p $(DIST_DIR)
rm -f $(DIST) $(CHECKSUM)
ditto -c -k --keepParent $(SAVER) $(DIST)
cd $(DIST_DIR) && shasum -a 256 $(notdir $(DIST)) > $(notdir $(CHECKSUM))
@echo
@echo "Ready to distribute:"
@cat $(CHECKSUM)

.PHONY: clean
clean:
rm -rf $(DERIVED_DATA) dist
42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ Requires macOS 13 or later. Universal (Apple silicon and Intel).
## Install

```sh
xcodebuild -project Macstify.xcodeproj -scheme Macstify -configuration Release -derivedDataPath build build
mkdir -p ~/Library/"Screen Savers"
cp -R build/Build/Products/Release/Macstify.saver ~/Library/"Screen Savers"/
make install
```

Then open System Settings → Screen Saver and pick **Macstify**.
Expand Down Expand Up @@ -52,7 +50,8 @@ snapshot path drive it directly.
`CGRect` and a `CGContext`. `MacstifyPreview` is a development harness around it:

```sh
xcodebuild -project Macstify.xcodeproj -scheme MacstifyPreview -configuration Release -derivedDataPath build build
make preview # build it and open the live window

P=build/Build/Products/Release/MacstifyPreview.app/Contents/MacOS/MacstifyPreview

$P # live window
Expand All @@ -69,12 +68,8 @@ snapshots are written at 2x, as on a Retina display.

The screenshots above were produced with `--snapshot`.

CI builds and analyses both schemes and runs the same lint and format checks you can run locally:

```sh
swiftlint --strict
swiftformat Sources Preview --lint
```
CI builds and analyses both schemes and runs the same checks `make lint` and `make format` run
locally.

### Thumbnail

Expand All @@ -91,11 +86,30 @@ $P --snapshot Resources/thumbnail@2x.png --preview --size 90x58 --speed 4 --fram
sips -Z 90 Resources/thumbnail@2x.png --out Resources/thumbnail.png
```

## Signing
## Releasing

`make install` signs with whatever certificate the project is configured for, which is enough for a
saver you build and install yourself. Copying that bundle to another Mac is not: it arrives
quarantined, and Gatekeeper refuses anything without a Developer ID signature and a notarization
ticket.

```sh
make release
```

builds with the Developer ID identity and a secure timestamp, submits the bundle to Apple's notary
service, staples the returned ticket, and writes `dist/Macstify.zip` alongside a SHA-256
`dist/checksum.txt`. The zip that gets submitted and the zip that gets distributed are different
files — stapling applies to the bundle, so the distributable has to be re-zipped afterwards.

It needs a `Developer ID Application` certificate in the keychain and notary credentials stored
under the profile name `macstify`:

```sh
xcrun notarytool store-credentials macstify --apple-id you@example.com --team-id TEAMID
```

The build is ad-hoc signed, which is enough for a saver you build and install yourself. Copying the
bundle to another Mac means it arrives quarantined, and Gatekeeper will refuse it — that needs a
Developer ID signature and notarization.
Override the defaults with `make release CODESIGN_IDENTITY="..." NOTARY_PROFILE="..."`.

## License

Expand Down
Loading