-
-
Notifications
You must be signed in to change notification settings - Fork 470
feat(android): Add standalone app start tracing #5342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
buenaflor
wants to merge
45
commits into
main
Choose a base branch
from
feat/standalone-app-start-tracing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,378
−129
Open
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
ddd9bed
feat: Add standalone app start transaction (happy path)
buenaflor fdd26df
feat: Add non-activity app start path with end time resolution
buenaflor e5b7a18
feat: Support non-activity app start tracing without bytecode instrum…
buenaflor 11898dc
refactor: Consolidate non-activity app start time-span resolution
buenaflor 5efab1d
chore(samples): Register TestBroadcastReceiver in manifest
buenaflor f6c070d
Merge remote-tracking branch 'origin/main' into feat/standalone-app-s…
buenaflor 215a549
fix(app-start): resolve standalone tracing misclassification and dupl…
buenaflor 50a2f41
fix(android): refine standalone app start tracing
buenaflor 26a83cc
chore: Update generated files
buenaflor a60d966
style(core): Apply spotless formatting
buenaflor 09bac52
changelog
buenaflor 5387e76
Merge branch 'main' into feat/standalone-app-start-tracing
buenaflor f7c9c62
fix(android): Use stable app start transaction name
buenaflor 9adfa01
feat(android): Add standalone app start tracing
buenaflor 87ff0d6
fix(android): Handle non-activity app starts below API 24
buenaflor 706adad
fix(android): Guard app start timestamp clock base
buenaflor 9bf80c4
Merge branch 'main' into feat/standalone-app-start-tracing
buenaflor 5d6174e
ref(android): Remove app start reason plumbing
buenaflor dfb5aea
Merge remote-tracking branch 'origin/feat/standalone-app-start-tracin…
buenaflor e8be515
ref(android): Clarify no-activity app start handling
buenaflor b5a6336
docs(android): Clarify non-activity app start fallback
buenaflor f3ef6ce
fix(android): Preserve legacy no-activity app start guard
buenaflor 9da8369
test(android): Opt into standalone no-activity API 35 tests
buenaflor 72268a3
fix(android): Schedule no-activity idle check when standalone listene…
buenaflor cbaebe2
ref(android): Remove dead foregroundImportance check in standalone ap…
buenaflor b1d738b
fix(android): Prevent duplicate standalone app start measurements
buenaflor 9f5cac5
ref(android): Remove unused app start application context
buenaflor 51c2bff
ref(android): Rename getAppStartTimeSpanDirect to getAppStartTimeSpan…
buenaflor b7fabc6
fix(android): Do not set TTID/TTFD contributing flags on standalone a…
buenaflor b871644
fix(android): Add volatile to noActivityStartedListener for cross-thr…
buenaflor e681be9
fix(android): Clear stale app start sampling decision in non-activity…
buenaflor cdc6178
fix: Format adb test commands in TestBroadcastReceiver JavaDoc
buenaflor 38ac42c
ref(android): Rename headless app start handling
buenaflor d1262c2
fix(android): Align foreground app start measurements
buenaflor be261f6
fix(android): Gate headless app start end time
buenaflor 41ebd25
test(android): Update API 35 headless app start expectation
buenaflor e0d714c
Fix headless app-start idle scheduling
buenaflor 58dda40
ref(android): Clarify headless app start state names
buenaflor 12719e6
Merge remote-tracking branch 'origin/main' into feat/standalone-app-s…
buenaflor 2bc059f
ref(android): Use app.start origin for headless app start transaction
buenaflor cf623f3
ref(android): refine standalone app start trace continuation
buenaflor 17a9c68
fix(android): align headless app start tests with uptime-based onCrea…
buenaflor 467d0ce
test(android): add standalone app start E2E harness
buenaflor 6e264d6
chore(android): remove standalone app start report
buenaflor e3ef325
test(android): clarify app start transaction shapes
buenaflor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,370 @@ | ||
| #!/usr/bin/env bash | ||
| # End-to-end test harness for standalone app start tracing (issue #5046). | ||
| # Exercises scenarios 1a, 1c, 2a-2f against two running emulators. | ||
| # Requires: adb, two running emulators (API 36 + API 33), JDK 17. | ||
|
|
||
| set -u | ||
|
|
||
| REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | ||
| cd "$REPO_ROOT" | ||
|
|
||
| export JAVA_HOME="${JAVA_HOME:-/Library/Java/JavaVirtualMachines/openjdk-17.jdk/Contents/Home}" | ||
|
|
||
| PKG="io.sentry.samples.android" | ||
| APK_SRC="sentry-samples/sentry-samples-android/build/outputs/apk/debug/sentry-samples-android-debug.apk" | ||
| EMU_API36="emulator-5554" | ||
| EMU_API33="emulator-5556" | ||
| OUT_DIR="/tmp/standalone-app-start-logs" | ||
| APK_A="$OUT_DIR/APK-A.apk" # flag=on, simulate-plugin=on (tier 1) | ||
| APK_B="$OUT_DIR/APK-B.apk" # flag=off, simulate-plugin=off (regression) | ||
| APK_C="$OUT_DIR/APK-C.apk" # flag=on, simulate-plugin=off (tier 2 / tier 3) | ||
| MAIN_ACTIVITY="${PKG}/.MainActivity" | ||
| RECEIVER="${PKG}/.TestBroadcastReceiver" | ||
| BROADCAST_ACTION="io.sentry.samples.android.TEST_BROADCAST" | ||
| SERVICE="${PKG}/.DummyService" | ||
|
|
||
| # Wait durations (seconds). Must exceed the transaction deadline timeout (30s default) | ||
| # because the sample MainActivity keeps the main thread busy so idleTimeout doesn't fire. | ||
| WAIT_ACTIVITY=35 | ||
| WAIT_BROADCAST=8 | ||
| WAIT_COMBO=40 | ||
| # Max time we wait for Sentry to actually flush cached envelopes to sentry.io after we | ||
| # foreground the app. Emulators often fail on IPv6 first and take ~60-75s before falling | ||
| # back to IPv4 on the transport's retry, so 120s gives comfortable slack. | ||
| DELIVERY_TIMEOUT=120 | ||
|
|
||
| mkdir -p "$OUT_DIR" | ||
| rm -f "$OUT_DIR"/*.log | ||
|
|
||
| PASS=0 | ||
| FAIL=0 | ||
| FAIL_LINES=() | ||
|
|
||
| red() { printf "\033[31m%s\033[0m" "$*"; } | ||
| green() { printf "\033[32m%s\033[0m" "$*"; } | ||
| bold() { printf "\033[1m%s\033[0m" "$*"; } | ||
|
|
||
| banner() { echo ""; bold "========= $* ========="; echo ""; } | ||
|
|
||
| build_apk() { | ||
| local flag=$1 simulate=$2 dest=$3 label=$4 | ||
| echo "--- build $label: -PstandaloneAppStart=$flag -PsimulateSentryGradlePlugin=$simulate ---" | ||
| ./gradlew :sentry-samples:sentry-samples-android:assembleDebug \ | ||
| -PstandaloneAppStart="$flag" \ | ||
| -PsimulateSentryGradlePlugin="$simulate" \ | ||
| -q 2>&1 | tail -3 | ||
| if [[ ! -f "$APK_SRC" ]]; then | ||
| echo "BUILD FAILED: APK not produced" | ||
| exit 1 | ||
| fi | ||
| cp "$APK_SRC" "$dest" | ||
| echo " saved -> $dest" | ||
| } | ||
|
|
||
| install_from() { | ||
| local device=$1 apk=$2 | ||
| echo "--- install $(basename $apk) on $device ---" | ||
| adb -s "$device" install -r -t "$apk" 2>&1 | tail -1 | ||
| } | ||
|
|
||
| wake_device() { | ||
| local device=$1 | ||
| adb -s "$device" shell input keyevent KEYCODE_WAKEUP >/dev/null 2>&1 || true | ||
| adb -s "$device" shell input keyevent 82 >/dev/null 2>&1 || true | ||
| } | ||
|
|
||
| prep_for_run() { | ||
| local device=$1 | ||
| adb -s "$device" shell am force-stop "$PKG" | ||
| adb -s "$device" logcat -c | ||
| # brief pause to let system settle | ||
| sleep 0.3 | ||
| } | ||
|
|
||
| dump_log() { | ||
| local device=$1 file=$2 | ||
| adb -s "$device" logcat -d -v time SentryE2E:D '*:S' > "$file" | ||
| # Also capture full Sentry SDK debug log for diagnostics. | ||
| adb -s "$device" logcat -d -v time Sentry:D SentryE2E:D StrictMode:D AndroidRuntime:E '*:S' \ | ||
| > "${file%.log}.full.log" | ||
| } | ||
|
|
||
| # Extract traceIds of logged transactions in order. | ||
| extract_traceids() { | ||
| local file=$1 | ||
| grep -oE 'traceId=[a-f0-9]+' "$file" | sed 's/traceId=//' | ||
| } | ||
|
|
||
| # Extract unique names of logged transactions in order. | ||
| extract_names() { | ||
| local file=$1 | ||
| awk '/SentryE2E.*TXN\|/ { | ||
| match($0, /name=[^|]+/); n = substr($0, RSTART+5, RLENGTH-5); print n | ||
| }' "$file" | ||
| } | ||
|
|
||
| assert() { | ||
| local desc=$1 cmd=$2 | ||
| if eval "$cmd"; then | ||
| echo " $(green PASS) $desc" | ||
| PASS=$((PASS+1)) | ||
| else | ||
| echo " $(red FAIL) $desc" | ||
| FAIL=$((FAIL+1)) | ||
| FAIL_LINES+=("$desc") | ||
| fi | ||
| } | ||
|
|
||
| # Post-scenario drain: foreground the app (via MainActivity) so AndroidConnectionStatusProvider | ||
| # flips from DISCONNECTED → CONNECTED (it reports null during broadcast-only cold starts), | ||
| # then poll logcat until at least one "Envelope sent successfully" appears. Safe to call after | ||
| # TXN assertions are done; the extra MainActivity launch emits its own ui.load but by then the | ||
| # scenario log was already captured. | ||
| verify_delivery() { | ||
| local device=$1 scenario=$2 | ||
| adb -s "$device" shell am start -n "$MAIN_ACTIVITY" >/dev/null | ||
| local drain_log="$OUT_DIR/${scenario}.delivery.log" | ||
| local deadline=$((SECONDS + DELIVERY_TIMEOUT)) | ||
| local sent=0 | ||
| echo " polling for envelope delivery (timeout ${DELIVERY_TIMEOUT}s)..." | ||
| while (( SECONDS < deadline )); do | ||
| adb -s "$device" logcat -d -v time Sentry:D '*:S' > "$drain_log" | ||
| sent=$(grep -c 'Envelope sent successfully' "$drain_log" || true) | ||
| [[ "$sent" -gt 0 ]] && break | ||
| sleep 3 | ||
| done | ||
| local queued | ||
| queued=$(grep -c 'Adding Envelope to offline storage' "$drain_log" || true) | ||
| assert "delivery: envelopes sent to Sentry (sent=$sent queued=$queued)" \ | ||
| "[[ '$sent' -gt '0' ]]" | ||
| } | ||
|
|
||
| # ================= SCENARIOS ================= | ||
|
|
||
| build_needed_variants() { | ||
| banner "Build variants" | ||
| [[ $needs_A -eq 1 ]] && build_apk true true "$APK_A" "APK-A (flag=on, simulate-plugin=on)" | ||
| [[ $needs_B -eq 1 ]] && build_apk false false "$APK_B" "APK-B (flag=off, simulate-plugin=off)" | ||
| [[ $needs_C -eq 1 ]] && build_apk true false "$APK_C" "APK-C (flag=on, simulate-plugin=off)" | ||
| } | ||
|
|
||
| scenario_1a() { | ||
| banner "1a: Cold + flag ON (launcher) — API 36 / APK-A" | ||
| install_from "$EMU_API36" "$APK_A" | ||
| wake_device "$EMU_API36" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am start -n "$MAIN_ACTIVITY" >/dev/null | ||
| sleep $WAIT_ACTIVITY | ||
| local log="$OUT_DIR/1a.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "MainActivity ui.load txn emitted" \ | ||
| "grep -qE 'name=MainActivity.*op=ui\.load' '$log'" | ||
| assert "ui.load txn does NOT contain app.start.* child span" \ | ||
| "! grep -E 'name=MainActivity.*op=ui\.load' '$log' | grep -qE 'children=.*app\.start\.(cold|warm)'" | ||
| assert "standalone txn has process.load or application.load child" \ | ||
| "grep -E '\|op=app\.start\|' '$log' | grep -qE 'children=.*(process\.load|application\.load)'" | ||
| # Both txns share same traceId | ||
| local tids=$(extract_traceids "$log" | sort -u) | ||
| local count=$(echo "$tids" | wc -l | tr -d ' ') | ||
| assert "standalone + ui.load share traceId (unique traceId count = 1)" \ | ||
| "[[ '$count' == '1' ]]" | ||
| verify_delivery "$EMU_API36" "1a" | ||
| } | ||
|
|
||
| scenario_1c() { | ||
| banner "1c: Cold + flag OFF (regression) — API 36 / APK-B" | ||
| install_from "$EMU_API36" "$APK_B" | ||
| wake_device "$EMU_API36" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am start -n "$MAIN_ACTIVITY" >/dev/null | ||
| sleep $WAIT_ACTIVITY | ||
| local log="$OUT_DIR/1c.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "MainActivity ui.load txn emitted" \ | ||
| "grep -qE 'name=MainActivity.*op=ui\.load' '$log'" | ||
| assert "ui.load txn CONTAINS app.start.* child span (legacy)" \ | ||
| "grep -E 'name=MainActivity.*op=ui\.load' '$log' | grep -qE 'children=.*app\.start\.(cold|warm)'" | ||
| assert "NO standalone App Start txn emitted" \ | ||
| "! grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| verify_delivery "$EMU_API36" "1c" | ||
| } | ||
|
|
||
| scenario_2a() { | ||
| banner "2a: Broadcast cold, tier 1 (simulated plugin) — API 36 / APK-A" | ||
| install_from "$EMU_API36" "$APK_A" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am broadcast -a "$BROADCAST_ACTION" -n "$RECEIVER" >/dev/null | ||
| sleep $WAIT_BROADCAST | ||
| local log="$OUT_DIR/2a.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "standalone has process.load child" \ | ||
| "grep -qE '\|op=app\.start\|.*children=.*process\.load' '$log'" | ||
| assert "standalone has application.load child" \ | ||
| "grep -qE '\|op=app\.start\|.*children=.*application\.load' '$log'" | ||
| assert "NO ui.load txn emitted" \ | ||
| "! grep -qE 'op=ui\.load' '$log'" | ||
| verify_delivery "$EMU_API36" "2a" | ||
| } | ||
|
|
||
| scenario_2b() { | ||
| banner "2b: Broadcast cold, tier 2 (ApplicationStartInfo) — API 36 / APK-C" | ||
| install_from "$EMU_API36" "$APK_C" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am broadcast -a "$BROADCAST_ACTION" -n "$RECEIVER" >/dev/null | ||
| sleep $WAIT_BROADCAST | ||
| local log="$OUT_DIR/2b.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted (tier 2 API 35+)" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "standalone has process.load child" \ | ||
| "grep -qE '\|op=app\.start\|.*children=.*process\.load' '$log'" | ||
| assert "NO ui.load txn emitted" \ | ||
| "! grep -qE 'op=ui\.load' '$log'" | ||
| verify_delivery "$EMU_API36" "2b" | ||
| } | ||
|
|
||
| scenario_2c() { | ||
| banner "2c: Broadcast cold, tier 3 (CLASS_LOADED fallback) — API 33 / APK-C" | ||
| install_from "$EMU_API33" "$APK_C" | ||
| prep_for_run "$EMU_API33" | ||
| adb -s "$EMU_API33" shell am broadcast -a "$BROADCAST_ACTION" -n "$RECEIVER" >/dev/null | ||
| sleep $WAIT_BROADCAST | ||
| local log="$OUT_DIR/2c.log" | ||
| dump_log "$EMU_API33" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted (tier 3 fallback)" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "standalone has process.load child" \ | ||
| "grep -qE '\|op=app\.start\|.*children=.*process\.load' '$log'" | ||
| assert "NO ui.load txn emitted" \ | ||
| "! grep -qE 'op=ui\.load' '$log'" | ||
| verify_delivery "$EMU_API33" "2c" | ||
| } | ||
|
|
||
| scenario_2d() { | ||
| banner "2d: Foreground service cold start — API 36 / APK-A" | ||
| install_from "$EMU_API36" "$APK_A" | ||
| # Notification permission is needed for the foreground service notification | ||
| adb -s "$EMU_API36" shell pm grant "$PKG" android.permission.POST_NOTIFICATIONS >/dev/null 2>&1 || true | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am start-foreground-service -n "$SERVICE" >/dev/null | ||
| sleep $WAIT_BROADCAST | ||
| local log="$OUT_DIR/2d.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted via foreground service" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "NO ui.load txn emitted" \ | ||
| "! grep -qE 'op=ui\.load' '$log'" | ||
| verify_delivery "$EMU_API36" "2d" | ||
| } | ||
|
|
||
| scenario_2e() { | ||
| banner "2e: Broadcast → launcher (trace reuse) — API 36 / APK-A" | ||
| install_from "$EMU_API36" "$APK_A" | ||
| wake_device "$EMU_API36" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am broadcast -a "$BROADCAST_ACTION" -n "$RECEIVER" >/dev/null | ||
| sleep 3 | ||
| adb -s "$EMU_API36" shell am start -n "$MAIN_ACTIVITY" >/dev/null | ||
| sleep $WAIT_COMBO | ||
| local log="$OUT_DIR/2e.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "App Start standalone txn emitted (from broadcast)" \ | ||
| "grep -qE 'name=App Start\|op=app\.start\|' '$log'" | ||
| assert "MainActivity ui.load txn emitted" \ | ||
| "grep -qE 'name=MainActivity.*op=ui\.load' '$log'" | ||
| assert "ui.load has NO app.start.* child" \ | ||
| "! grep -E 'name=MainActivity.*op=ui\.load' '$log' | grep -qE 'children=.*app\.start\.'" | ||
| local tids=$(extract_traceids "$log" | sort -u) | ||
| local count=$(echo "$tids" | wc -l | tr -d ' ') | ||
| assert "broadcast + launcher txns share same traceId" \ | ||
| "[[ '$count' == '1' ]]" | ||
| verify_delivery "$EMU_API36" "2e" | ||
| } | ||
|
|
||
| scenario_2f() { | ||
| banner "2f: Broadcast + flag OFF (regression) — API 36 / APK-B" | ||
| install_from "$EMU_API36" "$APK_B" | ||
| prep_for_run "$EMU_API36" | ||
| adb -s "$EMU_API36" shell am broadcast -a "$BROADCAST_ACTION" -n "$RECEIVER" >/dev/null | ||
| sleep $WAIT_BROADCAST | ||
| local log="$OUT_DIR/2f.log" | ||
| dump_log "$EMU_API36" "$log" | ||
|
|
||
| assert "NO transactions emitted (flag off + no activity)" \ | ||
| "! grep -qE 'SentryE2E.*TXN\\|' '$log'" | ||
| } | ||
|
|
||
| usage() { | ||
| cat <<EOF | ||
| Usage: $0 [scenario ...] | ||
|
|
||
| Without args, runs all 8 scenarios. With args, runs only those named. | ||
| Scenarios: 1a 1c 2a 2b 2c 2d 2e 2f | ||
|
|
||
| Variant needed per scenario: | ||
| 1a,2a,2d,2e → APK-A (flag=on, simulate-plugin=on) | ||
| 1c,2f → APK-B (flag=off, simulate-plugin=off) | ||
| 2b,2c → APK-C (flag=on, simulate-plugin=off) | ||
|
|
||
| Examples: | ||
| $0 # all scenarios | ||
| $0 2c # just the tier-3 fallback case | ||
| $0 1a 2e # happy path + broadcast-then-launcher | ||
| EOF | ||
| } | ||
|
|
||
| # Determine which variants are needed based on requested scenarios. | ||
| needs_A=0; needs_B=0; needs_C=0 | ||
| declare -a requested=() | ||
| if [[ $# -eq 0 ]]; then | ||
| requested=(1a 1c 2a 2b 2c 2d 2e 2f) | ||
| needs_A=1; needs_B=1; needs_C=1 | ||
| else | ||
| for s in "$@"; do | ||
| case "$s" in | ||
| -h|--help) usage; exit 0 ;; | ||
| 1a|2a|2d|2e) needs_A=1; requested+=("$s") ;; | ||
| 1c|2f) needs_B=1; requested+=("$s") ;; | ||
| 2b|2c) needs_C=1; requested+=("$s") ;; | ||
| *) echo "unknown scenario: $s"; usage; exit 2 ;; | ||
| esac | ||
| done | ||
| fi | ||
|
|
||
| # ================ RUN ================= | ||
|
|
||
| banner "Pre-flight" | ||
| adb devices -l | ||
| echo "" | ||
| adb -s "$EMU_API36" shell getprop ro.build.version.sdk | awk '{print "emulator-5554 API: "$0}' | ||
| adb -s "$EMU_API33" shell getprop ro.build.version.sdk | awk '{print "emulator-5556 API: "$0}' | ||
|
|
||
| build_needed_variants | ||
| for s in "${requested[@]}"; do | ||
| "scenario_$s" | ||
| done | ||
|
|
||
| banner "SUMMARY" | ||
| echo " PASS: $(green $PASS)" | ||
| echo " FAIL: $(red $FAIL)" | ||
| if [[ $FAIL -gt 0 ]]; then | ||
| echo "" | ||
| echo " Failing assertions:" | ||
| for l in "${FAIL_LINES[@]}"; do | ||
| echo " - $l" | ||
| done | ||
| fi | ||
| echo "" | ||
| echo " Logs: $OUT_DIR" | ||
| exit $FAIL |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.