Skip to content

Add app picker so block sets can quit apps during a block #15

Add app picker so block sets can quit apps during a block

Add app picker so block sets can quit apps during a block #15

Workflow file for this run

name: Release
on:
push:
tags: [ 'v*' ]
workflow_dispatch:
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
jobs:
build-macos:
name: Build & sign macOS
runs-on: macos-15
timeout-minutes: 60
env:
SCHEME: LockIn
APP_NAME: LockIn
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install XcodeGen
run: brew install xcodegen
- name: Derive version from tag
id: version
run: |
TAG="${GITHUB_REF_NAME:-v0.0.0}"
echo "marketing=${TAG#v}" >> "$GITHUB_OUTPUT"
echo "build=${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"
- name: Import Apple signing certificate
env:
AGENT_TEMPDIRECTORY: ${{ runner.temp }}
MAC_CERT_P12: ${{ secrets.MAC_CERT_P12 }}
MAC_CERT_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }}
run: |
KEYCHAIN_PATH="$AGENT_TEMPDIRECTORY/buildagent.keychain"
CERT_PATH="$AGENT_TEMPDIRECTORY/mac_cert.p12"
echo "$MAC_CERT_P12" | base64 --decode > "$CERT_PATH"
security create-keychain -p "" "$KEYCHAIN_PATH"
security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$MAC_CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security list-keychains -s "$KEYCHAIN_PATH"
security unlock-keychain -p "" "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" "$KEYCHAIN_PATH"
- name: Debug signing identities
env:
AGENT_TEMPDIRECTORY: ${{ runner.temp }}
run: security find-identity -v -p codesigning "$AGENT_TEMPDIRECTORY/buildagent.keychain"
- name: Generate project
run: xcodegen generate
- name: Build & sign (Developer ID)
env:
AGENT_TEMPDIRECTORY: ${{ runner.temp }}
MARKETING_VERSION: ${{ steps.version.outputs.marketing }}
BUILD_VERSION: ${{ steps.version.outputs.build }}
run: |
set -euo pipefail
KEYCHAIN_PATH="$AGENT_TEMPDIRECTORY/buildagent.keychain"
SIGN_IDENTITY="$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" \
| awk -v team="$APPLE_TEAM_ID" '/Developer ID Application/ && $0 ~ team {print $2; exit}')"
if [ -z "$SIGN_IDENTITY" ]; then
echo "No Developer ID Application identity for team $APPLE_TEAM_ID in build keychain"
exit 1
fi
echo "Using signing identity SHA: $SIGN_IDENTITY"
xcodebuild -project "$APP_NAME.xcodeproj" -scheme "$SCHEME" \
-configuration Release -derivedDataPath build \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$SIGN_IDENTITY" \
CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \
OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime --keychain $KEYCHAIN_PATH" \
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
MARKETING_VERSION="$MARKETING_VERSION" \
CURRENT_PROJECT_VERSION="$BUILD_VERSION" \
build
APP_PATH="build/Build/Products/Release/$APP_NAME.app"
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
- name: Prepare signed artifact
run: |
set -euo pipefail
APP_PATH="build/Build/Products/Release/$APP_NAME.app"
mkdir -p .artifacts
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" ".artifacts/$APP_NAME-macos-signed.zip"
- name: Upload signed artifact
uses: actions/upload-artifact@v7
with:
name: lockin-macos-signed
path: .artifacts/LockIn-macos-signed.zip
retention-days: 5
notarize-macos:
name: Notarize & publish macOS
runs-on: macos-15
timeout-minutes: 60
needs: build-macos
env:
APP_NAME: LockIn
steps:
- name: Download signed artifact
uses: actions/download-artifact@v8
with:
name: lockin-macos-signed
- name: Notarize, staple, and package DMG
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
set -euo pipefail
SIGNED_ZIP="$(find . -name "$APP_NAME-macos-signed.zip" -print -quit)"
if [ -z "$SIGNED_ZIP" ]; then
echo "Signed zip not found"; find . -maxdepth 3 -name "*.zip" -print; exit 1
fi
rm -rf .notarize && mkdir -p .notarize
ditto -x -k "$SIGNED_ZIP" .notarize
APP_PATH="$(find .notarize -maxdepth 3 -name "*.app" -print -quit)"
if [ -z "$APP_PATH" ]; then echo "No .app in payload"; exit 1; fi
DMG_ROOT=".dmgroot"; DMG_NAME="$APP_NAME-macos.dmg"
rm -rf "$DMG_ROOT" && mkdir -p "$DMG_ROOT"
cp -R "$APP_PATH" "$DMG_ROOT/"
ln -s /Applications "$DMG_ROOT/Applications"
rm -f "$DMG_NAME"
hdiutil detach "/Volumes/$APP_NAME" -force || true
hdiutil create -volname "$APP_NAME" -srcfolder "$DMG_ROOT" -ov -format UDZO "$DMG_NAME"
SUBMIT_JSON="$(xcrun notarytool submit "$DMG_NAME" \
--apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD" --output-format json)"
echo "$SUBMIT_JSON"
REQUEST_ID="$(echo "$SUBMIT_JSON" | /usr/bin/python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")"
echo "Notarization request id: $REQUEST_ID"
MAX_ATTEMPTS=60; SLEEP_SECONDS=60; ATTEMPT=1; STATUS="Pending"
while [ "$ATTEMPT" -le "$MAX_ATTEMPTS" ]; do
LOG_JSON="$(xcrun notarytool log "$REQUEST_ID" \
--apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_PASSWORD" --output-format json 2>/dev/null || true)"
STATUS="Pending"
if [ -n "$LOG_JSON" ] && { [[ "$LOG_JSON" == \{* ]] || [[ "$LOG_JSON" == \[* ]]; }; then
STATUS="$(/usr/bin/python3 -c 'import json,sys; print(json.load(sys.stdin).get("status","Pending"))' <<<"$LOG_JSON")"
fi
echo "Notarization status ($ATTEMPT/$MAX_ATTEMPTS): $STATUS"
[ "$STATUS" = "Accepted" ] && break
if [ "$STATUS" = "Invalid" ]; then echo "$LOG_JSON"; exit 1; fi
sleep "$SLEEP_SECONDS"; ATTEMPT=$((ATTEMPT + 1))
done
if [ "$STATUS" != "Accepted" ]; then
echo "Notarization did not finish. Request id: $REQUEST_ID"; exit 1
fi
xcrun stapler staple "$APP_PATH"
xcrun stapler staple "$DMG_NAME"
mv "$DMG_NAME" "$APP_NAME.dmg"
- name: Publish release
uses: softprops/action-gh-release@v3
with:
files: ${{ env.APP_NAME }}.dmg
generate_release_notes: true
publish-homebrew:
name: Publish Homebrew cask
runs-on: ubuntu-latest
needs: notarize-macos
timeout-minutes: 15
steps:
- name: Derive version from tag
id: version
run: |
TAG="${GITHUB_REF_NAME:-v0.0.0}"
echo "marketing=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Download published DMG and compute sha256
id: hash
run: |
set -euo pipefail
VERSION="${{ steps.version.outputs.marketing }}"
URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}/LockIn.dmg"
echo "Fetching $URL"
for i in 1 2 3 4 5 6; do
if curl -fL --retry 3 -o LockIn.dmg "$URL"; then break; fi
echo "asset not ready yet (attempt $i), waiting..."; sleep 10
done
test -f LockIn.dmg
echo "sha256=$(shasum -a 256 LockIn.dmg | awk '{print $1}')" >> "$GITHUB_OUTPUT"
- name: Checkout tap repo
uses: actions/checkout@v7
with:
repository: HumbleBee14/homebrew-lockin
token: ${{ secrets.TAP_GITHUB_TOKEN }}
path: tap
- name: Update and commit cask
run: |
set -euo pipefail
VERSION="${{ steps.version.outputs.marketing }}"
SHA="${{ steps.hash.outputs.sha256 }}"
cd tap
sed -i \
-e "s/^ version \".*\"/ version \"${VERSION}\"/" \
-e "s/^ sha256 \".*\"/ sha256 \"${SHA}\"/" \
Casks/lockin.rb
if git diff --quiet; then
echo "Cask already up to date for ${VERSION}; nothing to commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -am "lockin ${VERSION}"
git push