@credibledata/cred-cli 0.0.100 → 0.0.103
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-DEV.md +2 -2
- package/README.md +1 -1
- package/coverage/coverage-summary.json +40 -0
- package/coverage/tmp/coverage-2152-1789496974457-0.json +1 -0
- package/coverage/tmp/coverage-2153-1789496954404-0.json +1 -0
- package/dist/client/api.js +6151 -2254
- package/dist/client/api.js.map +1 -1
- package/dist/client/base.js +1 -1
- package/dist/client/common.js +1 -1
- package/dist/client/configuration.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/clientconfiguration.js +114 -26
- package/dist/clientconfiguration.js.map +1 -1
- package/dist/clientconfiguration.spec.js +159 -37
- package/dist/clientconfiguration.spec.js.map +1 -1
- package/dist/commands/add.js +36 -5
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/connection/createconnections.js +4 -4
- package/dist/commands/connection/createconnections.js.map +1 -1
- package/dist/commands/connection/createconnections.spec.js +11 -1
- package/dist/commands/connection/createconnections.spec.js.map +1 -1
- package/dist/commands/connection/deleteconnection.js +4 -4
- package/dist/commands/connection/deleteconnection.js.map +1 -1
- package/dist/commands/connection/deleteconnection.spec.js +1 -1
- package/dist/commands/connection/deleteconnection.spec.js.map +1 -1
- package/dist/commands/connection/listconnections.js +4 -4
- package/dist/commands/connection/listconnections.js.map +1 -1
- package/dist/commands/connection/listconnections.spec.js +1 -1
- package/dist/commands/connection/listconnections.spec.js.map +1 -1
- package/dist/commands/{project/createproject.js → environment/createenvironment.js} +17 -14
- package/dist/commands/environment/createenvironment.js.map +1 -0
- package/dist/commands/{project/createproject.spec.js → environment/createenvironment.spec.js} +46 -46
- package/dist/commands/environment/createenvironment.spec.js.map +1 -0
- package/dist/commands/{project/deleteproject.js → environment/deleteenvironment.js} +9 -9
- package/dist/commands/environment/deleteenvironment.js.map +1 -0
- package/dist/commands/{project/deleteproject.spec.js → environment/deleteenvironment.spec.js} +29 -29
- package/dist/commands/environment/deleteenvironment.spec.js.map +1 -0
- package/dist/commands/{project/getproject.js → environment/getenvironment.js} +8 -8
- package/dist/commands/environment/getenvironment.js.map +1 -0
- package/dist/commands/{project/getproject.spec.js → environment/getenvironment.spec.js} +32 -33
- package/dist/commands/environment/getenvironment.spec.js.map +1 -0
- package/dist/commands/{project/listprojects.js → environment/listenvironments.js} +10 -10
- package/dist/commands/environment/listenvironments.js.map +1 -0
- package/dist/commands/{project/listprojects.spec.js → environment/listenvironments.spec.js} +35 -35
- package/dist/commands/environment/listenvironments.spec.js.map +1 -0
- package/dist/commands/{project/setproject.js → environment/setenvironment.js} +6 -5
- package/dist/commands/environment/setenvironment.js.map +1 -0
- package/dist/commands/{project/setproject.spec.js → environment/setenvironment.spec.js} +9 -9
- package/dist/commands/environment/setenvironment.spec.js.map +1 -0
- package/dist/commands/environment/updateenvironment.js +130 -0
- package/dist/commands/environment/updateenvironment.js.map +1 -0
- package/dist/commands/environment/updateenvironment.spec.js +213 -0
- package/dist/commands/environment/updateenvironment.spec.js.map +1 -0
- package/dist/commands/get.js +66 -5
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/legacy.js +9 -0
- package/dist/commands/legacy.js.map +1 -0
- package/dist/commands/list.js +95 -4
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/materialization/cancelrun.js +62 -0
- package/dist/commands/materialization/cancelrun.js.map +1 -0
- package/dist/commands/materialization/getindex.js +52 -0
- package/dist/commands/materialization/getindex.js.map +1 -0
- package/dist/commands/materialization/getmaterialization.js +54 -0
- package/dist/commands/materialization/getmaterialization.js.map +1 -0
- package/dist/commands/materialization/getpreaggregate.js +55 -0
- package/dist/commands/materialization/getpreaggregate.js.map +1 -0
- package/dist/commands/materialization/getrun.js +54 -0
- package/dist/commands/materialization/getrun.js.map +1 -0
- package/dist/commands/materialization/listindexes.js +58 -0
- package/dist/commands/materialization/listindexes.js.map +1 -0
- package/dist/commands/materialization/listmaterializations.js +52 -0
- package/dist/commands/materialization/listmaterializations.js.map +1 -0
- package/dist/commands/materialization/listpreaggregates.js +61 -0
- package/dist/commands/materialization/listpreaggregates.js.map +1 -0
- package/dist/commands/materialization/listruns.js +60 -0
- package/dist/commands/materialization/listruns.js.map +1 -0
- package/dist/commands/materialization/rerun.js +111 -0
- package/dist/commands/materialization/rerun.js.map +1 -0
- package/dist/commands/misc/login.js +16 -2
- package/dist/commands/misc/login.js.map +1 -1
- package/dist/commands/misc/login.spec.js +133 -5
- package/dist/commands/misc/login.spec.js.map +1 -1
- package/dist/commands/package/deletepackage.js +4 -4
- package/dist/commands/package/deletepackage.js.map +1 -1
- package/dist/commands/package/deletepackage.spec.js +1 -1
- package/dist/commands/package/deletepackage.spec.js.map +1 -1
- package/dist/commands/package/listpackages.js +11 -4
- package/dist/commands/package/listpackages.js.map +1 -1
- package/dist/commands/package/listpackages.spec.js +13 -1
- package/dist/commands/package/listpackages.spec.js.map +1 -1
- package/dist/commands/package/publish.js +36 -13
- package/dist/commands/package/publish.js.map +1 -1
- package/dist/commands/package/publish.spec.js +27 -1
- package/dist/commands/package/publish.spec.js.map +1 -1
- package/dist/commands/package/updatepackage.js +4 -4
- package/dist/commands/package/updatepackage.js.map +1 -1
- package/dist/commands/package/updatepackage.spec.js +1 -1
- package/dist/commands/package/updatepackage.spec.js.map +1 -1
- package/dist/commands/permission/createpermission.js +123 -0
- package/dist/commands/permission/createpermission.js.map +1 -0
- package/dist/commands/permission/createpermission.spec.js +360 -0
- package/dist/commands/permission/createpermission.spec.js.map +1 -0
- package/dist/commands/permission/deletepermission.js +86 -0
- package/dist/commands/permission/deletepermission.js.map +1 -0
- package/dist/commands/permission/getpermission.js +85 -0
- package/dist/commands/permission/getpermission.js.map +1 -0
- package/dist/commands/permission/listpermissions.js +75 -0
- package/dist/commands/permission/listpermissions.js.map +1 -0
- package/dist/commands/permission/scope.js +140 -0
- package/dist/commands/permission/scope.js.map +1 -0
- package/dist/commands/rm.js +26 -4
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/set.js +12 -4
- package/dist/commands/set.js.map +1 -1
- package/dist/commands/update.js +29 -10
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/version/archive-unarchive-version.js +4 -5
- package/dist/commands/version/archive-unarchive-version.js.map +1 -1
- package/dist/commands/version/archive-unarchive-version.spec.js +4 -4
- package/dist/commands/version/archive-unarchive-version.spec.js.map +1 -1
- package/dist/commands/version/listversions.js +4 -4
- package/dist/commands/version/listversions.js.map +1 -1
- package/dist/commands/version/listversions.spec.js +1 -1
- package/dist/commands/version/listversions.spec.js.map +1 -1
- package/dist/common/UserGroupId.js +1 -1
- package/dist/common/UserGroupId.js.map +1 -1
- package/dist/common/UserGroupId.spec.js +2 -4
- package/dist/common/UserGroupId.spec.js.map +1 -1
- package/dist/constants.js +95 -19
- package/dist/constants.js.map +1 -1
- package/dist/constants.spec.js +90 -0
- package/dist/constants.spec.js.map +1 -0
- package/dist/index.js +128 -15
- package/dist/index.js.map +1 -1
- package/dist/logger.js +5 -1
- package/dist/logger.js.map +1 -1
- package/openapitools.json +4 -1
- package/package.json +10 -9
- package/src/client/api.ts +9363 -3093
- package/src/client/base.ts +1 -1
- package/src/client/common.ts +1 -1
- package/src/client/configuration.ts +1 -1
- package/src/client/index.ts +1 -1
- package/src/clientconfiguration.spec.ts +238 -113
- package/src/clientconfiguration.ts +131 -30
- package/src/commands/add.ts +78 -12
- package/src/commands/connection/createconnections.spec.ts +11 -1
- package/src/commands/connection/createconnections.ts +4 -4
- package/src/commands/connection/deleteconnection.spec.ts +1 -1
- package/src/commands/connection/deleteconnection.ts +4 -4
- package/src/commands/connection/listconnections.spec.ts +1 -1
- package/src/commands/connection/listconnections.ts +4 -4
- package/src/commands/{project/createproject.spec.ts → environment/createenvironment.spec.ts} +56 -54
- package/src/commands/{project/createproject.ts → environment/createenvironment.ts} +17 -14
- package/src/commands/{project/deleteproject.spec.ts → environment/deleteenvironment.spec.ts} +59 -30
- package/src/commands/{project/deleteproject.ts → environment/deleteenvironment.ts} +10 -10
- package/src/commands/environment/getenvironment.spec.ts +110 -0
- package/src/commands/{project/getproject.ts → environment/getenvironment.ts} +9 -9
- package/src/commands/{project/listprojects.spec.ts → environment/listenvironments.spec.ts} +37 -35
- package/src/commands/{project/listprojects.ts → environment/listenvironments.ts} +13 -8
- package/src/commands/{project/setproject.spec.ts → environment/setenvironment.spec.ts} +8 -8
- package/src/commands/{project/setproject.ts → environment/setenvironment.ts} +4 -4
- package/src/commands/environment/updateenvironment.spec.ts +386 -0
- package/src/commands/environment/updateenvironment.ts +168 -0
- package/src/commands/get.ts +131 -9
- package/src/commands/legacy.ts +5 -0
- package/src/commands/list.ts +204 -9
- package/src/commands/materialization/cancelrun.ts +70 -0
- package/src/commands/materialization/getindex.ts +55 -0
- package/src/commands/materialization/getmaterialization.ts +57 -0
- package/src/commands/materialization/getpreaggregate.ts +58 -0
- package/src/commands/materialization/getrun.ts +55 -0
- package/src/commands/materialization/listindexes.ts +69 -0
- package/src/commands/materialization/listmaterializations.ts +53 -0
- package/src/commands/materialization/listpreaggregates.ts +74 -0
- package/src/commands/materialization/listruns.ts +77 -0
- package/src/commands/materialization/rerun.ts +123 -0
- package/src/commands/misc/login.spec.ts +165 -7
- package/src/commands/misc/login.ts +20 -2
- package/src/commands/package/deletepackage.spec.ts +1 -1
- package/src/commands/package/deletepackage.ts +4 -4
- package/src/commands/package/listpackages.spec.ts +17 -1
- package/src/commands/package/listpackages.ts +11 -4
- package/src/commands/package/publish.spec.ts +46 -1
- package/src/commands/package/publish.ts +38 -13
- package/src/commands/package/updatepackage.spec.ts +1 -1
- package/src/commands/package/updatepackage.ts +4 -4
- package/src/commands/permission/createpermission.spec.ts +507 -0
- package/src/commands/permission/createpermission.ts +181 -0
- package/src/commands/permission/deletepermission.ts +117 -0
- package/src/commands/permission/getpermission.ts +115 -0
- package/src/commands/permission/listpermissions.ts +101 -0
- package/src/commands/permission/scope.ts +180 -0
- package/src/commands/rm.ts +54 -9
- package/src/commands/set.ts +13 -4
- package/src/commands/update.ts +66 -23
- package/src/commands/version/archive-unarchive-version.spec.ts +4 -4
- package/src/commands/version/archive-unarchive-version.ts +4 -7
- package/src/commands/version/listversions.spec.ts +1 -1
- package/src/commands/version/listversions.ts +4 -4
- package/src/common/UserGroupId.spec.ts +2 -4
- package/src/common/UserGroupId.ts +1 -1
- package/src/constants.spec.ts +111 -0
- package/src/constants.ts +131 -19
- package/src/index.ts +187 -18
- package/src/logger.ts +5 -1
- package/bun.lock +0 -1158
- package/dist/commands/project/createproject.js.map +0 -1
- package/dist/commands/project/createproject.spec.js.map +0 -1
- package/dist/commands/project/deleteproject.js.map +0 -1
- package/dist/commands/project/deleteproject.spec.js.map +0 -1
- package/dist/commands/project/getproject.js.map +0 -1
- package/dist/commands/project/getproject.spec.js.map +0 -1
- package/dist/commands/project/listprojects.js.map +0 -1
- package/dist/commands/project/listprojects.spec.js.map +0 -1
- package/dist/commands/project/setproject.js.map +0 -1
- package/dist/commands/project/setproject.spec.js.map +0 -1
- package/dist/commands/project/updateproject.js +0 -68
- package/dist/commands/project/updateproject.js.map +0 -1
- package/dist/commands/project/updateproject.spec.js +0 -129
- package/dist/commands/project/updateproject.spec.js.map +0 -1
- package/src/commands/project/getproject.spec.ts +0 -109
- package/src/commands/project/updateproject.spec.ts +0 -217
- package/src/commands/project/updateproject.ts +0 -82
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { RunsApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { prettifyJson, printOutError } from "../../utils";
|
|
6
|
+
|
|
7
|
+
const runsApi = new RunsApi(new Configuration());
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get a single build/refresh run by id, including its stateful build plan. The
|
|
11
|
+
* `buildPlan.nodes` carry each produced unit's live state — per source node the
|
|
12
|
+
* physical `tableName` and `rowCount`, per index node the dimension and its
|
|
13
|
+
* status — and `buildPlan.edges` wire sources to the indexes that depend on
|
|
14
|
+
* them. This is the detailed view (`listRuns` omits the graph); use it to see
|
|
15
|
+
* exactly which physical tables a run produced and how the units relate.
|
|
16
|
+
*/
|
|
17
|
+
export async function getRun(
|
|
18
|
+
controlPlane: string,
|
|
19
|
+
packagePath: string,
|
|
20
|
+
runId: string,
|
|
21
|
+
debug: boolean,
|
|
22
|
+
) {
|
|
23
|
+
const clientConfig = new ClientConfiguration();
|
|
24
|
+
const organizationName = clientConfig.getOrganization();
|
|
25
|
+
const environmentName = clientConfig.getProject();
|
|
26
|
+
|
|
27
|
+
if (!clientConfig.doesConfigExists()) {
|
|
28
|
+
logger.error("Please, make sure that you are logged in");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!environmentName) {
|
|
33
|
+
logger.error("Please, make sure to set an environment");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const packageName = packagePath.replace("/", "");
|
|
39
|
+
const response = await runsApi.getRun(
|
|
40
|
+
organizationName,
|
|
41
|
+
environmentName,
|
|
42
|
+
packageName,
|
|
43
|
+
runId,
|
|
44
|
+
{
|
|
45
|
+
baseURL: controlPlane,
|
|
46
|
+
headers: clientConfig.getHeaders(),
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
console.log(prettifyJson(response.data));
|
|
51
|
+
} catch (e) {
|
|
52
|
+
logger.error("Failed to get run");
|
|
53
|
+
printOutError(e, debug);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { IndexesApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import {
|
|
6
|
+
prettifyJson,
|
|
7
|
+
printOutError,
|
|
8
|
+
parsePaginationParams,
|
|
9
|
+
extractTotalCount,
|
|
10
|
+
} from "../../utils";
|
|
11
|
+
|
|
12
|
+
const indexesApi = new IndexesApi(new Configuration());
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List the dimensional search indexes bound to a package version (the per-`#(index)`
|
|
16
|
+
* serving anchors). Each entry prints its `status` and, when FAILED, the `error`;
|
|
17
|
+
* plus the serving `rowCount`, `lastIndexedAt`, freshness policy, and the
|
|
18
|
+
* `stale`/`staleReasons` triple — so a failing or stale index is diagnosable
|
|
19
|
+
* from the CLI. The total available count is logged to stderr.
|
|
20
|
+
*/
|
|
21
|
+
export async function listIndexes(
|
|
22
|
+
controlPlane: string,
|
|
23
|
+
packagePath: string,
|
|
24
|
+
versionId: string,
|
|
25
|
+
debug: boolean,
|
|
26
|
+
page?: string | number,
|
|
27
|
+
pageSize?: string | number,
|
|
28
|
+
) {
|
|
29
|
+
const clientConfig = new ClientConfiguration();
|
|
30
|
+
const organizationName = clientConfig.getOrganization();
|
|
31
|
+
const environmentName = clientConfig.getProject();
|
|
32
|
+
|
|
33
|
+
if (!clientConfig.doesConfigExists()) {
|
|
34
|
+
logger.error("Please, make sure that you are logged in");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!environmentName) {
|
|
39
|
+
logger.error("Please, make sure to set an environment");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const packageName = packagePath.replace("/", "");
|
|
45
|
+
const { limit, offset } = parsePaginationParams(page, pageSize);
|
|
46
|
+
const response = await indexesApi.listIndexes(
|
|
47
|
+
organizationName,
|
|
48
|
+
environmentName,
|
|
49
|
+
packageName,
|
|
50
|
+
versionId,
|
|
51
|
+
limit,
|
|
52
|
+
offset,
|
|
53
|
+
{
|
|
54
|
+
baseURL: controlPlane,
|
|
55
|
+
headers: clientConfig.getHeaders(),
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const totalCount = extractTotalCount(response.headers);
|
|
60
|
+
if (totalCount !== undefined) {
|
|
61
|
+
logger.info(`Total indexes available: ${totalCount}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(prettifyJson(response.data));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
logger.error("Failed to list indexes");
|
|
67
|
+
printOutError(e, debug);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MaterializationsApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { prettifyJson, printOutError } from "../../utils";
|
|
6
|
+
|
|
7
|
+
const materializationsApi = new MaterializationsApi(new Configuration());
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* List the Malloy Persistence materializations bound to a package version.
|
|
11
|
+
* Each materialization prints its `status` and, when FAILED, the `error`
|
|
12
|
+
* explaining why the build failed (e.g. a warehouse permission denial) — so a
|
|
13
|
+
* failing materialization is diagnosable from the CLI without opening the app.
|
|
14
|
+
*/
|
|
15
|
+
export async function listMaterializations(
|
|
16
|
+
controlPlane: string,
|
|
17
|
+
packagePath: string,
|
|
18
|
+
versionId: string,
|
|
19
|
+
debug: boolean,
|
|
20
|
+
) {
|
|
21
|
+
const clientConfig = new ClientConfiguration();
|
|
22
|
+
const organizationName = clientConfig.getOrganization();
|
|
23
|
+
const environmentName = clientConfig.getProject();
|
|
24
|
+
|
|
25
|
+
if (!clientConfig.doesConfigExists()) {
|
|
26
|
+
logger.error("Please, make sure that you are logged in");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!environmentName) {
|
|
31
|
+
logger.error("Please, make sure to set an environment");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const packageName = packagePath.replace("/", "");
|
|
37
|
+
const response = await materializationsApi.listMaterializations(
|
|
38
|
+
organizationName,
|
|
39
|
+
environmentName,
|
|
40
|
+
packageName,
|
|
41
|
+
versionId,
|
|
42
|
+
{
|
|
43
|
+
baseURL: controlPlane,
|
|
44
|
+
headers: clientConfig.getHeaders(),
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
console.log(prettifyJson(response.data));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
logger.error("Failed to list materializations");
|
|
51
|
+
printOutError(e, debug);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { PreaggregatesApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import {
|
|
6
|
+
prettifyJson,
|
|
7
|
+
printOutError,
|
|
8
|
+
parsePaginationParams,
|
|
9
|
+
extractTotalCount,
|
|
10
|
+
} from "../../utils";
|
|
11
|
+
|
|
12
|
+
const preaggregatesApi = new PreaggregatesApi(new Configuration());
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List the pre-aggregated measures of a package version (one entry per `#@ preaggregate`
|
|
16
|
+
* measure). Each entry prints its `status` and, when FAILED, the `error`; plus the
|
|
17
|
+
* `grainDimensions` it is rolled up to, the serving `rowCount`, `lastBuiltAt`, freshness
|
|
18
|
+
* policy, and the `stale`/`staleReasons` triple. `--source` narrows to the measures of one
|
|
19
|
+
* authored source. The total available count is logged to stderr.
|
|
20
|
+
*
|
|
21
|
+
* <p>One rollup table can serve several measures, so entries here outnumber the tables
|
|
22
|
+
* behind them: the measure is what a modeler wrote, and it is the unit this lists.
|
|
23
|
+
*/
|
|
24
|
+
export async function listPreaggregates(
|
|
25
|
+
controlPlane: string,
|
|
26
|
+
packagePath: string,
|
|
27
|
+
versionId: string,
|
|
28
|
+
sourceName: string | undefined,
|
|
29
|
+
debug: boolean,
|
|
30
|
+
page?: string | number,
|
|
31
|
+
pageSize?: string | number,
|
|
32
|
+
) {
|
|
33
|
+
const clientConfig = new ClientConfiguration();
|
|
34
|
+
const organizationName = clientConfig.getOrganization();
|
|
35
|
+
const environmentName = clientConfig.getProject();
|
|
36
|
+
|
|
37
|
+
if (!clientConfig.doesConfigExists()) {
|
|
38
|
+
logger.error("Please, make sure that you are logged in");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!environmentName) {
|
|
43
|
+
logger.error("Please, make sure to set an environment");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const packageName = packagePath.replace("/", "");
|
|
49
|
+
const { limit, offset } = parsePaginationParams(page, pageSize);
|
|
50
|
+
const response = await preaggregatesApi.listPreaggregates(
|
|
51
|
+
organizationName,
|
|
52
|
+
environmentName,
|
|
53
|
+
packageName,
|
|
54
|
+
versionId,
|
|
55
|
+
sourceName,
|
|
56
|
+
limit,
|
|
57
|
+
offset,
|
|
58
|
+
{
|
|
59
|
+
baseURL: controlPlane,
|
|
60
|
+
headers: clientConfig.getHeaders(),
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const totalCount = extractTotalCount(response.headers);
|
|
65
|
+
if (totalCount !== undefined) {
|
|
66
|
+
logger.info(`Total pre-aggregations available: ${totalCount}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(prettifyJson(response.data));
|
|
70
|
+
} catch (e) {
|
|
71
|
+
logger.error("Failed to list pre-aggregations");
|
|
72
|
+
printOutError(e, debug);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { RunsApi } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import {
|
|
6
|
+
prettifyJson,
|
|
7
|
+
printOutError,
|
|
8
|
+
parsePaginationParams,
|
|
9
|
+
extractTotalCount,
|
|
10
|
+
} from "../../utils";
|
|
11
|
+
|
|
12
|
+
const runsApi = new RunsApi(new Configuration());
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List the build/refresh runs for a package (most recent first). Runs are one
|
|
16
|
+
* package-scoped resource spanning both families — materialized sources and
|
|
17
|
+
* dimensional indexes. Optional filters narrow the list: `versionId` limits to
|
|
18
|
+
* the runs that impacted a version; `sourceName`/`dimension`/`measure` limit to runs
|
|
19
|
+
* targeting a specific unit (Phase C per-unit dispatch). Each run prints its
|
|
20
|
+
* `status`, `trigger`, `buildNumber`, target scope, and, when failed, its
|
|
21
|
+
* `error`. The per-unit build-plan graph is available via `cred get run`.
|
|
22
|
+
*/
|
|
23
|
+
export async function listRuns(
|
|
24
|
+
controlPlane: string,
|
|
25
|
+
packagePath: string,
|
|
26
|
+
versionId: string | undefined,
|
|
27
|
+
debug: boolean,
|
|
28
|
+
sourceName?: string,
|
|
29
|
+
dimension?: string,
|
|
30
|
+
measure?: string,
|
|
31
|
+
page?: string | number,
|
|
32
|
+
pageSize?: string | number,
|
|
33
|
+
) {
|
|
34
|
+
const clientConfig = new ClientConfiguration();
|
|
35
|
+
const organizationName = clientConfig.getOrganization();
|
|
36
|
+
const environmentName = clientConfig.getProject();
|
|
37
|
+
|
|
38
|
+
if (!clientConfig.doesConfigExists()) {
|
|
39
|
+
logger.error("Please, make sure that you are logged in");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!environmentName) {
|
|
44
|
+
logger.error("Please, make sure to set an environment");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const packageName = packagePath.replace("/", "");
|
|
50
|
+
const { limit, offset } = parsePaginationParams(page, pageSize);
|
|
51
|
+
const response = await runsApi.listRuns(
|
|
52
|
+
organizationName,
|
|
53
|
+
environmentName,
|
|
54
|
+
packageName,
|
|
55
|
+
versionId,
|
|
56
|
+
sourceName,
|
|
57
|
+
measure,
|
|
58
|
+
dimension,
|
|
59
|
+
limit,
|
|
60
|
+
offset,
|
|
61
|
+
{
|
|
62
|
+
baseURL: controlPlane,
|
|
63
|
+
headers: clientConfig.getHeaders(),
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const totalCount = extractTotalCount(response.headers);
|
|
68
|
+
if (totalCount !== undefined) {
|
|
69
|
+
logger.info(`Total runs available: ${totalCount}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(prettifyJson(response.data));
|
|
73
|
+
} catch (e) {
|
|
74
|
+
logger.error("Failed to list runs");
|
|
75
|
+
printOutError(e, debug);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { RunsApi, CreateRunRequest } from "../../client/api";
|
|
2
|
+
import { Configuration } from "../../client/configuration";
|
|
3
|
+
import { ClientConfiguration } from "../../clientconfiguration";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { getConfirmation, prettifyJson, printOutError } from "../../utils";
|
|
6
|
+
|
|
7
|
+
const runsApi = new RunsApi(new Configuration());
|
|
8
|
+
|
|
9
|
+
export interface RerunScope {
|
|
10
|
+
source?: string;
|
|
11
|
+
dimension?: string;
|
|
12
|
+
measure?: string;
|
|
13
|
+
modelFile?: string;
|
|
14
|
+
includeUpstream?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Trigger an on-demand run (rebuild/refresh) for a package version. With no
|
|
19
|
+
* scope selectors this is a full run: re-materialize every persist source and
|
|
20
|
+
* rebuild every indexed dimension. The optional `--source` / `--dimension` /
|
|
21
|
+
* `--measure` / `--model-file` selectors narrow it to a single unit (Phase C
|
|
22
|
+
* per-unit dispatch); `--measure` rebuilds the rollup serving that
|
|
23
|
+
* pre-aggregated measure, and fails when the measure is declared at more than
|
|
24
|
+
* one grain (pass `--source` to disambiguate); `--include-upstream` additionally force-rebuilds the target's
|
|
25
|
+
* upstream persisted closure. Prints the started run (poll it with
|
|
26
|
+
* `cred get run <package> <runId>`).
|
|
27
|
+
*/
|
|
28
|
+
export async function rerun(
|
|
29
|
+
controlPlane: string,
|
|
30
|
+
packagePath: string,
|
|
31
|
+
versionId: string,
|
|
32
|
+
scope: RerunScope,
|
|
33
|
+
debug: boolean,
|
|
34
|
+
autoConfirm: boolean,
|
|
35
|
+
) {
|
|
36
|
+
const clientConfig = new ClientConfiguration();
|
|
37
|
+
const organizationName = clientConfig.getOrganization();
|
|
38
|
+
const environmentName = clientConfig.getProject();
|
|
39
|
+
|
|
40
|
+
if (!clientConfig.doesConfigExists()) {
|
|
41
|
+
logger.error("Please, make sure that you are logged in");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!environmentName) {
|
|
46
|
+
logger.error("Please, make sure to set an environment");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!versionId) {
|
|
51
|
+
logger.error("Please provide a version ID");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Caught here rather than at the server so the message can name the flags. A
|
|
56
|
+
// source may carry a dimension and a measure of the same name, so which one
|
|
57
|
+
// you pass decides what gets rebuilt — there is no safe default.
|
|
58
|
+
if (scope.dimension && scope.measure) {
|
|
59
|
+
logger.error(
|
|
60
|
+
"--dimension and --measure both narrow a run to one field; pass only one",
|
|
61
|
+
);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const packageName = packagePath.replace("/", "");
|
|
66
|
+
|
|
67
|
+
const request: CreateRunRequest = { versionId };
|
|
68
|
+
if (scope.source) request.sourceName = scope.source;
|
|
69
|
+
if (scope.dimension) request.dimension = scope.dimension;
|
|
70
|
+
if (scope.measure) request.measure = scope.measure;
|
|
71
|
+
if (scope.modelFile) request.modelFilePath = scope.modelFile;
|
|
72
|
+
if (scope.includeUpstream) request.includeUpstream = true;
|
|
73
|
+
|
|
74
|
+
const targetLabel = describeScope(scope);
|
|
75
|
+
|
|
76
|
+
if (!autoConfirm) {
|
|
77
|
+
const confirmation = await getConfirmation(
|
|
78
|
+
`Trigger a run for ${packageName}/${versionId} (${targetLabel})?`,
|
|
79
|
+
);
|
|
80
|
+
if (!confirmation) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
logger.info(`Starting run for ${packageName}/${versionId} (${targetLabel})`);
|
|
86
|
+
try {
|
|
87
|
+
const response = await runsApi.createRun(
|
|
88
|
+
organizationName,
|
|
89
|
+
environmentName,
|
|
90
|
+
packageName,
|
|
91
|
+
request,
|
|
92
|
+
{
|
|
93
|
+
baseURL: controlPlane,
|
|
94
|
+
headers: clientConfig.getHeaders(),
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
console.log(prettifyJson(response.data));
|
|
99
|
+
} catch (e) {
|
|
100
|
+
logger.error("Failed to start run");
|
|
101
|
+
printOutError(e, debug);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function describeScope(scope: RerunScope): string {
|
|
106
|
+
const parts: string[] = [];
|
|
107
|
+
if (scope.source) parts.push(`source=${scope.source}`);
|
|
108
|
+
if (scope.dimension) parts.push(`dimension=${scope.dimension}`);
|
|
109
|
+
if (scope.measure) parts.push(`measure=${scope.measure}`);
|
|
110
|
+
if (scope.modelFile) parts.push(`modelFile=${scope.modelFile}`);
|
|
111
|
+
if (parts.length === 0) {
|
|
112
|
+
return "full run — all sources, indexes and pre-aggregations";
|
|
113
|
+
}
|
|
114
|
+
if (scope.includeUpstream) parts.push("includeUpstream");
|
|
115
|
+
const scoped = `scoped: ${parts.join(", ")}`;
|
|
116
|
+
// A measure names a COLUMN in a rollup table, and the whole table is what
|
|
117
|
+
// gets rebuilt — so `measure=revenue` alone understates what the caller is
|
|
118
|
+
// agreeing to. This string is the confirmation prompt, so the sibling
|
|
119
|
+
// measures have to be named as consequences before the y/n, not after.
|
|
120
|
+
return scope.measure
|
|
121
|
+
? `${scoped} — rebuilds the measure's whole rollup table, and so every measure declared at the same grain`
|
|
122
|
+
: scoped;
|
|
123
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
1
2
|
import axios from "axios";
|
|
2
3
|
import { expect } from "chai";
|
|
3
4
|
import fs from "fs";
|
|
4
5
|
import os from "os";
|
|
5
|
-
import { join } from "path";
|
|
6
|
+
import { join, resolve } from "path";
|
|
6
7
|
import sinon from "sinon";
|
|
7
8
|
import * as credConstants from "../../constants";
|
|
8
9
|
import { ClientConfiguration } from "../../clientconfiguration";
|
|
9
|
-
import { login } from "./login";
|
|
10
|
+
import { isLoginInvocation, login } from "./login";
|
|
10
11
|
import { OrganizationsApi } from "../../client/api";
|
|
11
12
|
import * as loopbackOAuth from "./loopbackOAuth";
|
|
12
13
|
|
|
@@ -19,6 +20,7 @@ function createFakeIdToken(payload: Record<string, unknown>): string {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
describe("login", () => {
|
|
23
|
+
let tmpConfigDir: string;
|
|
22
24
|
let tmpConfigFilePath: string;
|
|
23
25
|
let axiosStub: sinon.SinonStub;
|
|
24
26
|
let orgPermissionsStub: sinon.SinonStub;
|
|
@@ -51,8 +53,26 @@ describe("login", () => {
|
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
beforeEach(() => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
// mkdtempSync atomically creates a private (mode 0700) directory
|
|
57
|
+
// with an unguessable suffix, sidestepping the symlink / clobber
|
|
58
|
+
// class that CodeQL flags as js/insecure-temporary-file when a
|
|
59
|
+
// fixed name is reused inside the shared OS tmpdir.
|
|
60
|
+
tmpConfigDir = fs.mkdtempSync(join(os.tmpdir(), "cred-login-spec-"));
|
|
61
|
+
tmpConfigFilePath = join(tmpConfigDir, "cred.json");
|
|
62
|
+
// Seed with a valid ConfigFile. loadConfig now throws on malformed
|
|
63
|
+
// input rather than silently resetting, so an empty `{}` is no longer
|
|
64
|
+
// an acceptable starting state.
|
|
65
|
+
fs.writeFileSync(
|
|
66
|
+
tmpConfigFilePath,
|
|
67
|
+
JSON.stringify({
|
|
68
|
+
configPerEnvironment: {
|
|
69
|
+
production: {},
|
|
70
|
+
staging: {},
|
|
71
|
+
localhost: {},
|
|
72
|
+
},
|
|
73
|
+
activeEnvironment: "production",
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
56
76
|
axiosStub = sinon.stub(axios, "request");
|
|
57
77
|
orgPermissionsStub = sinon
|
|
58
78
|
.stub(OrganizationsApi.prototype, "getOrganization")
|
|
@@ -60,7 +80,7 @@ describe("login", () => {
|
|
|
60
80
|
});
|
|
61
81
|
|
|
62
82
|
afterEach(() => {
|
|
63
|
-
fs.rmSync(
|
|
83
|
+
fs.rmSync(tmpConfigDir, { recursive: true, force: true });
|
|
64
84
|
sinon.restore();
|
|
65
85
|
});
|
|
66
86
|
|
|
@@ -159,7 +179,7 @@ describe("login", () => {
|
|
|
159
179
|
"https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
|
|
160
180
|
tmpConfigFilePath,
|
|
161
181
|
);
|
|
162
|
-
expect(orgIdStub.calledOnceWithExactly("videoamp")).to.be.true;
|
|
182
|
+
expect(orgIdStub.calledOnceWithExactly("videoamp", undefined)).to.be.true;
|
|
163
183
|
expect(loopbackStub.calledOnce).to.be.true;
|
|
164
184
|
const call = loopbackStub.getCall(0);
|
|
165
185
|
expect(call.args[0].authorizeUrl).to.contain(
|
|
@@ -190,12 +210,35 @@ describe("login", () => {
|
|
|
190
210
|
"https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
|
|
191
211
|
tmpConfigFilePath,
|
|
192
212
|
);
|
|
193
|
-
expect(orgIdStub.calledOnceWithExactly("VideoAmp")).to.be.true;
|
|
213
|
+
expect(orgIdStub.calledOnceWithExactly("VideoAmp", undefined)).to.be.true;
|
|
194
214
|
expect(loopbackStub.getCall(0).args[0].authorizeUrl).to.contain(
|
|
195
215
|
"organization=org_nMRKafeSuxOiuJeG",
|
|
196
216
|
);
|
|
197
217
|
});
|
|
198
218
|
|
|
219
|
+
it("forwards the cluster arg to getAuth0OrganizationIdForCliLogin (so -c aws triggers PKCE)", async function () {
|
|
220
|
+
this.timeout(2000);
|
|
221
|
+
const orgIdStub = sinon
|
|
222
|
+
.stub(credConstants, "getAuth0OrganizationIdForCliLogin")
|
|
223
|
+
.returns("org_nMRKafeSuxOiuJeG");
|
|
224
|
+
sinon
|
|
225
|
+
.stub(loopbackOAuth, "authorizationCodeViaLoopback")
|
|
226
|
+
.callsFake(async (opts) => {
|
|
227
|
+
opts.onListening?.();
|
|
228
|
+
return { code: "test-pkce-auth-code" };
|
|
229
|
+
});
|
|
230
|
+
axiosStub.onFirstCall().resolves(mockedAccessTokenResponse);
|
|
231
|
+
const config = new ClientConfiguration(tmpConfigFilePath);
|
|
232
|
+
config.setEnvironment("videoamp");
|
|
233
|
+
await login(
|
|
234
|
+
"videoamp",
|
|
235
|
+
"https://admin.videoamp-aws-us-east-1.credibledata.com/api/v0",
|
|
236
|
+
tmpConfigFilePath,
|
|
237
|
+
"aws",
|
|
238
|
+
);
|
|
239
|
+
expect(orgIdStub.calledOnceWithExactly("videoamp", "aws")).to.be.true;
|
|
240
|
+
});
|
|
241
|
+
|
|
199
242
|
it("should not pass Auth0 organization for unmapped org names", async function () {
|
|
200
243
|
this.timeout(2000);
|
|
201
244
|
axiosStub
|
|
@@ -211,3 +254,118 @@ describe("login", () => {
|
|
|
211
254
|
);
|
|
212
255
|
});
|
|
213
256
|
});
|
|
257
|
+
|
|
258
|
+
describe("isLoginInvocation", () => {
|
|
259
|
+
it("is true for a `cred login <org>` invocation", () => {
|
|
260
|
+
expect(isLoginInvocation(["node", "/x/index.js", "login", "myorg"])).to.be
|
|
261
|
+
.true;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("is false for other commands (they stay fail-loud on a corrupt config)", () => {
|
|
265
|
+
expect(isLoginInvocation(["node", "/x/index.js", "status"])).to.be.false;
|
|
266
|
+
expect(isLoginInvocation(["node", "/x/index.js", "publish"])).to.be.false;
|
|
267
|
+
expect(isLoginInvocation(["node", "/x/index.js"])).to.be.false;
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Drives the real binary entrypoint (src/index.ts), not recoverIfCorrupt() in
|
|
272
|
+
// isolation. index.ts reads the active environment at module load (axios
|
|
273
|
+
// baseURL + command registration), so a corrupt ~/.cred used to throw before
|
|
274
|
+
// the login action ran — dead-ending the documented recovery path. This guards
|
|
275
|
+
// that ordering: with HOME pointed at a fixture dir holding a corrupt config,
|
|
276
|
+
// `index.ts login <org>` must reset the file rather than crash with the
|
|
277
|
+
// structure error.
|
|
278
|
+
describe("entrypoint recovery for a corrupt ~/.cred (real index.ts)", () => {
|
|
279
|
+
let tmpHome: string;
|
|
280
|
+
const indexPath = resolve(__dirname, "../../index.ts");
|
|
281
|
+
const cliRoot = resolve(__dirname, "../../..");
|
|
282
|
+
|
|
283
|
+
beforeEach(() => {
|
|
284
|
+
tmpHome = fs.mkdtempSync(join(os.tmpdir(), "cred-entrypoint-"));
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
afterEach(() => {
|
|
288
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const findBackup = (): string | undefined =>
|
|
292
|
+
fs.readdirSync(tmpHome).find((f) => f.startsWith(".cred.corrupt-"));
|
|
293
|
+
|
|
294
|
+
it("backs up the corrupt file and resets instead of crashing", async function () {
|
|
295
|
+
this.timeout(60000);
|
|
296
|
+
|
|
297
|
+
// A structurally-invalid config: parses as JSON but has neither
|
|
298
|
+
// activeEnvironment nor configPerEnvironment — exactly what triggered the
|
|
299
|
+
// customer's "re-run cred login" loop.
|
|
300
|
+
const credPath = join(tmpHome, ".cred");
|
|
301
|
+
fs.writeFileSync(credPath, JSON.stringify({ garbage: true }));
|
|
302
|
+
|
|
303
|
+
const child = spawn(
|
|
304
|
+
process.execPath,
|
|
305
|
+
["-r", "ts-node/register", indexPath, "login", "testorg"],
|
|
306
|
+
{
|
|
307
|
+
cwd: cliRoot,
|
|
308
|
+
env: {
|
|
309
|
+
...process.env,
|
|
310
|
+
HOME: tmpHome,
|
|
311
|
+
USERPROFILE: tmpHome,
|
|
312
|
+
// Runtime behavior is what we're testing; skip type-checking the
|
|
313
|
+
// whole import graph so an unrelated type error can't derail it.
|
|
314
|
+
TS_NODE_TRANSPILE_ONLY: "1",
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
let output = "";
|
|
320
|
+
child.stdout.on("data", (d) => (output += d.toString()));
|
|
321
|
+
child.stderr.on("data", (d) => (output += d.toString()));
|
|
322
|
+
|
|
323
|
+
// Recovery runs synchronously at module load, before login() touches the
|
|
324
|
+
// network. It renames the corrupt file aside (.cred.corrupt-*) and THEN
|
|
325
|
+
// rewrites .cred to a valid default. Gate on the rewrite, not the backup:
|
|
326
|
+
// the backup appears first, so killing the instant it shows can catch the
|
|
327
|
+
// child in the gap before .cred is rewritten — a flaky ENOENT on the read
|
|
328
|
+
// below. resetComplete() only goes true once .cred is the valid default
|
|
329
|
+
// (which implies the backup already happened). Then stop the process
|
|
330
|
+
// (login otherwise blocks on the device-code flow).
|
|
331
|
+
const resetComplete = (): boolean => {
|
|
332
|
+
try {
|
|
333
|
+
return (
|
|
334
|
+
JSON.parse(fs.readFileSync(credPath, "utf8"))
|
|
335
|
+
.activeEnvironment === "production"
|
|
336
|
+
);
|
|
337
|
+
} catch {
|
|
338
|
+
return false; // .cred absent or mid-rewrite
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
await new Promise<void>((resolveWait) => {
|
|
342
|
+
const deadline = Date.now() + 55000;
|
|
343
|
+
const timer = setInterval(() => {
|
|
344
|
+
if (resetComplete() || Date.now() > deadline) {
|
|
345
|
+
clearInterval(timer);
|
|
346
|
+
resolveWait();
|
|
347
|
+
}
|
|
348
|
+
}, 200);
|
|
349
|
+
child.on("exit", () => {
|
|
350
|
+
clearInterval(timer);
|
|
351
|
+
resolveWait();
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
child.kill("SIGKILL");
|
|
355
|
+
|
|
356
|
+
const backup = findBackup();
|
|
357
|
+
expect(backup, "corrupt config should be backed up aside").to.be.a(
|
|
358
|
+
"string",
|
|
359
|
+
);
|
|
360
|
+
// The bad contents are preserved in the backup, not destroyed.
|
|
361
|
+
expect(
|
|
362
|
+
JSON.parse(fs.readFileSync(join(tmpHome, backup!), "utf8")),
|
|
363
|
+
).to.deep.equal({ garbage: true });
|
|
364
|
+
// ~/.cred is rewritten to a valid default the next read can load.
|
|
365
|
+
const reset = JSON.parse(fs.readFileSync(credPath, "utf8"));
|
|
366
|
+
expect(reset.activeEnvironment).to.equal("production");
|
|
367
|
+
expect(reset.configPerEnvironment).to.be.an("object");
|
|
368
|
+
// The startup read no longer dead-ends on the structure error.
|
|
369
|
+
expect(output).to.not.match(/Invalid config structure/);
|
|
370
|
+
});
|
|
371
|
+
});
|