Skip to content
Open
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
54 changes: 43 additions & 11 deletions Launch RepoPrompt CE.command
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -uo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONDUCTOR="$ROOT_DIR/conductor"
APP_ARGS=("$@")
LAUNCHER_ADHOC_SIGNING=0

if ! command -v python3 >/dev/null 2>&1; then
echo "RepoPrompt CE's safe coordinated launcher requires Python 3."
Expand All @@ -21,23 +22,39 @@ elif [[ ! -x "$CONDUCTOR" ]]; then
exit 1
fi

configure_debug_signing() {
if [[ -n "${SIGN_IDENTITY:-}" || "${ALLOW_ADHOC_SIGNING:-0}" == "1" || "${ALLOW_ADHOC_SIGNING:-0}" == "true" ]]; then
return 0
fi

local apple_development_identity
apple_development_identity="$(security find-identity -v -p codesigning 2>/dev/null | awk -F'"' '/"Apple Development: / { print $2; exit }' || true)"
if [[ -n "$apple_development_identity" ]]; then
return 0
fi

export ALLOW_ADHOC_SIGNING=1
LAUNCHER_ADHOC_SIGNING=1
}

launch_app() {
echo
echo "Building and relaunching RepoPrompt CE..."
echo "This run becomes the active launch; any older build or launch jobs still in flight are canceled."
if (( LAUNCHER_ADHOC_SIGNING )); then
echo "No Apple Development signing identity was found, so this launcher is using explicit ad-hoc debug signing."
echo "Debug secure storage will be in-memory; saved API keys and secure permission changes will not persist across app launches."
fi
echo
local launch_log
launch_log="$(mktemp -t repoprompt-ce-launch)"
local relaunch_rc=0
if (( ${#APP_ARGS[@]} > 0 )); then
if "$CONDUCTOR" app relaunch -- "${APP_ARGS[@]}"; then
echo
echo "RepoPrompt CE has been relaunched."
else
echo
echo "RepoPrompt CE was not relaunched."
echo "Check the result above to see whether the build failed or this run was canceled/replaced."
echo "If the build failed, fix the errors (or let in-flight edits settle), then press r to retry."
echo "Press s to check the current app and job state."
fi
elif "$CONDUCTOR" app relaunch; then
"$CONDUCTOR" app relaunch -- "${APP_ARGS[@]}" 2>&1 | tee "$launch_log" || relaunch_rc=${PIPESTATUS[0]}
else
"$CONDUCTOR" app relaunch 2>&1 | tee "$launch_log" || relaunch_rc=${PIPESTATUS[0]}
fi
if (( relaunch_rc == 0 )); then
echo
echo "RepoPrompt CE has been relaunched."
else
Expand All @@ -46,7 +63,21 @@ launch_app() {
echo "Check the result above to see whether the build failed or this run was canceled/replaced."
echo "If the build failed, fix the errors (or let in-flight edits settle), then press r to retry."
echo "Press s to check the current app and job state."
if grep -q "Debug ad-hoc signing is disabled by default" "$launch_log"; then
echo
echo "Debug signing was refused even though this launcher tried to configure it automatically."
echo "Run the same debug app from Terminal with explicit ad-hoc signing:"
echo
echo " ALLOW_ADHOC_SIGNING=1 ./conductor app relaunch"
echo
echo "Ad-hoc debug builds use in-memory secure storage, so saved API keys and secure"
echo "permission changes do not persist across launches. For persistent debug"
echo "Keychain storage, pass a stable Apple Development identity explicitly:"
echo
echo " SIGN_IDENTITY=\"Apple Development: Your Name (TEAMID)\" ./conductor app relaunch"
fi
fi
rm -f "$launch_log"
}

show_status() {
Expand Down Expand Up @@ -112,6 +143,7 @@ echo "Project: $ROOT_DIR"
echo "Mode: coordinated (builds and launches run through the dev daemon)"

cd "$ROOT_DIR" || exit 1
configure_debug_signing
launch_app

while true; do
Expand Down
41 changes: 33 additions & 8 deletions Scripts/patch_keyboard_shortcuts_resource_lookup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ RUN_WITHOUT_GITHUB_TOKENS="${REPOPROMPT_RUN_WITHOUT_GITHUB_TOKENS:-$SCRIPT_DIR/r
SWIFTPM_SCRATCH_PATH="${REPOPROMPT_SWIFTPM_SCRATCH_PATH:-$ROOT_DIR/.build}"
CHECKOUT_DIR="$SWIFTPM_SCRATCH_PATH/checkouts/KeyboardShortcuts"
UTILITIES_FILE="$CHECKOUT_DIR/Sources/KeyboardShortcuts/Utilities.swift"
PATCH_FILE="$SCRIPT_DIR/patches/keyboardshortcuts-2.3.0-resource-lookup.patch"
RECORDER_FILE="$CHECKOUT_DIR/Sources/KeyboardShortcuts/Recorder.swift"
RESOURCE_PATCH_FILE="$SCRIPT_DIR/patches/keyboardshortcuts-2.3.0-resource-lookup.patch"
PREVIEW_PATCH_FILE="$SCRIPT_DIR/patches/keyboardshortcuts-2.3.0-remove-previews.patch"
EXPECTED_VERSION="2.3.0"
EXPECTED_REVISION="045cf174010beb335fa1d2567d18c057b8787165"
PATCH_MARKER="RepoPromptKeyboardShortcutsResourceLookupV1"
Expand All @@ -29,7 +31,8 @@ run() {
}

[[ -n "$ROOT_DIR" ]] || fail "Usage: $0 <repo-root>"
[[ -f "$PATCH_FILE" ]] || fail "Missing KeyboardShortcuts resource lookup patch: $PATCH_FILE"
[[ -f "$RESOURCE_PATCH_FILE" ]] || fail "Missing KeyboardShortcuts resource lookup patch: $RESOURCE_PATCH_FILE"
[[ -f "$PREVIEW_PATCH_FILE" ]] || fail "Missing KeyboardShortcuts preview patch: $PREVIEW_PATCH_FILE"

if [[ ! -f "$UTILITIES_FILE" ]]; then
run "$RUN_WITHOUT_GITHUB_TOKENS" swift package \
Expand All @@ -38,6 +41,7 @@ if [[ ! -f "$UTILITIES_FILE" ]]; then
resolve
fi
[[ -f "$UTILITIES_FILE" ]] || fail "Could not locate KeyboardShortcuts Utilities.swift after package resolution: $UTILITIES_FILE"
[[ -f "$RECORDER_FILE" ]] || fail "Could not locate KeyboardShortcuts Recorder.swift after package resolution: $RECORDER_FILE"

python3 - "$ROOT_DIR/Package.resolved" "$EXPECTED_VERSION" "$EXPECTED_REVISION" <<'PY'
import json
Expand Down Expand Up @@ -68,14 +72,35 @@ else:
raise SystemExit("ERROR: KeyboardShortcuts dependency pin is missing from Package.resolved")
PY

RESOURCE_PATCH_NEEDED=1
if grep -Fq "$PATCH_MARKER" "$UTILITIES_FILE"; then
printf 'KeyboardShortcuts resource lookup patch already applied: %s\n' "$UTILITIES_FILE"
RESOURCE_PATCH_NEEDED=0
fi

PREVIEW_PATCH_NEEDED=1
if ! grep -Fq "#Preview {" "$RECORDER_FILE"; then
PREVIEW_PATCH_NEEDED=0
fi

if (( ! RESOURCE_PATCH_NEEDED )) && (( ! PREVIEW_PATCH_NEEDED )); then
printf 'KeyboardShortcuts patches already applied: %s, %s\n' "$UTILITIES_FILE" "$RECORDER_FILE"
exit 0
fi

run chmod u+w "$UTILITIES_FILE"
if ! (cd "$CHECKOUT_DIR" && git apply --unidiff-zero --check "$PATCH_FILE"); then
fail "KeyboardShortcuts resource lookup patch no longer applies cleanly. Review $PATCH_FILE against $UTILITIES_FILE."
if (( RESOURCE_PATCH_NEEDED )); then
run chmod u+w "$UTILITIES_FILE"
if ! (cd "$CHECKOUT_DIR" && git apply --unidiff-zero --check "$RESOURCE_PATCH_FILE"); then
fail "KeyboardShortcuts resource lookup patch no longer applies cleanly. Review $RESOURCE_PATCH_FILE against $UTILITIES_FILE."
fi
run bash -c 'cd "$1" && git apply --unidiff-zero "$2"' bash "$CHECKOUT_DIR" "$RESOURCE_PATCH_FILE"
printf 'Applied KeyboardShortcuts resource lookup patch: %s\n' "$RESOURCE_PATCH_FILE"
fi

if (( PREVIEW_PATCH_NEEDED )); then
run chmod u+w "$RECORDER_FILE"
if ! (cd "$CHECKOUT_DIR" && git apply --unidiff-zero --check "$PREVIEW_PATCH_FILE"); then
fail "KeyboardShortcuts preview patch no longer applies cleanly. Review $PREVIEW_PATCH_FILE against $RECORDER_FILE."
fi
run bash -c 'cd "$1" && git apply --unidiff-zero "$2"' bash "$CHECKOUT_DIR" "$PREVIEW_PATCH_FILE"
printf 'Applied KeyboardShortcuts preview patch: %s\n' "$PREVIEW_PATCH_FILE"
fi
run bash -c 'cd "$1" && git apply --unidiff-zero "$2"' bash "$CHECKOUT_DIR" "$PATCH_FILE"
printf 'Applied KeyboardShortcuts resource lookup patch: %s\n' "$PATCH_FILE"
19 changes: 19 additions & 0 deletions Scripts/patches/keyboardshortcuts-2.3.0-remove-previews.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
diff --git a/Sources/KeyboardShortcuts/Recorder.swift b/Sources/KeyboardShortcuts/Recorder.swift
index 7e7869b..d0dc3b9 100644
--- a/Sources/KeyboardShortcuts/Recorder.swift
+++ b/Sources/KeyboardShortcuts/Recorder.swift
@@ -172,14 +171,0 @@
-#Preview {
- KeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
- .environment(\.locale, .init(identifier: "en"))
-}
-
-#Preview {
- KeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
- .environment(\.locale, .init(identifier: "zh-Hans"))
-}
-
-#Preview {
- KeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
- .environment(\.locale, .init(identifier: "ru"))
-}
91 changes: 91 additions & 0 deletions Scripts/test_debug_app_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,97 @@ def test_finder_launcher_without_python_exits_before_any_lifecycle_action(self)
self.assertIn("No uncoordinated fallback is provided", result.stdout)
self.assertNotIn("Building and relaunching", result.stdout)

def test_finder_launcher_uses_ad_hoc_signing_when_no_identity_exists(self) -> None:
dirname = shutil.which("dirname")
self.assertIsNotNone(dirname)
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
launcher = root / "Launch RepoPrompt CE.command"
launcher.write_text((SCRIPT_DIR.parent / launcher.name).read_text(encoding="utf-8"), encoding="utf-8")
bin_dir = root / "bin"
bin_dir.mkdir()
(bin_dir / "dirname").symlink_to(dirname)
python = bin_dir / "python3"
python.write_text("binary", encoding="utf-8")
python.chmod(0o755)
conductor_log = root / "conductor-env.log"
conductor = root / "conductor"
conductor.write_text(
"#!/bin/bash\n"
"printf 'ALLOW_ADHOC_SIGNING=%s\\n' \"${ALLOW_ADHOC_SIGNING:-}\" >> conductor-env.log\n"
"exit 0\n",
encoding="utf-8",
)
conductor.chmod(0o755)
security = bin_dir / "security"
security.write_text("#!/bin/bash\nprintf ' 0 valid identities found\\n'\n", encoding="utf-8")
security.chmod(0o755)
env = os.environ.copy()
env["PATH"] = str(bin_dir)
env.pop("SIGN_IDENTITY", None)
env.pop("ALLOW_ADHOC_SIGNING", None)

result = subprocess.run(
["/bin/bash", str(launcher)],
env=env,
input="q",
text=True,
capture_output=True,
timeout=2,
)
conductor_log_text = conductor_log.read_text(encoding="utf-8")

self.assertEqual(result.returncode, 0)
self.assertIn("using explicit ad-hoc debug signing", result.stdout)
self.assertIn("Debug secure storage will be in-memory", result.stdout)
self.assertIn("ALLOW_ADHOC_SIGNING=1", conductor_log_text)

def test_finder_launcher_shows_fallback_message_when_signing_still_refused(self) -> None:
dirname = shutil.which("dirname")
self.assertIsNotNone(dirname)
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
launcher = root / "Launch RepoPrompt CE.command"
launcher.write_text((SCRIPT_DIR.parent / launcher.name).read_text(encoding="utf-8"), encoding="utf-8")
bin_dir = root / "bin"
bin_dir.mkdir()
(bin_dir / "dirname").symlink_to(dirname)
python = bin_dir / "python3"
python.write_text("binary", encoding="utf-8")
python.chmod(0o755)
conductor = root / "conductor"
conductor.write_text(
"#!/bin/bash\n"
"echo 'ERROR: Debug ad-hoc signing is disabled by default. Set ALLOW_ADHOC_SIGNING=1 to build an ad-hoc package, or set SIGN_IDENTITY for stable signing.'\n"
"exit 1\n",
encoding="utf-8",
)
conductor.chmod(0o755)
security = bin_dir / "security"
security.write_text("#!/bin/bash\nprintf ' 0 valid identities found\\n'\n", encoding="utf-8")
security.chmod(0o755)
env = os.environ.copy()
# Include system paths so tee, mktemp, grep, and rm are available
# for the launcher's reactive fallback log capture and grep.
env["PATH"] = f"{bin_dir}:/usr/bin:/bin"
env.pop("SIGN_IDENTITY", None)
env.pop("ALLOW_ADHOC_SIGNING", None)

result = subprocess.run(
["/bin/bash", str(launcher)],
env=env,
input="q",
text=True,
capture_output=True,
timeout=5,
)

self.assertEqual(result.returncode, 0)
self.assertIn("RepoPrompt CE was not relaunched", result.stdout)
self.assertIn("Debug signing was refused even though this launcher tried to configure it automatically", result.stdout)
self.assertIn("ALLOW_ADHOC_SIGNING=1 ./conductor app relaunch", result.stdout)
self.assertIn('SIGN_IDENTITY="Apple Development: Your Name (TEAMID)" ./conductor app relaunch', result.stdout)


if __name__ == "__main__":
unittest.main()
25 changes: 25 additions & 0 deletions Scripts/test_release_tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,6 +1799,8 @@ def make_keyboard_shortcuts_patch_fixture(self, source: str | None = None) -> tu
utilities = root / ".build" / "checkouts" / "KeyboardShortcuts" / "Sources" / "KeyboardShortcuts" / "Utilities.swift"
utilities.parent.mkdir(parents=True)
utilities.write_text(source if source is not None else self.keyboard_shortcuts_upstream_utilities(), encoding="utf-8")
recorder = utilities.parent / "Recorder.swift"
recorder.write_text(self.keyboard_shortcuts_upstream_recorder(), encoding="utf-8")
self.write_package_resolved(root, "2.3.0")
return root, utilities

Expand All @@ -1824,6 +1826,29 @@ def keyboard_shortcuts_upstream_utilities() -> str:
extension Data {
\tvar toString: String? { String(data: self, encoding: .utf8) }
}
"""

@staticmethod
def keyboard_shortcuts_upstream_recorder() -> str:
# The preview-removal patch expects #Preview blocks at line 172.
# Pad with 171 lines so the hunk header @@ -172,14 +171,0 @@ applies.
padding = "\n".join(f"// recorder line {i + 1}" for i in range(171))
return f"""\
{padding}
#Preview {{
\tKeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
\t\t.environment(\\.locale, .init(identifier: "en"))
}}

#Preview {{
\tKeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
\t\t.environment(\\.locale, .init(identifier: "zh-Hans"))
}}

#Preview {{
\tKeyboardShortcuts.Recorder("record_shortcut", name: .init("xcodePreview"))
\t\t.environment(\\.locale, .init(identifier: "ru"))
}}
"""

@staticmethod
Expand Down
12 changes: 11 additions & 1 deletion Sources/RepoPrompt/App/FontPreset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,20 @@ enum FontScalePreset: Double, CaseIterable, Identifiable {
}
}

// swiftformat:disable environmentEntry
private struct RepoPromptFontScalePresetKey: EnvironmentKey {
static let defaultValue: FontScalePreset = .current
}

extension EnvironmentValues {
@Entry var repoPromptFontScalePreset: FontScalePreset = .current
var repoPromptFontScalePreset: FontScalePreset {
get { self[RepoPromptFontScalePresetKey.self] }
set { self[RepoPromptFontScalePresetKey.self] = newValue }
}
}

// swiftformat:enable environmentEntry

extension FontScalePreset {
var standardFont: Font {
#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import AppKit
import SwiftUI

// swiftformat:disable environmentEntry
private struct AgentWindowIsFocusedKey: EnvironmentKey {
static let defaultValue = true
}

extension EnvironmentValues {
@Entry var agentWindowIsFocused: Bool = true
var agentWindowIsFocused: Bool {
get { self[AgentWindowIsFocusedKey.self] }
set { self[AgentWindowIsFocusedKey.self] = newValue }
}
}

// swiftformat:enable environmentEntry

// MARK: - Message Footer Strip

/// An inline footer strip with timestamp and a subtle copy button.
Expand Down
Loading
Loading