diff --git a/.github/workflows/integration-mobile-test-lib-infer-diffusion.yml b/.github/workflows/integration-mobile-test-lib-infer-diffusion.yml index 7063d2223a..ef07cc4c3d 100644 --- a/.github/workflows/integration-mobile-test-lib-infer-diffusion.yml +++ b/.github/workflows/integration-mobile-test-lib-infer-diffusion.yml @@ -911,7 +911,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Increased timeout to 30 minutes (1800000ms) for long-running LLM tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(result,capabilities,specs){try{const fs=require("fs");const path=require("path");const artifactDir=path.resolve(process.cwd(),"tests","artifacts");const remoteArtifactDir="@'${{ env.APP_BUNDLE_ID }}':documents/test/generated-images/";const artifactPath=path.join(artifactDir,"ios-generated-images.zip");fs.mkdirSync(artifactDir,{recursive:true});if(typeof browser.pullFolder!=="function"){console.log("No iOS generated image artifacts collected: browser.pullFolder is not available");return;}console.log("Attempting to pull generated images from "+remoteArtifactDir);const folderData=await browser.pullFolder(remoteArtifactDir);fs.writeFileSync(artifactPath,Buffer.from(folderData,"base64"));console.log("Saved generated image artifacts to "+artifactPath);}catch(e){console.log("No iOS generated image artifacts collected: "+e.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(result,capabilities,specs){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}try{const fs=require("fs");const path=require("path");const artifactDir=path.resolve(process.cwd(),"tests","artifacts");const remoteArtifactDir="@'${{ env.APP_BUNDLE_ID }}':documents/test/generated-images/";const artifactPath=path.join(artifactDir,"ios-generated-images.zip");fs.mkdirSync(artifactDir,{recursive:true});if(typeof browser.pullFolder!=="function"){console.log("No iOS generated image artifacts collected: browser.pullFolder is not available");return;}console.log("Attempting to pull generated images from "+remoteArtifactDir);const folderData=await browser.pullFolder(remoteArtifactDir);fs.writeFileSync(artifactPath,Buffer.from(folderData,"base64"));console.log("Saved generated image artifacts to "+artifactPath);}catch(e){console.log("No iOS generated image artifacts collected: "+e.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1366,7 +1366,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1389,7 +1389,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1412,7 +1412,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1424,7 +1424,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn + uses: actions/upload-artifact@v4 + with: + name: console-logs-diffusion-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-ocr-onnx.yml b/.github/workflows/integration-mobile-test-ocr-onnx.yml index 742437fef8..2d352acfed 100644 --- a/.github/workflows/integration-mobile-test-ocr-onnx.yml +++ b/.github/workflows/integration-mobile-test-ocr-onnx.yml @@ -980,7 +980,7 @@ jobs: else BID="${{ env.APP_BUNDLE_ID }}" cat > /tmp/wdio-config.js << 'WDIO_EOF' - exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"__BUNDLE_ID__","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000,grep:"__MOCHA_GREP__"},before:async function(capabilities,specs,browser){const BUNDLE_ID="__BUNDLE_ID__";const TEST_FILTER="__TEST_FILTER__";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\n APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");if(TEST_FILTER!=="__TEST_FILTER__"){try{const b64=Buffer.from(TEST_FILTER).toString("base64");await browser.pushFile("@"+BUNDLE_ID+":documents/testFilter.txt",b64);console.log("Pushed test filter: "+TEST_FILTER);}catch(e){console.log("pushFile failed: "+e.message);}}console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);},after:async function(){console.log("[perf-extract] Pulling perf-report.json from device...");var BID="__BUNDLE_ID__";var pullPaths=["@"+BID+":documents/perf-report.json","@"+BID+":library/perf-report.json"];for(var i=0;i{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\n APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");if(TEST_FILTER!=="__TEST_FILTER__"){try{const b64=Buffer.from(TEST_FILTER).toString("base64");await browser.pushFile("@"+BUNDLE_ID+":documents/testFilter.txt",b64);console.log("Pushed test filter: "+TEST_FILTER);}catch(e){console.log("pushFile failed: "+e.message);}}console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);},after:async function(){console.log("[perf-extract] Pulling perf-report.json from device...");var BID="__BUNDLE_ID__";var pullPaths=["@"+BID+":documents/perf-report.json","@"+BID+":library/perf-report.json"];for(var i=0;i /tmp/wdio-config-final.js mv /tmp/wdio-config-final.js /tmp/wdio-config.js @@ -1723,7 +1723,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1746,7 +1746,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1769,7 +1769,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1782,7 +1782,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && (steps.schedule_run.outputs.run_arn_perf || steps.schedule_run.outputs.run_arn_regular) + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && (steps.schedule_run.outputs.run_arn_perf || steps.schedule_run.outputs.run_arn_regular) + uses: actions/upload-artifact@v4 + with: + name: console-logs-ocr-onnx-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && (steps.schedule_run.outputs.run_arn_perf || steps.schedule_run.outputs.run_arn_regular) uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-decoder-audio.yml b/.github/workflows/integration-mobile-test-qvac-lib-decoder-audio.yml index edfe28c0da..a06b752800 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-decoder-audio.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-decoder-audio.yml @@ -852,7 +852,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Timeout set to 10 minutes (600000ms) for audio decoding tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:600000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:600000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1294,7 +1294,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1317,7 +1317,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1340,7 +1340,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1352,7 +1352,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn + uses: actions/upload-artifact@v4 + with: + name: console-logs-decoder-audio-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-embed.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-embed.yml index 7b633bd4b9..10d014f6ea 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-embed.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-embed.yml @@ -898,7 +898,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Increased timeout to 30 minutes (1800000ms) for long-running embedding tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1340,7 +1340,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1363,7 +1363,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1386,7 +1386,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1398,7 +1398,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn + uses: actions/upload-artifact@v4 + with: + name: console-logs-llamacpp-embed-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-llm.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-llm.yml index 3086ba78bb..6ad0a82962 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-llm.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-llamacpp-llm.yml @@ -949,7 +949,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Increased timeout to 30 minutes (1800000ms) for long-running LLM tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000,grep:"__MOCHA_GREP__"},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";const TEST_FILTER="__TEST_FILTER__";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");if(TEST_FILTER!=="__TEST_FILTER__"){try{const b64=Buffer.from(TEST_FILTER).toString("base64");await browser.pushFile("@"+BUNDLE_ID+":documents/testFilter.txt",b64);console.log("Pushed test filter: "+TEST_FILTER);}catch(e){console.log("pushFile failed: "+e.message);}}console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");global.crashMonitor=setInterval(async()=>{if(global.appCrashed)return;try{const s=await browser.queryAppState(BUNDLE_ID);if(s<3){console.error("\\nšŸ›‘ BACKGROUND CRASH DETECTED! App state="+s);global.appCrashed=true;process.exit(1);}}catch(e){}},15000);},after:async function(){if(global.crashMonitor)clearInterval(global.crashMonitor);},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000,grep:"__MOCHA_GREP__"},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";const TEST_FILTER="__TEST_FILTER__";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");if(TEST_FILTER!=="__TEST_FILTER__"){try{const b64=Buffer.from(TEST_FILTER).toString("base64");await browser.pushFile("@"+BUNDLE_ID+":documents/testFilter.txt",b64);console.log("Pushed test filter: "+TEST_FILTER);}catch(e){console.log("pushFile failed: "+e.message);}}console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");global.crashMonitor=setInterval(async()=>{if(global.appCrashed)return;try{const s=await browser.queryAppState(BUNDLE_ID);if(s<3){console.error("\\nšŸ›‘ BACKGROUND CRASH DETECTED! App state="+s);global.appCrashed=true;process.exit(1);}}catch(e){}},15000);},after:async function(){if(global.crashMonitor)clearInterval(global.crashMonitor);console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi WDIO_B64=$(echo "$WDIO_CONFIG" | base64 | tr -d '\n') @@ -1527,7 +1527,7 @@ jobs: fi SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then echo ""; echo "::group::šŸ“‹ [$DEVICE_NAME] $SUITE_NAME -- $ART_NAME" @@ -1544,7 +1544,7 @@ jobs: [ -z "$ART_URL" ] && continue SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1564,7 +1564,7 @@ jobs: fi SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1575,7 +1575,53 @@ jobs: echo "šŸ“¦ All downloaded logs:" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + uses: actions/upload-artifact@v4 + with: + name: console-logs-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn_1 uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-nmtcpp.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-nmtcpp.yml index fd6e9790f6..bdde50f9dc 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-nmtcpp.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-nmtcpp.yml @@ -1070,7 +1070,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Increased timeout to 30 minutes (1800000ms) for long-running translation tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global._crashCheckFails=0;global._crashCheckDisabled=false;global.checkAppCrash=async(stage)=>{if(global._crashCheckDisabled)return-1;try{const state=await browser.execute("mobile: queryAppState",{bundleId:BUNDLE_ID});global._crashCheckFails=0;console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;if(global._healthInterval)clearInterval(global._healthInterval);process.exit(1);}return state;}catch(e){global._crashCheckFails++;if(global._crashCheckFails>=3){console.log("["+stage+"] queryAppState not supported, disabling crash detection");global._crashCheckDisabled=true;if(global._healthInterval)clearInterval(global._healthInterval);}return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");global._healthInterval=setInterval(async()=>{if(global.appCrashed)return;try{await global.checkAppCrash("health-monitor");}catch(e){}},15000);console.log("Health monitor started (15s interval)");},after:async function(){if(global._healthInterval)clearInterval(global._healthInterval);console.log("Health monitor stopped");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global._crashCheckFails=0;global._crashCheckDisabled=false;global.checkAppCrash=async(stage)=>{if(global._crashCheckDisabled)return-1;try{const state=await browser.execute("mobile: queryAppState",{bundleId:BUNDLE_ID});global._crashCheckFails=0;console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;if(global._healthInterval)clearInterval(global._healthInterval);process.exit(1);}return state;}catch(e){global._crashCheckFails++;if(global._crashCheckFails>=3){console.log("["+stage+"] queryAppState not supported, disabling crash detection");global._crashCheckDisabled=true;if(global._healthInterval)clearInterval(global._healthInterval);}return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");global._healthInterval=setInterval(async()=>{if(global.appCrashed)return;try{await global.checkAppCrash("health-monitor");}catch(e){}},15000);console.log("Health monitor started (15s interval)");},after:async function(){if(global._healthInterval)clearInterval(global._healthInterval);console.log("Health monitor stopped");console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1561,7 +1561,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1584,7 +1584,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1607,7 +1607,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1620,7 +1620,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0 + with: + name: console-logs-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn_1 uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-onnx-tts.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-onnx-tts.yml index b6a1f39604..a642272b56 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-onnx-tts.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-onnx-tts.yml @@ -990,7 +990,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Increased timeout to 30 minutes (1800000ms) for long-running TTS tests - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:1800000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1487,7 +1487,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1510,7 +1510,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1533,7 +1533,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1546,7 +1546,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}-${{ matrix.variant }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}-${{ matrix.variant }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + uses: actions/upload-artifact@v4 + with: + name: console-logs-tts-${{ matrix.platform }}-${{ matrix.variant }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn_1 uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-parakeet.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-parakeet.yml index 402518efdf..9a05d61300 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-parakeet.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-parakeet.yml @@ -914,7 +914,7 @@ jobs: AUTOMATION="XCUITest" HOST_LINE="ios_test_host: macos_sequoia" BUNDLE_ID="${{ env.APP_BUNDLE_ID }}" - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:600000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:2700000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:600000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:2700000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi WDIO_CONFIG_B64=$(echo "$WDIO_CONFIG" | base64 | tr -d '\n') @@ -1409,7 +1409,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1432,7 +1432,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1455,7 +1455,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1468,7 +1468,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + uses: actions/upload-artifact@v4 + with: + name: console-logs-parakeet-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn_1 uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/integration-mobile-test-qvac-lib-infer-whispercpp.yml b/.github/workflows/integration-mobile-test-qvac-lib-infer-whispercpp.yml index 3565b665e8..1151bef41a 100644 --- a/.github/workflows/integration-mobile-test-qvac-lib-infer-whispercpp.yml +++ b/.github/workflows/integration-mobile-test-qvac-lib-infer-whispercpp.yml @@ -943,7 +943,7 @@ jobs: # iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit) # usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent # Timeout set to 15 minutes (900000ms) for audio transcription tests (whisper models can be slow) - WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' + WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\nšŸ›‘ APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \"INITIALIZED\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \"Run Automated Tests\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},after:async function(){console.log("[bare-log] Waiting for log flush...");await browser.pause(3000);try{var BID="'${{ env.APP_BUNDLE_ID }}'";var _h=require("http");var lb64=await new Promise(function(ok,fail){var bd=JSON.stringify({path:"@"+BID+":documents/bare_console.log"});var rq=_h.request({hostname:"127.0.0.1",port:4723,path:"/wd/hub/session/"+browser.sessionId+"/appium/device/pull_file",method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(bd)}},function(rs){var d="";rs.on("data",function(c){d+=c;});rs.on("end",function(){try{ok(JSON.parse(d).value);}catch(e){fail(e);}});});rq.on("error",fail);rq.write(bd);rq.end();});var logTxt=Buffer.from(lb64,"base64").toString();var logDir=process.env.DEVICEFARM_LOG_DIR||".";require("fs").writeFileSync(logDir+"/bare_console.log",logTxt);console.log("[bare-log] Written bare_console.log ("+logTxt.length+" bytes)");}catch(le){console.log("[bare-log] pull failed: "+le.message);}},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};' fi # Base64 encode the wdio config to safely embed in YAML @@ -1444,7 +1444,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME" if echo "$ART_NAME" | grep -qiE "test.spec|testspec"; then @@ -1467,7 +1467,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_${SAFE_SUITE}_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded: $SUITE_NAME / $ART_NAME (LOG)" fi done @@ -1490,7 +1490,7 @@ jobs: SAFE_ART=$(echo "$ART_NAME" | tr ' /' '__' | tr -cd '[:alnum:]_-') DEST="$LOG_DIR/${SAFE_RUN}_${SAFE_NAME}_job_${SAFE_ART}.${ART_EXT}" - if curl -fsSL -o "$DEST" "$ART_URL" 2>/dev/null; then + if curl -fsSL --max-time 300 -o "$DEST" "$ART_URL" 2>/dev/null; then echo " Downloaded (job-level): $ART_NAME" fi done @@ -1503,7 +1503,53 @@ jobs: echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" find "$LOG_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no logs downloaded)" - - name: Upload Device Farm Logs + - name: Extract Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + run: | + LOG_DIR="devicefarm-logs/${{ matrix.platform }}" + CONSOLE_DIR="console-logs/${{ matrix.platform }}" + mkdir -p "$CONSOLE_DIR" + for f in "$LOG_DIR"/*; do + [ -f "$f" ] || continue + fname=$(basename "$f") + if echo "$fname" | grep -qiE "Logcat\."; then + cp "$f" "$CONSOLE_DIR/" + echo " Console log: $fname" + fi + if echo "$fname" | grep -qiE "Customer_Artifacts\.zip$"; then + prefix=$(echo "$fname" | sed 's/_Customer_Artifacts\.zip$//') + tmpdir=$(mktemp -d) + if unzip -qo "$f" -d "$tmpdir" 2>/dev/null; then + bare_log=$(find "$tmpdir" -name "bare_console.log" -type f 2>/dev/null | head -1) + if [ -n "$bare_log" ]; then + cp "$bare_log" "$CONSOLE_DIR/${prefix}_bare_console.log" + echo " Extracted: ${prefix}_bare_console.log" + fi + appium_log=$(find "$tmpdir" -name "appium.log" -type f 2>/dev/null | head -1) + if [ -n "$appium_log" ]; then + cp "$appium_log" "$CONSOLE_DIR/${prefix}_appium.log" + echo " Extracted: ${prefix}_appium.log" + fi + else + echo " WARNING: Failed to unzip $fname" + fi + rm -rf "$tmpdir" + fi + done + echo "" + echo "šŸ“‹ Console logs extracted:" + find "$CONSOLE_DIR" -type f -exec ls -lh {} \; 2>/dev/null || echo " (no console logs found)" + + - name: Upload Console Logs + if: always() && steps.schedule_run.outputs.run_arn_1 + uses: actions/upload-artifact@v4 + with: + name: console-logs-whispercpp-${{ matrix.platform }} + path: console-logs/ + retention-days: 30 + if-no-files-found: ignore + + - name: Upload Full Device Farm Logs if: always() && steps.schedule_run.outputs.run_arn_1 uses: actions/upload-artifact@v4 with: