@trayai/tray-sync-cli 1.0.1 → 1.0.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/README.md +9 -0
- package/dist/commands/env.js +139 -3
- package/dist/commands/promote.js +1 -1
- package/dist/lib/authDiscovery.js +53 -0
- package/dist/views/env/env.v2.js +55 -2
- package/dist/views/promote/promote.v2.js +11 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ npm install -g @trayai/tray-sync-cli
|
|
|
25
25
|
| `tray env add <name>` | Add a target environment |
|
|
26
26
|
| `tray env list` | List source and target environments with connectivity status |
|
|
27
27
|
| `tray env remove <name>` | Remove a target environment and its per-project mappings |
|
|
28
|
+
| `tray env discover [env]` | List an env's projects (id + name, default) and/or matching authentications — `env` defaults to `source` |
|
|
28
29
|
| `tray env resolve <env-name>` | Report unresolved auth requirements for a target env, with candidate matches |
|
|
29
30
|
| `tray auth set -w <workspace-id> -r <region> -t <token>` | Set (or overwrite) a workspace's token |
|
|
30
31
|
| `tray auth remove -w <workspace-id>` | Remove a workspace's stored token |
|
|
@@ -66,6 +67,14 @@ Example:
|
|
|
66
67
|
tray init -r us1 -w 11111111-1111-1111-1111-111111111111 -t <token> -p <project_id>
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
Don't know the project ID yet? Run `init` without `-p` (an empty scaffold is fine), then:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
tray env discover
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
lists every project in your source workspace — id + name — so you don't have to dig it out of Tray's UI.
|
|
77
|
+
|
|
69
78
|
A few notes on the flags:
|
|
70
79
|
|
|
71
80
|
- **`-p <project-id>`** pulls that project immediately after scaffolding. Omit it and `init` just scaffolds an empty repository — you can pull specific projects later with: `tray pull -p <project_id>` OR `tray project add <project-id>` followed by `tray pull`.
|
package/dist/commands/env.js
CHANGED
|
@@ -15,14 +15,15 @@ import { PUBLIC_REGIONS, parseRegion } from "../lib/regionParsing.js";
|
|
|
15
15
|
import { resolveToken } from "../lib/tokenResolution.js";
|
|
16
16
|
import { isUuid } from "../lib/uuid.js";
|
|
17
17
|
import { OutputFormats, parseFormat } from "../lib/outputFormat.js";
|
|
18
|
-
import { listWorkspaceAuthentications, matchAuthCandidates, prioritizeAuths, } from "../lib/authDiscovery.js";
|
|
18
|
+
import { dedupeUsedAuths, listWorkspaceAuthentications, matchAuthCandidates, prioritizeAuths, usedAuthsInProject, } from "../lib/authDiscovery.js";
|
|
19
19
|
import { isFullyResolved, resolveProjectRequirements, } from "../lib/promoteTarget.js";
|
|
20
20
|
import { checkProjectDrift, DriftStatuses } from "./status.js";
|
|
21
|
-
import { printAddedEnvironment, printConnectivityCheck, printEnvAlreadyExists, printEnvNotConfigured, printEnvResolveProject, printEnvResolveProjectFullyResolved, printInvalidEnvName, printNoTargetEnvironments, printNoTokenSkippingConnectivity, printRemovedCredentials, printRemovedEnvironment, printEnvResolveProjectError, printRemovedMapping, printSourceEnvTable, printTargetEnvHeader, printTargetEnvTable, printTokenNotSaved, printTrayYamlNotFound, } from "../views/env/env.v2.js";
|
|
21
|
+
import { printAddedEnvironment, printConnectivityCheck, printDiscoverAuthsHeader, printDiscoverEnvHeader, printDiscoverNoAuthsUsed, printDiscoverNoProjects, printDiscoverProjectsTable, printEnvAlreadyExists, printEnvNotConfigured, printEnvNotFound, printEnvResolveProject, printReservedEnvName, printEnvResolveProjectFullyResolved, printInvalidEnvName, printNoTargetEnvironments, printNoTokenSkippingConnectivity, printRemovedCredentials, printRemovedEnvironment, printEnvResolveProjectError, printRemovedMapping, printSourceAuthsTable, printSourceEnvTable, printTargetAuthsTable, printTargetEnvHeader, printTargetEnvTable, printTokenNotSaved, printTrayYamlNotFound, } from "../views/env/env.v2.js";
|
|
22
22
|
import { printEnvListJson, printEnvResolveResultJson, } from "../views/env/shared.js";
|
|
23
23
|
import { printError, printInvalidAuthMappings, printLocalDrift, printNoTargetProjectMapped, printProjectError, printProjectNoStateFile, printProjectNotPulledLocally, printTargetEnvNotFound, } from "../views/promote/promote.v2.js";
|
|
24
24
|
import { FAILURE_ICON, SUCCESS_ICON } from "../views/icons.js";
|
|
25
25
|
const ENV_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
26
|
+
export const SOURCE_ENV_NAME = "source";
|
|
26
27
|
function emptyMapping() {
|
|
27
28
|
return { authentications: {}, config: {}, connectors: [], services: [] };
|
|
28
29
|
}
|
|
@@ -60,6 +61,10 @@ export async function runEnvAdd(deps, options) {
|
|
|
60
61
|
printInvalidEnvName(stderr, ENV_NAME_RE, options.name);
|
|
61
62
|
return 1;
|
|
62
63
|
}
|
|
64
|
+
if (options.name === SOURCE_ENV_NAME) {
|
|
65
|
+
printReservedEnvName(stderr, options.name);
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
63
68
|
const environments = await readEnvironments(cwd);
|
|
64
69
|
if (environments.environments[options.name]) {
|
|
65
70
|
printEnvAlreadyExists(stderr, options.name);
|
|
@@ -194,6 +199,119 @@ export async function runEnvRemove(deps, options) {
|
|
|
194
199
|
}
|
|
195
200
|
return 0;
|
|
196
201
|
}
|
|
202
|
+
async function listProjectsWithNames(client, workspaceId) {
|
|
203
|
+
const result = (await client.request("GET", `/v2/projects/workspaces/${workspaceId}`));
|
|
204
|
+
return result.projects;
|
|
205
|
+
}
|
|
206
|
+
function resolveDiscoverTarget(envName, trayYaml, environments) {
|
|
207
|
+
if (envName === SOURCE_ENV_NAME) {
|
|
208
|
+
return { region: trayYaml.region, workspaceId: trayYaml.workspace_id };
|
|
209
|
+
}
|
|
210
|
+
const entry = environments.environments[envName];
|
|
211
|
+
if (!entry)
|
|
212
|
+
return undefined;
|
|
213
|
+
return { region: entry.region, workspaceId: entry.workspace_id };
|
|
214
|
+
}
|
|
215
|
+
async function usedAuthsAcrossLocalProjects(deps, cwd, projectIds) {
|
|
216
|
+
const projectsRoot = path.join(cwd, "projects");
|
|
217
|
+
let existingDirs;
|
|
218
|
+
try {
|
|
219
|
+
existingDirs = await deps.readdir(projectsRoot);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
existingDirs = [];
|
|
223
|
+
}
|
|
224
|
+
const used = [];
|
|
225
|
+
for (const projectId of projectIds) {
|
|
226
|
+
const dirName = existingDirs.find((name) => name.split("--")[0] === projectId);
|
|
227
|
+
if (!dirName)
|
|
228
|
+
continue;
|
|
229
|
+
const projectDirPath = path.join(projectsRoot, dirName);
|
|
230
|
+
const projectFiles = await deps.readdirRecursive(projectDirPath);
|
|
231
|
+
used.push(...(await usedAuthsInProject(deps.readFile, projectDirPath, projectFiles)));
|
|
232
|
+
}
|
|
233
|
+
return dedupeUsedAuths(used);
|
|
234
|
+
}
|
|
235
|
+
export async function runEnvDiscover(deps, options) {
|
|
236
|
+
const { cwd, env, createClient, readdir, readFile, readdirRecursive, stdout, stderr } = deps;
|
|
237
|
+
const trayYaml = await readTrayYaml(cwd);
|
|
238
|
+
if (!trayYaml) {
|
|
239
|
+
printTrayYamlNotFound(stderr);
|
|
240
|
+
return 1;
|
|
241
|
+
}
|
|
242
|
+
const environments = await readEnvironments(cwd);
|
|
243
|
+
const target = resolveDiscoverTarget(options.env, trayYaml, environments);
|
|
244
|
+
if (!target) {
|
|
245
|
+
printEnvNotFound(stderr, options.env);
|
|
246
|
+
return 1;
|
|
247
|
+
}
|
|
248
|
+
const credentials = await readCredentials(env);
|
|
249
|
+
let token;
|
|
250
|
+
try {
|
|
251
|
+
token = resolveToken({
|
|
252
|
+
tokenFlag: options.token,
|
|
253
|
+
credentials,
|
|
254
|
+
workspaceId: target.workspaceId,
|
|
255
|
+
envName: options.env === SOURCE_ENV_NAME ? undefined : options.env,
|
|
256
|
+
env,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
printError(stderr, err.message);
|
|
261
|
+
return 1;
|
|
262
|
+
}
|
|
263
|
+
const client = createClient(target.region, token);
|
|
264
|
+
const showProjects = options.all || options.projects || (!options.projects && !options.authentications);
|
|
265
|
+
const showAuths = Boolean(options.all || options.authentications);
|
|
266
|
+
const isSource = options.env === SOURCE_ENV_NAME;
|
|
267
|
+
printDiscoverEnvHeader(stdout, options.env, isSource);
|
|
268
|
+
if (showProjects) {
|
|
269
|
+
const projects = await listProjectsWithNames(client, target.workspaceId);
|
|
270
|
+
const rows = projects
|
|
271
|
+
.map((p) => ({ id: p.id, name: p.name }))
|
|
272
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
273
|
+
if (rows.length === 0) {
|
|
274
|
+
printDiscoverNoProjects(stdout, options.env);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
printDiscoverProjectsTable(stdout, rows);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (showAuths) {
|
|
281
|
+
const used = await usedAuthsAcrossLocalProjects({ readdir, readFile, readdirRecursive }, cwd, trayYaml.projects);
|
|
282
|
+
if (used.length === 0) {
|
|
283
|
+
printDiscoverNoAuthsUsed(stdout);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const { authentications } = await listWorkspaceAuthentications(client, target.region, target.workspaceId);
|
|
287
|
+
printDiscoverAuthsHeader(stdout);
|
|
288
|
+
if (isSource) {
|
|
289
|
+
const rows = used.map((requirement) => {
|
|
290
|
+
const self = authentications.find((a) => a.group === requirement.group);
|
|
291
|
+
return { name: self?.name ?? requirement.title, id: self?.id ?? "", service: requirement.service };
|
|
292
|
+
});
|
|
293
|
+
rows.sort((a, b) => a.service.name.localeCompare(b.service.name) || a.name.localeCompare(b.name));
|
|
294
|
+
printSourceAuthsTable(stdout, rows);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const matchedTitlesByCandidateId = new Map();
|
|
298
|
+
for (const requirement of used) {
|
|
299
|
+
for (const candidate of matchAuthCandidates(requirement, authentications)) {
|
|
300
|
+
const titles = matchedTitlesByCandidateId.get(candidate.id) ?? [];
|
|
301
|
+
titles.push(requirement.title);
|
|
302
|
+
matchedTitlesByCandidateId.set(candidate.id, titles);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const rows = authentications
|
|
306
|
+
.filter((a) => matchedTitlesByCandidateId.has(a.id))
|
|
307
|
+
.map((a) => ({ name: a.name, id: a.id, service: a.service, matchedTitles: matchedTitlesByCandidateId.get(a.id) }));
|
|
308
|
+
rows.sort((a, b) => a.service.name.localeCompare(b.service.name) || a.name.localeCompare(b.name));
|
|
309
|
+
printTargetAuthsTable(stdout, rows);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
197
315
|
function findProjectDirName(existingDirs, projectId) {
|
|
198
316
|
return existingDirs.find((name) => name.split("--")[0] === projectId);
|
|
199
317
|
}
|
|
@@ -310,7 +428,7 @@ export async function runEnvResolve(deps, options) {
|
|
|
310
428
|
}
|
|
311
429
|
if (resolution.status === "no_target_project_mapped") {
|
|
312
430
|
if (!options.dryRun) {
|
|
313
|
-
printNoTargetProjectMapped(stderr, sourceProjectName, projectId, options.to, resolution.mappingsPath);
|
|
431
|
+
printNoTargetProjectMapped(stderr, sourceProjectName, projectId, options.to, resolution.mappingsPath, "env resolve");
|
|
314
432
|
return 1;
|
|
315
433
|
}
|
|
316
434
|
anyUnresolved = true;
|
|
@@ -518,6 +636,24 @@ envCommand
|
|
|
518
636
|
});
|
|
519
637
|
process.exitCode = exitCode;
|
|
520
638
|
});
|
|
639
|
+
envCommand
|
|
640
|
+
.command("discover")
|
|
641
|
+
.description("List available projects and/or authentications for an env (default: source) — --projects (default) shows name+id; --authentications shows workspace auths matching what locally pulled projects use; --all shows both")
|
|
642
|
+
.argument("[env]", `"${SOURCE_ENV_NAME}" (default) or a target environment name (see \`tray env list\`)`, SOURCE_ENV_NAME)
|
|
643
|
+
.option("--projects", "list the env's projects (name + id) — the default when no flag is given")
|
|
644
|
+
.option("--authentications", "list authentications in the env's workspace matching what locally pulled source projects use")
|
|
645
|
+
.option("--all", "shorthand for --projects and --authentications together")
|
|
646
|
+
.option("-t, --token <token>", "Tray API token")
|
|
647
|
+
.action(async (envName, opts) => {
|
|
648
|
+
const exitCode = await runEnvDiscover(realDeps(), {
|
|
649
|
+
env: envName,
|
|
650
|
+
token: opts.token,
|
|
651
|
+
projects: Boolean(opts.projects),
|
|
652
|
+
authentications: Boolean(opts.authentications),
|
|
653
|
+
all: Boolean(opts.all),
|
|
654
|
+
});
|
|
655
|
+
process.exitCode = exitCode;
|
|
656
|
+
});
|
|
521
657
|
envCommand
|
|
522
658
|
.command("resolve")
|
|
523
659
|
.description("Report unresolved auth requirements for a target env, with candidate matches")
|
package/dist/commands/promote.js
CHANGED
|
@@ -163,7 +163,7 @@ export async function runPromote(deps, options) {
|
|
|
163
163
|
mappings_path: resolution.mappingsPath,
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
printNoTargetProjectMapped(stderr, sourceProjectName, projectId, options.to, resolution.mappingsPath);
|
|
166
|
+
printNoTargetProjectMapped(stderr, sourceProjectName, projectId, options.to, resolution.mappingsPath, "promote");
|
|
167
167
|
return 1;
|
|
168
168
|
}
|
|
169
169
|
if (resolution.status === "invalid_auth_mappings") {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { urlsForRegion } from "./region.js";
|
|
3
4
|
const GET_AUTHENTICATIONS_QUERY = `
|
|
4
5
|
query GetAuthenticationsPrivate(
|
|
@@ -73,4 +74,56 @@ export function matchAuthCandidates(requirement, authentications) {
|
|
|
73
74
|
export function prioritizeAuths(candidates) {
|
|
74
75
|
return candidates;
|
|
75
76
|
}
|
|
77
|
+
export function extractUsedAuths(workflow) {
|
|
78
|
+
const steps = workflow.steps;
|
|
79
|
+
if (typeof steps !== "object" || steps === null) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const used = [];
|
|
83
|
+
for (const step of Object.values(steps)) {
|
|
84
|
+
if (typeof step !== "object" || step === null)
|
|
85
|
+
continue;
|
|
86
|
+
const authentication = step.authentication;
|
|
87
|
+
if (typeof authentication !== "object" || authentication === null)
|
|
88
|
+
continue;
|
|
89
|
+
const auth = authentication;
|
|
90
|
+
const group = auth.group;
|
|
91
|
+
const title = auth.title;
|
|
92
|
+
const serviceName = auth.service_name;
|
|
93
|
+
const serviceVersion = auth.service_version;
|
|
94
|
+
if (typeof group !== "string" ||
|
|
95
|
+
typeof title !== "string" ||
|
|
96
|
+
typeof serviceName !== "string" ||
|
|
97
|
+
(typeof serviceVersion !== "string" && typeof serviceVersion !== "number")) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
used.push({ group, title, service: { name: serviceName, version: String(serviceVersion) } });
|
|
101
|
+
}
|
|
102
|
+
return used;
|
|
103
|
+
}
|
|
104
|
+
export function dedupeUsedAuths(used) {
|
|
105
|
+
const seen = new Map();
|
|
106
|
+
for (const auth of used) {
|
|
107
|
+
if (!seen.has(auth.group)) {
|
|
108
|
+
seen.set(auth.group, auth);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...seen.values()];
|
|
112
|
+
}
|
|
113
|
+
export async function usedAuthsInProject(readFile, projectDirPath, projectFiles) {
|
|
114
|
+
const workflowJsonPaths = projectFiles.filter((relativePath) => relativePath.startsWith(`workflows${path.sep}`) && relativePath.endsWith(`${path.sep}workflow.json`));
|
|
115
|
+
const used = [];
|
|
116
|
+
for (const relativePath of workflowJsonPaths) {
|
|
117
|
+
let workflow;
|
|
118
|
+
try {
|
|
119
|
+
const raw = await readFile(path.join(projectDirPath, relativePath));
|
|
120
|
+
workflow = JSON.parse(raw);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
used.push(...extractUsedAuths(workflow));
|
|
126
|
+
}
|
|
127
|
+
return used;
|
|
128
|
+
}
|
|
76
129
|
//# sourceMappingURL=authDiscovery.js.map
|
package/dist/views/env/env.v2.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import ansis from "ansis";
|
|
2
2
|
import Table from "cli-table3";
|
|
3
|
-
import { FAILURE_ICON, INFO_ICON, WARNING_ICON } from "../icons.js";
|
|
3
|
+
import { FAILURE_ICON, INFO_ICON, SUCCESS_ICON, WARNING_ICON } from "../icons.js";
|
|
4
4
|
export function printInvalidEnvName(stderr, nameRe, name) {
|
|
5
5
|
stderr(`${FAILURE_ICON} Environment name must match ${nameRe}: "${name}"`);
|
|
6
6
|
}
|
|
7
|
+
export function printReservedEnvName(stderr, name) {
|
|
8
|
+
stderr(`${FAILURE_ICON} "${ansis.bold(name)}" is reserved (used by \`${ansis.bold("tray env discover")}\` to mean the source workspace) and cannot be a target environment name.`);
|
|
9
|
+
}
|
|
7
10
|
export function printEnvAlreadyExists(stderr, name) {
|
|
8
11
|
stderr(`${FAILURE_ICON} Environment "${ansis.bold(name)}" already exists. Run \`${ansis.bold("tray env remove")}\` first to replace it.`);
|
|
9
12
|
}
|
|
@@ -34,6 +37,9 @@ export function printConnectivityCheck(stdout, description) {
|
|
|
34
37
|
export function printTrayYamlNotFound(stderr) {
|
|
35
38
|
stderr(`${FAILURE_ICON} tray.yaml not found. Run \`${ansis.bold("tray init")}\` first.`);
|
|
36
39
|
}
|
|
40
|
+
export function printEnvNotFound(stderr, name) {
|
|
41
|
+
stderr(`${FAILURE_ICON} "${ansis.bold(name)}" is not "${ansis.bold("source")}" and is not a configured target environment. See \`${ansis.bold("tray env list")}\` for configured names.`);
|
|
42
|
+
}
|
|
37
43
|
function envTable() {
|
|
38
44
|
return new Table({
|
|
39
45
|
head: ["Environment", "Region", "Workspace ID", "Status"].map((h) => ansis.bold(h)),
|
|
@@ -73,7 +79,7 @@ export function printRemovedCredentials(stdout, workspaceId) {
|
|
|
73
79
|
stdout(`${ansis.bold("Removed")} credentials for workspace ${workspaceId}.`);
|
|
74
80
|
}
|
|
75
81
|
export function printEnvResolveProjectFullyResolved(stdout, projectId, sourceProjectName) {
|
|
76
|
-
stdout(`${ansis.bold(`${sourceProjectName} (${projectId})`)}: fully resolved, nothing to do`);
|
|
82
|
+
stdout(`${SUCCESS_ICON} ${ansis.bold(`${sourceProjectName} (${projectId})`)}: fully resolved, nothing to do`);
|
|
77
83
|
}
|
|
78
84
|
export function printEnvResolveProjectError(stdout, projectId, sourceProjectName, error) {
|
|
79
85
|
stdout(`${ansis.bold(`${sourceProjectName} (${projectId})`)}:`);
|
|
@@ -112,4 +118,51 @@ export function printEnvResolveProject(stdout, project) {
|
|
|
112
118
|
stdout(` new config keys require mapping — see mappings.json's config entry`);
|
|
113
119
|
}
|
|
114
120
|
}
|
|
121
|
+
export function printDiscoverEnvHeader(stdout, envName, isSource) {
|
|
122
|
+
stdout(ansis.bold(isSource ? "Environment: source" : `Environment: target (${envName})`));
|
|
123
|
+
stdout("");
|
|
124
|
+
}
|
|
125
|
+
export function printDiscoverNoProjects(stdout, envName) {
|
|
126
|
+
stdout(`${INFO_ICON} No projects found in "${ansis.bold(envName)}".`);
|
|
127
|
+
}
|
|
128
|
+
export function printDiscoverProjectsTable(stdout, rows) {
|
|
129
|
+
stdout("[Projects]");
|
|
130
|
+
const table = new Table({
|
|
131
|
+
head: ["Name", "ID"].map((h) => ansis.bold(h)),
|
|
132
|
+
style: { head: [] },
|
|
133
|
+
});
|
|
134
|
+
for (const row of rows) {
|
|
135
|
+
table.push([row.name, row.id]);
|
|
136
|
+
}
|
|
137
|
+
stdout(table.toString());
|
|
138
|
+
stdout("");
|
|
139
|
+
}
|
|
140
|
+
export function printDiscoverNoAuthsUsed(stdout) {
|
|
141
|
+
stdout(`${INFO_ICON} No locally pulled project uses an authentication.`);
|
|
142
|
+
}
|
|
143
|
+
export function printDiscoverAuthsHeader(stdout) {
|
|
144
|
+
stdout("[Available Authentications]");
|
|
145
|
+
}
|
|
146
|
+
export function printSourceAuthsTable(stdout, rows) {
|
|
147
|
+
const table = new Table({
|
|
148
|
+
head: ["Name", "ID", "Service"].map((h) => ansis.bold(h)),
|
|
149
|
+
style: { head: [] },
|
|
150
|
+
});
|
|
151
|
+
for (const row of rows) {
|
|
152
|
+
table.push([row.name, row.id, `${row.service.name} v${row.service.version}`]);
|
|
153
|
+
}
|
|
154
|
+
stdout(table.toString());
|
|
155
|
+
stdout("");
|
|
156
|
+
}
|
|
157
|
+
export function printTargetAuthsTable(stdout, rows) {
|
|
158
|
+
const table = new Table({
|
|
159
|
+
head: ["Name", "Service", "ID", "Match"].map((h) => ansis.bold(h)),
|
|
160
|
+
style: { head: [] },
|
|
161
|
+
});
|
|
162
|
+
for (const row of rows) {
|
|
163
|
+
table.push([row.name, `${row.service.name} v${row.service.version}`, row.id, row.matchedTitles.join("\n")]);
|
|
164
|
+
}
|
|
165
|
+
stdout(table.toString());
|
|
166
|
+
stdout("");
|
|
167
|
+
}
|
|
115
168
|
//# sourceMappingURL=env.v2.js.map
|
|
@@ -22,9 +22,17 @@ export function printProjectNoStateFile(stderr, projectId) {
|
|
|
22
22
|
export function printLocalDrift(stderr, projectId, dirName, dirty) {
|
|
23
23
|
printIssueBlock(stderr, `Project ${ansis.bold(`${projectId} (${dirName})`)} has local drift; refusing to promote:`, dirty.map((d) => `${d.status}\t${d.path}`), `Run \`${ansis.bold("tray pull")}\` to refresh, or resolve the drift, then retry.`);
|
|
24
24
|
}
|
|
25
|
-
export function printNoTargetProjectMapped(stderr, sourceProjectName, projectId, envName, mappingsPath) {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
export function printNoTargetProjectMapped(stderr, sourceProjectName, projectId, envName, mappingsPath, command) {
|
|
26
|
+
if (command === "promote") {
|
|
27
|
+
stderr(`${FAILURE_ICON} ${ansis.bold(`"${sourceProjectName}" (${projectId})`)} has no target project mapped for "${ansis.bold(envName)}".`);
|
|
28
|
+
stderr(`Fill in target_project_id in ${mappingsPath}, or re-run with ${ansis.bold("--auto-create-projects")}.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
stderr("");
|
|
32
|
+
stderr("Your source environment is missing the following resolution:");
|
|
33
|
+
stderr(`${FAILURE_ICON} Project ${ansis.bold(`"${sourceProjectName}" (${projectId})`)} has no target project mapped for "${ansis.bold(envName)}".`);
|
|
34
|
+
stderr(`Fill in ${ansis.bold("target_project_id")} in ${mappingsPath}.`);
|
|
35
|
+
stderr(`${INFO_ICON} Run \`${ansis.bold(`tray env discover ${envName}`)}\` to see available target projects.`);
|
|
28
36
|
}
|
|
29
37
|
export function printInvalidAuthMappings(stderr, projectId, sourceProjectName, invalidAuthMappings) {
|
|
30
38
|
printIssueBlock(stderr, `Project ${ansis.bold(`${projectId} (${sourceProjectName})`)} has unresolved authentication mappings:`, invalidAuthMappings.map((invalid) => `${ansis.bold(invalid.authExportId)}: "${invalid.value}" is not a valid target authentication id`), "Fill in a real target authentication id for each entry above, then retry.");
|
package/package.json
CHANGED