From 50410488a17c59fc8887eaeec999f855b3e552a2 Mon Sep 17 00:00:00 2001 From: tracer Date: Sun, 19 Apr 2026 15:24:58 +0200 Subject: [PATCH] docs: update install docs and release automation --- .gitignore | 1 + CHANGELOG.md | 5 ++ README.md | 102 ++++++++------------------------- scripts/build_release.sh | 2 + scripts/publish_release.sh | 2 +- scripts/update_homebrew_tap.sh | 62 ++++++++++++++++++++ 6 files changed, 96 insertions(+), 78 deletions(-) create mode 100755 scripts/update_homebrew_tap.sh diff --git a/.gitignore b/.gitignore index 9f00021..353f398 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/ Build/ dist/ .signing.env +homebrew-tap/ diff --git a/CHANGELOG.md b/CHANGELOG.md index d2eb71e..76d2a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 26.1.6 +- Publish Gitea releases as stable by default instead of pre-releases. +- Update the Homebrew tap automatically after each successful release by rewriting the cask version and DMG checksum, then pushing the tap repo. +- Simplified the README for end users by adding clear install options and trimming internal release-engineering details. +- Ignore the local `homebrew-tap/` checkout in the main app repository. ## 26.1.3 - Fixed version handling for changelogs. diff --git a/README.md b/README.md index f194ed4..1b63944 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ iKeyMon is a native macOS app written in SwiftUI that provides live monitoring f - Automatic refreshes: - Ping every 10 seconds - Server info every 60 seconds +- Preferences dialog - Built-in Sparkle updater (automatic checks, downloads, and relaunch once a signed release is available) +- macOS notifications if servers or services become unavailable - Organized layout using tabs: General / Resources / Services - Stores API keys securely in the macOS Keychain - Native macOS look & feel using SwiftUI @@ -24,11 +26,30 @@ iKeyMon is a native macOS app written in SwiftUI that provides live monitoring f ## 🛠️ Planned Features -- Preferences dialog -- macOS notifications if servers or services become unavailable -- Optional iOS support if there is demand +- iOS support is a possibility, but it is unclear if or when that will happen -## 🚀 How to Run +## 📦 Install + +### Option 1: Download the app + +Download the latest release from: + +https://git.24unix.net/tracer/iKeyMon/releases + +Then open the DMG and move `iKeyMon.app` to your `Applications` folder. + +### Option 2: Install with Homebrew + +```bash +brew tap tracer/tap https://git.24unix.net/tracer/homebrew-tap.git +brew install --cask ikeymon +``` + +### Option 3: Build it yourself + +Clone the repository, open it in Xcode, and run the app locally on macOS 14+. + +## 🚀 How to Build Clone the repo and open it in [Xcode](https://developer.apple.com/xcode/). You can build and run the app on macOS 14+. @@ -38,79 +59,6 @@ cd iKeyMon open iKeyMon.xcodeproj ``` -### Local release build - -Use the helper script to produce distributables in `dist/`: - -```bash -./scripts/build_release.sh -``` - -It cleans previous artifacts, builds the `Release` configuration, and drops both `iKeyMon-.zip` and `iKeyMon-.dmg` into the `dist` folder (ignored by git). To enable codesigning + notarization, copy `signing.env.example` to `.signing.env`, fill in your Developer ID identity, Apple ID, team ID, and app-specific password. The script sources that file locally (it remains gitignored) and performs signing/notarization when the values are present. - -To auto-publish the artifacts as a Gitea release, extend `.signing.env` with: - -``` -GITEA_TOKEN="..." -GITEA_OWNER="tracer" -GITEA_REPO="iKeyMon" -# optional: GITEA_API_BASE="https://git.24unix.net/api/v1" -# optional: GITEA_TARGET_COMMIT="master" -# optional: GITEA_PRERELEASE="false" # defaults to true until preferences are done -# optional Sparkle feed helpers: -# SPARKLE_EDDSA_KEY_FILE="$HOME/.config/Sparkle/iKeyMon.key" -# SPARKLE_DOWNLOAD_BASE_TEMPLATE="https://git.24unix.net/tracer/iKeyMon/releases/download/v{{VERSION}}" -# If you prefer SPARKLE_DOWNLOAD_BASE_URL, it will automatically append `/v` for you. -# SPARKLE_APPCAST_OUTPUT="$ROOT_DIR/Sparkle/appcast.xml" # default -``` - -`GITEA_TARGET_COMMIT` defaults to the current `HEAD` commit, so overriding it lets you publish from another branch if needed. Whenever those variables are set, the script will create (or reuse) tag `v` and upload both ZIP and DMG as release assets automatically. - -If you re-run the release script for the same version, it removes any existing assets with the same filenames before uploading, so you never end up with duplicate ZIP/DMG files on the release page. - -### Sparkle updates - -iKeyMon uses [Sparkle](https://sparkle-project.org/) for macOS-safe updates. - -1. Generate an EdDSA key pair once (`./Packages/Sparkle/bin/generate_keys`). Store the private key on-disk (for example `~/.config/Sparkle/iKeyMon.key`, which the build script expects) and copy the public key into the `SUPublicEDKey` entry (see Info.plist notes below). -2. `./scripts/build_release.sh` signs the ZIP with Sparkle’s `sign_update` tool and invokes `generate_appcast` automatically when the Sparkle variables are present. The generated feed is written to `Sparkle/appcast.xml`, so commit that file after every release. Point `SPARKLE_DOWNLOAD_BASE_TEMPLATE` at your release-download prefix (e.g. `https://git.24unix.net/tracer/iKeyMon/releases/download/v{{VERSION}}`) so the generated URLs already match Gitea’s asset paths. The feed stays inside the repo (it is not uploaded as a release asset). -3. Set `SUFeedURL` in Info.plist (or the corresponding build setting) to the raw URL of `Sparkle/appcast.xml` inside this repo (e.g. `https://git.24unix.net/tracer/iKeyMon/raw/branch/master/Sparkle/appcast.xml`). - -Preferences expose Sparkle’s built-in toggles for “Automatically check” and “Automatically download”, and the toolbar button simply calls Sparkle’s “Check for Updates…” sheet. - -> `./scripts/build_release.sh` will call `generate_appcast` for you when `SPARKLE_EDDSA_KEY_FILE` and `SPARKLE_DOWNLOAD_BASE_TEMPLATE` (or `SPARKLE_DOWNLOAD_BASE_URL`) are set. It tries to locate Sparkle’s CLI in DerivedData automatically, but you can override the path via `SPARKLE_GENERATE_APPCAST`. The resulting feed is written to `SPARKLE_APPCAST_OUTPUT` (defaults to `Sparkle/appcast.xml`). - -> Build settings include `INFOPLIST_KEY_SUFeedURL` and `INFOPLIST_KEY_SUPublicEDKey`. Make sure to fill both before shipping a build so Sparkle knows where to fetch updates and how to verify them. - -#### Debugging Sparkle updates - -Launch the shipped app via CLI with `SPARKLE_VERBOSE_LOGGING=1` to mirror Sparkle’s activity in stdout/stderr: - -```bash -SPARKLE_VERBOSE_LOGGING=1 /Applications/iKeyMon.app/Contents/MacOS/iKeyMon -``` - -Sparkle’s installer helper runs in a separate `SUPipedBinary` process. If the installer fails, collect additional details with `log show --process SUPipedBinary --last 5m`. - -### Automated release push - -If you want `git push origin master` to build/sign/notarize/upload automatically, enable the provided pre-push hook: - -```bash -git config core.hooksPath hooks -``` - -The hook (see `hooks/pre-push`) watches for pushes that include `refs/heads/master`, automatically bumps `marketing_version` (incrementing the last component), runs `scripts/build_release.sh`, stages `version.json`, `iKeyMon.xcodeproj/project.pbxproj`, and `Sparkle/appcast.xml`, then creates a commit `chore: release `. It performs its own `git push` behind the scenes and cancels the original push command so you don't upload the same refs twice—once you see “Release … pushed. Original push cancelled”, you're done (Git will report the original push failed; that's expected). To skip the automation temporarily, prepend `SKIP_RELEASE=1` to your `git push` command. - -The bumping logic lives in `scripts/bump_version.sh` (feel free to run it manually if you need to create a release without pushing). - -### Versioning workflow - -- The canonical marketing version lives in `version.json` and follows the format `YY.major.minor` (example: `26.1.2`). Update that file manually whenever you cut a new release branch. -- The build number is derived automatically from the git commit count on the current branch (you can override it by exporting `BUILD_NUMBER` before running the script if needed). -- Run `./scripts/sync_version.sh` anytime after editing `version.json` (the release script already calls it). The helper updates `MARKETING_VERSION` and `CURRENT_PROJECT_VERSION` inside `iKeyMon.xcodeproj`, keeping Xcode, the app bundle, and release artifacts in sync. -- `scripts/build_release.sh` reads the same `version.json` for naming the generated ZIP/DMG, so the artifact names, Info.plist values, and UI displays all stay aligned. - ## 📦 License MIT — see [LICENSE](LICENSE) for details. diff --git a/scripts/build_release.sh b/scripts/build_release.sh index 58523e3..1c1ec85 100755 --- a/scripts/build_release.sh +++ b/scripts/build_release.sh @@ -216,8 +216,10 @@ generate_appcast if [[ -n "${GITEA_TOKEN:-}" && -n "${GITEA_OWNER:-}" && -n "${GITEA_REPO:-}" ]]; then "$ROOT_DIR/scripts/publish_release.sh" "$VERSION" "$ARTIFACTS_DIR/$ZIP_NAME" "$ARTIFACTS_DIR/$DMG_NAME" + "$ROOT_DIR/scripts/update_homebrew_tap.sh" "$VERSION" "$ARTIFACTS_DIR/$DMG_NAME" else echo "ℹ️ Skipping Gitea release publishing (GITEA_* variables not fully set)." + echo "ℹ️ Skipping Homebrew tap update because release publishing was skipped." fi echo "✅ Build complete. Artifacts:" diff --git a/scripts/publish_release.sh b/scripts/publish_release.sh index d3660d4..84b1825 100755 --- a/scripts/publish_release.sh +++ b/scripts/publish_release.sh @@ -49,7 +49,7 @@ if [[ -z "$CHANGELOG_BODY" ]]; then CHANGELOG_BODY="See commit history for details." fi -PRERELEASE_FLAG="${GITEA_PRERELEASE:-true}" +PRERELEASE_FLAG="${GITEA_PRERELEASE:-false}" create_payload="$(jq -n \ --arg tag "$RELEASE_TAG" \ diff --git a/scripts/update_homebrew_tap.sh b/scripts/update_homebrew_tap.sh new file mode 100755 index 0000000..7a55fe8 --- /dev/null +++ b/scripts/update_homebrew_tap.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION="${1:?usage: update_homebrew_tap.sh }" +DMG_PATH="${2:?usage: update_homebrew_tap.sh }" +TAP_DIR="${HOMEBREW_TAP_DIR:-$ROOT_DIR/homebrew-tap}" +CASK_FILE="$TAP_DIR/Casks/ikeymon.rb" + +if [[ ! -d "$TAP_DIR/.git" ]]; then + echo "❌ Homebrew tap repo not found at $TAP_DIR" + exit 1 +fi + +if [[ ! -f "$CASK_FILE" ]]; then + echo "❌ Homebrew cask file not found at $CASK_FILE" + exit 1 +fi + +if [[ ! -f "$DMG_PATH" ]]; then + echo "❌ DMG artifact not found at $DMG_PATH" + exit 1 +fi + +if [[ -n "$(git -C "$TAP_DIR" status --porcelain)" ]]; then + echo "❌ Homebrew tap repo has uncommitted changes: $TAP_DIR" + exit 1 +fi + +SHA256="$(shasum -a 256 "$DMG_PATH" | awk '{print $1}')" + +python3 - <<'PY' "$CASK_FILE" "$VERSION" "$SHA256" +from pathlib import Path +import re +import sys + +cask_path = Path(sys.argv[1]) +version = sys.argv[2] +sha256 = sys.argv[3] +content = cask_path.read_text(encoding="utf-8") + +content, version_count = re.subn(r'version "[^"]+"', f'version "{version}"', content, count=1) +content, sha_count = re.subn(r'sha256 "[^"]+"', f'sha256 "{sha256}"', content, count=1) + +if version_count != 1 or sha_count != 1: + raise SystemExit("Failed to update version or sha256 in cask file") + +cask_path.write_text(content, encoding="utf-8") +PY + +ruby -c "$CASK_FILE" >/dev/null + +if git -C "$TAP_DIR" diff --quiet -- "$CASK_FILE"; then + echo "ℹ️ Homebrew tap already points to iKeyMon ${VERSION}" + exit 0 +fi + +git -C "$TAP_DIR" add "$CASK_FILE" +git -C "$TAP_DIR" commit -m "cask: update ikeymon to ${VERSION}" +git -C "$TAP_DIR" push origin master + +echo "✅ Updated Homebrew tap to iKeyMon ${VERSION}"