commit 90bab2173d9b03c8ffcc2450ad4402b9f0a8a54c parent ad0998b50272099a9f21af09a5d85f055f0525c3 Author: triesap <tyson@radroots.org> Date: Sun, 19 Apr 2026 20:32:38 +0000 build: rename app shell scripts Diffstat:
| D | platforms/macos/Scripts/build-macos-host.sh | | | 101 | ------------------------------------------------------------------------------- |
| A | platforms/macos/Scripts/build_host.sh | | | 101 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| R | platforms/macos/Scripts/generate-macos-app-icon.sh -> platforms/macos/Scripts/build_icon.sh | | | 0 | |
| D | platforms/macos/Scripts/run-macos-host.sh | | | 82 | ------------------------------------------------------------------------------- |
| A | platforms/macos/Scripts/run_host.sh | | | 82 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | platforms/macos/Scripts/test-macos-host.sh | | | 221 | ------------------------------------------------------------------------------- |
| A | platforms/macos/Scripts/test_host.sh | | | 221 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | scripts/launch-config.sh | | | 117 | ------------------------------------------------------------------------------- |
| M | scripts/run.sh | | | 6 | ++++-- |
| A | scripts/runtime_env.sh | | | 130 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
10 files changed, 538 insertions(+), 523 deletions(-)
diff --git a/platforms/macos/Scripts/build-macos-host.sh b/platforms/macos/Scripts/build-macos-host.sh @@ -1,101 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" -platform_root="$(cd "${script_dir}/.." && pwd -P)" -repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" -requested_configuration="${CONFIGURATION:-Debug}" -bundle_name="Radroots.app" -plist_template="${platform_root}/App/Resources/Info.plist" - -require_command() { - if command -v "$1" >/dev/null 2>&1; then - return - fi - echo "missing required command: $1" >&2 - exit 1 -} - -workspace_version() { - python3 - <<'PY' "${repo_root}/Cargo.toml" -import re -import sys - -path = sys.argv[1] -with open(path, "r", encoding="utf-8") as handle: - cargo_toml = handle.read() - -match = re.search(r'^\[workspace\.package\][\s\S]*?^version\s*=\s*"([^"]+)"', cargo_toml, re.MULTILINE) -if not match: - raise SystemExit("missing workspace.package.version") - -print(match.group(1), end="") -PY -} - -cargo_target_dir() { - ( - cd "${repo_root}" - cargo metadata --format-version 1 --no-deps | python3 -c 'import json, sys; print(json.load(sys.stdin)["target_directory"], end="")' - ) -} - -configure_build_lane() { - case "${requested_configuration}" in - Debug|debug) - bundle_configuration="Debug" - cargo_profile="debug" - ;; - Release|release) - bundle_configuration="Release" - cargo_profile="release" - ;; - *) - echo "unsupported CONFIGURATION: ${requested_configuration}" >&2 - exit 1 - ;; - esac -} - -require_command cargo -require_command git -require_command python3 -require_command /usr/libexec/PlistBuddy - -configure_build_lane - -bundle_root="${platform_root}/.derived-data/Build/Products/${bundle_configuration}/${bundle_name}" -contents_root="${bundle_root}/Contents" -executable_root="${contents_root}/MacOS" -resources_root="${contents_root}/Resources" -plist_path="${contents_root}/Info.plist" -binary_target="${executable_root}/Radroots" -app_icon_path="${resources_root}/AppIcon.icns" - -( - cd "${repo_root}" - if [[ "${cargo_profile}" == "release" ]]; then - cargo build -p radroots_app --release - else - cargo build -p radroots_app - fi -) - -binary_source="$(cargo_target_dir)/${cargo_profile}/radroots_app" - -if [[ ! -x "${binary_source}" ]]; then - echo "missing desktop launcher binary: ${binary_source}" >&2 - exit 1 -fi - -rm -rf "${bundle_root}" -mkdir -p "${executable_root}" "${resources_root}" -cp "${plist_template}" "${plist_path}" -cp "${binary_source}" "${binary_target}" -chmod +x "${binary_target}" -"${script_dir}/generate-macos-app-icon.sh" "${app_icon_path}" - -/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(workspace_version)" "${plist_path}" -/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${RADROOTS_APP_BUILD:-1}" "${plist_path}" - -printf '%s\n' "${bundle_root}" diff --git a/platforms/macos/Scripts/build_host.sh b/platforms/macos/Scripts/build_host.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +platform_root="$(cd "${script_dir}/.." && pwd -P)" +repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" +requested_configuration="${CONFIGURATION:-Debug}" +bundle_name="Radroots.app" +plist_template="${platform_root}/App/Resources/Info.plist" + +require_command() { + if command -v "$1" >/dev/null 2>&1; then + return + fi + echo "missing required command: $1" >&2 + exit 1 +} + +workspace_version() { + python3 - <<'PY' "${repo_root}/Cargo.toml" +import re +import sys + +path = sys.argv[1] +with open(path, "r", encoding="utf-8") as handle: + cargo_toml = handle.read() + +match = re.search(r'^\[workspace\.package\][\s\S]*?^version\s*=\s*"([^"]+)"', cargo_toml, re.MULTILINE) +if not match: + raise SystemExit("missing workspace.package.version") + +print(match.group(1), end="") +PY +} + +cargo_target_dir() { + ( + cd "${repo_root}" + cargo metadata --format-version 1 --no-deps | python3 -c 'import json, sys; print(json.load(sys.stdin)["target_directory"], end="")' + ) +} + +configure_build_lane() { + case "${requested_configuration}" in + Debug|debug) + bundle_configuration="Debug" + cargo_profile="debug" + ;; + Release|release) + bundle_configuration="Release" + cargo_profile="release" + ;; + *) + echo "unsupported CONFIGURATION: ${requested_configuration}" >&2 + exit 1 + ;; + esac +} + +require_command cargo +require_command git +require_command python3 +require_command /usr/libexec/PlistBuddy + +configure_build_lane + +bundle_root="${platform_root}/.derived-data/Build/Products/${bundle_configuration}/${bundle_name}" +contents_root="${bundle_root}/Contents" +executable_root="${contents_root}/MacOS" +resources_root="${contents_root}/Resources" +plist_path="${contents_root}/Info.plist" +binary_target="${executable_root}/Radroots" +app_icon_path="${resources_root}/AppIcon.icns" + +( + cd "${repo_root}" + if [[ "${cargo_profile}" == "release" ]]; then + cargo build -p radroots_app --release + else + cargo build -p radroots_app + fi +) + +binary_source="$(cargo_target_dir)/${cargo_profile}/radroots_app" + +if [[ ! -x "${binary_source}" ]]; then + echo "missing desktop launcher binary: ${binary_source}" >&2 + exit 1 +fi + +rm -rf "${bundle_root}" +mkdir -p "${executable_root}" "${resources_root}" +cp "${plist_template}" "${plist_path}" +cp "${binary_source}" "${binary_target}" +chmod +x "${binary_target}" +"${script_dir}/build_icon.sh" "${app_icon_path}" + +/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(workspace_version)" "${plist_path}" +/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${RADROOTS_APP_BUILD:-1}" "${plist_path}" + +printf '%s\n' "${bundle_root}" diff --git a/platforms/macos/Scripts/generate-macos-app-icon.sh b/platforms/macos/Scripts/build_icon.sh diff --git a/platforms/macos/Scripts/run-macos-host.sh b/platforms/macos/Scripts/run-macos-host.sh @@ -1,82 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" -date_utc="$(date -u +%F)" - -require_command() { - if command -v "$1" >/dev/null 2>&1; then - return - fi - echo "missing required command: $1" >&2 - exit 1 -} - -require_env() { - local name="$1" - - if [[ -z "${!name:-}" ]]; then - echo "missing required environment variable: ${name}" >&2 - exit 1 - fi -} - -forward_signal() { - local signal="$1" - - if [[ -n "${app_pid:-}" ]] && kill -0 "${app_pid}" 2>/dev/null; then - kill "-${signal}" "${app_pid}" 2>/dev/null || kill "${app_pid}" 2>/dev/null || true - fi -} - -require_command grep -require_command /usr/libexec/PlistBuddy -require_env RADROOTS_APP_RUNTIME_CONFIG_JSON -require_env RADROOTS_APP_LOCAL_LOG_ROOT - -app_path="$("${script_dir}/build-macos-host.sh")" -plist_path="${app_path}/Contents/Info.plist" -executable_name="$( - /usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "${plist_path}" -)" -executable_path="${app_path}/Contents/MacOS/${executable_name}" -app_log_root="${RADROOTS_APP_LOCAL_LOG_ROOT}/apps/local/app/app-macos-native" -structured_log_file="${app_log_root}/${date_utc}.jsonl" -stdout_file="${app_log_root}/raw/stdout.${date_utc}.log" -stderr_file="${app_log_root}/raw/stderr.${date_utc}.log" - -mkdir -p "${app_log_root}/raw" -export RUST_LOG="${RADROOTS_APP_RUST_LOG:-info}" - -trap 'forward_signal TERM' TERM -trap 'forward_signal INT' INT -trap 'forward_signal HUP' HUP - -"${executable_path}" "$@" >>"${stdout_file}" 2>>"${stderr_file}" & -app_pid="$!" - -launch_confirmed=false -for _ in $(seq 1 100); do - if [[ -f "${structured_log_file}" ]] && grep -q '"event":"runtime.launch"' "${structured_log_file}" 2>/dev/null; then - launch_confirmed=true - break - fi - - if ! kill -0 "${app_pid}" 2>/dev/null; then - wait "${app_pid}" - exit $? - fi - - sleep 0.1 -done - -if [[ "${launch_confirmed}" != "true" ]]; then - if kill -0 "${app_pid}" 2>/dev/null; then - kill "${app_pid}" 2>/dev/null || true - wait "${app_pid}" || true - fi - echo "app launch did not emit runtime.launch within startup timeout" >&2 - exit 1 -fi - -wait "${app_pid}" diff --git a/platforms/macos/Scripts/run_host.sh b/platforms/macos/Scripts/run_host.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +date_utc="$(date -u +%F)" + +require_command() { + if command -v "$1" >/dev/null 2>&1; then + return + fi + echo "missing required command: $1" >&2 + exit 1 +} + +require_env() { + local name="$1" + + if [[ -z "${!name:-}" ]]; then + echo "missing required environment variable: ${name}" >&2 + exit 1 + fi +} + +forward_signal() { + local signal="$1" + + if [[ -n "${app_pid:-}" ]] && kill -0 "${app_pid}" 2>/dev/null; then + kill "-${signal}" "${app_pid}" 2>/dev/null || kill "${app_pid}" 2>/dev/null || true + fi +} + +require_command grep +require_command /usr/libexec/PlistBuddy +require_env RADROOTS_APP_RUNTIME_CONFIG_JSON +require_env RADROOTS_APP_LOCAL_LOG_ROOT + +app_path="$("${script_dir}/build_host.sh")" +plist_path="${app_path}/Contents/Info.plist" +executable_name="$( + /usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "${plist_path}" +)" +executable_path="${app_path}/Contents/MacOS/${executable_name}" +app_log_root="${RADROOTS_APP_LOCAL_LOG_ROOT}/apps/local/app/app-macos-native" +structured_log_file="${app_log_root}/${date_utc}.jsonl" +stdout_file="${app_log_root}/raw/stdout.${date_utc}.log" +stderr_file="${app_log_root}/raw/stderr.${date_utc}.log" + +mkdir -p "${app_log_root}/raw" +export RUST_LOG="${RADROOTS_APP_RUST_LOG:-info}" + +trap 'forward_signal TERM' TERM +trap 'forward_signal INT' INT +trap 'forward_signal HUP' HUP + +"${executable_path}" "$@" >>"${stdout_file}" 2>>"${stderr_file}" & +app_pid="$!" + +launch_confirmed=false +for _ in $(seq 1 100); do + if [[ -f "${structured_log_file}" ]] && grep -q '"event":"runtime.launch"' "${structured_log_file}" 2>/dev/null; then + launch_confirmed=true + break + fi + + if ! kill -0 "${app_pid}" 2>/dev/null; then + wait "${app_pid}" + exit $? + fi + + sleep 0.1 +done + +if [[ "${launch_confirmed}" != "true" ]]; then + if kill -0 "${app_pid}" 2>/dev/null; then + kill "${app_pid}" 2>/dev/null || true + wait "${app_pid}" || true + fi + echo "app launch did not emit runtime.launch within startup timeout" >&2 + exit 1 +fi + +wait "${app_pid}" diff --git a/platforms/macos/Scripts/test-macos-host.sh b/platforms/macos/Scripts/test-macos-host.sh @@ -1,221 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" -platform_root="$(cd "${script_dir}/.." && pwd -P)" -repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" -date_utc="$(date -u +%F)" - -source "${repo_root}/scripts/launch-config.sh" - -require_command() { - if command -v "$1" >/dev/null 2>&1; then - return - fi - echo "missing required command: $1" >&2 - exit 1 -} - -require_command /usr/libexec/PlistBuddy -require_command mktemp -require_command readlink - -app_path="$("${script_dir}/build-macos-host.sh")" -plist_path="${app_path}/Contents/Info.plist" -executable_name="$( - /usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "${plist_path}" -)" -bundle_name="$( - /usr/libexec/PlistBuddy -c 'Print :CFBundleName' "${plist_path}" -)" -bundle_id="$( - /usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "${plist_path}" -)" -icon_name="$( - /usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "${plist_path}" -)" - -[[ "${bundle_name}" == "Radroots" ]] || { - echo "unexpected CFBundleName: ${bundle_name}" >&2 - exit 1 -} - -[[ "${bundle_id}" == "org.radroots.app.macos" ]] || { - echo "unexpected CFBundleIdentifier: ${bundle_id}" >&2 - exit 1 -} - -[[ -x "${app_path}/Contents/MacOS/${executable_name}" ]] || { - echo "missing bundle executable: ${app_path}/Contents/MacOS/${executable_name}" >&2 - exit 1 -} - -[[ -f "${app_path}/Contents/Resources/${icon_name}.icns" ]] || { - echo "missing bundle icon: ${app_path}/Contents/Resources/${icon_name}.icns" >&2 - exit 1 -} - -release_app_path="$( - CONFIGURATION=Release "${script_dir}/build-macos-host.sh" -)" -[[ "${release_app_path}" == "${platform_root}/.derived-data/Build/Products/Release/Radroots.app" ]] || { - echo "unexpected release bundle path: ${release_app_path}" >&2 - exit 1 -} -[[ -x "${release_app_path}/Contents/MacOS/Radroots" ]] || { - echo "missing release bundle executable: ${release_app_path}/Contents/MacOS/Radroots" >&2 - exit 1 -} - -tmp_root="$(mktemp -d)" -runner_pid="" -degraded_runner_pid="" - -wait_for_log_event() { - local structured_log_file="$1" - local expected_event="$2" - local runner_pid="$3" - local event_verified=false - - for _ in $(seq 1 150); do - if [[ -f "${structured_log_file}" ]] && grep -q "\"event\":\"${expected_event}\"" "${structured_log_file}" 2>/dev/null; then - event_verified=true - break - fi - - if ! kill -0 "${runner_pid}" 2>/dev/null; then - wait "${runner_pid}" - exit $? - fi - - sleep 0.1 - done - - [[ "${event_verified}" == "true" ]] || { - echo "${expected_event} was not recorded by run-macos-host.sh" >&2 - exit 1 - } -} - -assert_latest_alias() { - local latest_log_path="$1" - - [[ -e "${latest_log_path}" ]] || { - echo "missing latest structured log alias: ${latest_log_path}" >&2 - exit 1 - } - - [[ "$(readlink "${latest_log_path}")" == "${date_utc}.jsonl" ]] || { - echo "latest structured log alias does not point at ${date_utc}.jsonl" >&2 - exit 1 - } -} - -assert_raw_logs_exist() { - local stdout_file="$1" - local stderr_file="$2" - - [[ -f "${stdout_file}" ]] || { - echo "missing raw stdout log: ${stdout_file}" >&2 - exit 1 - } - - [[ -f "${stderr_file}" ]] || { - echo "missing raw stderr log: ${stderr_file}" >&2 - exit 1 - } -} - -terminate_runner() { - local runner_pid="$1" - - if [[ -n "${runner_pid}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then - kill "${runner_pid}" 2>/dev/null || true - fi - - set +e - wait "${runner_pid}" - local exit_code="$?" - set -e - [[ "${exit_code}" == "0" ]] || [[ "${exit_code}" == "143" ]] || [[ "${exit_code}" == "130" ]] || { - echo "unexpected runner exit code after termination: ${exit_code}" >&2 - exit 1 - } -} - -cleanup() { - if [[ -n "${runner_pid:-}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then - kill "${runner_pid}" 2>/dev/null || true - wait "${runner_pid}" || true - fi - if [[ -n "${degraded_runner_pid:-}" ]] && kill -0 "${degraded_runner_pid}" 2>/dev/null; then - kill "${degraded_runner_pid}" 2>/dev/null || true - wait "${degraded_runner_pid}" || true - fi - rm -rf "${tmp_root}" -} -trap cleanup EXIT - -runtime_mode="localhost-dev" -run_id="$(radroots_app_run_id "${runtime_mode}")" -platform_name="$(radroots_app_platform_name)" -bundle_identifier="$(radroots_app_bundle_identifier)" -local_log_root="${tmp_root}/logs" -structured_log_file="${local_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" -latest_log_path="${local_log_root}/apps/local/app/app-macos-native/latest.jsonl" -stdout_file="${local_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" -stderr_file="${local_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" - -RADROOTS_APP_RUN_ID="${run_id}" \ -RADROOTS_APP_LOCAL_LOG_ROOT="${local_log_root}" \ -RADROOTS_APP_RUNTIME_CONFIG_JSON="$( - radroots_app_build_runtime_config_json \ - "${repo_root}" \ - "${runtime_mode}" \ - "${run_id}" \ - "${bundle_identifier}" \ - "${platform_name}" \ - "${local_log_root}" -)" \ -"${script_dir}/run-macos-host.sh" & -runner_pid="$!" - -wait_for_log_event "${structured_log_file}" "runtime.launch" "${runner_pid}" -assert_latest_alias "${latest_log_path}" -assert_raw_logs_exist "${stdout_file}" "${stderr_file}" -terminate_runner "${runner_pid}" -runner_pid="" - -degraded_run_id="$(radroots_app_run_id "${runtime_mode}")" -degraded_log_root="${tmp_root}/degraded-logs" -degraded_structured_log_file="${degraded_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" -degraded_latest_log_path="${degraded_log_root}/apps/local/app/app-macos-native/latest.jsonl" -degraded_stdout_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" -degraded_stderr_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" -degraded_runtime_config_json="$( - radroots_app_build_runtime_config_json \ - "${repo_root}" \ - "${runtime_mode}" \ - "${degraded_run_id}" \ - "${bundle_identifier}" \ - "${platform_name}" \ - "${degraded_log_root}" -)" - -env -u HOME \ - RADROOTS_APP_RUN_ID="${degraded_run_id}" \ - RADROOTS_APP_LOCAL_LOG_ROOT="${degraded_log_root}" \ - RADROOTS_APP_RUNTIME_CONFIG_JSON="${degraded_runtime_config_json}" \ - "${script_dir}/run-macos-host.sh" & -degraded_runner_pid="$!" - -wait_for_log_event "${degraded_structured_log_file}" "runtime.launch" "${degraded_runner_pid}" -wait_for_log_event "${degraded_structured_log_file}" "runtime.degraded" "${degraded_runner_pid}" -assert_latest_alias "${degraded_latest_log_path}" -assert_raw_logs_exist "${degraded_stdout_file}" "${degraded_stderr_file}" -grep -q '"startup_issue":"desktop runtime roots require HOME for macos"' "${degraded_structured_log_file}" || { - echo "runtime.degraded did not record the expected startup issue" >&2 - exit 1 -} -terminate_runner "${degraded_runner_pid}" -degraded_runner_pid="" diff --git a/platforms/macos/Scripts/test_host.sh b/platforms/macos/Scripts/test_host.sh @@ -0,0 +1,221 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +platform_root="$(cd "${script_dir}/.." && pwd -P)" +repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" +date_utc="$(date -u +%F)" + +source "${repo_root}/scripts/runtime_env.sh" + +require_command() { + if command -v "$1" >/dev/null 2>&1; then + return + fi + echo "missing required command: $1" >&2 + exit 1 +} + +require_command /usr/libexec/PlistBuddy +require_command mktemp +require_command readlink + +app_path="$("${script_dir}/build_host.sh")" +plist_path="${app_path}/Contents/Info.plist" +executable_name="$( + /usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "${plist_path}" +)" +bundle_name="$( + /usr/libexec/PlistBuddy -c 'Print :CFBundleName' "${plist_path}" +)" +bundle_id="$( + /usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "${plist_path}" +)" +icon_name="$( + /usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "${plist_path}" +)" + +[[ "${bundle_name}" == "Radroots" ]] || { + echo "unexpected CFBundleName: ${bundle_name}" >&2 + exit 1 +} + +[[ "${bundle_id}" == "org.radroots.app.macos" ]] || { + echo "unexpected CFBundleIdentifier: ${bundle_id}" >&2 + exit 1 +} + +[[ -x "${app_path}/Contents/MacOS/${executable_name}" ]] || { + echo "missing bundle executable: ${app_path}/Contents/MacOS/${executable_name}" >&2 + exit 1 +} + +[[ -f "${app_path}/Contents/Resources/${icon_name}.icns" ]] || { + echo "missing bundle icon: ${app_path}/Contents/Resources/${icon_name}.icns" >&2 + exit 1 +} + +release_app_path="$( + CONFIGURATION=Release "${script_dir}/build_host.sh" +)" +[[ "${release_app_path}" == "${platform_root}/.derived-data/Build/Products/Release/Radroots.app" ]] || { + echo "unexpected release bundle path: ${release_app_path}" >&2 + exit 1 +} +[[ -x "${release_app_path}/Contents/MacOS/Radroots" ]] || { + echo "missing release bundle executable: ${release_app_path}/Contents/MacOS/Radroots" >&2 + exit 1 +} + +tmp_root="$(mktemp -d)" +runner_pid="" +degraded_runner_pid="" + +wait_for_log_event() { + local structured_log_file="$1" + local expected_event="$2" + local runner_pid="$3" + local event_verified=false + + for _ in $(seq 1 150); do + if [[ -f "${structured_log_file}" ]] && grep -q "\"event\":\"${expected_event}\"" "${structured_log_file}" 2>/dev/null; then + event_verified=true + break + fi + + if ! kill -0 "${runner_pid}" 2>/dev/null; then + wait "${runner_pid}" + exit $? + fi + + sleep 0.1 + done + + [[ "${event_verified}" == "true" ]] || { + echo "${expected_event} was not recorded by run_host.sh" >&2 + exit 1 + } +} + +assert_latest_alias() { + local latest_log_path="$1" + + [[ -e "${latest_log_path}" ]] || { + echo "missing latest structured log alias: ${latest_log_path}" >&2 + exit 1 + } + + [[ "$(readlink "${latest_log_path}")" == "${date_utc}.jsonl" ]] || { + echo "latest structured log alias does not point at ${date_utc}.jsonl" >&2 + exit 1 + } +} + +assert_raw_logs_exist() { + local stdout_file="$1" + local stderr_file="$2" + + [[ -f "${stdout_file}" ]] || { + echo "missing raw stdout log: ${stdout_file}" >&2 + exit 1 + } + + [[ -f "${stderr_file}" ]] || { + echo "missing raw stderr log: ${stderr_file}" >&2 + exit 1 + } +} + +terminate_runner() { + local runner_pid="$1" + + if [[ -n "${runner_pid}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then + kill "${runner_pid}" 2>/dev/null || true + fi + + set +e + wait "${runner_pid}" + local exit_code="$?" + set -e + [[ "${exit_code}" == "0" ]] || [[ "${exit_code}" == "143" ]] || [[ "${exit_code}" == "130" ]] || { + echo "unexpected runner exit code after termination: ${exit_code}" >&2 + exit 1 + } +} + +cleanup() { + if [[ -n "${runner_pid:-}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then + kill "${runner_pid}" 2>/dev/null || true + wait "${runner_pid}" || true + fi + if [[ -n "${degraded_runner_pid:-}" ]] && kill -0 "${degraded_runner_pid}" 2>/dev/null; then + kill "${degraded_runner_pid}" 2>/dev/null || true + wait "${degraded_runner_pid}" || true + fi + rm -rf "${tmp_root}" +} +trap cleanup EXIT + +runtime_mode="localhost-dev" +run_id="$(radroots_app_run_id "${runtime_mode}")" +platform_name="$(radroots_app_platform_name)" +bundle_identifier="$(radroots_app_bundle_identifier)" +local_log_root="${tmp_root}/logs" +structured_log_file="${local_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" +latest_log_path="${local_log_root}/apps/local/app/app-macos-native/latest.jsonl" +stdout_file="${local_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" +stderr_file="${local_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" + +RADROOTS_APP_RUN_ID="${run_id}" \ +RADROOTS_APP_LOCAL_LOG_ROOT="${local_log_root}" \ +RADROOTS_APP_RUNTIME_CONFIG_JSON="$( + radroots_app_build_runtime_config_json \ + "${repo_root}" \ + "${runtime_mode}" \ + "${run_id}" \ + "${bundle_identifier}" \ + "${platform_name}" \ + "${local_log_root}" +)" \ +"${script_dir}/run_host.sh" & +runner_pid="$!" + +wait_for_log_event "${structured_log_file}" "runtime.launch" "${runner_pid}" +assert_latest_alias "${latest_log_path}" +assert_raw_logs_exist "${stdout_file}" "${stderr_file}" +terminate_runner "${runner_pid}" +runner_pid="" + +degraded_run_id="$(radroots_app_run_id "${runtime_mode}")" +degraded_log_root="${tmp_root}/degraded-logs" +degraded_structured_log_file="${degraded_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" +degraded_latest_log_path="${degraded_log_root}/apps/local/app/app-macos-native/latest.jsonl" +degraded_stdout_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" +degraded_stderr_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" +degraded_runtime_config_json="$( + radroots_app_build_runtime_config_json \ + "${repo_root}" \ + "${runtime_mode}" \ + "${degraded_run_id}" \ + "${bundle_identifier}" \ + "${platform_name}" \ + "${degraded_log_root}" +)" + +env -u HOME \ + RADROOTS_APP_RUN_ID="${degraded_run_id}" \ + RADROOTS_APP_LOCAL_LOG_ROOT="${degraded_log_root}" \ + RADROOTS_APP_RUNTIME_CONFIG_JSON="${degraded_runtime_config_json}" \ + "${script_dir}/run_host.sh" & +degraded_runner_pid="$!" + +wait_for_log_event "${degraded_structured_log_file}" "runtime.launch" "${degraded_runner_pid}" +wait_for_log_event "${degraded_structured_log_file}" "runtime.degraded" "${degraded_runner_pid}" +assert_latest_alias "${degraded_latest_log_path}" +assert_raw_logs_exist "${degraded_stdout_file}" "${degraded_stderr_file}" +grep -q '"startup_issue":"desktop runtime roots require HOME for macos"' "${degraded_structured_log_file}" || { + echo "runtime.degraded did not record the expected startup issue" >&2 + exit 1 +} +terminate_runner "${degraded_runner_pid}" +degraded_runner_pid="" diff --git a/scripts/launch-config.sh b/scripts/launch-config.sh @@ -1,117 +0,0 @@ -#!/usr/bin/env bash - -radroots_app_workspace_version() { - local repo_root="$1" - - python3 - <<'PY' "${repo_root}/Cargo.toml" -import re -import sys - -path = sys.argv[1] -with open(path, "r", encoding="utf-8") as handle: - cargo_toml = handle.read() - -match = re.search(r'^\[workspace\.package\][\s\S]*?^version\s*=\s*"([^"]+)"', cargo_toml, re.MULTILINE) -if not match: - raise SystemExit("missing workspace.package.version") - -print(match.group(1), end="") -PY -} - -radroots_app_runtime_mode() { - printf '%s' "${RADROOTS_APP_RUNTIME_MODE:-localhost-dev}" -} - -radroots_app_run_id() { - local runtime_mode="$1" - - if [[ -n "${RADROOTS_APP_RUN_ID:-}" ]]; then - printf '%s' "${RADROOTS_APP_RUN_ID}" - return - fi - - RADROOTS_APP_RUNTIME_MODE_FOR_RUN_ID="${runtime_mode}" python3 - <<'PY' -import os -import secrets -import time - -runtime_mode = os.environ["RADROOTS_APP_RUNTIME_MODE_FOR_RUN_ID"].strip().lower() or "unknown" -timestamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime()) -suffix = secrets.token_hex(8) -print(f"app-{runtime_mode}-{timestamp}-{suffix}", end="") -PY -} - -radroots_app_platform_name() { - case "$(uname -s)" in - Darwin) printf 'macos' ;; - Linux) printf 'linux' ;; - *) uname -s | tr '[:upper:]' '[:lower:]' ;; - esac -} - -radroots_app_bundle_identifier() { - if [[ "$(uname -s)" == "Darwin" ]]; then - printf 'org.radroots.app.macos' - return - fi - - printf 'org.radroots.app.desktop' -} - -radroots_app_os_version() { - printf '%s-%s' "$(radroots_app_platform_name)" "$(uname -r)" -} - -radroots_app_local_log_root() { - local repo_root="$1" - - if [[ -n "${RADROOTS_APP_LOCAL_LOG_ROOT:-}" ]]; then - printf '%s' "${RADROOTS_APP_LOCAL_LOG_ROOT}" - return - fi - - printf '%s' "${repo_root}/logs" -} - -radroots_app_build_runtime_config_json() { - local repo_root="$1" - local runtime_mode="$2" - local run_id="$3" - local bundle_identifier="$4" - local platform_name="$5" - local local_log_root="$6" - - RADROOTS_APP_RUNTIME_CONFIG_SCHEMA="radroots.app.runtime-config.v1" \ - RADROOTS_APP_RUNTIME_MODE="${runtime_mode}" \ - RADROOTS_APP_RUN_ID="${run_id}" \ - RADROOTS_APP_BUNDLE_IDENTIFIER="${bundle_identifier}" \ - RADROOTS_APP_BUNDLE_NAME="Radroots" \ - RADROOTS_APP_MARKETING_VERSION="$(radroots_app_workspace_version "${repo_root}")" \ - RADROOTS_APP_BUILD_NUMBER="${RADROOTS_APP_BUILD:-dev}" \ - RADROOTS_APP_PLATFORM_NAME="${platform_name}" \ - RADROOTS_APP_OS_VERSION="$(radroots_app_os_version)" \ - RADROOTS_APP_HOST_LOCALE="${LANG:-system-default}" \ - RADROOTS_APP_RUNTIME_ORIGIN="gpui://localhost" \ - RADROOTS_APP_LOCAL_LOG_ROOT="${local_log_root}" \ - python3 - <<'PY' -import json -import os - -print(json.dumps({ - "schema_version": os.environ["RADROOTS_APP_RUNTIME_CONFIG_SCHEMA"], - "runtime_mode": os.environ["RADROOTS_APP_RUNTIME_MODE"], - "run_id": os.environ["RADROOTS_APP_RUN_ID"], - "bundle_identifier": os.environ["RADROOTS_APP_BUNDLE_IDENTIFIER"], - "bundle_name": os.environ["RADROOTS_APP_BUNDLE_NAME"], - "marketing_version": os.environ["RADROOTS_APP_MARKETING_VERSION"], - "build_number": os.environ["RADROOTS_APP_BUILD_NUMBER"], - "platform_name": os.environ["RADROOTS_APP_PLATFORM_NAME"], - "operating_system_version": os.environ["RADROOTS_APP_OS_VERSION"], - "host_locale": os.environ["RADROOTS_APP_HOST_LOCALE"], - "runtime_origin": os.environ["RADROOTS_APP_RUNTIME_ORIGIN"], - "local_log_root": os.environ["RADROOTS_APP_LOCAL_LOG_ROOT"], -}, sort_keys=True, separators=(",", ":")), end="") -PY -} diff --git a/scripts/run.sh b/scripts/run.sh @@ -4,12 +4,13 @@ set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" -source "${script_dir}/launch-config.sh" +source "${script_dir}/runtime_env.sh" cd "${repo_root}" runtime_mode="$(radroots_app_runtime_mode)" run_id="$(radroots_app_run_id "${runtime_mode}")" +default_nostr_relay_url="$(radroots_app_default_nostr_relay_url)" platform_name="$(radroots_app_platform_name)" bundle_identifier="$(radroots_app_bundle_identifier)" local_log_root="$(radroots_app_local_log_root "${repo_root}")" @@ -22,13 +23,14 @@ export RADROOTS_APP_RUNTIME_CONFIG_JSON="$( "${repo_root}" \ "${runtime_mode}" \ "${run_id}" \ + "${default_nostr_relay_url}" \ "${bundle_identifier}" \ "${platform_name}" \ "${local_log_root}" )" if [[ "$(uname -s)" == "Darwin" ]]; then - exec "${repo_root}/platforms/macos/Scripts/run-macos-host.sh" "$@" + exec "${repo_root}/platforms/macos/Scripts/run_host.sh" "$@" fi exec cargo run -p radroots_app -- "$@" diff --git a/scripts/runtime_env.sh b/scripts/runtime_env.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +radroots_app_workspace_version() { + local repo_root="$1" + + python3 - <<'PY' "${repo_root}/Cargo.toml" +import re +import sys + +path = sys.argv[1] +with open(path, "r", encoding="utf-8") as handle: + cargo_toml = handle.read() + +match = re.search(r'^\[workspace\.package\][\s\S]*?^version\s*=\s*"([^"]+)"', cargo_toml, re.MULTILINE) +if not match: + raise SystemExit("missing workspace.package.version") + +print(match.group(1), end="") +PY +} + +radroots_app_runtime_mode() { + printf '%s' "${RADROOTS_APP_RUNTIME_MODE:-localhost-dev}" +} + +radroots_app_run_id() { + local runtime_mode="$1" + + if [[ -n "${RADROOTS_APP_RUN_ID:-}" ]]; then + printf '%s' "${RADROOTS_APP_RUN_ID}" + return + fi + + RADROOTS_APP_RUNTIME_MODE_FOR_RUN_ID="${runtime_mode}" python3 - <<'PY' +import os +import secrets +import time + +runtime_mode = os.environ["RADROOTS_APP_RUNTIME_MODE_FOR_RUN_ID"].strip().lower() or "unknown" +timestamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime()) +suffix = secrets.token_hex(8) +print(f"app-{runtime_mode}-{timestamp}-{suffix}", end="") +PY +} + +radroots_app_platform_name() { + case "$(uname -s)" in + Darwin) printf 'macos' ;; + Linux) printf 'linux' ;; + *) uname -s | tr '[:upper:]' '[:lower:]' ;; + esac +} + +radroots_app_bundle_identifier() { + if [[ "$(uname -s)" == "Darwin" ]]; then + printf 'org.radroots.app.macos' + return + fi + + printf 'org.radroots.app.desktop' +} + +radroots_app_os_version() { + printf '%s-%s' "$(radroots_app_platform_name)" "$(uname -r)" +} + +radroots_app_local_log_root() { + local repo_root="$1" + + if [[ -n "${RADROOTS_APP_LOCAL_LOG_ROOT:-}" ]]; then + printf '%s' "${RADROOTS_APP_LOCAL_LOG_ROOT}" + return + fi + + printf '%s' "${repo_root}/logs" +} + +radroots_app_default_nostr_relay_url() { + if [[ -n "${RADROOTS_APP_DEFAULT_NOSTR_RELAY_URL:-}" ]]; then + printf '%s' "${RADROOTS_APP_DEFAULT_NOSTR_RELAY_URL}" + return + fi + + printf 'missing required env: RADROOTS_APP_DEFAULT_NOSTR_RELAY_URL\n' >&2 + exit 1 +} + +radroots_app_build_runtime_config_json() { + local repo_root="$1" + local runtime_mode="$2" + local run_id="$3" + local default_nostr_relay_url="$4" + local bundle_identifier="$5" + local platform_name="$6" + local local_log_root="$7" + + RADROOTS_APP_RUNTIME_CONFIG_SCHEMA="radroots.app.runtime-config.v1" \ + RADROOTS_APP_RUNTIME_MODE="${runtime_mode}" \ + RADROOTS_APP_RUN_ID="${run_id}" \ + RADROOTS_APP_DEFAULT_NOSTR_RELAY_URL="${default_nostr_relay_url}" \ + RADROOTS_APP_BUNDLE_IDENTIFIER="${bundle_identifier}" \ + RADROOTS_APP_BUNDLE_NAME="Radroots" \ + RADROOTS_APP_MARKETING_VERSION="$(radroots_app_workspace_version "${repo_root}")" \ + RADROOTS_APP_BUILD_NUMBER="${RADROOTS_APP_BUILD:-dev}" \ + RADROOTS_APP_PLATFORM_NAME="${platform_name}" \ + RADROOTS_APP_OS_VERSION="$(radroots_app_os_version)" \ + RADROOTS_APP_HOST_LOCALE="${LANG:-system-default}" \ + RADROOTS_APP_RUNTIME_ORIGIN="gpui://localhost" \ + RADROOTS_APP_LOCAL_LOG_ROOT="${local_log_root}" \ + python3 - <<'PY' +import json +import os + +print(json.dumps({ + "schema_version": os.environ["RADROOTS_APP_RUNTIME_CONFIG_SCHEMA"], + "runtime_mode": os.environ["RADROOTS_APP_RUNTIME_MODE"], + "run_id": os.environ["RADROOTS_APP_RUN_ID"], + "default_nostr_relay_url": os.environ["RADROOTS_APP_DEFAULT_NOSTR_RELAY_URL"], + "bundle_identifier": os.environ["RADROOTS_APP_BUNDLE_IDENTIFIER"], + "bundle_name": os.environ["RADROOTS_APP_BUNDLE_NAME"], + "marketing_version": os.environ["RADROOTS_APP_MARKETING_VERSION"], + "build_number": os.environ["RADROOTS_APP_BUILD_NUMBER"], + "platform_name": os.environ["RADROOTS_APP_PLATFORM_NAME"], + "operating_system_version": os.environ["RADROOTS_APP_OS_VERSION"], + "host_locale": os.environ["RADROOTS_APP_HOST_LOCALE"], + "runtime_origin": os.environ["RADROOTS_APP_RUNTIME_ORIGIN"], + "local_log_root": os.environ["RADROOTS_APP_LOCAL_LOG_ROOT"], +}, sort_keys=True, separators=(",", ":")), end="") +PY +}