@griffin-app/griffin-cli 1.0.39 → 1.0.40
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 +0 -0
- package/dist/commands/apply.d.ts +9 -0
- package/dist/commands/apply.js +76 -0
- package/dist/commands/config.d.ts +36 -0
- package/dist/commands/config.js +144 -0
- package/dist/commands/configure-runner-host.d.ts +2 -0
- package/dist/commands/configure-runner-host.js +41 -0
- package/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.js +36 -0
- package/dist/commands/execute-remote.d.ts +1 -0
- package/dist/commands/execute-remote.js +33 -0
- package/dist/commands/hub/config.d.ts +27 -0
- package/dist/commands/hub/config.js +102 -0
- package/dist/commands/hub/plan.d.ts +8 -0
- package/dist/commands/hub/plan.js +75 -0
- package/dist/commands/local/config.d.ts +28 -0
- package/dist/commands/local/config.js +82 -0
- package/dist/commands/logs.d.ts +1 -0
- package/dist/commands/logs.js +20 -0
- package/dist/commands/plan.d.ts +8 -0
- package/dist/commands/plan.js +58 -0
- package/dist/commands/run-remote.d.ts +11 -0
- package/dist/commands/run-remote.js +98 -0
- package/dist/commands/run.d.ts +4 -0
- package/dist/commands/run.js +86 -0
- package/dist/commands/runner.d.ts +12 -0
- package/dist/commands/runner.js +53 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +75 -0
- package/dist/core/plan-diff.d.ts +41 -0
- package/dist/core/plan-diff.js +257 -0
- package/dist/output/context.d.ts +18 -0
- package/dist/output/context.js +22 -0
- package/dist/output/index.d.ts +3 -0
- package/dist/output/index.js +2 -0
- package/dist/output/renderer.d.ts +6 -0
- package/dist/output/renderer.js +348 -0
- package/dist/output/types.d.ts +153 -0
- package/dist/output/types.js +1 -0
- package/dist/providers/registry.d.ts +24 -0
- package/dist/providers/registry.js +109 -0
- package/dist/schemas/payload.d.ts +6 -0
- package/dist/schemas/payload.js +8 -0
- package/dist/test-discovery.d.ts +4 -0
- package/dist/test-discovery.js +25 -0
- package/dist/test-runner.d.ts +6 -0
- package/dist/test-runner.js +56 -0
- package/dist/utils/console.d.ts +5 -0
- package/dist/utils/console.js +5 -0
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { loadState, resolveEnvironment } from "../core/state.js";
|
|
2
|
+
import { discoverMonitors, formatDiscoveryErrors } from "../core/discovery.js";
|
|
3
|
+
import { computeDiff, formatDiff } from "../core/diff.js";
|
|
4
|
+
import { applyDiff, formatApplyResult } from "../core/apply.js";
|
|
5
|
+
import { createSdkClients } from "../core/sdk.js";
|
|
6
|
+
/**
|
|
7
|
+
* Apply changes to the runner
|
|
8
|
+
*/
|
|
9
|
+
export async function executeApply(options) {
|
|
10
|
+
try {
|
|
11
|
+
// Load state
|
|
12
|
+
const state = await loadState();
|
|
13
|
+
// Resolve environment
|
|
14
|
+
const envName = await resolveEnvironment(options.env);
|
|
15
|
+
// Check if runner is configured
|
|
16
|
+
if (!state.runner?.baseUrl) {
|
|
17
|
+
console.error("Error: Runner URL not configured.");
|
|
18
|
+
console.log("Configure with:");
|
|
19
|
+
console.log(" griffin runner set --base-url <url> --api-token <token>");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log(`Applying to '${envName}' environment`);
|
|
23
|
+
console.log("");
|
|
24
|
+
// Create SDK clients
|
|
25
|
+
const { planApi } = createSdkClients({
|
|
26
|
+
baseUrl: state.runner.baseUrl,
|
|
27
|
+
apiToken: state.runner.apiToken || undefined,
|
|
28
|
+
});
|
|
29
|
+
// Discover local monitors
|
|
30
|
+
const discoveryPattern = state.discovery?.pattern || "**/__griffin__/*.{ts,js}";
|
|
31
|
+
const discoveryIgnore = state.discovery?.ignore || [
|
|
32
|
+
"node_modules/**",
|
|
33
|
+
"dist/**",
|
|
34
|
+
];
|
|
35
|
+
const { monitors, errors } = await discoverMonitors(discoveryPattern, discoveryIgnore);
|
|
36
|
+
if (errors.length > 0) {
|
|
37
|
+
console.error(formatDiscoveryErrors(errors));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
// Fetch remote monitors for this project
|
|
41
|
+
const response = await planApi.planGet(state.projectId);
|
|
42
|
+
const remoteMonitors = response.data.data.map((p) => p);
|
|
43
|
+
// Compute diff for this environment
|
|
44
|
+
const diff = computeDiff(monitors.map((p) => p.monitor), state, remoteMonitors, envName);
|
|
45
|
+
// Show monitor
|
|
46
|
+
console.log(formatDiff(diff));
|
|
47
|
+
console.log("");
|
|
48
|
+
// Check if there are changes
|
|
49
|
+
if (diff.summary.creates + diff.summary.updates + diff.summary.deletes ===
|
|
50
|
+
0) {
|
|
51
|
+
console.log("No changes to apply.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Ask for confirmation unless auto-approved
|
|
55
|
+
if (!options.autoApprove && !options.dryRun) {
|
|
56
|
+
console.log("Do you want to perform these actions? (yes/no)");
|
|
57
|
+
// For now, just proceed - in a real implementation, we'd use readline
|
|
58
|
+
// to get user input
|
|
59
|
+
console.log("Note: Use --auto-approve flag to skip confirmation");
|
|
60
|
+
console.log("");
|
|
61
|
+
}
|
|
62
|
+
// Apply changes
|
|
63
|
+
const result = await applyDiff(diff, state, planApi, envName, {
|
|
64
|
+
dryRun: options.dryRun,
|
|
65
|
+
});
|
|
66
|
+
console.log("");
|
|
67
|
+
console.log(formatApplyResult(result));
|
|
68
|
+
if (!result.success) {
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`Error: ${error.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface ConfigSetOptions {
|
|
2
|
+
organizationId: string;
|
|
3
|
+
environment: string;
|
|
4
|
+
targetKey: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Set a target for an organization and environment
|
|
9
|
+
*/
|
|
10
|
+
export declare function executeConfigSet(options: ConfigSetOptions): Promise<void>;
|
|
11
|
+
export interface ConfigGetOptions {
|
|
12
|
+
organizationId: string;
|
|
13
|
+
environment: string;
|
|
14
|
+
targetKey: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get a target for an organization and environment
|
|
18
|
+
*/
|
|
19
|
+
export declare function executeConfigGet(options: ConfigGetOptions): Promise<void>;
|
|
20
|
+
export interface ConfigListOptions {
|
|
21
|
+
organizationId?: string;
|
|
22
|
+
environment?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* List all runner configs
|
|
26
|
+
*/
|
|
27
|
+
export declare function executeConfigList(options: ConfigListOptions): Promise<void>;
|
|
28
|
+
export interface ConfigDeleteOptions {
|
|
29
|
+
organizationId: string;
|
|
30
|
+
environment: string;
|
|
31
|
+
targetKey: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Delete a target from an organization and environment
|
|
35
|
+
*/
|
|
36
|
+
export declare function executeConfigDelete(options: ConfigDeleteOptions): Promise<void>;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { loadState } from "../core/state.js";
|
|
2
|
+
import { createSdkClients } from "../core/sdk.js";
|
|
3
|
+
/**
|
|
4
|
+
* Set a target for an organization and environment
|
|
5
|
+
*/
|
|
6
|
+
export async function executeConfigSet(options) {
|
|
7
|
+
try {
|
|
8
|
+
const state = await loadState();
|
|
9
|
+
if (!state.runner?.baseUrl) {
|
|
10
|
+
console.error("Error: Runner URL not configured.");
|
|
11
|
+
console.log("Configure with:");
|
|
12
|
+
console.log(" griffin runner set --base-url <url> --api-token <token>");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const { configApi } = createSdkClients({
|
|
16
|
+
baseUrl: state.runner.baseUrl,
|
|
17
|
+
apiToken: state.runner.apiToken,
|
|
18
|
+
});
|
|
19
|
+
const result = await configApi.configOrganizationIdEnvironmentTargetsTargetKeyPut(options.organizationId, options.environment, options.targetKey, {
|
|
20
|
+
baseUrl: options.baseUrl,
|
|
21
|
+
});
|
|
22
|
+
//.setTarget(options.organizationId, options.environment, options.targetKey, options.baseUrl);
|
|
23
|
+
//const result = await runnerRequest<{ data: RunnerConfig }>(
|
|
24
|
+
// state.runner.baseUrl,
|
|
25
|
+
// state.runner.apiToken,
|
|
26
|
+
// path,
|
|
27
|
+
// {
|
|
28
|
+
// method: "PUT",
|
|
29
|
+
// body: JSON.stringify({ baseUrl: options.baseUrl }),
|
|
30
|
+
// },
|
|
31
|
+
//);
|
|
32
|
+
console.log(`✓ Target "${options.targetKey}" set to ${options.baseUrl}`);
|
|
33
|
+
console.log(` Organization: ${options.organizationId}`);
|
|
34
|
+
console.log(` Environment: ${options.environment}`);
|
|
35
|
+
console.log(` Updated at: ${new Date(result.data.data.updatedAt).toLocaleString()}`);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(`Error: ${error.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get a target for an organization and environment
|
|
44
|
+
*/
|
|
45
|
+
export async function executeConfigGet(options) {
|
|
46
|
+
try {
|
|
47
|
+
const state = await loadState();
|
|
48
|
+
if (!state.runner?.baseUrl) {
|
|
49
|
+
console.error("Error: Runner URL not configured.");
|
|
50
|
+
console.log("Configure with:");
|
|
51
|
+
console.log(" griffin runner set --base-url <url> --api-token <token>");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const { configApi } = createSdkClients({
|
|
55
|
+
baseUrl: state.runner.baseUrl,
|
|
56
|
+
apiToken: state.runner.apiToken,
|
|
57
|
+
});
|
|
58
|
+
const result = await configApi.configOrganizationIdEnvironmentTargetsTargetKeyGet(options.organizationId, options.environment, options.targetKey);
|
|
59
|
+
console.log(`Target: ${options.targetKey}`);
|
|
60
|
+
console.log(`Base URL: ${result.data.data.baseUrl}`);
|
|
61
|
+
console.log(`Organization: ${options.organizationId}`);
|
|
62
|
+
console.log(`Environment: ${options.environment}`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error(`Error: ${error.message}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* List all runner configs
|
|
71
|
+
*/
|
|
72
|
+
export async function executeConfigList(options) {
|
|
73
|
+
try {
|
|
74
|
+
const state = await loadState();
|
|
75
|
+
if (!state.runner?.baseUrl) {
|
|
76
|
+
console.error("Error: Runner URL not configured.");
|
|
77
|
+
console.log("Configure with:");
|
|
78
|
+
console.log(" griffin runner set --base-url <url> --api-token <token>");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const params = new URLSearchParams();
|
|
82
|
+
if (options.organizationId)
|
|
83
|
+
params.append("organizationId", options.organizationId);
|
|
84
|
+
if (options.environment)
|
|
85
|
+
params.append("environment", options.environment);
|
|
86
|
+
const { configApi } = createSdkClients({
|
|
87
|
+
baseUrl: state.runner.baseUrl,
|
|
88
|
+
apiToken: state.runner.apiToken,
|
|
89
|
+
});
|
|
90
|
+
const result = await configApi.configGet(options.organizationId, options.environment);
|
|
91
|
+
if (result.data.data.length === 0 || !result.data.data) {
|
|
92
|
+
console.log("No runner configs found.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`Found ${result.data.data.length} runner config(s):`);
|
|
96
|
+
console.log("");
|
|
97
|
+
for (const config of result.data.data) {
|
|
98
|
+
console.log(`Organization: ${config.organizationId}`);
|
|
99
|
+
console.log(`Environment: ${config.environment}`);
|
|
100
|
+
console.log(`Targets:`);
|
|
101
|
+
const targetCount = Object.keys(config.targets).length;
|
|
102
|
+
if (targetCount === 0) {
|
|
103
|
+
console.log(" (none)");
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
for (const [key, baseUrl] of Object.entries(config.targets)) {
|
|
107
|
+
console.log(` ${key}: ${baseUrl}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
console.log(`Updated: ${new Date(config.updatedAt).toLocaleString()}`);
|
|
111
|
+
console.log("");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(`Error: ${error.message}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Delete a target from an organization and environment
|
|
121
|
+
*/
|
|
122
|
+
export async function executeConfigDelete(options) {
|
|
123
|
+
try {
|
|
124
|
+
const state = await loadState();
|
|
125
|
+
if (!state.runner?.baseUrl) {
|
|
126
|
+
console.error("Error: Runner URL not configured.");
|
|
127
|
+
console.log("Configure with:");
|
|
128
|
+
console.log(" griffin runner set --base-url <url> --api-token <token>");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const { configApi } = createSdkClients({
|
|
132
|
+
baseUrl: state.runner.baseUrl,
|
|
133
|
+
apiToken: state.runner.apiToken,
|
|
134
|
+
});
|
|
135
|
+
await configApi.configOrganizationIdEnvironmentTargetsTargetKeyDelete(options.organizationId, options.environment, options.targetKey);
|
|
136
|
+
console.log(`✓ Target "${options.targetKey}" deleted`);
|
|
137
|
+
console.log(` Organization: ${options.organizationId}`);
|
|
138
|
+
console.log(` Environment: ${options.environment}`);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.error(`Error: ${error.message}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), ".griffin");
|
|
5
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
6
|
+
export async function executeConfigureRunnerHost(host) {
|
|
7
|
+
console.log(`Configuring runner host: ${host}`);
|
|
8
|
+
// Ensure config directory exists
|
|
9
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
10
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
// Read existing config or create new one
|
|
13
|
+
let config = {};
|
|
14
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
15
|
+
try {
|
|
16
|
+
const configData = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
17
|
+
config = JSON.parse(configData);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.warn("Warning: Could not read existing config, creating new one");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Update runner host
|
|
24
|
+
config.runnerHost = host;
|
|
25
|
+
// Write config
|
|
26
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
27
|
+
console.log(`Configuration saved to ${CONFIG_FILE}`);
|
|
28
|
+
}
|
|
29
|
+
export function getRunnerHost() {
|
|
30
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const configData = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
35
|
+
const config = JSON.parse(configData);
|
|
36
|
+
return config.runnerHost || null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function executeDeploy(): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { findTestFiles } from "../test-discovery";
|
|
2
|
+
import { getRunnerHost } from "./configure-runner-host";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
export async function executeDeploy() {
|
|
6
|
+
const runnerHost = getRunnerHost();
|
|
7
|
+
if (!runnerHost) {
|
|
8
|
+
console.error("ERROR: No runner host configured. Run: griffin configure-runner-host <host>");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
console.log("Deploying tests to runner...");
|
|
12
|
+
const testFiles = findTestFiles();
|
|
13
|
+
const workspaceRoot = findWorkspaceRoot();
|
|
14
|
+
const testSystemPath = path.join(workspaceRoot, "griffin-ts", "dist");
|
|
15
|
+
if (!fs.existsSync(testSystemPath)) {
|
|
16
|
+
throw new Error("Test system not built. Please run: cd griffin-ts && npm install && npm run build");
|
|
17
|
+
}
|
|
18
|
+
// TODO: Implement deployment logic
|
|
19
|
+
// - Read each test file
|
|
20
|
+
// - Execute it to get JSON monitor
|
|
21
|
+
// - Send to runner API
|
|
22
|
+
// - Handle responses
|
|
23
|
+
console.log("Deployed.");
|
|
24
|
+
}
|
|
25
|
+
function findWorkspaceRoot() {
|
|
26
|
+
let current = process.cwd();
|
|
27
|
+
while (current !== path.dirname(current)) {
|
|
28
|
+
const griffinCliPath = path.join(current, "griffin-cli");
|
|
29
|
+
const testSystemPath = path.join(current, "griffin-ts");
|
|
30
|
+
if (fs.existsSync(griffinCliPath) && fs.existsSync(testSystemPath)) {
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
current = path.dirname(current);
|
|
34
|
+
}
|
|
35
|
+
return process.cwd();
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function executeExecuteRemote(checkName: string): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getRunnerHost } from "./configure-runner-host";
|
|
2
|
+
const axios = require("axios");
|
|
3
|
+
import * as readline from "readline";
|
|
4
|
+
export async function executeExecuteRemote(checkName) {
|
|
5
|
+
const runnerHost = getRunnerHost();
|
|
6
|
+
if (!runnerHost) {
|
|
7
|
+
console.error("ERROR: No runner host configured. Run: griffin configure-runner-host <host>");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
console.log("WARNING: Your local checks will be deployed and executed remotely.");
|
|
11
|
+
console.log("Do you want to continue? (y/n)");
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout,
|
|
15
|
+
});
|
|
16
|
+
rl.question("", async (answer) => {
|
|
17
|
+
rl.close();
|
|
18
|
+
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
|
19
|
+
console.log("Cancelled.");
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
// TODO: Implement remote execution
|
|
24
|
+
const response = await axios.post(`${runnerHost}/api/tests/${checkName}/execute`);
|
|
25
|
+
console.log(JSON.stringify(response.data, null, 2));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error("ERROR: Failed to execute remotely");
|
|
29
|
+
console.error(error.message || String(error));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ConfigAddTargetOptions {
|
|
2
|
+
org: string;
|
|
3
|
+
env: string;
|
|
4
|
+
key: string;
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ConfigRemoveTargetOptions {
|
|
8
|
+
org: string;
|
|
9
|
+
env: string;
|
|
10
|
+
key: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ConfigListOptions {
|
|
13
|
+
org?: string;
|
|
14
|
+
env?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Add a target to hub configuration
|
|
18
|
+
*/
|
|
19
|
+
export declare function executeConfigAddTarget(options: ConfigAddTargetOptions): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Remove a target from hub configuration
|
|
22
|
+
*/
|
|
23
|
+
export declare function executeConfigRemoveTarget(options: ConfigRemoveTargetOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* List all hub configurations
|
|
26
|
+
*/
|
|
27
|
+
export declare function executeConfigList(options: ConfigListOptions): Promise<void>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { loadState } from "../../core/state.js";
|
|
2
|
+
import { createSdkClients } from "../../core/sdk.js";
|
|
3
|
+
/**
|
|
4
|
+
* Add a target to hub configuration
|
|
5
|
+
*/
|
|
6
|
+
export async function executeConfigAddTarget(options) {
|
|
7
|
+
try {
|
|
8
|
+
const state = await loadState();
|
|
9
|
+
if (!state.runner?.baseUrl) {
|
|
10
|
+
console.error("Error: Hub connection not configured.");
|
|
11
|
+
console.log("Connect with:");
|
|
12
|
+
console.log(" griffin hub connect --url <url> --token <token>");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const { configApi } = createSdkClients({
|
|
16
|
+
baseUrl: state.runner.baseUrl,
|
|
17
|
+
apiToken: state.runner.apiToken,
|
|
18
|
+
});
|
|
19
|
+
const result = await configApi.configOrganizationIdEnvironmentTargetsTargetKeyPut(options.org, options.env, options.key, {
|
|
20
|
+
baseUrl: options.url,
|
|
21
|
+
});
|
|
22
|
+
console.log(`✓ Target "${options.key}" set to ${options.url}`);
|
|
23
|
+
console.log(` Organization: ${options.org}`);
|
|
24
|
+
console.log(` Environment: ${options.env}`);
|
|
25
|
+
console.log(` Updated at: ${new Date(result.data.data.updatedAt).toLocaleString()}`);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`Error: ${error.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Remove a target from hub configuration
|
|
34
|
+
*/
|
|
35
|
+
export async function executeConfigRemoveTarget(options) {
|
|
36
|
+
try {
|
|
37
|
+
const state = await loadState();
|
|
38
|
+
if (!state.runner?.baseUrl) {
|
|
39
|
+
console.error("Error: Hub connection not configured.");
|
|
40
|
+
console.log("Connect with:");
|
|
41
|
+
console.log(" griffin hub connect --url <url> --token <token>");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const { configApi } = createSdkClients({
|
|
45
|
+
baseUrl: state.runner.baseUrl,
|
|
46
|
+
apiToken: state.runner.apiToken,
|
|
47
|
+
});
|
|
48
|
+
await configApi.configOrganizationIdEnvironmentTargetsTargetKeyDelete(options.org, options.env, options.key);
|
|
49
|
+
console.log(`✓ Target "${options.key}" deleted`);
|
|
50
|
+
console.log(` Organization: ${options.org}`);
|
|
51
|
+
console.log(` Environment: ${options.env}`);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`Error: ${error.message}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* List all hub configurations
|
|
60
|
+
*/
|
|
61
|
+
export async function executeConfigList(options) {
|
|
62
|
+
try {
|
|
63
|
+
const state = await loadState();
|
|
64
|
+
if (!state.runner?.baseUrl) {
|
|
65
|
+
console.error("Error: Hub connection not configured.");
|
|
66
|
+
console.log("Connect with:");
|
|
67
|
+
console.log(" griffin hub connect --url <url> --token <token>");
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const { configApi } = createSdkClients({
|
|
71
|
+
baseUrl: state.runner.baseUrl,
|
|
72
|
+
apiToken: state.runner.apiToken,
|
|
73
|
+
});
|
|
74
|
+
const result = await configApi.configGet(options.org, options.env);
|
|
75
|
+
if (result.data.data.length === 0 || !result.data.data) {
|
|
76
|
+
console.log("No hub configurations found.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
console.log(`Found ${result.data.data.length} configuration(s):`);
|
|
80
|
+
console.log("");
|
|
81
|
+
for (const config of result.data.data) {
|
|
82
|
+
console.log(`Organization: ${config.organizationId}`);
|
|
83
|
+
console.log(`Environment: ${config.environment}`);
|
|
84
|
+
console.log(`Targets:`);
|
|
85
|
+
const targetCount = Object.keys(config.targets).length;
|
|
86
|
+
if (targetCount === 0) {
|
|
87
|
+
console.log(" (none)");
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
for (const [key, baseUrl] of Object.entries(config.targets)) {
|
|
91
|
+
console.log(` ${key}: ${baseUrl}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log(`Updated: ${new Date(config.updatedAt).toLocaleString()}`);
|
|
95
|
+
console.log("");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error(`Error: ${error.message}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { loadState, resolveEnvironment } from "../../core/state.js";
|
|
2
|
+
import { discoverMonitors, formatDiscoveryErrors } from "../../core/discovery.js";
|
|
3
|
+
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
4
|
+
import { computeDiff, formatDiff, formatDiffJson } from "../../core/diff.js";
|
|
5
|
+
import { terminal } from "../../utils/terminal.js";
|
|
6
|
+
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
7
|
+
import { loadVariables } from "../../core/variables.js";
|
|
8
|
+
import { resolveMonitor } from "../../resolve.js";
|
|
9
|
+
/**
|
|
10
|
+
* Show what changes would be applied
|
|
11
|
+
*/
|
|
12
|
+
export async function executeMonitor(options) {
|
|
13
|
+
try {
|
|
14
|
+
// Load state
|
|
15
|
+
const state = await loadState();
|
|
16
|
+
// Resolve environment
|
|
17
|
+
const envName = await resolveEnvironment(options.env);
|
|
18
|
+
if (!state.hub?.baseUrl) {
|
|
19
|
+
terminal.error("Hub connection not configured.");
|
|
20
|
+
terminal.dim("Connect with:");
|
|
21
|
+
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
22
|
+
terminal.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// Discover local monitors
|
|
25
|
+
const discoveryPattern = state.discovery?.pattern || "**/__griffin__/*.{ts,js}";
|
|
26
|
+
const discoveryIgnore = state.discovery?.ignore || [
|
|
27
|
+
"node_modules/**",
|
|
28
|
+
"dist/**",
|
|
29
|
+
];
|
|
30
|
+
const spinner = terminal.spinner("Discovering local monitors...").start();
|
|
31
|
+
const { monitors, errors } = await discoverMonitors(discoveryPattern, discoveryIgnore);
|
|
32
|
+
if (errors.length > 0) {
|
|
33
|
+
spinner.fail("Discovery failed");
|
|
34
|
+
terminal.error(formatDiscoveryErrors(errors));
|
|
35
|
+
terminal.exit(1);
|
|
36
|
+
}
|
|
37
|
+
spinner.succeed(`Found ${monitors.length} local monitor(s)`);
|
|
38
|
+
// Create SDK clients with credentials
|
|
39
|
+
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
40
|
+
// Fetch remote monitors for this project + environment
|
|
41
|
+
const fetchSpinner = terminal.spinner("Fetching remote monitors...").start();
|
|
42
|
+
const response = await withSDKErrorHandling(() => sdk.getMonitor({
|
|
43
|
+
query: {
|
|
44
|
+
projectId: state.projectId,
|
|
45
|
+
environment: envName,
|
|
46
|
+
},
|
|
47
|
+
}), "Failed to fetch remote monitors");
|
|
48
|
+
const remoteMonitors = response?.data?.data;
|
|
49
|
+
fetchSpinner.succeed(`Found ${remoteMonitors.length} remote monitor(s)`);
|
|
50
|
+
// Load variables and resolve local monitors before computing diff
|
|
51
|
+
const variables = await loadVariables(envName);
|
|
52
|
+
const resolvedMonitors = monitors.map((p) => resolveMonitor(p.monitor, state.projectId, envName, variables));
|
|
53
|
+
// Compute diff (no deletions shown by default)
|
|
54
|
+
const diff = computeDiff(resolvedMonitors, remoteMonitors, {
|
|
55
|
+
includeDeletions: false,
|
|
56
|
+
});
|
|
57
|
+
terminal.blank();
|
|
58
|
+
// Output
|
|
59
|
+
if (options.json) {
|
|
60
|
+
terminal.log(formatDiffJson(diff));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
terminal.log(formatDiff(diff));
|
|
64
|
+
}
|
|
65
|
+
// Exit with error code if there are changes
|
|
66
|
+
if (diff.summary.creates + diff.summary.updates + diff.summary.deletes >
|
|
67
|
+
0) {
|
|
68
|
+
terminal.exit(2); // Exit code 2 indicates changes pending
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
terminal.error(error.message);
|
|
73
|
+
terminal.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ConfigAddTargetOptions {
|
|
2
|
+
env: string;
|
|
3
|
+
key: string;
|
|
4
|
+
url: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ConfigRemoveTargetOptions {
|
|
7
|
+
env: string;
|
|
8
|
+
key: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ConfigSetDefaultEnvOptions {
|
|
11
|
+
env: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* List all local environments and their targets
|
|
15
|
+
*/
|
|
16
|
+
export declare function executeConfigList(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Add a target to a local environment
|
|
19
|
+
*/
|
|
20
|
+
export declare function executeConfigAddTarget(options: ConfigAddTargetOptions): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Remove a target from a local environment
|
|
23
|
+
*/
|
|
24
|
+
export declare function executeConfigRemoveTarget(options: ConfigRemoveTargetOptions): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Set the default environment
|
|
27
|
+
*/
|
|
28
|
+
export declare function executeConfigSetDefaultEnv(options: ConfigSetDefaultEnvOptions): Promise<void>;
|