diff --git a/README.md b/README.md index e5d3d99..17d6c30 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,11 @@ make bundle # AppImage / .app zip / Windows složka s DLL Volitelně CMake: `cmake -S . -B build && cmake --build build`. +Na macOS `make bundle` automaticky vloží aktuální Git commit, aby fungovala +kontrola aktualizací. Při balení ze zdrojů bez `.git` zadejte +`COMMIT= make bundle`. Samotné `make` nadále vytváří vývojový +build bez aktualizací. + Aby stažená aplikace na macOS šla otevřít bez potvrzení v Nastavení, CI potřebuje **Apple Developer Program** a GitHub Secrets: @@ -236,6 +241,11 @@ make bundle # AppImage / .app zip / Windows DLL folder Optional CMake: `cmake -S . -B build && cmake --build build`. +On macOS, `make bundle` embeds the current Git commit so the installed app +can check for updates. When packaging a source export without `.git`, use +`COMMIT= make bundle`. Plain `make` still produces a development +build without updates. + Skipping Gatekeeper on a downloaded macOS build needs an **Apple Developer Program** membership and these GitHub Secrets: diff --git a/scripts/bundle-macos.sh b/scripts/bundle-macos.sh index 41010b7..ffd8432 100755 --- a/scripts/bundle-macos.sh +++ b/scripts/bundle-macos.sh @@ -6,6 +6,18 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" +# An installed .app needs a source identity for the updater, including when +# packaged locally. CI can still supply COMMIT explicitly. Plain `make` +# builds keep their "dev" default. +if [[ -z "${COMMIT:-}" ]]; then + if [[ ! -e "$ROOT/.git" ]] || + ! COMMIT="$(git -C "$ROOT" rev-parse --verify HEAD 2>/dev/null)"; then + echo "error: cannot determine the source commit; set COMMIT when bundling without Git metadata" >&2 + exit 1 + fi +fi +export COMMIT + ARCH="$(uname -m)" case "$ARCH" in arm64|aarch64) ARCH_TAG=arm64 ;; diff --git a/scripts/test-macos-bundle-commit.sh b/scripts/test-macos-bundle-commit.sh new file mode 100755 index 0000000..5ecada7 --- /dev/null +++ b/scripts/test-macos-bundle-commit.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# Exercise build identity at the real bundler entry point without building GTK. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +TMP="$(mktemp -d "${TMPDIR:-/tmp}/maturita-bundle-commit.XXXXXX")" +trap 'rm -rf "$TMP"' EXIT + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +mkdir -p "$TMP/bin" +cat > "$TMP/bin/make" <<'EOF' +#!/usr/bin/env bash +printf '%s\n' "${COMMIT-UNSET}" >> "$MATURITA_TEST_MAKE_LOG" +if [[ "$*" == *" clean" ]]; then + exit 0 +fi +# Stop before packaging, after observing the real build's environment. +exit 97 +EOF +cat > "$TMP/bin/brew" <<'EOF' +#!/bin/sh +echo /unused-homebrew +EOF +cat > "$TMP/bin/uname" <<'EOF' +#!/bin/sh +echo arm64 +EOF +chmod +x "$TMP/bin/make" "$TMP/bin/brew" "$TMP/bin/uname" + +new_fixture() { + mkdir -p "$1/scripts" + cp "$ROOT/scripts/bundle-macos.sh" "$1/scripts/bundle-macos.sh" +} + +run_bundle() { + local fixture="$1" mode="$2" value="${3-}" + MAKE_LOG="$fixture/make.log" + OUTPUT_LOG="$fixture/output.log" + rm -f "$MAKE_LOG" + if ( + unset COMMIT GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE + export PATH="$TMP/bin:$PATH" + export MATURITA_TEST_MAKE_LOG="$MAKE_LOG" + # A source archive must not accidentally inherit an enclosing checkout. + export GIT_CEILING_DIRECTORIES="$TMP" + if [[ "$mode" == supplied ]]; then + export COMMIT="$value" + fi + bash "$fixture/scripts/bundle-macos.sh" + ) > "$OUTPUT_LOG" 2>&1; then + STATUS=0 + else + STATUS=$? + fi +} + +expect_build_commit() { + local expected="$1" actual + [[ "$STATUS" -eq 97 ]] || { + cat "$OUTPUT_LOG" >&2 + fail "expected the stubbed build to run (status $STATUS)" + } + [[ -f "$MAKE_LOG" ]] || fail "make was not called" + actual="$(cat "$MAKE_LOG")" + [[ "$actual" == "$expected"$'\n'"$expected" ]] || + fail "clean/build did not both receive COMMIT=$expected (got '$actual')" +} + +expect_missing_identity() { + [[ "$STATUS" -ne 0 ]] || fail 'missing identity unexpectedly succeeded' + [[ ! -e "$MAKE_LOG" ]] || fail 'missing identity reached make' + grep -q 'COMMIT' "$OUTPUT_LOG" || fail 'missing identity has no COMMIT guidance' +} + +GIT_FIXTURE="$TMP/checkout with spaces" +new_fixture "$GIT_FIXTURE" +git init -q "$GIT_FIXTURE" +git -C "$GIT_FIXTURE" -c user.name='Bundle test' \ + -c user.email='bundle-test@example.invalid' -c commit.gpgsign=false \ + -c core.hooksPath=/dev/null commit --allow-empty -qm 'Fixture commit' +EXPECTED="$(git -C "$GIT_FIXTURE" rev-parse HEAD)" + +run_bundle "$GIT_FIXTURE" unset +expect_build_commit "$EXPECTED" +echo 'PASS: Git checkout identity with spaces in its path' + +run_bundle "$GIT_FIXTURE" supplied '' +expect_build_commit "$EXPECTED" +echo 'PASS: empty COMMIT falls back to checkout identity' + +ARCHIVE_FIXTURE="$TMP/source archive" +new_fixture "$ARCHIVE_FIXTURE" +EXPLICIT=0123456789abcdef0123456789abcdef01234567 +run_bundle "$ARCHIVE_FIXTURE" supplied "$EXPLICIT" +expect_build_commit "$EXPLICIT" +echo 'PASS: explicit COMMIT works without Git metadata' + +run_bundle "$ARCHIVE_FIXTURE" supplied dev +expect_build_commit dev +echo 'PASS: explicit development identity is preserved' + +run_bundle "$ARCHIVE_FIXTURE" unset +expect_missing_identity +echo 'PASS: source archive without COMMIT fails before building' + +NESTED_FIXTURE="$GIT_FIXTURE/exported sources" +new_fixture "$NESTED_FIXTURE" +run_bundle "$NESTED_FIXTURE" unset +expect_missing_identity +echo 'PASS: source archive does not inherit an unrelated parent Git identity' diff --git a/src/maturita.h b/src/maturita.h index dbfb445..c0402b1 100644 --- a/src/maturita.h +++ b/src/maturita.h @@ -23,7 +23,8 @@ #define APP_ID "org.maturita.Maturita" /* Published builds bake in the commit they were made from (see COMMIT in the * Makefile), which is what the updater compares against the build published - * in the repository. Local builds stay at "dev" and never offer an update. */ + * in the repository. Plain source builds stay at "dev" and never offer an + * update; the macOS bundler supplies the checkout commit for local packages. */ #ifndef APP_COMMIT #define APP_COMMIT "dev" #endif diff --git a/src/update.c b/src/update.c index 6cca1ed..6bfe2c8 100644 --- a/src/update.c +++ b/src/update.c @@ -196,7 +196,7 @@ static gboolean same_commit(const char *a, const char *b) { return ok; } -/* Only builds published by CI carry a commit; see APP_COMMIT in maturita.h. */ +/* Packaged builds carry a source commit; see APP_COMMIT in maturita.h. */ static gboolean is_published_build(void) { return g_ascii_isxdigit(APP_COMMIT[0]) && strlen(APP_COMMIT) >= 7; }