@devicecloud.dev/dcd 4.1.2-beta.1 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cloud.js +53 -5
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +19 -1
- package/dist/utils/connectivity.d.ts +29 -0
- package/dist/utils/connectivity.js +100 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/commands/cloud.js
CHANGED
|
@@ -14,6 +14,7 @@ const api_gateway_1 = require("../gateways/api-gateway");
|
|
|
14
14
|
const methods_1 = require("../methods");
|
|
15
15
|
const plan_1 = require("../plan");
|
|
16
16
|
const compatibility_1 = require("../utils/compatibility");
|
|
17
|
+
const connectivity_1 = require("../utils/connectivity");
|
|
17
18
|
const StreamZip = require("node-stream-zip");
|
|
18
19
|
exports.mimeTypeLookupByExtension = {
|
|
19
20
|
apk: 'application/vnd.android.package-archive',
|
|
@@ -580,6 +581,7 @@ class Cloud extends core_1.Command {
|
|
|
580
581
|
this.log('\nYou can safely close this terminal and the tests will continue\n');
|
|
581
582
|
}
|
|
582
583
|
let sequentialPollFaillures = 0;
|
|
584
|
+
let previousSummary = '';
|
|
583
585
|
if (debug) {
|
|
584
586
|
this.log(`DEBUG: Starting polling loop for results`);
|
|
585
587
|
}
|
|
@@ -599,11 +601,34 @@ class Cloud extends core_1.Command {
|
|
|
599
601
|
this.log(`DEBUG: Result status: ${result.test_file_name} - ${result.status}`);
|
|
600
602
|
}
|
|
601
603
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
604
|
+
// Calculate summary statistics
|
|
605
|
+
const statusCounts = {};
|
|
606
|
+
for (const result of updatedResults) {
|
|
607
|
+
statusCounts[result.status] = (statusCounts[result.status] || 0) + 1;
|
|
608
|
+
}
|
|
609
|
+
const passed = statusCounts.PASSED || 0;
|
|
610
|
+
const failed = statusCounts.FAILED || 0;
|
|
611
|
+
const pending = statusCounts.PENDING || 0;
|
|
612
|
+
const running = statusCounts.RUNNING || 0;
|
|
613
|
+
const total = updatedResults.length;
|
|
614
|
+
const completed = passed + failed;
|
|
615
|
+
// Display quantitative summary in quiet mode only
|
|
616
|
+
const summary = `${completed}/${total} | ✓ ${passed} | ✗ ${failed} | ▶ ${running} | ⏸ ${pending}`;
|
|
617
|
+
if (!json) {
|
|
618
|
+
if (quiet) {
|
|
619
|
+
// Only update status when the summary changes in quiet mode
|
|
620
|
+
if (summary !== previousSummary) {
|
|
621
|
+
core_1.ux.action.status = summary;
|
|
622
|
+
previousSummary = summary;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
// Show detailed table in non-quiet mode (no summary)
|
|
627
|
+
core_1.ux.action.status =
|
|
628
|
+
'\nStatus Test\n─────────── ───────────────';
|
|
629
|
+
for (const { retry_of: isRetry, status, test_file_name: test, } of updatedResults) {
|
|
630
|
+
core_1.ux.action.status += `\n${status.padEnd(10, ' ')} ${test} ${isRetry ? '(retry)' : ''}`;
|
|
631
|
+
}
|
|
607
632
|
}
|
|
608
633
|
}
|
|
609
634
|
if (updatedResults.every((result) => !['PENDING', 'RUNNING'].includes(result.status))) {
|
|
@@ -728,6 +753,29 @@ class Cloud extends core_1.Command {
|
|
|
728
753
|
if (sequentialPollFaillures > 10) {
|
|
729
754
|
// dropped poll requests shouldn't err user CI
|
|
730
755
|
clearInterval(intervalId);
|
|
756
|
+
// Check if the failure is due to internet connectivity issues
|
|
757
|
+
if (debug) {
|
|
758
|
+
this.log('DEBUG: Checking internet connectivity...');
|
|
759
|
+
}
|
|
760
|
+
const connectivityCheck = await (0, connectivity_1.checkInternetConnectivity)();
|
|
761
|
+
if (debug) {
|
|
762
|
+
this.log(`DEBUG: ${connectivityCheck.message}`);
|
|
763
|
+
for (const result of connectivityCheck.endpointResults) {
|
|
764
|
+
if (result.success) {
|
|
765
|
+
this.log(`DEBUG: ✓ ${result.endpoint} - ${result.statusCode} (${result.latencyMs}ms)`);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
this.log(`DEBUG: ✗ ${result.endpoint} - ${result.error} (${result.latencyMs}ms)`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (!connectivityCheck.connected) {
|
|
773
|
+
// Build detailed error message with endpoint diagnostics
|
|
774
|
+
const endpointDetails = connectivityCheck.endpointResults
|
|
775
|
+
.map((r) => ` - ${r.endpoint}: ${r.error} (${r.latencyMs}ms)`)
|
|
776
|
+
.join('\n');
|
|
777
|
+
throw new Error(`Unable to fetch results after 10 attempts.\n\nInternet connectivity check failed - all test endpoints unreachable:\n${endpointDetails}\n\nPlease verify your network connection and DNS resolution.`);
|
|
778
|
+
}
|
|
731
779
|
throw new Error('unable to fetch results after 10 attempts');
|
|
732
780
|
}
|
|
733
781
|
this.log('unable to fetch results, trying again...');
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
+
import { ConnectivityCheckResult } from '../utils/connectivity';
|
|
2
3
|
type StatusResponse = {
|
|
3
4
|
appBinaryId?: string;
|
|
4
5
|
attempts?: number;
|
|
6
|
+
connectivityCheck?: {
|
|
7
|
+
connected: boolean;
|
|
8
|
+
endpointResults: ConnectivityCheckResult['endpointResults'];
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
5
11
|
consoleUrl?: string;
|
|
6
12
|
error?: string;
|
|
7
13
|
status: 'CANCELLED' | 'FAILED' | 'PASSED' | 'PENDING' | 'RUNNING';
|
package/dist/commands/status.js
CHANGED
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const constants_1 = require("../constants");
|
|
5
5
|
const api_gateway_1 = require("../gateways/api-gateway");
|
|
6
6
|
const methods_1 = require("../methods");
|
|
7
|
+
const connectivity_1 = require("../utils/connectivity");
|
|
7
8
|
class Status extends core_1.Command {
|
|
8
9
|
static description = 'Get the status of an upload by name or upload ID';
|
|
9
10
|
static enableJsonFlag = true;
|
|
@@ -64,10 +65,27 @@ class Status extends core_1.Command {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
if (!status) {
|
|
67
|
-
|
|
68
|
+
// Check if the failure is due to internet connectivity issues
|
|
69
|
+
const connectivityCheck = await (0, connectivity_1.checkInternetConnectivity)();
|
|
70
|
+
let errorMessage;
|
|
71
|
+
if (connectivityCheck.connected) {
|
|
72
|
+
errorMessage = `Failed to get status after 5 attempts. Internet appears functional but unable to reach API. Last error: ${lastError?.message || 'Unknown error'}`;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Build detailed error message with endpoint diagnostics
|
|
76
|
+
const endpointDetails = connectivityCheck.endpointResults
|
|
77
|
+
.map((r) => ` - ${r.endpoint}: ${r.error} (${r.latencyMs}ms)`)
|
|
78
|
+
.join('\n');
|
|
79
|
+
errorMessage = `Failed to get status after 5 attempts.\n\nInternet connectivity check failed - all test endpoints unreachable:\n${endpointDetails}\n\nPlease verify your network connection and DNS resolution.\nLast API error: ${lastError?.message || 'Unknown error'}`;
|
|
80
|
+
}
|
|
68
81
|
if (json) {
|
|
69
82
|
return {
|
|
70
83
|
attempts: 5,
|
|
84
|
+
connectivityCheck: {
|
|
85
|
+
connected: connectivityCheck.connected,
|
|
86
|
+
endpointResults: connectivityCheck.endpointResults,
|
|
87
|
+
message: connectivityCheck.message,
|
|
88
|
+
},
|
|
71
89
|
error: errorMessage,
|
|
72
90
|
status: 'FAILED',
|
|
73
91
|
tests: [],
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for checking internet connectivity using third-party endpoints
|
|
3
|
+
*/
|
|
4
|
+
export type ConnectivityCheckResult = {
|
|
5
|
+
/** Whether internet connectivity was detected */
|
|
6
|
+
connected: boolean;
|
|
7
|
+
/** Detailed results for each endpoint tested */
|
|
8
|
+
endpointResults: Array<{
|
|
9
|
+
/** The endpoint URL that was tested */
|
|
10
|
+
endpoint: string;
|
|
11
|
+
/** Error message if request failed */
|
|
12
|
+
error?: string;
|
|
13
|
+
/** Time taken for the request in milliseconds */
|
|
14
|
+
latencyMs?: number;
|
|
15
|
+
/** HTTP status code if request succeeded */
|
|
16
|
+
statusCode?: number;
|
|
17
|
+
/** Whether this endpoint was reachable */
|
|
18
|
+
success: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
/** Summary message for developers */
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Check if the system has internet connectivity by testing against
|
|
25
|
+
* multiple reliable third-party endpoints with detailed diagnostics.
|
|
26
|
+
*
|
|
27
|
+
* @returns Promise<ConnectivityCheckResult> - Detailed connectivity check results
|
|
28
|
+
*/
|
|
29
|
+
export declare function checkInternetConnectivity(): Promise<ConnectivityCheckResult>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility for checking internet connectivity using third-party endpoints
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.checkInternetConnectivity = checkInternetConnectivity;
|
|
7
|
+
/**
|
|
8
|
+
* Check if the system has internet connectivity by testing against
|
|
9
|
+
* multiple reliable third-party endpoints with detailed diagnostics.
|
|
10
|
+
*
|
|
11
|
+
* @returns Promise<ConnectivityCheckResult> - Detailed connectivity check results
|
|
12
|
+
*/
|
|
13
|
+
async function checkInternetConnectivity() {
|
|
14
|
+
// Use multiple reliable endpoints to test connectivity
|
|
15
|
+
const testEndpoints = [
|
|
16
|
+
{ url: 'https://www.google.com/generate_204', description: 'Google' },
|
|
17
|
+
{ url: 'https://www.cloudflare.com/cdn-cgi/trace', description: 'Cloudflare' },
|
|
18
|
+
{ url: 'https://1.1.1.1/', description: 'Cloudflare DNS' },
|
|
19
|
+
];
|
|
20
|
+
const endpointResults = [];
|
|
21
|
+
let anySuccess = false;
|
|
22
|
+
// Try each endpoint with a short timeout
|
|
23
|
+
for (const { url, description } of testEndpoints) {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
try {
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 second timeout
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
method: 'HEAD', // Use HEAD to minimize data transfer
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
// Disable redirects to get faster response
|
|
32
|
+
redirect: 'manual',
|
|
33
|
+
});
|
|
34
|
+
clearTimeout(timeoutId);
|
|
35
|
+
const latencyMs = Date.now() - startTime;
|
|
36
|
+
// Any response (including 3xx redirects) indicates connectivity
|
|
37
|
+
if (response) {
|
|
38
|
+
anySuccess = true;
|
|
39
|
+
endpointResults.push({
|
|
40
|
+
endpoint: `${description} (${url})`,
|
|
41
|
+
success: true,
|
|
42
|
+
statusCode: response.status,
|
|
43
|
+
latencyMs,
|
|
44
|
+
});
|
|
45
|
+
// Found working connection, no need to test more
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const latencyMs = Date.now() - startTime;
|
|
51
|
+
let errorMessage = 'Unknown error';
|
|
52
|
+
if (error instanceof Error) {
|
|
53
|
+
if (error.name === 'AbortError') {
|
|
54
|
+
errorMessage = 'Request timeout (>3s)';
|
|
55
|
+
}
|
|
56
|
+
else if (error.message.includes('fetch failed')) {
|
|
57
|
+
errorMessage = 'Network request failed (DNS/connection error)';
|
|
58
|
+
}
|
|
59
|
+
else if (error.message.includes('ENOTFOUND')) {
|
|
60
|
+
errorMessage = 'DNS resolution failed';
|
|
61
|
+
}
|
|
62
|
+
else if (error.message.includes('ECONNREFUSED')) {
|
|
63
|
+
errorMessage = 'Connection refused';
|
|
64
|
+
}
|
|
65
|
+
else if (error.message.includes('ETIMEDOUT')) {
|
|
66
|
+
errorMessage = 'Connection timeout';
|
|
67
|
+
}
|
|
68
|
+
else if (error.message.includes('ENETUNREACH')) {
|
|
69
|
+
errorMessage = 'Network unreachable';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
errorMessage = error.message;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
endpointResults.push({
|
|
76
|
+
endpoint: `${description} (${url})`,
|
|
77
|
+
success: false,
|
|
78
|
+
error: errorMessage,
|
|
79
|
+
latencyMs,
|
|
80
|
+
});
|
|
81
|
+
// Continue to next endpoint if this one fails
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Generate developer-friendly message
|
|
86
|
+
let message;
|
|
87
|
+
if (anySuccess) {
|
|
88
|
+
const successfulEndpoint = endpointResults.find((r) => r.success);
|
|
89
|
+
message = `Internet connectivity verified via ${successfulEndpoint?.endpoint} (${successfulEndpoint?.latencyMs}ms)`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const testedEndpoints = endpointResults.map((r) => r.endpoint).join(', ');
|
|
93
|
+
message = `No internet connectivity detected. Tested endpoints: ${testedEndpoints}`;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
connected: anySuccess,
|
|
97
|
+
endpointResults,
|
|
98
|
+
message,
|
|
99
|
+
};
|
|
100
|
+
}
|
package/oclif.manifest.json
CHANGED