Skip to content

Cap every scenario at 5 minutes (sample, not play-to-end) #5

Cap every scenario at 5 minutes (sample, not play-to-end)

Cap every scenario at 5 minutes (sample, not play-to-end) #5

Workflow file for this run

name: video-playback
# Runs NRSampleApp playback automation across iOS + tvOS simulators.
#
# Layered config — change a setting WITHOUT a PR by editing GitHub Actions
# variables (Settings → Secrets and variables → Actions → Variables tab).
# Priority high → low:
# 1. workflow_dispatch inputs (nr_overrides, extra_streams)
# 2. GitHub repo vars.NEW_RELIC_*
# 3. playback-config.json per_leg block (matched by leg tag)
# 4. playback-config.json global block
# 5. NewRelicSetup.swift defaults
#
# This workflow lives only on `internal/video-rig` and is not merged to master.
on:
schedule:
# GitHub Actions cron is UTC-only. IST (India Standard Time) is UTC+5:30.
# Times below are written as UTC, with IST equivalents in comments.
- cron: '30 3 * * *' # 03:30 UTC = 09:00 IST · daily comprehensive
- cron: '30 */6 * * *' # 00:30/06:30/12:30/18:30 UTC = 06:00/12:00/18:00/00:00 IST · smoke every 6h
workflow_dispatch:
inputs:
scenarios:
description: 'Scenario subset (comma-separated). Empty = all.'
required: false
default: ''
# ------ Per-leg toggles ------
run_iOS_18_iPhone_16_Pro:
description: 'Run iOS 18 / iPhone 16 Pro'
type: boolean
default: true
run_iOS_17_iPhone_15:
description: 'Run iOS 17.5 / iPhone 15'
type: boolean
default: true
run_iOS_16_iPhone_14:
description: 'Run iOS 16.4 / iPhone 14 (downloads runtime, +10 min)'
type: boolean
default: false
run_tvOS_18_Apple_TV_4K:
description: 'Run tvOS 18 / Apple TV 4K'
type: boolean
default: true
# ------ Per-run NRVA overrides ------
nr_overrides:
description: 'NRVA per-run JSON override, e.g. {"harvest_cycle_secs":60,"debug_logging":false}'
required: false
default: '{}'
# ------ Per-run extra streams (ContentItem JSON array) ------
extra_streams:
description: 'Extra ContentItems (JSON array). Empty = none.'
required: false
default: ''
config_file:
description: 'Path to playback-config.json (relative to NRSampleApp dir)'
required: false
default: 'playback-config.json'
push:
branches: [internal/video-rig]
paths:
- 'Examples/iOS/NRSampleApp/**'
- '.github/workflows/playback.yml'
permissions:
contents: read
concurrency:
group: video-playback-${{ github.ref }}
cancel-in-progress: true
# Defaults for cron + push (no inputs available there).
env:
CRON_DEFAULT_iOS_18_iPhone_16_Pro: 'true'
CRON_DEFAULT_iOS_17_iPhone_15: 'true'
CRON_DEFAULT_iOS_16_iPhone_14: 'false'
CRON_DEFAULT_tvOS_18_Apple_TV_4K: 'true'
jobs:
playback:
name: ${{ matrix.tag }}
runs-on: macos-14
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- tag: iOS-18-iPhone-16-Pro
platform: iOS
scheme: NRSampleApp_iOS
device: iPhone 16 Pro
os_version: ""
download_os: ""
input_key: run_iOS_18_iPhone_16_Pro
- tag: iOS-17-iPhone-15
platform: iOS
scheme: NRSampleApp_iOS
device: iPhone 15
os_version: "" # use whatever iPhone 15 has pre-installed
download_os: ""
input_key: run_iOS_17_iPhone_15
- tag: iOS-16-iPhone-14
platform: iOS
scheme: NRSampleApp_iOS
device: iPhone 14
os_version: "16.4"
download_os: "16.4"
input_key: run_iOS_16_iPhone_14
- tag: tvOS-18-Apple-TV-4K
platform: tvOS
scheme: NRSampleApp_tvOS
device: Apple TV 4K (3rd generation)
os_version: ""
download_os: ""
input_key: run_tvOS_18_Apple_TV_4K
steps:
- name: Resolve whether this leg should run
id: gate
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_iOS_18_iPhone_16_Pro: ${{ inputs.run_iOS_18_iPhone_16_Pro }}
INPUT_iOS_17_iPhone_15: ${{ inputs.run_iOS_17_iPhone_15 }}
INPUT_iOS_16_iPhone_14: ${{ inputs.run_iOS_16_iPhone_14 }}
INPUT_tvOS_18_Apple_TV_4K: ${{ inputs.run_tvOS_18_Apple_TV_4K }}
KEY: ${{ matrix.input_key }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
VAR_NAME="INPUT_${KEY#run_}"
else
VAR_NAME="CRON_DEFAULT_${KEY#run_}"
fi
VAL=$(eval echo "\${$VAR_NAME:-true}")
case "$VAL" in
true|1|yes) SHOULD_RUN=true ;;
*) SHOULD_RUN=false ;;
esac
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
echo "Leg ${{ matrix.tag }} → $SHOULD_RUN (event=$EVENT_NAME)"
- uses: actions/checkout@v4
if: steps.gate.outputs.should_run == 'true'
- name: Select Xcode 16+ (macos-14 defaults to 15.4 which can't open
format-77 project files generated by current XcodeGen)
if: steps.gate.outputs.should_run == 'true'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 'latest-stable'
- name: Show Xcode + simulator versions
if: steps.gate.outputs.should_run == 'true'
run: |
xcodebuild -version
xcrun simctl --version
- name: Install XcodeGen + jq
if: steps.gate.outputs.should_run == 'true'
run: brew install xcodegen jq
- name: Show pre-installed simulator runtimes
if: steps.gate.outputs.should_run == 'true'
run: xcrun simctl list runtimes
- name: Download iOS runtime ${{ matrix.download_os }}
if: steps.gate.outputs.should_run == 'true' && matrix.download_os != ''
run: |
set -euxo pipefail
sudo xcodebuild -downloadPlatform iOS -buildVersion ${{ matrix.download_os }} \
|| xcodebuild -downloadPlatform iOS -buildVersion ${{ matrix.download_os }}
xcrun simctl list runtimes | grep -i "iOS ${{ matrix.download_os }}" \
|| { echo "ERROR: runtime did not install"; exit 1; }
- name: Create simulator for ${{ matrix.tag }}
if: steps.gate.outputs.should_run == 'true' && matrix.os_version != ''
run: |
set -euxo pipefail
RUNTIME_ID="iOS-${{ matrix.os_version }}"
RUNTIME_ID="${RUNTIME_ID//./-}"
xcrun simctl create "auto-${{ matrix.tag }}" "${{ matrix.device }}" "$RUNTIME_ID" \
|| echo "(device may already exist — continuing)"
- name: Run playback automation
if: steps.gate.outputs.should_run == 'true'
working-directory: Examples/iOS/NRSampleApp
env:
SCENARIOS: ${{ inputs.scenarios }}
PLATFORM: ${{ matrix.platform }}
SCHEME: ${{ matrix.scheme }}
DEVICE_NAME: >-
${{ matrix.os_version != ''
&& format('auto-{0}', matrix.tag)
|| matrix.device }}
OS_VERSION: ${{ matrix.os_version }}
LEG_TAG: ${{ matrix.tag }}
CONFIG_FILE: ${{ inputs.config_file || 'playback-config.json' }}
# ------ Sensitive (always from secrets) ------
NEW_RELIC_APP_TOKEN: ${{ secrets.NEW_RELIC_APP_TOKEN }}
# ------ Non-sensitive NRVA settings (from repo vars; edit in GitHub UI, no PR) ------
NEW_RELIC_COLLECTOR_ADDRESS: ${{ vars.NEW_RELIC_COLLECTOR_ADDRESS }}
NEW_RELIC_HARVEST_CYCLE_SECS: ${{ vars.NEW_RELIC_HARVEST_CYCLE_SECS }}
NEW_RELIC_LIVE_HARVEST_CYCLE_SECS: ${{ vars.NEW_RELIC_LIVE_HARVEST_CYCLE_SECS }}
NEW_RELIC_REGULAR_BATCH_SIZE_BYTES: ${{ vars.NEW_RELIC_REGULAR_BATCH_SIZE_BYTES }}
NEW_RELIC_LIVE_BATCH_SIZE_BYTES: ${{ vars.NEW_RELIC_LIVE_BATCH_SIZE_BYTES }}
NEW_RELIC_MAX_DEAD_LETTER_SIZE: ${{ vars.NEW_RELIC_MAX_DEAD_LETTER_SIZE }}
NEW_RELIC_MAX_OFFLINE_STORAGE_MB: ${{ vars.NEW_RELIC_MAX_OFFLINE_STORAGE_MB }}
NEW_RELIC_QOE_ENABLED: ${{ vars.NEW_RELIC_QOE_ENABLED }}
NEW_RELIC_QOE_INTERVAL_MULTIPLIER: ${{ vars.NEW_RELIC_QOE_INTERVAL_MULTIPLIER }}
NEW_RELIC_DEBUG_LOGGING: ${{ vars.NEW_RELIC_DEBUG_LOGGING }}
NEW_RELIC_MEMORY_OPTIMIZATION: ${{ vars.NEW_RELIC_MEMORY_OPTIMIZATION }}
# ------ Per-run overrides (workflow_dispatch only) ------
NR_OVERRIDES_JSON: ${{ inputs.nr_overrides }}
# ------ Extra streams: input wins, fallback to repo var ------
PLAYBACK_EXTRA_STREAMS: >-
${{ inputs.extra_streams != ''
&& inputs.extra_streams
|| vars.PLAYBACK_EXTRA_STREAMS }}
run: ./scripts/run-playback.sh
- name: Upload artifacts
if: always() && steps.gate.outputs.should_run == 'true'
uses: actions/upload-artifact@v4
with:
name: playback-${{ matrix.tag }}-${{ github.run_id }}
path: Examples/iOS/NRSampleApp/build/playback-artifacts/
retention-days: 14
report:
name: Status report
needs: playback
if: always()
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Download all leg artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: 'playback-*'
- name: Aggregate green/red across legs
id: aggregate
run: |
set -uo pipefail
cd artifacts || { echo "no artifacts to aggregate"; exit 0; }
{
echo "# 🎬 Video Playback — $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo ""
echo "Branch: \`${{ github.ref_name }}\` · Trigger: \`${{ github.event_name }}\` · Run: [#${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
echo ""
echo "| Leg | Status | OK | Fail | Cap-hit | Scenarios |"
echo "|---|---|---:|---:|---:|---|"
} >> "$GITHUB_STEP_SUMMARY"
TOTAL=0
PASSED=0
FAILED=0
for dir in playback-*; do
[ -d "$dir" ] || continue
TAG=$(echo "$dir" | sed -E 's/^playback-(.*)-[0-9]+$/\1/')
SUMMARY="$dir/SUMMARY.txt"
TOTAL=$((TOTAL + 1))
if [ ! -f "$SUMMARY" ]; then
echo "| \`$TAG\` | ⚠️ no summary | — | — | — | (artifact missing) |" >> "$GITHUB_STEP_SUMMARY"
FAILED=$((FAILED + 1))
continue
fi
OK=$(grep -c " ok " "$SUMMARY" 2>/dev/null | head -1)
FL=$(grep -c " fail " "$SUMMARY" 2>/dev/null | head -1)
CH=$(grep -c " cap-hit " "$SUMMARY" 2>/dev/null | head -1)
: "${OK:=0}"; : "${FL:=0}"; : "${CH:=0}"
# Build a compact scenario list (id:result, id:result, ...)
SCEN=$(awk 'NR>10 && $1 != "----" && $1 != "OVERALL:" && NF>=6 { printf "%s:%s, ", $1, $6 }' "$SUMMARY" | sed 's/, $//')
if [ "$FL" -gt 0 ]; then
STATUS="❌ red"
FAILED=$((FAILED + 1))
elif [ "$CH" -gt 0 ]; then
STATUS="⚠️ amber"
PASSED=$((PASSED + 1))
else
STATUS="✅ green"
PASSED=$((PASSED + 1))
fi
echo "| \`$TAG\` | $STATUS | $OK | $FL | $CH | $SCEN |" >> "$GITHUB_STEP_SUMMARY"
done
{
echo ""
if [ "$FAILED" -eq 0 ] && [ "$TOTAL" -gt 0 ]; then
echo "## ✅ All $TOTAL legs passed"
elif [ "$TOTAL" -eq 0 ]; then
echo "## ⚠️ No leg artifacts found (all legs may have been skipped)"
else
echo "## ❌ $FAILED / $TOTAL legs failed"
fi
} >> "$GITHUB_STEP_SUMMARY"
echo "passed=$PASSED" >> "$GITHUB_OUTPUT"
echo "failed=$FAILED" >> "$GITHUB_OUTPUT"
echo "total=$TOTAL" >> "$GITHUB_OUTPUT"
- name: Fail workflow if any leg failed
if: steps.aggregate.outputs.failed != '0' && steps.aggregate.outputs.failed != ''
run: |
echo "::error::${{ steps.aggregate.outputs.failed }}/${{ steps.aggregate.outputs.total }} legs failed."
exit 1