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
7 changes: 0 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,3 @@ jobs:
env:
RUN_LAUNCH_SERVICES_TESTS: "1"
run: ./scripts/test.sh

- name: Upload development build
uses: actions/upload-artifact@v4
with:
name: JLabTextQuickLook-development
path: build/JLabTextQuickLook.app
if-no-files-found: error
139 changes: 139 additions & 0 deletions .github/workflows/notarized-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Future notarized binary release

on:
workflow_dispatch:
inputs:
tag:
description: Existing source-release tag to build
required: true
type: string

permissions:
contents: write

defaults:
run:
shell: zsh -eo pipefail {0}

jobs:
release:
runs-on: macos-15
timeout-minutes: 45

steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}

- name: Validate tag and required secrets
env:
TAG: ${{ inputs.tag }}
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
run: |
version=$(/usr/libexec/PlistBuddy -c \
'Print :CFBundleShortVersionString' App-Info.plist)
[[ "$TAG" == "v$version" ]] || {
echo "Tag $TAG does not match bundle version $version" >&2
exit 1
}
for name in MACOS_CERTIFICATE MACOS_CERTIFICATE_PASSWORD \
APPLE_TEAM_ID APPLE_API_ISSUER_ID APPLE_API_KEY_ID \
APPLE_API_PRIVATE_KEY; do
[[ -n "${(P)name}" ]] || {
echo "Required secret $name is missing" >&2
exit 1
}
done

- name: Import Developer ID certificate
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
certificate="$RUNNER_TEMP/developer-id.p12"
keychain="$RUNNER_TEMP/release.keychain-db"
keychain_password=$(openssl rand -hex 24)
print -r -- "$MACOS_CERTIFICATE" | base64 --decode > "$certificate"
security create-keychain -p "$keychain_password" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$keychain_password" "$keychain"
security import "$certificate" -k "$keychain" \
-P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$keychain_password" "$keychain"
security list-keychains -d user -s "$keychain"
identity=$(security find-identity -v -p codesigning "$keychain" |
sed -n 's/.*"\\(Developer ID Application:.*\\)"/\\1/p' | head -1)
[[ -n "$identity" ]] || {
echo "Developer ID Application identity not found" >&2
exit 1
}
print "SIGN_IDENTITY=$identity" >> "$GITHUB_ENV"

- name: Build and verify signed application
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
./build.sh
codesign --verify --deep --strict --verbose=2 \
build/JLabTextQuickLook.app
signed_team=$(codesign -dv --verbose=4 \
build/JLabTextQuickLook.app 2>&1 |
sed -n 's/^TeamIdentifier=//p')
[[ "$signed_team" == "$APPLE_TEAM_ID" ]] || {
echo "Signed TeamIdentifier $signed_team does not match APPLE_TEAM_ID" >&2
exit 1
}
./scripts/test.sh

- name: Notarize and staple application
env:
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
run: |
api_key="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY_ID.p8"
print -r -- "$APPLE_API_PRIVATE_KEY" > "$api_key"
ditto -c -k --keepParent \
build/JLabTextQuickLook.app "$RUNNER_TEMP/JLabTextQuickLook.zip"
xcrun notarytool submit "$RUNNER_TEMP/JLabTextQuickLook.zip" \
--key "$api_key" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--wait
xcrun stapler staple build/JLabTextQuickLook.app
xcrun stapler validate build/JLabTextQuickLook.app

- name: Create, notarize, and validate DMG
env:
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: |
api_key="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY_ID.p8"
./scripts/create-dmg.sh
xcrun notarytool submit dist/JLabTextQuickLook.dmg \
--key "$api_key" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--wait
xcrun stapler staple dist/JLabTextQuickLook.dmg
xcrun stapler validate dist/JLabTextQuickLook.dmg
spctl --assess --type open --context context:primary-signature \
--verbose=2 dist/JLabTextQuickLook.dmg
shasum -a 256 dist/JLabTextQuickLook.dmg \
> dist/JLabTextQuickLook.dmg.sha256

- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
run: |
gh release upload "$TAG" \
dist/JLabTextQuickLook.dmg \
dist/JLabTextQuickLook.dmg.sha256 \
--clobber
107 changes: 9 additions & 98 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Signed release
name: Source release

on:
push:
Expand All @@ -13,123 +13,34 @@ defaults:
shell: zsh -eo pipefail {0}

jobs:
release:
validate-and-release:
runs-on: macos-15
timeout-minutes: 45
timeout-minutes: 20

steps:
- uses: actions/checkout@v4

- name: Validate tag and required secrets
- name: Validate tag matches application version
env:
TAG: ${{ github.ref_name }}
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
run: |
version=$(/usr/libexec/PlistBuddy -c \
'Print :CFBundleShortVersionString' App-Info.plist)
[[ "$TAG" == "v$version" ]] || {
echo "Tag $TAG does not match bundle version $version" >&2
exit 1
}
for name in MACOS_CERTIFICATE MACOS_CERTIFICATE_PASSWORD \
APPLE_TEAM_ID APPLE_API_ISSUER_ID APPLE_API_KEY_ID \
APPLE_API_PRIVATE_KEY; do
[[ -n "${(P)name}" ]] || {
echo "Required secret $name is missing" >&2
exit 1
}
done

- name: Import Developer ID certificate
- name: Test universal build and Quick Look registration
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
certificate="$RUNNER_TEMP/developer-id.p12"
keychain="$RUNNER_TEMP/release.keychain-db"
keychain_password=$(openssl rand -hex 24)
print -r -- "$MACOS_CERTIFICATE" | base64 --decode > "$certificate"
security create-keychain -p "$keychain_password" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$keychain_password" "$keychain"
security import "$certificate" -k "$keychain" \
-P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$keychain_password" "$keychain"
security list-keychains -d user -s "$keychain"
identity=$(security find-identity -v -p codesigning "$keychain" |
sed -n 's/.*"\\(Developer ID Application:.*\\)"/\\1/p' | head -1)
[[ -n "$identity" ]] || {
echo "Developer ID Application identity not found" >&2
exit 1
}
print "SIGN_IDENTITY=$identity" >> "$GITHUB_ENV"

- name: Build and verify signed application
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
./build.sh
codesign --verify --deep --strict --verbose=2 \
build/JLabTextQuickLook.app
signed_team=$(codesign -dv --verbose=4 \
build/JLabTextQuickLook.app 2>&1 |
sed -n 's/^TeamIdentifier=//p')
[[ "$signed_team" == "$APPLE_TEAM_ID" ]] || {
echo "Signed TeamIdentifier $signed_team does not match APPLE_TEAM_ID" >&2
exit 1
}
./scripts/test.sh

- name: Notarize and staple application
env:
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
run: |
api_key="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY_ID.p8"
print -r -- "$APPLE_API_PRIVATE_KEY" > "$api_key"
ditto -c -k --keepParent \
build/JLabTextQuickLook.app "$RUNNER_TEMP/JLabTextQuickLook.zip"
xcrun notarytool submit "$RUNNER_TEMP/JLabTextQuickLook.zip" \
--key "$api_key" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--wait
xcrun stapler staple build/JLabTextQuickLook.app
xcrun stapler validate build/JLabTextQuickLook.app

- name: Create, notarize, and validate DMG
env:
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: |
api_key="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY_ID.p8"
./scripts/create-dmg.sh
xcrun notarytool submit dist/JLabTextQuickLook.dmg \
--key "$api_key" \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_ISSUER_ID" \
--wait
xcrun stapler staple dist/JLabTextQuickLook.dmg
xcrun stapler validate dist/JLabTextQuickLook.dmg
spctl --assess --type open --context context:primary-signature \
--verbose=2 dist/JLabTextQuickLook.dmg
shasum -a 256 dist/JLabTextQuickLook.dmg \
> dist/JLabTextQuickLook.dmg.sha256
RUN_LAUNCH_SERVICES_TESTS: "1"
run: ./scripts/test.sh

- name: Publish GitHub release
- name: Create source-only GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${GITHUB_REF_NAME}" \
dist/JLabTextQuickLook.dmg \
dist/JLabTextQuickLook.dmg.sha256 \
gh release create "$GITHUB_REF_NAME" \
--verify-tag \
--generate-notes \
--title "JLabTextQuickLook ${GITHUB_REF_NAME#v}"
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ Versioning.
- Prepare the project for public distribution.
- Build a universal Apple silicon and Intel application.
- Set the minimum supported operating system to macOS 13.
- Add automated validation and signed, notarized DMG releases.
- Add one-command, user-level installation and uninstallation from source.
- Publish a source-only GitHub release with no unsigned binary downloads.
- Preserve notarized binary release automation for possible future use.
- Register `.inp`, `.hist`, `.report`, `.dat`, `.gen`, and `.geni` as plain
text formats for Finder Quick Look.

Expand Down
Loading
Loading