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
13 changes: 8 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,19 @@ jobs:

- name: Package unsigned app
run: |
mkdir -p build/artifacts
ditto \
-c -k --sequesterRsrc --keepParent \
./Scripts/package-unsigned-release.sh \
build/DerivedData/Build/Products/Release/Startle.app \
build/artifacts/Startle-macOS-unsigned.zip
build/artifacts \
Startle-macOS-unsigned

- name: Upload unsigned app
uses: actions/upload-artifact@v7
with:
name: Startle-${{ github.sha }}-macOS-unsigned
path: build/artifacts/Startle-macOS-unsigned.zip
path: |
build/artifacts/Startle-macOS-unsigned.dmg
build/artifacts/Startle-macOS-unsigned.dmg.sha256
build/artifacts/Startle-macOS-unsigned.zip
build/artifacts/Startle-macOS-unsigned.zip.sha256
if-no-files-found: error
retention-days: 14
26 changes: 13 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,34 +70,34 @@ jobs:
- name: Package release assets
run: |
release_version=${GITHUB_REF_NAME#v}
archive_name="Startle-${release_version}-macOS-unsigned.zip"
mkdir -p dist
ditto \
-c -k --sequesterRsrc --keepParent \
./Scripts/package-unsigned-release.sh \
build/DerivedData/Build/Products/Release/Startle.app \
"dist/$archive_name"
cd dist
shasum -a 256 "$archive_name" > "$archive_name.sha256"
dist \
"Startle-${release_version}-macOS-unsigned"

- name: Publish prerelease
env:
GH_TOKEN: ${{ github.token }}
run: |
release_version=${GITHUB_REF_NAME#v}
archive_name="Startle-${release_version}-macOS-unsigned.zip"
release_notes="Unsigned preview build. macOS will identify this app as coming from an unidentified developer. Review the installation notes in the repository before opening it."
asset_name="Startle-${release_version}-macOS-unsigned"
release_notes="Unsigned preview build. Download the DMG, drag Startle to Applications, then review the first-launch instructions in the repository. macOS will identify this app as coming from an unidentified developer."

if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
gh release upload \
"$GITHUB_REF_NAME" \
"dist/$archive_name" \
"dist/$archive_name.sha256" \
"dist/$asset_name.dmg" \
"dist/$asset_name.dmg.sha256" \
"dist/$asset_name.zip" \
"dist/$asset_name.zip.sha256" \
--clobber
else
gh release create \
"$GITHUB_REF_NAME" \
"dist/$archive_name" \
"dist/$archive_name.sha256" \
"dist/$asset_name.dmg" \
"dist/$asset_name.dmg.sha256" \
"dist/$asset_name.zip" \
"dist/$asset_name.zip.sha256" \
--verify-tag \
--prerelease \
--title "Startle $GITHUB_REF_NAME (unsigned)" \
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes will be documented here. The project follows [Semantic Versi

## Unreleased

## 1.0.1 - 2026-07-29

### Added

- Compressed DMG release downloads with an Applications shortcut.

## 1.0.0 - 2026-07-29

### Added
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ The Xcode target enables App Sandbox and Hardened Runtime. Distribution still re

## Preview downloads

Version tags matching `v*` build a universal macOS app and publish it as a GitHub prerelease with a SHA-256 checksum. These downloads are unsigned because the project does not currently use a paid Apple Developer identity.
Version tags matching `v*` build a universal macOS app and publish it as a GitHub prerelease. The DMG is the recommended download; a ZIP is also available as a fallback. Each asset has a SHA-256 checksum. These downloads are unsigned because the project does not currently use a paid Apple Developer identity.

macOS will identify an unsigned download as coming from an unidentified developer. After verifying the checksum, extract the ZIP and use **Control-click → Open** for the first launch. Do not treat an unsigned preview as equivalent to a Developer ID-signed and notarized release.
[Download an unsigned preview](https://github.com/Charlie284/Startle/releases)

After verifying the checksum, open the DMG and drag **Startle** to the **Applications** shortcut. macOS will identify the app as coming from an unidentified developer, so use **Control-click → Open** in Applications for the first launch. Do not treat an unsigned preview as equivalent to a Developer ID-signed and notarized release.

## Architecture

Expand Down
2 changes: 1 addition & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Startle is not ready to distribute merely because `swift test` passes. A release

## Unsigned preview releases

Pushing a version tag such as `v1.0.0` runs `.github/workflows/release.yml`. The workflow repeats the source and Xcode tests, builds a universal macOS app, verifies the unsigned bundle, and publishes a prerelease ZIP with its SHA-256 checksum.
Pushing a version tag such as `v1.0.1` runs `.github/workflows/release.yml`. The workflow repeats the source and Xcode tests, builds a universal macOS app, verifies the unsigned bundle, and publishes a compressed DMG and fallback ZIP with SHA-256 checksums. The DMG contains the app and an Applications shortcut.

Unsigned previews are intended for development and evaluation. Gatekeeper will identify them as coming from an unidentified developer, and they do not satisfy the production release process below.

Expand Down
54 changes: 54 additions & 0 deletions Scripts/package-unsigned-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/sh
set -eu

if [ "$#" -ne 3 ]; then
echo "Usage: $0 /path/to/Startle.app /path/to/output asset-name" >&2
exit 64
fi

app_path=$1
output_dir=$2
asset_name=$3

if [ ! -d "$app_path" ] || [ ! -f "$app_path/Contents/Info.plist" ]; then
echo "Not a macOS application bundle: $app_path" >&2
exit 66
fi

case "$asset_name" in
*/* | "")
echo "The asset name must be a non-empty file name without slashes." >&2
exit 64
;;
esac

mkdir -p "$output_dir"
output_dir=$(CDPATH= cd "$output_dir" && pwd)
archive_path="$output_dir/$asset_name.zip"
dmg_path="$output_dir/$asset_name.dmg"
staging_root=$(mktemp -d "${TMPDIR:-/tmp}/startle-release.XXXXXX")
staging_dir="$staging_root/Startle"

cleanup() {
rm -rf "$staging_root"
}
trap cleanup EXIT HUP INT TERM

mkdir -p "$staging_dir"
ditto "$app_path" "$staging_dir/Startle.app"
ln -s /Applications "$staging_dir/Applications"

ditto -c -k --sequesterRsrc --keepParent "$app_path" "$archive_path"
hdiutil create \
-volname Startle \
-srcfolder "$staging_dir" \
-ov \
-format UDZO \
"$dmg_path"

for asset_path in "$archive_path" "$dmg_path"; do
shasum -a 256 "$asset_path" > "$asset_path.sha256"
done

echo "Packaged $archive_path"
echo "Packaged $dmg_path"