@qawolf/ci-sdk 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/cjs/lib/api/ci-greenlight.js +1 -0
- package/dist/cjs/lib/api/deploy.js +4 -4
- package/dist/cjs/lib/sdk/domain/attempt-deploy.js +18 -7
- package/dist/cjs/lib/sdk/domain/poll-ci-greenlight.js +56 -9
- package/dist/esm/lib/api/ci-greenlight.js +1 -0
- package/dist/esm/lib/api/deploy.js +4 -4
- package/dist/esm/lib/sdk/domain/attempt-deploy.js +18 -7
- package/dist/esm/lib/sdk/domain/poll-ci-greenlight.js +57 -10
- package/dist/types/lib/api/ci-greenlight.d.ts +1 -1
- package/dist/types/lib/api/deploy.d.ts +4 -4
- package/dist/types/lib/sdk/domain/attempt-deploy.d.ts +14 -0
- package/dist/types/lib/sdk/domain/poll-ci-greenlight.d.ts +40 -2
- package/dist/types/lib/sdk/index.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,19 @@ an up-to-date API usage version.
|
|
|
101
101
|
|
|
102
102
|
# Changelog
|
|
103
103
|
|
|
104
|
+
## v0.14.0
|
|
105
|
+
|
|
106
|
+
- New `failReason`, `abortReason` and `httpStatus` fields added to the `attemptNotifyDeploy`
|
|
107
|
+
function result object to provide more context on why the operation failed or was aborted.
|
|
108
|
+
- Exported missing TypeScript typings describing result objects and fields for both functions.
|
|
109
|
+
|
|
110
|
+
## v0.13.0
|
|
111
|
+
|
|
112
|
+
- New `pollTimeout` parameter for `pollCiGreenlightStatus` to allow for a custom
|
|
113
|
+
timeout in milliseconds for the polling operation. It now defaults to two hours.
|
|
114
|
+
- New `abortReason` and `httpStatus` fields added to the `PollCiGreenlightStatus` type
|
|
115
|
+
with `"aborted"` outcome to provide more context on why the poll was aborted.
|
|
116
|
+
|
|
104
117
|
## v0.12.1
|
|
105
118
|
|
|
106
119
|
- Export dependencies types from root module.
|
|
@@ -131,7 +144,7 @@ BREAKING CHANGES:
|
|
|
131
144
|
|
|
132
145
|
BREAKING CHANGES:
|
|
133
146
|
|
|
134
|
-
- Renamed `attemptDeploy` to `attemptNotifyDeploy
|
|
147
|
+
- Renamed `attemptDeploy` to `attemptNotifyDeploy` .
|
|
135
148
|
|
|
136
149
|
## v0.10.2
|
|
137
150
|
|
|
@@ -49,27 +49,27 @@ async function postDeploySuccess({ apiKey, serviceBase }, { branch, commitUrl, d
|
|
|
49
49
|
else {
|
|
50
50
|
if (response.status >= 500) {
|
|
51
51
|
return {
|
|
52
|
-
failureReason: "server-error",
|
|
52
|
+
failureReason: "5XX-server-error",
|
|
53
53
|
httpStatus: response.status,
|
|
54
54
|
success: false,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
if (response.status === 401) {
|
|
58
58
|
return {
|
|
59
|
-
failureReason: "unauthorized",
|
|
59
|
+
failureReason: "401-unauthorized",
|
|
60
60
|
httpStatus: response.status,
|
|
61
61
|
success: false,
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
if (response.status === 403) {
|
|
65
65
|
return {
|
|
66
|
-
failureReason: "forbidden",
|
|
66
|
+
failureReason: "403-forbidden",
|
|
67
67
|
httpStatus: response.status,
|
|
68
68
|
success: false,
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
return {
|
|
72
|
-
failureReason: "
|
|
72
|
+
failureReason: "4XX-client-error",
|
|
73
73
|
httpStatus: response.status,
|
|
74
74
|
success: false,
|
|
75
75
|
};
|
|
@@ -9,29 +9,40 @@ async function attemptNotifyDeploy(deps, apiConfig, config) {
|
|
|
9
9
|
if (!responseState.success) {
|
|
10
10
|
if (responseState.failureReason === "no-matched-trigger") {
|
|
11
11
|
log.error(`🚫 No trigger matched the request. Please contact support.`);
|
|
12
|
-
return { outcome: "failed" };
|
|
12
|
+
return { outcome: "failed", failReason: "no-matched-trigger" };
|
|
13
13
|
}
|
|
14
14
|
else if (responseState.failureReason === "run-could-not-be-created") {
|
|
15
15
|
log.error(`🚫 Run could not be created. Reason: "${responseState.details}". Please contact support.`);
|
|
16
|
-
return { outcome: "failed" };
|
|
16
|
+
return { outcome: "failed", failReason: "run-could-not-be-created" };
|
|
17
17
|
}
|
|
18
18
|
if (responseState.failureReason === "network-error")
|
|
19
19
|
log.error(`🚫 Network error from deploy_success, aborting.`);
|
|
20
|
-
else if (responseState.failureReason === "server-error")
|
|
20
|
+
else if (responseState.failureReason === "5XX-server-error")
|
|
21
21
|
log.error(`🚫 Server error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
22
|
-
else if (responseState.failureReason === "
|
|
22
|
+
else if (responseState.failureReason === "4XX-client-error") {
|
|
23
23
|
log.error(`🚫 Unrecoverable error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
24
24
|
}
|
|
25
|
-
else if (responseState.failureReason === "forbidden") {
|
|
25
|
+
else if (responseState.failureReason === "403-forbidden") {
|
|
26
26
|
log.error(`🚫 Forbidden error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
27
27
|
}
|
|
28
|
-
else if (responseState.failureReason === "unauthorized") {
|
|
28
|
+
else if (responseState.failureReason === "401-unauthorized") {
|
|
29
29
|
log.error(`🚫 Unauthorized error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
30
30
|
}
|
|
31
31
|
else {
|
|
32
32
|
(0, utils_js_1.assertType)(responseState);
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
if (responseState.failureReason === "network-error") {
|
|
35
|
+
return {
|
|
36
|
+
outcome: "aborted",
|
|
37
|
+
abortReason: "network-error",
|
|
38
|
+
httpStatus: undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
outcome: "aborted",
|
|
43
|
+
abortReason: responseState.failureReason,
|
|
44
|
+
httpStatus: responseState.httpStatus,
|
|
45
|
+
};
|
|
35
46
|
}
|
|
36
47
|
log.info(`✅ Run created with ID: ${responseState.runId}`);
|
|
37
48
|
return {
|
|
@@ -3,24 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.pollCiGreenlightStatus = void 0;
|
|
4
4
|
const utils_js_1 = require("../../utils.js");
|
|
5
5
|
const ci_greenlight_js_1 = require("../../api/ci-greenlight.js");
|
|
6
|
+
// If you change these values, make sure you update the JSDoc
|
|
7
|
+
// comments in the PollConfig type.
|
|
6
8
|
const safeDefaults = {
|
|
7
9
|
maxRetries: 10,
|
|
8
10
|
retryInterval: 10 * 1000,
|
|
9
11
|
runInProgressInterval: 30 * 1000,
|
|
10
12
|
runUnderReviewInterval: 60 * 1000,
|
|
13
|
+
pollTimeout: 120 * 60 * 1000,
|
|
11
14
|
};
|
|
12
15
|
async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStageChanged: onRunStageChanged = () => void 0, ...pollConfig }) {
|
|
13
16
|
let retries = 0;
|
|
14
17
|
const { log } = deps;
|
|
15
|
-
const { maxRetries = safeDefaults.maxRetries, retryInterval = safeDefaults.retryInterval, runInProgressInterval = safeDefaults.runInProgressInterval, runUnderReviewInterval = safeDefaults.runUnderReviewInterval, } = pollConfig;
|
|
18
|
+
const { maxRetries = safeDefaults.maxRetries, retryInterval = safeDefaults.retryInterval, runInProgressInterval = safeDefaults.runInProgressInterval, runUnderReviewInterval = safeDefaults.runUnderReviewInterval, pollTimeout = safeDefaults.pollTimeout, } = pollConfig;
|
|
16
19
|
let currentStatus = undefined;
|
|
17
|
-
|
|
20
|
+
const startedAt = Date.now();
|
|
21
|
+
let hasTimedOut = false;
|
|
22
|
+
while (!(hasTimedOut = Date.now() - startedAt > pollTimeout)) {
|
|
18
23
|
const responseState = await (0, ci_greenlight_js_1.fetchCiGreenlightStatus)(apiConfig, {
|
|
19
24
|
runId,
|
|
20
25
|
}, deps);
|
|
21
26
|
if (!responseState.ok && !responseState.canRetry) {
|
|
22
27
|
log.error(`🚫 Unrecoverable error (status ${responseState.httpStatus}) from CI greenlight, aborting.`);
|
|
23
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
outcome: "aborted",
|
|
30
|
+
abortReason: "4XX-client-error",
|
|
31
|
+
httpStatus: responseState.httpStatus,
|
|
32
|
+
};
|
|
24
33
|
}
|
|
25
34
|
if (!responseState.ok) {
|
|
26
35
|
// This is a non-OK retryable status.
|
|
@@ -29,7 +38,24 @@ async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStageChange
|
|
|
29
38
|
log.error(`🚫 Unrecoverable error${responseState.httpStatus
|
|
30
39
|
? ` (status ${responseState.httpStatus})`
|
|
31
40
|
: ""} from CI greenlight, aborting after ${maxRetries} attempts.`);
|
|
32
|
-
|
|
41
|
+
if (responseState.errorType === "network")
|
|
42
|
+
return {
|
|
43
|
+
outcome: "aborted",
|
|
44
|
+
abortReason: "network-error",
|
|
45
|
+
httpStatus: undefined,
|
|
46
|
+
};
|
|
47
|
+
let abortReason = "XXX-other-http-code";
|
|
48
|
+
if (responseState.errorType === "not-found")
|
|
49
|
+
abortReason = "404-run-not-found";
|
|
50
|
+
else if (responseState.errorType === "server")
|
|
51
|
+
abortReason = "5XX-server-error";
|
|
52
|
+
else
|
|
53
|
+
(0, utils_js_1.assertType)(responseState);
|
|
54
|
+
return {
|
|
55
|
+
outcome: "aborted",
|
|
56
|
+
abortReason,
|
|
57
|
+
httpStatus: responseState.httpStatus,
|
|
58
|
+
};
|
|
33
59
|
}
|
|
34
60
|
const { errorType, httpStatus } = responseState;
|
|
35
61
|
const retryLabel = `${httpStatus ? ` (status ${httpStatus})` : ""}, will try again... (${retries}/${maxRetries})`;
|
|
@@ -88,6 +114,23 @@ async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStageChange
|
|
|
88
114
|
return { outcome: "failed", greenlightStatus };
|
|
89
115
|
}
|
|
90
116
|
}
|
|
117
|
+
if (runStage === "canceled") {
|
|
118
|
+
log.info(`🚫 Run was canceled. Aborting... Please contact support.\n${urlInfo}`);
|
|
119
|
+
return {
|
|
120
|
+
outcome: "aborted",
|
|
121
|
+
abortReason: "run-canceled",
|
|
122
|
+
httpStatus: undefined,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Non-terminal run stages
|
|
126
|
+
if (hasTimedOut) {
|
|
127
|
+
log.warn(`❌ Poll timed out after ${pollTimeout / 1000}s. Aborting...`);
|
|
128
|
+
return {
|
|
129
|
+
outcome: "aborted",
|
|
130
|
+
abortReason: "poll-timed-out",
|
|
131
|
+
httpStatus: undefined,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
91
134
|
if (runStage === "initializing") {
|
|
92
135
|
log.info(`⏳ Run is in progress... ${urlInfo}`);
|
|
93
136
|
await (0, utils_js_1.sleep)(runInProgressInterval);
|
|
@@ -96,12 +139,16 @@ async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStageChange
|
|
|
96
139
|
log.info(`🕵️ Run is under review... ${urlInfo}`);
|
|
97
140
|
await (0, utils_js_1.sleep)(runUnderReviewInterval);
|
|
98
141
|
}
|
|
99
|
-
else
|
|
100
|
-
|
|
101
|
-
return { outcome: "aborted" };
|
|
102
|
-
}
|
|
103
|
-
else
|
|
142
|
+
else {
|
|
143
|
+
(0, utils_js_1.assertType)(runStage);
|
|
104
144
|
throw new Error(`Unexpected run stage: ${runStage}`);
|
|
145
|
+
}
|
|
105
146
|
}
|
|
147
|
+
log.warn(`❌ Poll timed out after ${pollTimeout / 1000}s. Aborting...`);
|
|
148
|
+
return {
|
|
149
|
+
outcome: "aborted",
|
|
150
|
+
abortReason: "poll-timed-out",
|
|
151
|
+
httpStatus: undefined,
|
|
152
|
+
};
|
|
106
153
|
}
|
|
107
154
|
exports.pollCiGreenlightStatus = pollCiGreenlightStatus;
|
|
@@ -46,27 +46,27 @@ export async function postDeploySuccess({ apiKey, serviceBase }, { branch, commi
|
|
|
46
46
|
else {
|
|
47
47
|
if (response.status >= 500) {
|
|
48
48
|
return {
|
|
49
|
-
failureReason: "server-error",
|
|
49
|
+
failureReason: "5XX-server-error",
|
|
50
50
|
httpStatus: response.status,
|
|
51
51
|
success: false,
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
if (response.status === 401) {
|
|
55
55
|
return {
|
|
56
|
-
failureReason: "unauthorized",
|
|
56
|
+
failureReason: "401-unauthorized",
|
|
57
57
|
httpStatus: response.status,
|
|
58
58
|
success: false,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
if (response.status === 403) {
|
|
62
62
|
return {
|
|
63
|
-
failureReason: "forbidden",
|
|
63
|
+
failureReason: "403-forbidden",
|
|
64
64
|
httpStatus: response.status,
|
|
65
65
|
success: false,
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
return {
|
|
69
|
-
failureReason: "
|
|
69
|
+
failureReason: "4XX-client-error",
|
|
70
70
|
httpStatus: response.status,
|
|
71
71
|
success: false,
|
|
72
72
|
};
|
|
@@ -6,29 +6,40 @@ export async function attemptNotifyDeploy(deps, apiConfig, config) {
|
|
|
6
6
|
if (!responseState.success) {
|
|
7
7
|
if (responseState.failureReason === "no-matched-trigger") {
|
|
8
8
|
log.error(`🚫 No trigger matched the request. Please contact support.`);
|
|
9
|
-
return { outcome: "failed" };
|
|
9
|
+
return { outcome: "failed", failReason: "no-matched-trigger" };
|
|
10
10
|
}
|
|
11
11
|
else if (responseState.failureReason === "run-could-not-be-created") {
|
|
12
12
|
log.error(`🚫 Run could not be created. Reason: "${responseState.details}". Please contact support.`);
|
|
13
|
-
return { outcome: "failed" };
|
|
13
|
+
return { outcome: "failed", failReason: "run-could-not-be-created" };
|
|
14
14
|
}
|
|
15
15
|
if (responseState.failureReason === "network-error")
|
|
16
16
|
log.error(`🚫 Network error from deploy_success, aborting.`);
|
|
17
|
-
else if (responseState.failureReason === "server-error")
|
|
17
|
+
else if (responseState.failureReason === "5XX-server-error")
|
|
18
18
|
log.error(`🚫 Server error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
19
|
-
else if (responseState.failureReason === "
|
|
19
|
+
else if (responseState.failureReason === "4XX-client-error") {
|
|
20
20
|
log.error(`🚫 Unrecoverable error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
21
21
|
}
|
|
22
|
-
else if (responseState.failureReason === "forbidden") {
|
|
22
|
+
else if (responseState.failureReason === "403-forbidden") {
|
|
23
23
|
log.error(`🚫 Forbidden error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
24
24
|
}
|
|
25
|
-
else if (responseState.failureReason === "unauthorized") {
|
|
25
|
+
else if (responseState.failureReason === "401-unauthorized") {
|
|
26
26
|
log.error(`🚫 Unauthorized error from deploy_success (status ${responseState.httpStatus}), aborting. Please contact support.`);
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
29
29
|
assertType(responseState);
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
if (responseState.failureReason === "network-error") {
|
|
32
|
+
return {
|
|
33
|
+
outcome: "aborted",
|
|
34
|
+
abortReason: "network-error",
|
|
35
|
+
httpStatus: undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
outcome: "aborted",
|
|
40
|
+
abortReason: responseState.failureReason,
|
|
41
|
+
httpStatus: responseState.httpStatus,
|
|
42
|
+
};
|
|
32
43
|
}
|
|
33
44
|
log.info(`✅ Run created with ID: ${responseState.runId}`);
|
|
34
45
|
return {
|
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
import { sleep, pluralize } from "../../utils.js";
|
|
1
|
+
import { sleep, pluralize, assertType } from "../../utils.js";
|
|
2
2
|
import { fetchCiGreenlightStatus, } from "../../api/ci-greenlight.js";
|
|
3
|
+
// If you change these values, make sure you update the JSDoc
|
|
4
|
+
// comments in the PollConfig type.
|
|
3
5
|
const safeDefaults = {
|
|
4
6
|
maxRetries: 10,
|
|
5
7
|
retryInterval: 10 * 1000,
|
|
6
8
|
runInProgressInterval: 30 * 1000,
|
|
7
9
|
runUnderReviewInterval: 60 * 1000,
|
|
10
|
+
pollTimeout: 120 * 60 * 1000,
|
|
8
11
|
};
|
|
9
12
|
export async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStageChanged: onRunStageChanged = () => void 0, ...pollConfig }) {
|
|
10
13
|
let retries = 0;
|
|
11
14
|
const { log } = deps;
|
|
12
|
-
const { maxRetries = safeDefaults.maxRetries, retryInterval = safeDefaults.retryInterval, runInProgressInterval = safeDefaults.runInProgressInterval, runUnderReviewInterval = safeDefaults.runUnderReviewInterval, } = pollConfig;
|
|
15
|
+
const { maxRetries = safeDefaults.maxRetries, retryInterval = safeDefaults.retryInterval, runInProgressInterval = safeDefaults.runInProgressInterval, runUnderReviewInterval = safeDefaults.runUnderReviewInterval, pollTimeout = safeDefaults.pollTimeout, } = pollConfig;
|
|
13
16
|
let currentStatus = undefined;
|
|
14
|
-
|
|
17
|
+
const startedAt = Date.now();
|
|
18
|
+
let hasTimedOut = false;
|
|
19
|
+
while (!(hasTimedOut = Date.now() - startedAt > pollTimeout)) {
|
|
15
20
|
const responseState = await fetchCiGreenlightStatus(apiConfig, {
|
|
16
21
|
runId,
|
|
17
22
|
}, deps);
|
|
18
23
|
if (!responseState.ok && !responseState.canRetry) {
|
|
19
24
|
log.error(`🚫 Unrecoverable error (status ${responseState.httpStatus}) from CI greenlight, aborting.`);
|
|
20
|
-
return {
|
|
25
|
+
return {
|
|
26
|
+
outcome: "aborted",
|
|
27
|
+
abortReason: "4XX-client-error",
|
|
28
|
+
httpStatus: responseState.httpStatus,
|
|
29
|
+
};
|
|
21
30
|
}
|
|
22
31
|
if (!responseState.ok) {
|
|
23
32
|
// This is a non-OK retryable status.
|
|
@@ -26,7 +35,24 @@ export async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStag
|
|
|
26
35
|
log.error(`🚫 Unrecoverable error${responseState.httpStatus
|
|
27
36
|
? ` (status ${responseState.httpStatus})`
|
|
28
37
|
: ""} from CI greenlight, aborting after ${maxRetries} attempts.`);
|
|
29
|
-
|
|
38
|
+
if (responseState.errorType === "network")
|
|
39
|
+
return {
|
|
40
|
+
outcome: "aborted",
|
|
41
|
+
abortReason: "network-error",
|
|
42
|
+
httpStatus: undefined,
|
|
43
|
+
};
|
|
44
|
+
let abortReason = "XXX-other-http-code";
|
|
45
|
+
if (responseState.errorType === "not-found")
|
|
46
|
+
abortReason = "404-run-not-found";
|
|
47
|
+
else if (responseState.errorType === "server")
|
|
48
|
+
abortReason = "5XX-server-error";
|
|
49
|
+
else
|
|
50
|
+
assertType(responseState);
|
|
51
|
+
return {
|
|
52
|
+
outcome: "aborted",
|
|
53
|
+
abortReason,
|
|
54
|
+
httpStatus: responseState.httpStatus,
|
|
55
|
+
};
|
|
30
56
|
}
|
|
31
57
|
const { errorType, httpStatus } = responseState;
|
|
32
58
|
const retryLabel = `${httpStatus ? ` (status ${httpStatus})` : ""}, will try again... (${retries}/${maxRetries})`;
|
|
@@ -85,6 +111,23 @@ export async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStag
|
|
|
85
111
|
return { outcome: "failed", greenlightStatus };
|
|
86
112
|
}
|
|
87
113
|
}
|
|
114
|
+
if (runStage === "canceled") {
|
|
115
|
+
log.info(`🚫 Run was canceled. Aborting... Please contact support.\n${urlInfo}`);
|
|
116
|
+
return {
|
|
117
|
+
outcome: "aborted",
|
|
118
|
+
abortReason: "run-canceled",
|
|
119
|
+
httpStatus: undefined,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Non-terminal run stages
|
|
123
|
+
if (hasTimedOut) {
|
|
124
|
+
log.warn(`❌ Poll timed out after ${pollTimeout / 1000}s. Aborting...`);
|
|
125
|
+
return {
|
|
126
|
+
outcome: "aborted",
|
|
127
|
+
abortReason: "poll-timed-out",
|
|
128
|
+
httpStatus: undefined,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
88
131
|
if (runStage === "initializing") {
|
|
89
132
|
log.info(`⏳ Run is in progress... ${urlInfo}`);
|
|
90
133
|
await sleep(runInProgressInterval);
|
|
@@ -93,11 +136,15 @@ export async function pollCiGreenlightStatus(deps, apiConfig, { runId, onRunStag
|
|
|
93
136
|
log.info(`🕵️ Run is under review... ${urlInfo}`);
|
|
94
137
|
await sleep(runUnderReviewInterval);
|
|
95
138
|
}
|
|
96
|
-
else
|
|
97
|
-
|
|
98
|
-
return { outcome: "aborted" };
|
|
99
|
-
}
|
|
100
|
-
else
|
|
139
|
+
else {
|
|
140
|
+
assertType(runStage);
|
|
101
141
|
throw new Error(`Unexpected run stage: ${runStage}`);
|
|
142
|
+
}
|
|
102
143
|
}
|
|
144
|
+
log.warn(`❌ Poll timed out after ${pollTimeout / 1000}s. Aborting...`);
|
|
145
|
+
return {
|
|
146
|
+
outcome: "aborted",
|
|
147
|
+
abortReason: "poll-timed-out",
|
|
148
|
+
httpStatus: undefined,
|
|
149
|
+
};
|
|
103
150
|
}
|
|
@@ -101,9 +101,9 @@ export declare function fetchCiGreenlightStatus({ serviceBase, apiKey }: ApiConf
|
|
|
101
101
|
} | {
|
|
102
102
|
readonly canRetry: false;
|
|
103
103
|
readonly httpStatus: number;
|
|
104
|
+
readonly errorType: "client";
|
|
104
105
|
readonly ok: false;
|
|
105
106
|
readonly greenlightStatus?: never;
|
|
106
|
-
readonly errorType?: never;
|
|
107
107
|
} | {
|
|
108
108
|
readonly canRetry: true;
|
|
109
109
|
readonly errorType: "network";
|
|
@@ -39,25 +39,25 @@ export declare function postDeploySuccess({ apiKey, serviceBase }: ApiConfig, {
|
|
|
39
39
|
readonly details?: never;
|
|
40
40
|
readonly httpStatus?: never;
|
|
41
41
|
} | {
|
|
42
|
-
readonly failureReason: "server-error";
|
|
42
|
+
readonly failureReason: "5XX-server-error";
|
|
43
43
|
readonly httpStatus: number;
|
|
44
44
|
readonly success: false;
|
|
45
45
|
readonly details?: never;
|
|
46
46
|
readonly runId?: never;
|
|
47
47
|
} | {
|
|
48
|
-
readonly failureReason: "unauthorized";
|
|
48
|
+
readonly failureReason: "401-unauthorized";
|
|
49
49
|
readonly httpStatus: 401;
|
|
50
50
|
readonly success: false;
|
|
51
51
|
readonly details?: never;
|
|
52
52
|
readonly runId?: never;
|
|
53
53
|
} | {
|
|
54
|
-
readonly failureReason: "forbidden";
|
|
54
|
+
readonly failureReason: "403-forbidden";
|
|
55
55
|
readonly httpStatus: 403;
|
|
56
56
|
readonly success: false;
|
|
57
57
|
readonly details?: never;
|
|
58
58
|
readonly runId?: never;
|
|
59
59
|
} | {
|
|
60
|
-
readonly failureReason: "
|
|
60
|
+
readonly failureReason: "4XX-client-error";
|
|
61
61
|
readonly httpStatus: number;
|
|
62
62
|
readonly success: false;
|
|
63
63
|
readonly details?: never;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { DeployConfig } from "../../api/deploy.js";
|
|
2
2
|
import { SdkDependencies } from "../dependencies.js";
|
|
3
3
|
import { ApiConfig } from "../../api-types.js";
|
|
4
|
+
export type AttemptDeployFailedReason = "no-matched-trigger" | "run-could-not-be-created";
|
|
5
|
+
export type AttemptDeployHttpAbortReason = "4XX-client-error" | "401-unauthorized" | "403-forbidden" | "5XX-server-error";
|
|
6
|
+
export type AttemptDeployNonHttpAbortReason = "network-error";
|
|
7
|
+
export type AttemptDeployAbortReason = AttemptDeployHttpAbortReason | AttemptDeployNonHttpAbortReason;
|
|
4
8
|
export type NotifyDeployStatus = {
|
|
5
9
|
/**
|
|
6
10
|
* A run was successfully created.
|
|
@@ -12,10 +16,20 @@ export type NotifyDeployStatus = {
|
|
|
12
16
|
* A run could not be created.
|
|
13
17
|
*/
|
|
14
18
|
outcome: "failed";
|
|
19
|
+
failReason: AttemptDeployFailedReason;
|
|
15
20
|
} | {
|
|
16
21
|
/**
|
|
17
22
|
* An unrecoverable error occurred.
|
|
18
23
|
*/
|
|
19
24
|
outcome: "aborted";
|
|
25
|
+
abortReason: AttemptDeployNonHttpAbortReason;
|
|
26
|
+
httpStatus: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
/**
|
|
29
|
+
* An unrecoverable HTTP error occurred.
|
|
30
|
+
*/
|
|
31
|
+
outcome: "aborted";
|
|
32
|
+
abortReason: AttemptDeployHttpAbortReason;
|
|
33
|
+
httpStatus: number;
|
|
20
34
|
};
|
|
21
35
|
export declare function attemptNotifyDeploy(deps: SdkDependencies, apiConfig: ApiConfig, config: Partial<DeployConfig>): Promise<NotifyDeployStatus>;
|
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
import { CiGreenlightStatus } from "../../api/ci-greenlight.js";
|
|
2
2
|
import { ApiConfig } from "../../api-types.js";
|
|
3
3
|
import { SdkDependencies } from "../dependencies.js";
|
|
4
|
-
export type
|
|
4
|
+
export type PollCiGreenlightPollConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Maximum number of network operations retries before aborting the job.
|
|
7
|
+
*
|
|
8
|
+
* @default 10
|
|
9
|
+
*/
|
|
5
10
|
maxRetries: number;
|
|
11
|
+
/**
|
|
12
|
+
* Interval in milliseconds between network operations retries.
|
|
13
|
+
*
|
|
14
|
+
* @default 10 seconds
|
|
15
|
+
*/
|
|
6
16
|
retryInterval: number;
|
|
17
|
+
/**
|
|
18
|
+
* Interval in milliseconds between polling for a run in progress.
|
|
19
|
+
*
|
|
20
|
+
* @default 30 seconds
|
|
21
|
+
*/
|
|
7
22
|
runInProgressInterval: number;
|
|
23
|
+
/**
|
|
24
|
+
* Interval in milliseconds between polling for a run under review.
|
|
25
|
+
*
|
|
26
|
+
* @default 60 minutes
|
|
27
|
+
*/
|
|
8
28
|
runUnderReviewInterval: number;
|
|
29
|
+
/**
|
|
30
|
+
* Timeout in milliseconds before aborting this polling operation.
|
|
31
|
+
*
|
|
32
|
+
* @default 120 minutes
|
|
33
|
+
*/
|
|
34
|
+
pollTimeout: number;
|
|
9
35
|
};
|
|
36
|
+
export type PollCiGreenlightHttpAbortReason = "4XX-client-error" | "404-run-not-found" | "5XX-server-error" | "XXX-other-http-code";
|
|
37
|
+
export type PollCiGreenlightNonHttpAbortReason = "poll-timed-out" | "run-canceled" | "network-error";
|
|
38
|
+
export type PollCiGreenlightAbortReason = PollCiGreenlightNonHttpAbortReason | PollCiGreenlightHttpAbortReason;
|
|
10
39
|
export type PollCiGreenlightStatus = {
|
|
11
40
|
/**
|
|
12
41
|
* No blocking bugs were found in the run.
|
|
@@ -28,6 +57,15 @@ export type PollCiGreenlightStatus = {
|
|
|
28
57
|
* The run was aborted due to an unrecoverable error.
|
|
29
58
|
*/
|
|
30
59
|
outcome: "aborted";
|
|
60
|
+
abortReason: PollCiGreenlightHttpAbortReason;
|
|
61
|
+
httpStatus: number;
|
|
62
|
+
} | {
|
|
63
|
+
/**
|
|
64
|
+
* The run was aborted due to an unrecoverable error.
|
|
65
|
+
*/
|
|
66
|
+
outcome: "aborted";
|
|
67
|
+
abortReason: PollCiGreenlightNonHttpAbortReason;
|
|
68
|
+
httpStatus: undefined;
|
|
31
69
|
};
|
|
32
70
|
export type PollCiGreenlightParams = {
|
|
33
71
|
runId: string;
|
|
@@ -35,5 +73,5 @@ export type PollCiGreenlightParams = {
|
|
|
35
73
|
* Callback to be called when the lifecycle stage of the run changes.
|
|
36
74
|
*/
|
|
37
75
|
onRunStageChanged?: (current: CiGreenlightStatus, previous: CiGreenlightStatus | undefined) => unknown | Promise<unknown>;
|
|
38
|
-
} & Partial<
|
|
76
|
+
} & Partial<PollCiGreenlightPollConfig>;
|
|
39
77
|
export declare function pollCiGreenlightStatus(deps: SdkDependencies, apiConfig: ApiConfig, { runId, onRunStageChanged: onRunStageChanged, ...pollConfig }: PollCiGreenlightParams): Promise<PollCiGreenlightStatus>;
|
|
@@ -6,3 +6,5 @@ export declare function makeQaWolfSdk({ apiKey, serviceBase, }: {
|
|
|
6
6
|
attemptNotifyDeploy: (config: Partial<import("../api/deploy.js").DeployConfig>) => Promise<import("./domain/attempt-deploy.js").NotifyDeployStatus>;
|
|
7
7
|
pollCiGreenlightStatus: (args_0: import("./domain/poll-ci-greenlight.js").PollCiGreenlightParams) => Promise<import("./domain/poll-ci-greenlight.js").PollCiGreenlightStatus>;
|
|
8
8
|
};
|
|
9
|
+
export type * from "./domain/poll-ci-greenlight.js";
|
|
10
|
+
export type * from "./domain/attempt-deploy.js";
|