@itcase/config 1.7.27 → 1.7.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/app/android-apk-debug.sh +44 -0
- package/app/android-apk-emulator-prod.sh +143 -0
- package/app/android-common.sh +104 -0
- package/app/android-perfetto.sh +133 -0
- package/app/check-sentry.sh +30 -0
- package/app/common.sh +35 -0
- package/package.json +2 -1
- package/semantic-release/androidAppPrepare.js +11 -69
- package/semantic-release/index.js +5 -1
- package/semantic-release/semanticReleaseApp.js +10 -10
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ npm install --save-dev @itcase/config
|
|
|
19
19
|
|
|
20
20
|
- **Smart commit** — CLI `smart-commit` (Cursor Agent → preview → commit)
|
|
21
21
|
|
|
22
|
+
- **App** — скрипты в `app/`, путь `node_modules/@itcase/config/app/`. `check-sentry.sh VAR` берёт URL из `.env.defaults` или `.env`. Android-сборка читает пакет из `android/app/build.gradle` (`def appId`), debug-суффикс `.debug`, у `PROD` суффикса окружения нет.
|
|
23
|
+
|
|
22
24
|
- **Husky** — общие git-хуки в `husky-hooks/`
|
|
23
25
|
- В git только `.husky/config`; обёртки и `_run.sh` создаёт `itcase-config-husky-install` при `npm i`
|
|
24
26
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Сборка debug и установка на эмулятор или USB-устройство.
|
|
4
|
+
# CLI react-native ищет app-debug.apk, поэтому APK передаём через --binary-path.
|
|
5
|
+
#
|
|
6
|
+
# Usage: android-apk-debug.sh device|emulator -PENV=STAGE
|
|
7
|
+
# device первое устройство не emulator-*
|
|
8
|
+
# emulator первый emulator-*; если нет — первый AVD
|
|
9
|
+
# Иначе: ANDROID_SERIAL=xxx
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
source "$(dirname "$0")/android-common.sh"
|
|
14
|
+
|
|
15
|
+
root=$(android_repo_root)
|
|
16
|
+
cd "$root"
|
|
17
|
+
|
|
18
|
+
target="${1:-}"
|
|
19
|
+
|
|
20
|
+
if [ "$target" != "device" ] && [ "$target" != "emulator" ] || [ -z "${2:-}" ]; then
|
|
21
|
+
echo "Usage: android-apk-debug.sh device|emulator -PENV=STAGE"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
env_name=$(gradle_env "$2")
|
|
26
|
+
app_id=$(debug_app_id "$env_name" "$root")
|
|
27
|
+
|
|
28
|
+
serial=$(android_serial "$target")
|
|
29
|
+
|
|
30
|
+
if [ "$target" = "emulator" ] && [ -z "$serial" ]; then
|
|
31
|
+
avd=$(emulator -list-avds | awk 'NF { print; exit }')
|
|
32
|
+
[ -n "$avd" ] || {
|
|
33
|
+
echo "Нет AVD"
|
|
34
|
+
exit 1
|
|
35
|
+
}
|
|
36
|
+
emulator -avd "$avd" -no-boot-anim >/dev/null 2>&1 &
|
|
37
|
+
adb wait-for-device
|
|
38
|
+
serial=$(require_android_serial emulator)
|
|
39
|
+
elif [ -z "$serial" ]; then
|
|
40
|
+
serial=$(require_android_serial "$target")
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "→ $serial (ENV=$env_name, $app_id)"
|
|
44
|
+
install_debug_apk "$env_name" "$app_id" "$serial" "$root"
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Прод на эмуляторе. Системный раздел должен быть записываемым,
|
|
4
|
+
# иначе имена из URL_VAR не попадут в hosts.
|
|
5
|
+
#
|
|
6
|
+
# Usage: android-apk-emulator-prod.sh -PENV=PROD [-AVD=name] URL_VAR...
|
|
7
|
+
# -PENV уходит в Gradle. У PROD в имени APK нет «PROD».
|
|
8
|
+
# -AVD имя эмулятора. Без флага — первый AVD.
|
|
9
|
+
# URL_VAR имена переменных из .env.defaults, по одному на комплекс
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
source "$(dirname "$0")/android-common.sh"
|
|
14
|
+
|
|
15
|
+
root=$(android_repo_root)
|
|
16
|
+
cd "$root"
|
|
17
|
+
|
|
18
|
+
env_flag=
|
|
19
|
+
avd_name=
|
|
20
|
+
url_keys=()
|
|
21
|
+
for arg in "$@"; do
|
|
22
|
+
case "$arg" in
|
|
23
|
+
-PENV=*) env_flag=$arg ;;
|
|
24
|
+
-AVD=*) avd_name=${arg#-AVD=} ;;
|
|
25
|
+
*) url_keys+=("$arg") ;;
|
|
26
|
+
esac
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
if [ -z "$env_flag" ] || [ ${#url_keys[@]} -eq 0 ]; then
|
|
30
|
+
echo "Usage: android-apk-emulator-prod.sh -PENV=PROD [-AVD=name] URL_VAR..."
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
env_name=$(gradle_env "$env_flag")
|
|
35
|
+
app_id=$(debug_app_id "$env_name" "$root")
|
|
36
|
+
|
|
37
|
+
resolve_host() {
|
|
38
|
+
dig +time=3 +tries=1 "$1" A +short | awk '/^[0-9]+(\.[0-9]+){3}$/ { print; exit }'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
wait_boot() {
|
|
42
|
+
adb -s "$1" wait-for-device
|
|
43
|
+
i=0
|
|
44
|
+
while [ "$i" -lt 90 ]; do
|
|
45
|
+
boot=$(adb -s "$1" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
|
|
46
|
+
[ "$boot" = "1" ] && return 0
|
|
47
|
+
sleep 2
|
|
48
|
+
i=$((i + 1))
|
|
49
|
+
done
|
|
50
|
+
echo "Эмулятор не загрузился"
|
|
51
|
+
exit 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
emulator_serial() {
|
|
55
|
+
adb devices | awk '$2=="device" && $1~/^emulator-/ { print $1; exit }'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
start_avd() {
|
|
59
|
+
local avd="$avd_name"
|
|
60
|
+
if [ -z "$avd" ]; then
|
|
61
|
+
avd=$(emulator -list-avds | awk 'NF { print; exit }')
|
|
62
|
+
elif ! emulator -list-avds | grep -qx "$avd"; then
|
|
63
|
+
echo "Нет AVD $avd"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
[ -n "$avd" ] || {
|
|
67
|
+
echo "Нет AVD"
|
|
68
|
+
exit 1
|
|
69
|
+
}
|
|
70
|
+
echo "→ старт $avd -writable-system"
|
|
71
|
+
emulator -avd "$avd" -writable-system -no-snapshot-load -no-boot-anim >/dev/null 2>&1 &
|
|
72
|
+
adb wait-for-device
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
serial="${ANDROID_SERIAL:-}"
|
|
76
|
+
[ -n "$serial" ] || serial=$(emulator_serial)
|
|
77
|
+
|
|
78
|
+
if [ -n "$serial" ]; then
|
|
79
|
+
adb -s "$serial" root >/dev/null
|
|
80
|
+
if ! adb -s "$serial" shell "touch /system/etc/.rw-test && rm /system/etc/.rw-test" >/dev/null 2>&1; then
|
|
81
|
+
echo "→ перезапуск: системный раздел только для чтения"
|
|
82
|
+
adb -s "$serial" emu kill >/dev/null 2>&1 || true
|
|
83
|
+
sleep 2
|
|
84
|
+
serial=
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [ -z "$serial" ]; then
|
|
89
|
+
start_avd
|
|
90
|
+
serial=$(emulator_serial)
|
|
91
|
+
wait_boot "$serial"
|
|
92
|
+
adb -s "$serial" root
|
|
93
|
+
adb -s "$serial" remount || {
|
|
94
|
+
adb -s "$serial" disable-verity
|
|
95
|
+
adb -s "$serial" reboot
|
|
96
|
+
wait_boot "$serial"
|
|
97
|
+
adb -s "$serial" root
|
|
98
|
+
adb -s "$serial" remount
|
|
99
|
+
}
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
host_lines=()
|
|
103
|
+
host_names=()
|
|
104
|
+
|
|
105
|
+
read_url_host() {
|
|
106
|
+
local value
|
|
107
|
+
value=$(env_value "$1" "$root")
|
|
108
|
+
printf '%s' "$value" | sed -E 's#^https?://##; s#/.*##; s#:.*##'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for key in "${url_keys[@]}"; do
|
|
112
|
+
name=$(read_url_host "$key")
|
|
113
|
+
[ -n "$name" ] || {
|
|
114
|
+
echo "Нет $key в .env.defaults"
|
|
115
|
+
exit 1
|
|
116
|
+
}
|
|
117
|
+
ip=$(resolve_host "$name")
|
|
118
|
+
[ -n "$ip" ] || {
|
|
119
|
+
echo "Не резолвится $name ($key)"
|
|
120
|
+
exit 1
|
|
121
|
+
}
|
|
122
|
+
host_names+=("$name")
|
|
123
|
+
host_lines+=("$ip $name")
|
|
124
|
+
done
|
|
125
|
+
|
|
126
|
+
current_hosts=$(adb -s "$serial" shell cat /system/etc/hosts | tr -d '\r')
|
|
127
|
+
{
|
|
128
|
+
while IFS= read -r line; do
|
|
129
|
+
name="${line##* }"
|
|
130
|
+
skip=
|
|
131
|
+
for managed_name in "${host_names[@]}"; do
|
|
132
|
+
if [ "$name" = "$managed_name" ]; then
|
|
133
|
+
skip=1
|
|
134
|
+
break
|
|
135
|
+
fi
|
|
136
|
+
done
|
|
137
|
+
[ -n "$skip" ] || printf '%s\n' "$line"
|
|
138
|
+
done <<< "$current_hosts"
|
|
139
|
+
printf '%s\n' "${host_lines[@]}"
|
|
140
|
+
} | adb -s "$serial" shell "cat > /data/local/tmp/hosts && cp /data/local/tmp/hosts /system/etc/hosts"
|
|
141
|
+
|
|
142
|
+
echo "→ $serial (ENV=$env_name, $app_id, hosts)"
|
|
143
|
+
install_debug_apk "$env_name" "$app_id" "$serial" "$root"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Общие шаги для app/*.sh. Файл только подключают через source.
|
|
4
|
+
|
|
5
|
+
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
|
6
|
+
|
|
7
|
+
android_repo_root() {
|
|
8
|
+
app_root
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
gradle_env() {
|
|
12
|
+
local raw="${1:-}"
|
|
13
|
+
local name="${raw#-PENV=}"
|
|
14
|
+
|
|
15
|
+
if [ -z "$name" ] || [ "$name" = "$raw" ]; then
|
|
16
|
+
echo "Нужен -PENV=DEV|STAGE|PROD, получено: ${raw:-пусто}" >&2
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
printf '%s\n' "$name"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
debug_app_id() {
|
|
24
|
+
local env_name="$1"
|
|
25
|
+
local root="$2"
|
|
26
|
+
local app_id
|
|
27
|
+
|
|
28
|
+
app_id=$(
|
|
29
|
+
awk -F'"' '/^[[:space:]]*def appId = / { print $2; exit }' \
|
|
30
|
+
"$root/android/app/build.gradle"
|
|
31
|
+
)
|
|
32
|
+
[ -n "$app_id" ] || {
|
|
33
|
+
echo "Не найден appId в android/app/build.gradle" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Как defaultConfig: PROD без суффикса окружения, debug всегда .debug.
|
|
38
|
+
if [ "$env_name" != "PROD" ]; then
|
|
39
|
+
app_id="${app_id}.${env_name}"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
printf '%s\n' "${app_id}.debug"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
android_serial() {
|
|
46
|
+
local target="$1"
|
|
47
|
+
|
|
48
|
+
if [ -n "${ANDROID_SERIAL:-}" ]; then
|
|
49
|
+
printf '%s\n' "$ANDROID_SERIAL"
|
|
50
|
+
return
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [ "$target" = "device" ]; then
|
|
54
|
+
adb devices | awk '$2=="device" && $1!~/^emulator-/ { print $1; exit }'
|
|
55
|
+
return
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
adb devices | awk '$2=="device" && $1~/^emulator-/ { print $1; exit }'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
require_android_serial() {
|
|
62
|
+
local target="$1"
|
|
63
|
+
local serial
|
|
64
|
+
|
|
65
|
+
serial=$(android_serial "$target")
|
|
66
|
+
if [ -z "$serial" ]; then
|
|
67
|
+
if [ "$target" = "device" ]; then
|
|
68
|
+
echo "Нет устройства:"
|
|
69
|
+
else
|
|
70
|
+
echo "Нет эмулятора:"
|
|
71
|
+
fi
|
|
72
|
+
adb devices
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
printf '%s\n' "$serial"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
install_debug_apk() {
|
|
80
|
+
local env_name="$1"
|
|
81
|
+
local app_id="$2"
|
|
82
|
+
local serial="$3"
|
|
83
|
+
local root="$4"
|
|
84
|
+
local apk_dir="$root/android/app/build/outputs/apk/debug"
|
|
85
|
+
local apk
|
|
86
|
+
|
|
87
|
+
(cd "$root/android" && ./gradlew assembleDebug -x lint -PENV="$env_name")
|
|
88
|
+
|
|
89
|
+
if ls "$apk_dir"/*"$env_name"*.apk >/dev/null 2>&1; then
|
|
90
|
+
apk=$(ls -t "$apk_dir"/*"$env_name"*.apk | head -1)
|
|
91
|
+
else
|
|
92
|
+
apk=$(
|
|
93
|
+
ls -t "$apk_dir"/*.apk |
|
|
94
|
+
awk '!/STAGE/ && !/DEV/ && !/BETA/ { print; exit }'
|
|
95
|
+
)
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
[ -n "$apk" ] || {
|
|
99
|
+
echo "APK не найден"
|
|
100
|
+
exit 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
(cd "$root" && npx react-native run-android --device "$serial" --binary-path "$apk" --appId "$app_id")
|
|
104
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
source "$(dirname "$0")/android-common.sh"
|
|
6
|
+
|
|
7
|
+
print_help() {
|
|
8
|
+
echo "Пишет Perfetto-трейс с подключённого Android-устройства."
|
|
9
|
+
echo
|
|
10
|
+
echo "Пока команда не закончилась, на устройстве откройте приложение"
|
|
11
|
+
echo "или сделайте жест, который хотите разобрать (чипы, переход экрана)."
|
|
12
|
+
echo "Файл откройте на https://ui.perfetto.dev"
|
|
13
|
+
echo
|
|
14
|
+
echo "Usage: android-perfetto.sh device|emulator -PENV=STAGE [TRACE_SECONDS] [output.pftrace]"
|
|
15
|
+
echo
|
|
16
|
+
echo " device|emulator USB-терминал или уже запущенный эмулятор"
|
|
17
|
+
echo " -PENV пакет debug для этого окружения"
|
|
18
|
+
echo " TRACE_SECONDS длина записи, по умолчанию 15"
|
|
19
|
+
echo
|
|
20
|
+
echo "Холодный старт: перед запуском adb shell am force-stop <пакет>,"
|
|
21
|
+
echo "затем в окне записи откройте приложение с иконки."
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
25
|
+
print_help
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
target="${1:-}"
|
|
30
|
+
TRACE_SECONDS="${3:-15}"
|
|
31
|
+
OUTPUT_FILE="${4:-trace.pftrace}"
|
|
32
|
+
REMOTE_PATH="/data/misc/perfetto-traces/trace.pftrace"
|
|
33
|
+
|
|
34
|
+
if [[ "$target" != "device" && "$target" != "emulator" ]] || [[ -z "${2:-}" ]]; then
|
|
35
|
+
print_help
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
env_name=$(gradle_env "$2")
|
|
40
|
+
APP_ID=$(debug_app_id "$env_name" "$(android_repo_root)")
|
|
41
|
+
|
|
42
|
+
serial=$(require_android_serial "$target")
|
|
43
|
+
export ANDROID_SERIAL="$serial"
|
|
44
|
+
echo "→ $serial"
|
|
45
|
+
|
|
46
|
+
if ! [[ "$TRACE_SECONDS" =~ ^[0-9]+$ ]] || [[ "$TRACE_SECONDS" -lt 1 ]]; then
|
|
47
|
+
echo "TRACE_SECONDS должно быть числом больше 0, получено: $TRACE_SECONDS"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
DURATION_MS=$((TRACE_SECONDS * 1000))
|
|
52
|
+
|
|
53
|
+
# Android 11 (API 30, SL20) не знает поля Perfetto, которые есть на эмуляторе с Android 12+.
|
|
54
|
+
SDK="$(adb shell getprop ro.build.version.sdk | tr -d '\r')"
|
|
55
|
+
FTRACE_EXTRA=""
|
|
56
|
+
SYS_STATS_EXTRA=""
|
|
57
|
+
FRAME_TIMELINE=""
|
|
58
|
+
if [[ "$SDK" -ge 31 ]]; then
|
|
59
|
+
FTRACE_EXTRA="
|
|
60
|
+
symbolize_ksyms: true
|
|
61
|
+
disable_generic_events: true"
|
|
62
|
+
SYS_STATS_EXTRA="
|
|
63
|
+
cpufreq_period_ms: 250"
|
|
64
|
+
FRAME_TIMELINE="
|
|
65
|
+
data_sources {
|
|
66
|
+
config {
|
|
67
|
+
name: \"android.surfaceflinger.frametimeline\"
|
|
68
|
+
}
|
|
69
|
+
}"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
CONFIG=$(cat <<EOF
|
|
73
|
+
buffers {
|
|
74
|
+
size_kb: 131072
|
|
75
|
+
fill_policy: DISCARD
|
|
76
|
+
}
|
|
77
|
+
data_sources {
|
|
78
|
+
config {
|
|
79
|
+
name: "linux.ftrace"
|
|
80
|
+
ftrace_config {
|
|
81
|
+
ftrace_events: "sched/sched_switch"
|
|
82
|
+
ftrace_events: "sched/sched_wakeup"
|
|
83
|
+
ftrace_events: "sched/sched_waking"
|
|
84
|
+
ftrace_events: "sched/sched_blocked_reason"
|
|
85
|
+
ftrace_events: "power/cpu_frequency"
|
|
86
|
+
ftrace_events: "power/cpu_idle"
|
|
87
|
+
ftrace_events: "ftrace/print"
|
|
88
|
+
atrace_categories: "gfx"
|
|
89
|
+
atrace_categories: "input"
|
|
90
|
+
atrace_categories: "view"
|
|
91
|
+
atrace_categories: "wm"
|
|
92
|
+
atrace_categories: "am"
|
|
93
|
+
atrace_categories: "dalvik"
|
|
94
|
+
atrace_categories: "adb"
|
|
95
|
+
atrace_categories: "binder_driver"${FTRACE_EXTRA}
|
|
96
|
+
atrace_apps: "$APP_ID"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
data_sources {
|
|
101
|
+
config {
|
|
102
|
+
name: "linux.process_stats"
|
|
103
|
+
process_stats_config {
|
|
104
|
+
scan_all_processes_on_start: true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
data_sources {
|
|
109
|
+
config {
|
|
110
|
+
name: "linux.sys_stats"
|
|
111
|
+
sys_stats_config {
|
|
112
|
+
stat_period_ms: 250
|
|
113
|
+
stat_counters: STAT_CPU_TIMES${SYS_STATS_EXTRA}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}${FRAME_TIMELINE}
|
|
117
|
+
duration_ms: $DURATION_MS
|
|
118
|
+
EOF
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
LOCAL_CONFIG="$(mktemp)"
|
|
122
|
+
trap 'rm -f "$LOCAL_CONFIG"' EXIT
|
|
123
|
+
|
|
124
|
+
printf '%s\n' "$CONFIG" > "$LOCAL_CONFIG"
|
|
125
|
+
|
|
126
|
+
echo "Запись ${TRACE_SECONDS} с для $APP_ID"
|
|
127
|
+
echo "Сейчас откройте приложение или переключите экран. Окно закроется само."
|
|
128
|
+
# Конфиг в stdin: на части ТСД нет /data/misc/perfetto-configs, push туда даёт Permission denied.
|
|
129
|
+
adb shell perfetto -c - --txt -o "$REMOTE_PATH" < "$LOCAL_CONFIG"
|
|
130
|
+
|
|
131
|
+
echo "Скачиваю ./$OUTPUT_FILE"
|
|
132
|
+
adb pull "$REMOTE_PATH" "$OUTPUT_FILE"
|
|
133
|
+
echo "Готово: $OUTPUT_FILE — откройте на https://ui.perfetto.dev"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Проверяет, что URL из .env.defaults (или .env) отвечает.
|
|
4
|
+
#
|
|
5
|
+
# Usage: check-sentry.sh VAR
|
|
6
|
+
# VAR имя переменной, например SENTRY_URL
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
source "$(dirname "$0")/common.sh"
|
|
11
|
+
|
|
12
|
+
root=$(app_root)
|
|
13
|
+
cd "$root"
|
|
14
|
+
|
|
15
|
+
key="${1:-}"
|
|
16
|
+
if [ -z "$key" ]; then
|
|
17
|
+
echo "Usage: check-sentry.sh VAR"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
url=$(env_value "$key" "$root")
|
|
22
|
+
|
|
23
|
+
[ -n "$url" ] || {
|
|
24
|
+
echo "Нет $key в .env.defaults"
|
|
25
|
+
exit 1
|
|
26
|
+
}
|
|
27
|
+
curl -kI --connect-timeout 8 --max-time 20 -sS -o /dev/null "$url" || {
|
|
28
|
+
echo "Sentry недоступен: $url"
|
|
29
|
+
exit 1
|
|
30
|
+
}
|
package/app/common.sh
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Общие шаги для скриптов приложения. Файл только подключают через source.
|
|
4
|
+
# Запускать из корня приложения: каталог пакета в node_modules — не корень.
|
|
5
|
+
|
|
6
|
+
app_root() {
|
|
7
|
+
pwd
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
env_value() {
|
|
11
|
+
local key="$1"
|
|
12
|
+
local root="$2"
|
|
13
|
+
local files=()
|
|
14
|
+
local missing=()
|
|
15
|
+
|
|
16
|
+
if [ -f "$root/.env.defaults" ]; then
|
|
17
|
+
files+=("$root/.env.defaults")
|
|
18
|
+
else
|
|
19
|
+
missing+=("$root/.env.defaults")
|
|
20
|
+
fi
|
|
21
|
+
if [ -f "$root/.env" ]; then
|
|
22
|
+
files+=("$root/.env")
|
|
23
|
+
else
|
|
24
|
+
missing+=("$root/.env")
|
|
25
|
+
fi
|
|
26
|
+
if [ ${#missing[@]} -gt 0 ]; then
|
|
27
|
+
echo "Нет ${missing[*]}" >&2
|
|
28
|
+
fi
|
|
29
|
+
[ ${#files[@]} -gt 0 ] || exit 1
|
|
30
|
+
|
|
31
|
+
awk -F= -v key="$key" '
|
|
32
|
+
$1 == key { value = substr($0, index($0, "=") + 1) }
|
|
33
|
+
END { print value }
|
|
34
|
+
' "${files[@]}" | tr -d "\"'"
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itcase/config",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.29",
|
|
4
4
|
"author": "ITCase",
|
|
5
5
|
"description": "ITCase Config",
|
|
6
6
|
"engines": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"url": "https://github.com/ITCase/itcase-config.git"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
+
"app",
|
|
28
29
|
"commitlint",
|
|
29
30
|
"husky-hooks",
|
|
30
31
|
"lint-staged",
|
|
@@ -2,25 +2,18 @@ import { execFileSync } from 'node:child_process'
|
|
|
2
2
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
|
|
5
|
-
const MAILRU_WEBDAV_BASE = 'https://webdav.cloud.mail.ru'
|
|
6
|
-
|
|
7
5
|
/**
|
|
8
|
-
* Prepare: bump Android → make APK
|
|
6
|
+
* Prepare: bump Android → make APK.
|
|
7
|
+
* Upload в Mail.ru — отдельный CI action, не здесь.
|
|
9
8
|
*
|
|
10
|
-
* Env: SKIP_APK_BUILD, RELEASE_APK_TARGETS (stage,prod)
|
|
11
|
-
* MAILRU_USER, MAILRU_APP_PASSWORD, MAILRU_PUBLIC_URL.
|
|
9
|
+
* Env: SKIP_APK_BUILD, RELEASE_APK_TARGETS (stage,prod).
|
|
12
10
|
*
|
|
13
|
-
* @param {{ apkPrefix: string
|
|
11
|
+
* @param {{ apkPrefix: string }} options
|
|
14
12
|
*/
|
|
15
|
-
function createAndroidAppPreparePlugin({ apkPrefix
|
|
13
|
+
function createAndroidAppPreparePlugin({ apkPrefix }) {
|
|
16
14
|
if (!apkPrefix) {
|
|
17
15
|
throw new Error('createAndroidAppPreparePlugin: apkPrefix is required')
|
|
18
16
|
}
|
|
19
|
-
if (!mailruDir) {
|
|
20
|
-
throw new Error('createAndroidAppPreparePlugin: mailruDir is required')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const uploadDir = mailruDir.replace(/^\/+|\/+$/g, '')
|
|
24
17
|
|
|
25
18
|
return {
|
|
26
19
|
prepare: async (_pluginConfig, context) => {
|
|
@@ -39,8 +32,6 @@ function createAndroidAppPreparePlugin({ apkPrefix, mailruDir }) {
|
|
|
39
32
|
logger.log(`make ${apk.make}`)
|
|
40
33
|
execFileSync('make', [apk.make], { stdio: 'inherit' })
|
|
41
34
|
}
|
|
42
|
-
|
|
43
|
-
uploadApks(apks, uploadDir, logger)
|
|
44
35
|
},
|
|
45
36
|
}
|
|
46
37
|
}
|
|
@@ -77,15 +68,15 @@ function bumpAndroidVersion(version, logger) {
|
|
|
77
68
|
/** @returns {{ make: string, name: string, fileName: string }[]} */
|
|
78
69
|
function resolveApks(apkPrefix, version) {
|
|
79
70
|
const defs = {
|
|
80
|
-
prod: {
|
|
81
|
-
name: 'PROD',
|
|
82
|
-
fileName: `${apkPrefix}-v${version}.apk`,
|
|
83
|
-
make: 'android-apk-prod',
|
|
84
|
-
},
|
|
85
71
|
stage: {
|
|
72
|
+
make: 'android-apk-stage',
|
|
86
73
|
name: 'STAGE',
|
|
87
74
|
fileName: `${apkPrefix}-v${version}-STAGE.apk`,
|
|
88
|
-
|
|
75
|
+
},
|
|
76
|
+
prod: {
|
|
77
|
+
make: 'android-apk-prod',
|
|
78
|
+
name: 'PROD',
|
|
79
|
+
fileName: `${apkPrefix}-v${version}.apk`,
|
|
89
80
|
},
|
|
90
81
|
}
|
|
91
82
|
|
|
@@ -109,53 +100,4 @@ function resolveApks(apkPrefix, version) {
|
|
|
109
100
|
})
|
|
110
101
|
}
|
|
111
102
|
|
|
112
|
-
function uploadApks(apks, uploadDir, logger) {
|
|
113
|
-
if (process.env.GIT_WORKFLOW !== 'true') {
|
|
114
|
-
logger.log('Skip Mail.ru upload (not CI)')
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const user = process.env.MAILRU_USER
|
|
119
|
-
const password = process.env.MAILRU_APP_PASSWORD
|
|
120
|
-
const publicUrl = (process.env.MAILRU_PUBLIC_URL || '').replace(/\/$/, '')
|
|
121
|
-
|
|
122
|
-
if (!user || !password || !publicUrl) {
|
|
123
|
-
throw new Error(
|
|
124
|
-
'MAILRU_USER, MAILRU_APP_PASSWORD and MAILRU_PUBLIC_URL are required',
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const uploadBase = `${MAILRU_WEBDAV_BASE}/${uploadDir}`
|
|
129
|
-
const downloads = []
|
|
130
|
-
|
|
131
|
-
for (const apk of apks) {
|
|
132
|
-
logger.log(`Upload ${apk.fileName} to ${uploadBase}/`)
|
|
133
|
-
execFileSync(
|
|
134
|
-
'curl',
|
|
135
|
-
[
|
|
136
|
-
'--fail',
|
|
137
|
-
'--silent',
|
|
138
|
-
'--show-error',
|
|
139
|
-
'--retry',
|
|
140
|
-
'2',
|
|
141
|
-
'--retry-delay',
|
|
142
|
-
'2',
|
|
143
|
-
'-T',
|
|
144
|
-
join(process.cwd(), 'builds', apk.fileName),
|
|
145
|
-
'--user',
|
|
146
|
-
`${user}:${password}`,
|
|
147
|
-
`${uploadBase}/${apk.fileName}`,
|
|
148
|
-
],
|
|
149
|
-
{ stdio: 'inherit' },
|
|
150
|
-
)
|
|
151
|
-
downloads.push({ name: apk.name, url: `${publicUrl}/${apk.fileName}` })
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
writeFileSync(
|
|
155
|
-
join(process.cwd(), 'builds', 'mailru-downloads.json'),
|
|
156
|
-
JSON.stringify(downloads),
|
|
157
|
-
)
|
|
158
|
-
logger.log('APK upload finished — Mail.ru links saved')
|
|
159
|
-
}
|
|
160
|
-
|
|
161
103
|
export { createAndroidAppPreparePlugin }
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createSemanticReleaseApp,
|
|
3
|
+
semanticReleaseApp,
|
|
4
|
+
} from './semanticReleaseApp.js'
|
|
2
5
|
import { semanticReleaseBot } from './semanticReleaseBot.js'
|
|
3
6
|
import { semanticReleaseDoc } from './semanticReleaseDoc.js'
|
|
4
7
|
import { semanticReleaseMonorepo } from './semanticReleaseMonorepo.js'
|
|
@@ -7,6 +10,7 @@ import { semanticReleaseProject } from './semanticReleaseProject.js'
|
|
|
7
10
|
import { semanticReleaseVSMarketplace } from './semanticReleaseVSMarketplace.js'
|
|
8
11
|
|
|
9
12
|
export {
|
|
13
|
+
createSemanticReleaseApp,
|
|
10
14
|
semanticReleaseApp,
|
|
11
15
|
semanticReleaseBot,
|
|
12
16
|
semanticReleaseDoc,
|
|
@@ -21,13 +21,13 @@ const DEFAULT_RELEASE_ASSETS = ['builds/*.apk']
|
|
|
21
21
|
* - Прикрепляет к GitHub release APK из `builds/`
|
|
22
22
|
* - Заметки релиза: сначала задачи, потом коммиты
|
|
23
23
|
*
|
|
24
|
-
* Если передан `apk`, в prepare: bump → make
|
|
24
|
+
* Если передан `apk`, в prepare: bump → make.
|
|
25
|
+
* Upload в Mail.ru — отдельный GitHub Action (не здесь).
|
|
25
26
|
*
|
|
26
27
|
* @param {Object} [options]
|
|
27
28
|
* @param {string|string[]} [options.branches=['master']]
|
|
28
29
|
* @param {Object} [options.apk]
|
|
29
30
|
* @param {string} options.apk.prefix - agro-pps → builds/agro-pps-v*.apk
|
|
30
|
-
* @param {string} options.apk.mailruDir - папка WebDAV, напр. APK
|
|
31
31
|
* @param {string[]} [options.versionAssets]
|
|
32
32
|
* @param {string[]|Object[]} [options.releaseAssets] - по умолчанию builds/{prefix}-v*.apk
|
|
33
33
|
* @param {Array} [options.preparePlugins]
|
|
@@ -44,17 +44,14 @@ function semanticReleaseApp({
|
|
|
44
44
|
versionAssets = DEFAULT_VERSION_ASSETS,
|
|
45
45
|
} = {}) {
|
|
46
46
|
const androidPrepare = apk
|
|
47
|
-
? [
|
|
48
|
-
createAndroidAppPreparePlugin({
|
|
49
|
-
apkPrefix: apk.prefix,
|
|
50
|
-
mailruDir: apk.mailruDir,
|
|
51
|
-
}),
|
|
52
|
-
]
|
|
47
|
+
? [createAndroidAppPreparePlugin({ apkPrefix: apk.prefix })]
|
|
53
48
|
: []
|
|
54
49
|
|
|
55
50
|
const githubAssets =
|
|
56
51
|
releaseAssets ??
|
|
57
|
-
(apk?.prefix
|
|
52
|
+
(apk?.prefix
|
|
53
|
+
? [`builds/${apk.prefix}-v*.apk`]
|
|
54
|
+
: DEFAULT_RELEASE_ASSETS)
|
|
58
55
|
|
|
59
56
|
return createSemanticReleaseConfig({
|
|
60
57
|
additionalPlugins: [releaseNotesFromIssuesPlugin],
|
|
@@ -76,4 +73,7 @@ function semanticReleaseApp({
|
|
|
76
73
|
})
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
|
|
76
|
+
/** @deprecated используй semanticReleaseApp — оставлено для совместимости */
|
|
77
|
+
const createSemanticReleaseApp = semanticReleaseApp
|
|
78
|
+
|
|
79
|
+
export { createSemanticReleaseApp, semanticReleaseApp }
|