@griffin-app/griffin-cli 1.0.18 → 1.0.20
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/cli.js +183 -196
- package/dist/commands/env.d.ts +3 -3
- package/dist/commands/env.js +28 -45
- package/dist/commands/hub/apply.d.ts +1 -1
- package/dist/commands/hub/apply.js +54 -95
- package/dist/commands/hub/connect.d.ts +1 -1
- package/dist/commands/hub/connect.js +2 -6
- package/dist/commands/hub/destroy.d.ts +10 -0
- package/dist/commands/hub/destroy.js +141 -0
- package/dist/commands/hub/integrations.d.ts +9 -9
- package/dist/commands/hub/integrations.js +104 -115
- package/dist/commands/hub/login.js +3 -0
- package/dist/commands/hub/metrics.d.ts +2 -2
- package/dist/commands/hub/metrics.js +41 -69
- package/dist/commands/hub/monitor.d.ts +1 -1
- package/dist/commands/hub/monitor.js +30 -70
- package/dist/commands/hub/notifications.d.ts +3 -3
- package/dist/commands/hub/notifications.js +74 -105
- package/dist/commands/hub/run.d.ts +1 -1
- package/dist/commands/hub/run.js +118 -144
- package/dist/commands/hub/runs.d.ts +1 -1
- package/dist/commands/hub/runs.js +60 -81
- package/dist/commands/hub/secrets.d.ts +8 -8
- package/dist/commands/hub/secrets.js +88 -148
- package/dist/commands/hub/status.d.ts +1 -1
- package/dist/commands/hub/status.js +13 -26
- package/dist/commands/init.js +3 -3
- package/dist/commands/local/run.d.ts +2 -2
- package/dist/commands/local/run.js +37 -42
- package/dist/commands/validate.d.ts +1 -1
- package/dist/commands/validate.js +19 -39
- package/dist/commands/variables.d.ts +3 -3
- package/dist/commands/variables.js +51 -69
- package/dist/core/discovery.js +0 -2
- package/dist/core/monitor-helpers.d.ts +19 -0
- package/dist/core/monitor-helpers.js +48 -0
- package/dist/core/sdk.d.ts +5 -0
- package/dist/core/sdk.js +11 -0
- package/dist/core/state.d.ts +1 -1
- package/dist/core/state.js +0 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/schemas/state.js +1 -1
- package/dist/utils/command-wrapper.d.ts +10 -0
- package/dist/utils/command-wrapper.js +27 -0
- package/dist/utils/sdk-error.js +1 -1
- package/dist/utils/terminal.d.ts +4 -0
- package/dist/utils/terminal.js +17 -0
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
1
|
+
import { createSdkAndState } from "../../core/sdk.js";
|
|
3
2
|
import { terminal } from "../../utils/terminal.js";
|
|
4
3
|
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
4
|
+
import { withCommandErrorHandler, outputJsonOrContinue, } from "../../utils/command-wrapper.js";
|
|
5
5
|
function orExit(value, errorMessage) {
|
|
6
6
|
if (value === undefined) {
|
|
7
7
|
terminal.error(errorMessage);
|
|
@@ -12,81 +12,61 @@ function orExit(value, errorMessage) {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* List notification rules (read-only).
|
|
15
|
-
* Rules are defined in monitor DSL and synced via `griffin
|
|
15
|
+
* Rules are defined in monitor DSL and synced via `griffin apply`.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}), "Failed to fetch notification rules");
|
|
33
|
-
const rules = response?.data?.data || [];
|
|
34
|
-
if (options.json) {
|
|
35
|
-
terminal.log(JSON.stringify(rules, null, 2));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (rules.length === 0) {
|
|
39
|
-
terminal.info("No notification rules found.");
|
|
40
|
-
terminal.dim("Define notifications in your monitor DSL and run griffin hub apply.");
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
terminal.info("Notification Rules (synced from monitor DSL)");
|
|
44
|
-
terminal.blank();
|
|
45
|
-
const table = terminal.table({
|
|
46
|
-
head: ["ID", "Monitor", "Integration", "Trigger", "Enabled"],
|
|
47
|
-
});
|
|
48
|
-
for (const rule of rules) {
|
|
49
|
-
const triggerDesc = formatTrigger(rule.trigger);
|
|
50
|
-
const enabled = rule.enabled ? "✓" : "✗";
|
|
51
|
-
table.push([
|
|
52
|
-
rule.id.substring(0, 8) + "...",
|
|
53
|
-
rule.monitorId ?? "-",
|
|
54
|
-
rule.integrationName ?? "-",
|
|
55
|
-
triggerDesc,
|
|
56
|
-
enabled,
|
|
57
|
-
]);
|
|
58
|
-
}
|
|
59
|
-
terminal.log(table.toString());
|
|
17
|
+
export const executeNotificationsList = withCommandErrorHandler(async (options) => {
|
|
18
|
+
const { sdk } = await createSdkAndState();
|
|
19
|
+
const response = await withSDKErrorHandling(() => sdk.getNotificationsRules({
|
|
20
|
+
query: {
|
|
21
|
+
monitorId: options.monitor,
|
|
22
|
+
enabled: options.enabled,
|
|
23
|
+
},
|
|
24
|
+
}), "Failed to fetch notification rules");
|
|
25
|
+
const rules = response?.data?.data || [];
|
|
26
|
+
if (outputJsonOrContinue(rules, options.json))
|
|
27
|
+
return;
|
|
28
|
+
if (rules.length === 0) {
|
|
29
|
+
terminal.info("No notification rules found.");
|
|
30
|
+
terminal.dim("Define notifications in your monitor DSL and run griffin apply.");
|
|
31
|
+
return;
|
|
60
32
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
33
|
+
terminal.info("Notification Rules (synced from monitor DSL)");
|
|
34
|
+
terminal.blank();
|
|
35
|
+
const table = terminal.table({
|
|
36
|
+
head: ["ID", "Monitor", "Integration", "Trigger", "Enabled"],
|
|
37
|
+
});
|
|
38
|
+
for (const rule of rules) {
|
|
39
|
+
const triggerDesc = formatTrigger(rule.trigger);
|
|
40
|
+
const enabled = rule.enabled ? "✓" : "✗";
|
|
41
|
+
table.push([
|
|
42
|
+
rule.id.substring(0, 8) + "...",
|
|
43
|
+
rule.monitorId ?? "-",
|
|
44
|
+
rule.integrationName ?? "-",
|
|
45
|
+
triggerDesc,
|
|
46
|
+
enabled,
|
|
47
|
+
]);
|
|
64
48
|
}
|
|
65
|
-
|
|
49
|
+
terminal.log(table.toString());
|
|
50
|
+
});
|
|
66
51
|
async function fetchNotificationIntegrations(sdk) {
|
|
67
52
|
const result = await withSDKErrorHandling(() => sdk.getIntegrations({
|
|
68
53
|
query: { category: "notifications", enabled: true },
|
|
69
54
|
}), "Failed to fetch integrations");
|
|
70
55
|
const list = result?.data?.data ?? [];
|
|
71
|
-
return list
|
|
72
|
-
.filter((i) => i.category === "notifications")
|
|
73
|
-
.map((i) => ({
|
|
74
|
-
id: i.id,
|
|
75
|
-
name: i.name,
|
|
76
|
-
provider: i.provider,
|
|
77
|
-
}));
|
|
56
|
+
return list.filter((i) => i.category === "notifications");
|
|
78
57
|
}
|
|
79
|
-
function promptRoutingForProvider(provider) {
|
|
58
|
+
async function promptRoutingForProvider(provider) {
|
|
80
59
|
switch (provider) {
|
|
81
60
|
case "slack": {
|
|
82
61
|
return terminal
|
|
83
62
|
.input("Slack channel (e.g. #alerts)", "#alerts")
|
|
84
63
|
.then((value) => ({
|
|
85
|
-
|
|
64
|
+
channelType: "slack",
|
|
86
65
|
channel: (value ?? "").trim() || "#alerts",
|
|
87
66
|
}));
|
|
88
67
|
}
|
|
89
|
-
case "email":
|
|
68
|
+
case "email":
|
|
69
|
+
case "resend":
|
|
90
70
|
return terminal.input("To addresses (comma-separated)").then((value) => {
|
|
91
71
|
const toAddresses = (value ?? "")
|
|
92
72
|
.split(",")
|
|
@@ -96,11 +76,13 @@ function promptRoutingForProvider(provider) {
|
|
|
96
76
|
terminal.error("At least one email address is required.");
|
|
97
77
|
terminal.exit(1);
|
|
98
78
|
}
|
|
99
|
-
return {
|
|
79
|
+
return { channelType: "email", toAddresses };
|
|
100
80
|
});
|
|
101
|
-
|
|
81
|
+
case "webhook":
|
|
82
|
+
return { channelType: "webhook" };
|
|
102
83
|
default:
|
|
103
|
-
|
|
84
|
+
terminal.error(`Unsupported provider: ${provider}`);
|
|
85
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
104
86
|
}
|
|
105
87
|
}
|
|
106
88
|
/**
|
|
@@ -108,7 +90,7 @@ function promptRoutingForProvider(provider) {
|
|
|
108
90
|
*/
|
|
109
91
|
function buildRoutingFromOptions(options) {
|
|
110
92
|
if (options.channel !== undefined && options.channel !== "") {
|
|
111
|
-
return {
|
|
93
|
+
return { channelType: "slack", channel: options.channel };
|
|
112
94
|
}
|
|
113
95
|
if (options.toAddresses !== undefined && options.toAddresses !== "") {
|
|
114
96
|
const toAddresses = options.toAddresses
|
|
@@ -116,10 +98,10 @@ function buildRoutingFromOptions(options) {
|
|
|
116
98
|
.map((s) => s.trim())
|
|
117
99
|
.filter(Boolean);
|
|
118
100
|
if (toAddresses.length > 0) {
|
|
119
|
-
return {
|
|
101
|
+
return { channelType: "email", toAddresses };
|
|
120
102
|
}
|
|
121
103
|
}
|
|
122
|
-
return {
|
|
104
|
+
return { channelType: "webhook" };
|
|
123
105
|
}
|
|
124
106
|
/**
|
|
125
107
|
* Run interactive wizard: select integration, then prompt for provider-specific options (channel, to-addresses, or none).
|
|
@@ -128,7 +110,7 @@ async function executeNotificationsTestWizard(sdk, options) {
|
|
|
128
110
|
const integrations = await fetchNotificationIntegrations(sdk);
|
|
129
111
|
if (integrations.length === 0) {
|
|
130
112
|
terminal.error("No notification integrations found.");
|
|
131
|
-
terminal.dim("Connect one with: griffin
|
|
113
|
+
terminal.dim("Connect one with: griffin integrations connect notifications <provider>");
|
|
132
114
|
terminal.exit(1);
|
|
133
115
|
}
|
|
134
116
|
if (options.integration) {
|
|
@@ -160,47 +142,34 @@ function canBuildRoutingFromOptions(options) {
|
|
|
160
142
|
* Test a notification integration. Without options, runs a wizard (select integration, then provider-specific prompts).
|
|
161
143
|
* With --integration and routing flags, runs non-interactively.
|
|
162
144
|
*/
|
|
163
|
-
export
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
173
|
-
const useWizard = !options.integration || !canBuildRoutingFromOptions(options);
|
|
174
|
-
let integration;
|
|
175
|
-
let routing;
|
|
176
|
-
if (useWizard) {
|
|
177
|
-
const wizardResult = await executeNotificationsTestWizard(sdk, options);
|
|
178
|
-
integration = wizardResult.integration;
|
|
179
|
-
routing = wizardResult.routing;
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
integration = options.integration;
|
|
183
|
-
routing = buildRoutingFromOptions(options);
|
|
184
|
-
}
|
|
185
|
-
const spinner = terminal.spinner("Sending test notification...").start();
|
|
186
|
-
const response = await withSDKErrorHandling(() => sdk.postNotificationsTest({
|
|
187
|
-
body: { integration, routing },
|
|
188
|
-
}), "Failed to send test notification");
|
|
189
|
-
spinner.stop();
|
|
190
|
-
const result = response?.data?.data;
|
|
191
|
-
if (result?.success) {
|
|
192
|
-
terminal.success(result.message || "Test notification delivered successfully");
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
terminal.error("Test notification failed");
|
|
196
|
-
terminal.exit(1);
|
|
197
|
-
}
|
|
145
|
+
export const executeNotificationsTest = withCommandErrorHandler(async (options) => {
|
|
146
|
+
const { sdk } = await createSdkAndState();
|
|
147
|
+
const useWizard = !options.integration || !canBuildRoutingFromOptions(options);
|
|
148
|
+
let integration;
|
|
149
|
+
let routing;
|
|
150
|
+
if (useWizard) {
|
|
151
|
+
const wizardResult = await executeNotificationsTestWizard(sdk, options);
|
|
152
|
+
integration = wizardResult.integration;
|
|
153
|
+
routing = wizardResult.routing;
|
|
198
154
|
}
|
|
199
|
-
|
|
200
|
-
|
|
155
|
+
else {
|
|
156
|
+
integration = options.integration;
|
|
157
|
+
routing = buildRoutingFromOptions(options);
|
|
158
|
+
}
|
|
159
|
+
const spinner = terminal.spinner("Sending test notification...").start();
|
|
160
|
+
const response = await withSDKErrorHandling(() => sdk.postNotificationsTest({
|
|
161
|
+
body: { integration, routing },
|
|
162
|
+
}), "Failed to send test notification");
|
|
163
|
+
spinner.stop();
|
|
164
|
+
const result = response?.data?.data;
|
|
165
|
+
if (result?.success) {
|
|
166
|
+
terminal.success(result.message || "Test notification delivered successfully");
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
terminal.error("Test notification failed");
|
|
201
170
|
terminal.exit(1);
|
|
202
171
|
}
|
|
203
|
-
}
|
|
172
|
+
});
|
|
204
173
|
/**
|
|
205
174
|
* Format trigger for display
|
|
206
175
|
*/
|
package/dist/commands/hub/run.js
CHANGED
|
@@ -1,162 +1,136 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { discoverMonitors, formatDiscoveryErrors, } from "../../core/discovery.js";
|
|
1
|
+
import { resolveEnvironment } from "../../core/state.js";
|
|
2
|
+
import { createSdkAndState } from "../../core/sdk.js";
|
|
4
3
|
import { computeDiff } from "../../core/diff.js";
|
|
5
4
|
import { terminal } from "../../utils/terminal.js";
|
|
6
5
|
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
6
|
+
import { withCommandErrorHandler } from "../../utils/command-wrapper.js";
|
|
7
|
+
import { discoverLocalMonitors, } from "../../core/monitor-helpers.js";
|
|
7
8
|
import { loadVariables } from "../../core/variables.js";
|
|
8
9
|
import { resolveMonitor } from "../../resolve.js";
|
|
9
10
|
/**
|
|
10
11
|
* Trigger a monitor run on the hub
|
|
11
12
|
*/
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
terminal.exit(1);
|
|
23
|
-
}
|
|
24
|
-
// Create SDK clients with credentials
|
|
25
|
-
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
26
|
-
// Discover local monitors
|
|
27
|
-
const discoveryPattern = state.discovery?.pattern || "**/__griffin__/*.{ts,js}";
|
|
28
|
-
const discoveryIgnore = state.discovery?.ignore || [
|
|
29
|
-
"node_modules/**",
|
|
30
|
-
"dist/**",
|
|
31
|
-
];
|
|
32
|
-
const spinner = terminal.spinner("Discovering local monitors...").start();
|
|
33
|
-
const { monitors: discoveredMonitors, errors } = await discoverMonitors(discoveryPattern, discoveryIgnore);
|
|
34
|
-
if (errors.length > 0) {
|
|
35
|
-
spinner.fail("Discovery failed");
|
|
36
|
-
terminal.error(formatDiscoveryErrors(errors));
|
|
37
|
-
terminal.exit(1);
|
|
38
|
-
}
|
|
39
|
-
// Find local monitor by name
|
|
40
|
-
const localMonitor = discoveredMonitors.find((p) => p.monitor.name === options.monitor);
|
|
41
|
-
if (!localMonitor) {
|
|
42
|
-
spinner.fail(`Monitor "${options.monitor}" not found locally`);
|
|
43
|
-
terminal.blank();
|
|
44
|
-
terminal.info("Available monitors:");
|
|
45
|
-
for (const p of discoveredMonitors) {
|
|
46
|
-
terminal.dim(` - ${p.monitor.name}`);
|
|
47
|
-
}
|
|
48
|
-
terminal.exit(1);
|
|
49
|
-
}
|
|
50
|
-
spinner.succeed(`Found local monitor: ${terminal.colors.cyan(options.monitor)}`);
|
|
51
|
-
// Fetch remote monitors for this project + environment
|
|
52
|
-
const fetchSpinner = terminal.spinner("Checking hub...").start();
|
|
53
|
-
const response = await withSDKErrorHandling(() => sdk.getMonitor({
|
|
54
|
-
query: {
|
|
55
|
-
projectId: state.projectId,
|
|
56
|
-
environment: envName,
|
|
57
|
-
},
|
|
58
|
-
}), "Failed to fetch monitors from hub");
|
|
59
|
-
const remoteMonitors = response?.data?.data;
|
|
60
|
-
// Find remote monitor by name
|
|
61
|
-
const remoteMonitor = remoteMonitors.find((p) => p.name === options.monitor);
|
|
62
|
-
if (!remoteMonitor) {
|
|
63
|
-
fetchSpinner.fail(`Monitor "${options.monitor}" not found on hub`);
|
|
64
|
-
terminal.dim("Run 'griffin hub apply' to sync your monitors first");
|
|
65
|
-
terminal.exit(1);
|
|
66
|
-
}
|
|
67
|
-
fetchSpinner.succeed("Monitor found on hub");
|
|
68
|
-
// Load variables and resolve local monitor before computing diff
|
|
69
|
-
const variables = await loadVariables(envName);
|
|
70
|
-
const resolvedLocalMonitor = resolveMonitor(localMonitor.monitor, state.projectId, envName, variables);
|
|
71
|
-
// Compute diff to check if local monitor differs from remote
|
|
72
|
-
const diff = computeDiff([resolvedLocalMonitor], [remoteMonitor], {
|
|
73
|
-
includeDeletions: false,
|
|
74
|
-
});
|
|
75
|
-
const hasDiff = diff.actions.length > 0 &&
|
|
76
|
-
diff.actions.some((a) => a.type === "update" || a.type === "create");
|
|
77
|
-
if (hasDiff && !options.force) {
|
|
78
|
-
terminal.error(`Local monitor "${options.monitor}" differs from hub`);
|
|
79
|
-
terminal.blank();
|
|
80
|
-
terminal.warn("The monitor on the hub is different from your local version.");
|
|
81
|
-
terminal.dim("Run 'griffin hub apply' to sync, or use --force to run anyway.");
|
|
82
|
-
terminal.exit(1);
|
|
83
|
-
}
|
|
84
|
-
// Trigger the run
|
|
13
|
+
export const executeRun = withCommandErrorHandler(async (options) => {
|
|
14
|
+
const { sdk, state } = await createSdkAndState();
|
|
15
|
+
// Resolve environment
|
|
16
|
+
const envName = await resolveEnvironment(options.env);
|
|
17
|
+
// Discover local monitors
|
|
18
|
+
const { monitors: discoveredMonitors } = await discoverLocalMonitors(state);
|
|
19
|
+
// Find local monitor by name
|
|
20
|
+
const localMonitor = discoveredMonitors.find((p) => p.monitor.name === options.monitor);
|
|
21
|
+
if (!localMonitor) {
|
|
22
|
+
terminal.error(`Monitor "${options.monitor}" not found locally`);
|
|
85
23
|
terminal.blank();
|
|
86
|
-
terminal.info(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
terminal.warn("Running with --force (local changes not applied)");
|
|
24
|
+
terminal.info("Available monitors:");
|
|
25
|
+
for (const p of discoveredMonitors) {
|
|
26
|
+
terminal.dim(` - ${p.monitor.name}`);
|
|
90
27
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
28
|
+
terminal.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Fetch remote monitors for this project + environment
|
|
31
|
+
const fetchSpinner = terminal.spinner("Checking hub...").start();
|
|
32
|
+
const response = await withSDKErrorHandling(() => sdk.getMonitor({
|
|
33
|
+
query: {
|
|
34
|
+
projectId: state.projectId,
|
|
35
|
+
environment: envName,
|
|
36
|
+
},
|
|
37
|
+
}), "Failed to fetch monitors from hub");
|
|
38
|
+
const remoteMonitors = response?.data?.data;
|
|
39
|
+
// Find remote monitor by name
|
|
40
|
+
const remoteMonitor = remoteMonitors.find((p) => p.name === options.monitor);
|
|
41
|
+
if (!remoteMonitor) {
|
|
42
|
+
fetchSpinner.fail(`Monitor "${options.monitor}" not found on hub`);
|
|
43
|
+
terminal.dim("Run 'griffin apply' to sync your monitors first");
|
|
44
|
+
terminal.exit(1);
|
|
45
|
+
}
|
|
46
|
+
fetchSpinner.succeed("Monitor found on hub");
|
|
47
|
+
// Load variables and resolve local monitor before computing diff
|
|
48
|
+
const variables = await loadVariables(envName);
|
|
49
|
+
const resolvedLocalMonitor = resolveMonitor(localMonitor.monitor, state.projectId, envName, variables);
|
|
50
|
+
// Compute diff to check if local monitor differs from remote
|
|
51
|
+
const diff = computeDiff([resolvedLocalMonitor], [remoteMonitor], {
|
|
52
|
+
includeDeletions: false,
|
|
53
|
+
});
|
|
54
|
+
const hasDiff = diff.actions.length > 0 &&
|
|
55
|
+
diff.actions.some((a) => a.type === "update" || a.type === "create");
|
|
56
|
+
if (hasDiff && !options.force) {
|
|
57
|
+
terminal.error(`Local monitor "${options.monitor}" differs from hub`);
|
|
102
58
|
terminal.blank();
|
|
103
|
-
terminal.
|
|
104
|
-
terminal.
|
|
105
|
-
terminal.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
59
|
+
terminal.warn("The monitor on the hub is different from your local version.");
|
|
60
|
+
terminal.dim("Run 'griffin apply' to sync, or use --force to run anyway.");
|
|
61
|
+
terminal.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// Trigger the run
|
|
64
|
+
terminal.blank();
|
|
65
|
+
terminal.info(`Triggering run for monitor: ${terminal.colors.cyan(options.monitor)}`);
|
|
66
|
+
terminal.log(`Target environment: ${terminal.colors.cyan(envName)}`);
|
|
67
|
+
if (hasDiff && options.force) {
|
|
68
|
+
terminal.warn("Running with --force (local changes not applied)");
|
|
69
|
+
}
|
|
70
|
+
const triggerSpinner = terminal.spinner("Triggering run...").start();
|
|
71
|
+
const runResponse = await withSDKErrorHandling(() => sdk.postRunsTriggerByMonitorId({
|
|
72
|
+
path: {
|
|
73
|
+
monitorId: remoteMonitor.id,
|
|
74
|
+
},
|
|
75
|
+
body: {
|
|
76
|
+
environment: envName,
|
|
77
|
+
},
|
|
78
|
+
}), "Failed to trigger run");
|
|
79
|
+
const run = runResponse?.data?.data;
|
|
80
|
+
triggerSpinner.succeed("Run triggered");
|
|
81
|
+
terminal.blank();
|
|
82
|
+
terminal.log(`Run ID: ${terminal.colors.dim(run.id)}`);
|
|
83
|
+
terminal.log(`Status: ${terminal.colors.cyan(run.status)}`);
|
|
84
|
+
terminal.log(`Started: ${terminal.colors.dim(new Date(run.startedAt).toLocaleString())}`);
|
|
85
|
+
// Wait for completion if requested
|
|
86
|
+
if (options.wait) {
|
|
87
|
+
terminal.blank();
|
|
88
|
+
const waitSpinner = terminal
|
|
89
|
+
.spinner("Waiting for run to complete...")
|
|
90
|
+
.start();
|
|
91
|
+
const runId = run.id;
|
|
92
|
+
let completed = false;
|
|
93
|
+
while (!completed) {
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, 2000)); // Poll every 2 seconds
|
|
95
|
+
const runStatusResponse = await withSDKErrorHandling(() => sdk.getRunsById({
|
|
96
|
+
path: {
|
|
97
|
+
id: runId,
|
|
98
|
+
},
|
|
99
|
+
}), "Failed to fetch run status");
|
|
100
|
+
const run = runStatusResponse?.data?.data;
|
|
101
|
+
if (run.status === "completed" || run.status === "failed") {
|
|
102
|
+
completed = true;
|
|
103
|
+
if (run.success) {
|
|
104
|
+
waitSpinner.succeed(`Run ${run.status}`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
waitSpinner.fail(`Run ${run.status}`);
|
|
108
|
+
}
|
|
109
|
+
terminal.blank();
|
|
110
|
+
if (run.duration_ms) {
|
|
111
|
+
terminal.log(`Duration: ${terminal.colors.dim((run.duration_ms / 1000).toFixed(2) + "s")}`);
|
|
112
|
+
}
|
|
113
|
+
if (run.success !== undefined) {
|
|
114
|
+
const successText = run.success
|
|
115
|
+
? terminal.colors.green("Yes")
|
|
116
|
+
: terminal.colors.red("No");
|
|
117
|
+
terminal.log(`Success: ${successText}`);
|
|
118
|
+
}
|
|
119
|
+
if (run.errors && run.errors.length > 0) {
|
|
130
120
|
terminal.blank();
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (run.success !== undefined) {
|
|
135
|
-
const successText = run.success
|
|
136
|
-
? terminal.colors.green("Yes")
|
|
137
|
-
: terminal.colors.red("No");
|
|
138
|
-
terminal.log(`Success: ${successText}`);
|
|
139
|
-
}
|
|
140
|
-
if (run.errors && run.errors.length > 0) {
|
|
141
|
-
terminal.blank();
|
|
142
|
-
terminal.error("Errors:");
|
|
143
|
-
for (const error of run.errors) {
|
|
144
|
-
terminal.dim(` - ${error}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (!run.success) {
|
|
148
|
-
terminal.exit(1);
|
|
121
|
+
terminal.error("Errors:");
|
|
122
|
+
for (const error of run.errors) {
|
|
123
|
+
terminal.dim(` - ${error}`);
|
|
149
124
|
}
|
|
150
125
|
}
|
|
126
|
+
if (!run.success) {
|
|
127
|
+
terminal.exit(1);
|
|
128
|
+
}
|
|
151
129
|
}
|
|
152
130
|
}
|
|
153
|
-
else {
|
|
154
|
-
terminal.blank();
|
|
155
|
-
terminal.dim("Run started. Use 'griffin hub runs' to check progress.");
|
|
156
|
-
}
|
|
157
131
|
}
|
|
158
|
-
|
|
159
|
-
terminal.
|
|
160
|
-
terminal.
|
|
132
|
+
else {
|
|
133
|
+
terminal.blank();
|
|
134
|
+
terminal.dim("Run started. Use 'griffin runs' to check progress.");
|
|
161
135
|
}
|
|
162
|
-
}
|
|
136
|
+
});
|