@zeroheight/adoption-cli 0.4.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +49 -10
- package/dist/cli.js +6 -4
- package/dist/commands/monitor-repo.d.ts +9 -0
- package/dist/commands/monitor-repo.js +36 -0
- package/dist/commands/monitor-repo.utils.d.ts +31 -0
- package/dist/commands/monitor-repo.utils.js +81 -0
- package/dist/commands/track-package.d.ts +5 -1
- package/dist/commands/track-package.js +15 -5
- package/dist/commands/track-package.utils.d.ts +8 -14
- package/dist/commands/track-package.utils.js +24 -15
- package/dist/common/api.d.ts +30 -2
- package/dist/common/api.js +13 -1
- package/dist/common/types/package-file.d.ts +8 -0
- package/dist/common/types/package-file.js +1 -0
- package/dist/components/analyze/analyze.js +16 -3
- package/dist/components/auth/auth.js +16 -2
- package/dist/components/auth/no-credentials-onboarding.d.ts +2 -1
- package/dist/components/auth/no-credentials-onboarding.js +13 -3
- package/dist/components/monitor-repo/monitor-repo.d.ts +2 -0
- package/dist/components/monitor-repo/monitor-repo.js +9 -0
- package/dist/components/monitor-repo/non-interactive-monitor-repo.d.ts +6 -0
- package/dist/components/monitor-repo/non-interactive-monitor-repo.js +112 -0
- package/dist/components/track-package/non-interactive-track-package.d.ts +2 -0
- package/dist/components/track-package/non-interactive-track-package.js +113 -0
- package/dist/components/track-package/track-package.js +119 -11
- package/dist/lockfile-parsers/lock-parser.d.ts +9 -0
- package/dist/lockfile-parsers/lock-parser.js +5 -0
- package/dist/lockfile-parsers/npm-lock-parser.d.ts +6 -0
- package/dist/lockfile-parsers/npm-lock-parser.js +54 -0
- package/dist/lockfile-parsers/pnpm-lock-parser.d.ts +6 -0
- package/dist/lockfile-parsers/pnpm-lock-parser.js +45 -0
- package/dist/lockfile-parsers/yarn-lock-parser.d.ts +6 -0
- package/dist/lockfile-parsers/yarn-lock-parser.js +70 -0
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/1.2.0) - 17th October 2024
|
|
4
|
+
- Support scoped packages in Yarn lockfiles
|
|
5
|
+
|
|
6
|
+
## [1.1.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/1.1.0) - 17th October 2024
|
|
7
|
+
- Validate authentication credentials on set up
|
|
8
|
+
|
|
9
|
+
## [1.0.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/1.0.0) - 16th October 2024
|
|
10
|
+
|
|
11
|
+
New! Package adoption can now be tracked from the CLI
|
|
12
|
+
|
|
13
|
+
- Keep track of the latest version of your packages
|
|
14
|
+
- Understand which version of packages your repositories are using
|
|
15
|
+
|
|
16
|
+
## [0.4.5](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.4.5) - 23rd September 2024
|
|
17
|
+
|
|
18
|
+
- Fix URL generated when using a token associated with a workspaces account
|
|
19
|
+
|
|
3
20
|
## [0.4.4](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.4.4) - 17th September 2024
|
|
4
21
|
|
|
5
22
|
- Bug fixes
|
|
@@ -36,32 +53,42 @@
|
|
|
36
53
|
- Stop the flow breaking due to parsing errors
|
|
37
54
|
|
|
38
55
|
## [0.2.12](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.12) - 24th July 2024
|
|
56
|
+
|
|
39
57
|
- Update release notes
|
|
40
58
|
|
|
41
59
|
## [0.2.11](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.11) - 24th July 2024
|
|
60
|
+
|
|
42
61
|
- Add accessible release notes
|
|
43
62
|
|
|
44
63
|
## [0.2.10](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.10) - 15th July 2024
|
|
64
|
+
|
|
45
65
|
- Add ability to reset authentication credentials
|
|
46
66
|
|
|
47
67
|
## [0.2.9](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.9) - 20th June 2024
|
|
68
|
+
|
|
48
69
|
- Update documentation links
|
|
49
70
|
|
|
50
71
|
## [0.2.8](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.8) - 19th June 2024
|
|
72
|
+
|
|
51
73
|
- Bug fixes
|
|
52
74
|
|
|
53
75
|
## [0.2.7](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.7) - 19th June 2024
|
|
76
|
+
|
|
54
77
|
- Bug fixes
|
|
55
78
|
|
|
56
79
|
## [0.2.6](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.6) - 19th June 2024
|
|
80
|
+
|
|
57
81
|
- Bug fixes
|
|
58
82
|
|
|
59
83
|
## [0.2.5](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.5) - 14th June 2024
|
|
84
|
+
|
|
60
85
|
- Add more support for TypeScript files
|
|
61
86
|
- Allow `--ignore` to be passed as an option
|
|
62
87
|
|
|
63
88
|
## [0.2.0](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.2.0) - 10th June 2024
|
|
89
|
+
|
|
64
90
|
- Add ability to define name of repository being analyzed
|
|
65
91
|
|
|
66
92
|
## [0.1.1](https://www.npmjs.com/package/@zeroheight/adoption-cli/v/0.1.1) - 3rd June 2024
|
|
93
|
+
|
|
67
94
|
- Initial package set up
|
package/README.md
CHANGED
|
@@ -12,6 +12,29 @@ npm i @zeroheight/adoption-cli
|
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
|
+
### Authentication
|
|
16
|
+
|
|
17
|
+
#### Interactive mode
|
|
18
|
+
|
|
19
|
+
When running a command, you will be prompted to authenticate. This will save the Client ID and Access Token to your local machine.
|
|
20
|
+
|
|
21
|
+
Alternatively, you can authenticate by running the following command:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
zh-adoption auth
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
#### Non-interactive mode
|
|
28
|
+
|
|
29
|
+
When running a command with the `--interactive false` flag, you will need to provide the Client ID and Access Token as environment variables.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export ZEROHEIGHT_CLIENT_ID="your-client-id"
|
|
33
|
+
export ZEROHEIGHT_ACCESS_TOKEN="your-access-token"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
15
38
|
### Component usage analysis
|
|
16
39
|
|
|
17
40
|
In the repository in which you wish to analyze the component usage, run the following command:
|
|
@@ -54,28 +77,44 @@ Pass in `false` to disable the interactive mode e.g. when running in a CI enviro
|
|
|
54
77
|
zh-adoption analyze --interactive false -r "My Repo"
|
|
55
78
|
```
|
|
56
79
|
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Monitor repo
|
|
57
83
|
|
|
84
|
+
In the repository in which you wish to monitor the package versions being used, run the following command:
|
|
58
85
|
|
|
59
|
-
|
|
86
|
+
```bash
|
|
87
|
+
zh-adoption monitor-repo
|
|
88
|
+
```
|
|
60
89
|
|
|
61
|
-
|
|
62
|
-
#### Interactive mode
|
|
90
|
+
#### Options
|
|
63
91
|
|
|
64
|
-
|
|
92
|
+
`-d` / `--dir`
|
|
65
93
|
|
|
66
|
-
|
|
94
|
+
Provide a path to the specific folder to find package.json and lock file. This option can be passed through multiple times. If not included all folders with package.json and lockfile will be uploaded to zeroheight.
|
|
67
95
|
|
|
96
|
+
```bash
|
|
97
|
+
zh-adoption monitor-repo -d ./webApp
|
|
68
98
|
```
|
|
69
|
-
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Track package
|
|
103
|
+
|
|
104
|
+
In the repository containing the packages you wish to monitor the version of, run the following command:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
zh-adoption track-package
|
|
70
108
|
```
|
|
71
109
|
|
|
72
|
-
####
|
|
110
|
+
#### Options
|
|
73
111
|
|
|
74
|
-
|
|
112
|
+
`-in` / `--interactive`
|
|
113
|
+
|
|
114
|
+
Pass in `false` to disable the interactive mode e.g. when running in a CI environment.
|
|
75
115
|
|
|
76
116
|
```bash
|
|
77
|
-
|
|
78
|
-
export ZEROHEIGHT_ACCESS_TOKEN="your-access-token"
|
|
117
|
+
zh-adoption track-package --interactive false
|
|
79
118
|
```
|
|
80
119
|
|
|
81
120
|
---
|
package/dist/cli.js
CHANGED
|
@@ -5,17 +5,19 @@ import { render } from "ink-render-string";
|
|
|
5
5
|
import HelpInfo from "./components/help-info.js";
|
|
6
6
|
import { analyzeCommand } from "./commands/analyze.js";
|
|
7
7
|
import { authCommand } from "./commands/auth.js";
|
|
8
|
-
|
|
8
|
+
import { monitorRepoCommand } from "./commands/monitor-repo.js";
|
|
9
|
+
import { trackPackageCommand } from "./commands/track-package.js";
|
|
9
10
|
const program = new Command();
|
|
10
11
|
const { output, cleanup } = render(React.createElement(HelpInfo, null));
|
|
11
12
|
program
|
|
12
13
|
.name("zh-adoption")
|
|
13
14
|
.description("CLI for measuring design system usage usage in your products")
|
|
14
|
-
.version("
|
|
15
|
+
.version("1.2.0")
|
|
15
16
|
.addHelpText("before", output)
|
|
16
17
|
.addCommand(analyzeCommand())
|
|
17
|
-
.addCommand(authCommand())
|
|
18
|
-
|
|
18
|
+
.addCommand(authCommand())
|
|
19
|
+
.addCommand(monitorRepoCommand())
|
|
20
|
+
.addCommand(trackPackageCommand());
|
|
19
21
|
cleanup();
|
|
20
22
|
// Only start parsing if run as CLI, don't start parsing during testing
|
|
21
23
|
if (process.env["NODE_ENV"] !== "test") {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { RenderOptions } from "ink";
|
|
3
|
+
interface MonitorRepoOptions {
|
|
4
|
+
interactive: boolean;
|
|
5
|
+
dir?: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function monitorRepoAction(options: MonitorRepoOptions, _renderOptions?: RenderOptions): Promise<void>;
|
|
8
|
+
export declare function monitorRepoCommand(): Command;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
|
+
import { render } from "ink";
|
|
4
|
+
// import yn from "yn";
|
|
5
|
+
import NonInteractiveMonitorRepo from "../components/monitor-repo/non-interactive-monitor-repo.js";
|
|
6
|
+
export async function monitorRepoAction(options, _renderOptions) {
|
|
7
|
+
// if (options.interactive) {
|
|
8
|
+
// render(<MonitorRepo />, renderOptions);
|
|
9
|
+
// } else {
|
|
10
|
+
render(React.createElement(NonInteractiveMonitorRepo, { packageDirs: options.dir }));
|
|
11
|
+
// }
|
|
12
|
+
}
|
|
13
|
+
export function monitorRepoCommand() {
|
|
14
|
+
const command = new Command();
|
|
15
|
+
return (command
|
|
16
|
+
.command("monitor-repo")
|
|
17
|
+
.description("Monitor package usage in a repository")
|
|
18
|
+
// .addOption(
|
|
19
|
+
// new Option(
|
|
20
|
+
// "-in, --interactive [boolean]",
|
|
21
|
+
// "disable to skip manual input (useful when running in CI)"
|
|
22
|
+
// )
|
|
23
|
+
// .default(true)
|
|
24
|
+
// .argParser((value) => yn(value))
|
|
25
|
+
// )
|
|
26
|
+
.addOption(new Option("-d, --dir <path...>", "use package directory to find package.json and lockfile"))
|
|
27
|
+
.action(async (options) => {
|
|
28
|
+
try {
|
|
29
|
+
await monitorRepoAction(options);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
console.error(e);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import NPMLockParser from "../lockfile-parsers/npm-lock-parser.js";
|
|
2
|
+
import PNPMLockParser from "../lockfile-parsers/pnpm-lock-parser.js";
|
|
3
|
+
import YarnLockParser from "../lockfile-parsers/yarn-lock-parser.js";
|
|
4
|
+
/**
|
|
5
|
+
* Group a list of paths by the directory
|
|
6
|
+
*/
|
|
7
|
+
export declare function groupByBasename(paths: string[]): Record<string, string[]>;
|
|
8
|
+
/**
|
|
9
|
+
* Retrieve name and version from a package.json filepath
|
|
10
|
+
*/
|
|
11
|
+
export declare function getPackageMeta(packageFile: string): Promise<{
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function parseLockfile(lockfilePath: string): Promise<NPMLockParser | PNPMLockParser | YarnLockParser>;
|
|
16
|
+
/**
|
|
17
|
+
* Find the lock file based on known lock file names
|
|
18
|
+
* @param subpath - provide to search inside subpath instead of working directory
|
|
19
|
+
*/
|
|
20
|
+
export declare function findLockfiles(subpath?: string): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Get the lockfile parser so name, version and packages can be extracted from the lockfile
|
|
23
|
+
*
|
|
24
|
+
* @param filename the lockfile name
|
|
25
|
+
* @returns the parser to use or null if unrecognized
|
|
26
|
+
*/
|
|
27
|
+
export declare function getLockParserForFile(filename: string): typeof NPMLockParser | typeof PNPMLockParser | typeof YarnLockParser | null;
|
|
28
|
+
/**
|
|
29
|
+
* Get the type of lockfile given the name
|
|
30
|
+
*/
|
|
31
|
+
export declare function getParserTypeFromFileName(filename: string): "yarn" | "npm" | "pnpm" | null;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { basename, dirname, join as joinPath } from "path";
|
|
3
|
+
import { findFiles } from "./analyze.utils.js";
|
|
4
|
+
import NPMLockParser from "../lockfile-parsers/npm-lock-parser.js";
|
|
5
|
+
import PNPMLockParser from "../lockfile-parsers/pnpm-lock-parser.js";
|
|
6
|
+
import YarnLockParser from "../lockfile-parsers/yarn-lock-parser.js";
|
|
7
|
+
/**
|
|
8
|
+
* Group a list of paths by the directory
|
|
9
|
+
*/
|
|
10
|
+
export function groupByBasename(paths) {
|
|
11
|
+
return paths
|
|
12
|
+
.map((p) => {
|
|
13
|
+
return { base: dirname(p), filename: basename(p) };
|
|
14
|
+
})
|
|
15
|
+
.reduce((acc, pathObj) => {
|
|
16
|
+
acc[pathObj.base] = [...(acc[pathObj.base] ?? []), pathObj.filename];
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Retrieve name and version from a package.json filepath
|
|
22
|
+
*/
|
|
23
|
+
export async function getPackageMeta(packageFile) {
|
|
24
|
+
const packageContents = await readFile(packageFile, "utf-8");
|
|
25
|
+
const packageData = JSON.parse(packageContents);
|
|
26
|
+
return { name: packageData.name, version: packageData.version };
|
|
27
|
+
}
|
|
28
|
+
export async function parseLockfile(lockfilePath) {
|
|
29
|
+
const lockfileName = basename(lockfilePath);
|
|
30
|
+
const ParserClass = getLockParserForFile(lockfileName);
|
|
31
|
+
if (!ParserClass) {
|
|
32
|
+
throw new Error(`Can\'t find parser for ${lockfileName}`);
|
|
33
|
+
}
|
|
34
|
+
const fileContents = await readFile(lockfilePath, "utf-8");
|
|
35
|
+
return new ParserClass(fileContents);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Find the lock file based on known lock file names
|
|
39
|
+
* @param subpath - provide to search inside subpath instead of working directory
|
|
40
|
+
*/
|
|
41
|
+
export async function findLockfiles(subpath) {
|
|
42
|
+
const workingDir = joinPath(process.cwd(), subpath ?? "");
|
|
43
|
+
return findFiles(workingDir, "**/**/{package-lock.json,shrinkwrap.yaml,pnpm-lock.yaml,yarn.lock}", "");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the lockfile parser so name, version and packages can be extracted from the lockfile
|
|
47
|
+
*
|
|
48
|
+
* @param filename the lockfile name
|
|
49
|
+
* @returns the parser to use or null if unrecognized
|
|
50
|
+
*/
|
|
51
|
+
export function getLockParserForFile(filename) {
|
|
52
|
+
switch (filename) {
|
|
53
|
+
case "yarn.lock":
|
|
54
|
+
return YarnLockParser;
|
|
55
|
+
case "package-lock.json":
|
|
56
|
+
return NPMLockParser;
|
|
57
|
+
case "pnpm-lock.yaml":
|
|
58
|
+
return PNPMLockParser;
|
|
59
|
+
case "shrinkwrap.yaml":
|
|
60
|
+
return PNPMLockParser;
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the type of lockfile given the name
|
|
67
|
+
*/
|
|
68
|
+
export function getParserTypeFromFileName(filename) {
|
|
69
|
+
switch (filename) {
|
|
70
|
+
case "yarn.lock":
|
|
71
|
+
return "yarn";
|
|
72
|
+
case "package-lock.json":
|
|
73
|
+
return "npm";
|
|
74
|
+
case "pnpm-lock.yaml":
|
|
75
|
+
return "pnpm";
|
|
76
|
+
case "shrinkwrap.yaml":
|
|
77
|
+
return "pnpm";
|
|
78
|
+
default:
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { RenderOptions } from "ink";
|
|
3
|
-
|
|
3
|
+
interface TrackPackageOptions {
|
|
4
|
+
interactive: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function trackPackageAction(options: TrackPackageOptions, renderOptions?: RenderOptions): Promise<void>;
|
|
4
7
|
export declare function trackPackageCommand(): Command;
|
|
8
|
+
export {};
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Command } from "commander";
|
|
2
|
+
import { Command, Option } from "commander";
|
|
3
3
|
import { render } from "ink";
|
|
4
|
+
import yn from "yn";
|
|
5
|
+
import NonInteractiveTrackPackage from "../components/track-package/non-interactive-track-package.js";
|
|
4
6
|
import TrackPackage from "../components/track-package/track-package.js";
|
|
5
|
-
export async function trackPackageAction(renderOptions) {
|
|
6
|
-
|
|
7
|
+
export async function trackPackageAction(options, renderOptions) {
|
|
8
|
+
if (options.interactive) {
|
|
9
|
+
render(React.createElement(TrackPackage, null), renderOptions);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
render(React.createElement(NonInteractiveTrackPackage, null));
|
|
13
|
+
}
|
|
7
14
|
}
|
|
8
15
|
export function trackPackageCommand() {
|
|
9
16
|
const command = new Command();
|
|
10
17
|
return command
|
|
11
18
|
.command("track-package")
|
|
12
19
|
.description("Track the latest version of your package in zeroheight")
|
|
13
|
-
.
|
|
20
|
+
.addOption(new Option("-in, --interactive [boolean]", "disable to skip manual input (useful when running in CI)")
|
|
21
|
+
.default(true)
|
|
22
|
+
.argParser((value) => yn(value)))
|
|
23
|
+
.action(async (options) => {
|
|
14
24
|
try {
|
|
15
|
-
await trackPackageAction();
|
|
25
|
+
await trackPackageAction(options);
|
|
16
26
|
}
|
|
17
27
|
catch (e) {
|
|
18
28
|
console.error(e);
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
+
import { PackageFile } from "../common/types/package-file.js";
|
|
2
|
+
/**
|
|
3
|
+
* Find the package.json file
|
|
4
|
+
* @param subpath - provide to search inside subpath instead of working directory
|
|
5
|
+
*/
|
|
6
|
+
export declare function findPackageFiles(subpath?: string): Promise<string[]>;
|
|
1
7
|
export declare function getPackageInfo(): Promise<{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
version: null;
|
|
5
|
-
error: string;
|
|
6
|
-
} | {
|
|
7
|
-
name: any;
|
|
8
|
-
path: string;
|
|
9
|
-
version: any;
|
|
10
|
-
error: null;
|
|
11
|
-
} | {
|
|
12
|
-
name: null;
|
|
13
|
-
path: string;
|
|
14
|
-
version: null;
|
|
15
|
-
error: string;
|
|
8
|
+
files: PackageFile[];
|
|
9
|
+
error: string | null;
|
|
16
10
|
}>;
|
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
import { readFile } from "fs/promises";
|
|
2
|
+
import { join as joinPath } from "path";
|
|
2
3
|
import { findFiles } from "./analyze.utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* Find the package.json file
|
|
6
|
+
* @param subpath - provide to search inside subpath instead of working directory
|
|
7
|
+
*/
|
|
8
|
+
export async function findPackageFiles(subpath) {
|
|
9
|
+
const workingDir = joinPath(process.cwd(), subpath ?? "");
|
|
10
|
+
return findFiles(workingDir, "**/**/package.json", "");
|
|
11
|
+
}
|
|
3
12
|
export async function getPackageInfo() {
|
|
4
13
|
const base = process.cwd();
|
|
5
|
-
const files = await
|
|
14
|
+
const files = await findPackageFiles();
|
|
6
15
|
if (files.length === 0) {
|
|
7
16
|
return {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
version: null,
|
|
11
|
-
error: "Can't find any package files"
|
|
17
|
+
files: [],
|
|
18
|
+
error: "Can't find any package files",
|
|
12
19
|
};
|
|
13
20
|
}
|
|
14
|
-
const relativeBase = `.${files[0]?.split(base).pop()}`;
|
|
15
21
|
try {
|
|
16
|
-
const
|
|
22
|
+
const packageFiles = await Promise.all(files.map(async (file) => {
|
|
23
|
+
const fileContents = await readFile(file, "utf-8");
|
|
24
|
+
return {
|
|
25
|
+
name: JSON.parse(fileContents).name,
|
|
26
|
+
path: `.${files[0]?.split(base).pop()}`,
|
|
27
|
+
version: JSON.parse(fileContents).version,
|
|
28
|
+
};
|
|
29
|
+
}));
|
|
17
30
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
version: JSON.parse(fileContents).version,
|
|
21
|
-
error: null
|
|
31
|
+
files: packageFiles,
|
|
32
|
+
error: null,
|
|
22
33
|
};
|
|
23
34
|
}
|
|
24
35
|
catch {
|
|
25
36
|
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
version: null,
|
|
29
|
-
error: `Can't parse file ${files[0]}`
|
|
37
|
+
files: [],
|
|
38
|
+
error: `Can't parse file ${files[0]}`,
|
|
30
39
|
};
|
|
31
40
|
}
|
|
32
41
|
}
|
package/dist/common/api.d.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { RawUsageMap } from "../commands/analyze.js";
|
|
2
2
|
import { Credentials } from "./config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Open API V2 Response format
|
|
5
|
+
*/
|
|
6
|
+
interface APIResponse<T> {
|
|
7
|
+
status: string;
|
|
8
|
+
message: string;
|
|
9
|
+
data: T;
|
|
10
|
+
}
|
|
3
11
|
export declare function getZeroheightURL(): URL;
|
|
4
|
-
export declare function submitPackageDetails(name: string, path: string, version: string, credentials: Credentials): Promise<any
|
|
12
|
+
export declare function submitPackageDetails(name: string, path: string, version: string, credentials: Credentials): Promise<APIResponse<any>>;
|
|
13
|
+
export declare function submitMonitoredRepoDetails(name: string, version: string, lockfilePath: string, packages: {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
}[], credentials: Credentials): Promise<APIResponse<{
|
|
17
|
+
errors: string[];
|
|
18
|
+
}>>;
|
|
5
19
|
export declare function submitUsageData(usage: RawUsageMap, repoName: string, credentials: Credentials): Promise<any>;
|
|
6
|
-
export declare function getExistingRepoNames(credentials: Credentials): Promise<
|
|
20
|
+
export declare function getExistingRepoNames(credentials: Credentials): Promise<string[]>;
|
|
21
|
+
interface AuthTokensResponse {
|
|
22
|
+
scopes: string[];
|
|
23
|
+
email: string;
|
|
24
|
+
project: {
|
|
25
|
+
id: number;
|
|
26
|
+
name: string;
|
|
27
|
+
} | null;
|
|
28
|
+
team: {
|
|
29
|
+
id: number;
|
|
30
|
+
name: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare function getAuthDetails(credentials: Credentials): Promise<AuthTokensResponse>;
|
|
34
|
+
export {};
|
package/dist/common/api.js
CHANGED
|
@@ -13,6 +13,14 @@ export async function submitPackageDetails(name, path, version, credentials) {
|
|
|
13
13
|
latest_version: version,
|
|
14
14
|
}, credentials);
|
|
15
15
|
}
|
|
16
|
+
export async function submitMonitoredRepoDetails(name, version, lockfilePath, packages, credentials) {
|
|
17
|
+
return post("/open_api/v2/monitored_repositories", {
|
|
18
|
+
name,
|
|
19
|
+
lock_file_path: lockfilePath,
|
|
20
|
+
packages,
|
|
21
|
+
version,
|
|
22
|
+
}, credentials);
|
|
23
|
+
}
|
|
16
24
|
export async function submitUsageData(usage, repoName, credentials) {
|
|
17
25
|
return post("/open_api/v1/component_usages", {
|
|
18
26
|
repo_name: repoName,
|
|
@@ -22,6 +30,10 @@ export async function submitUsageData(usage, repoName, credentials) {
|
|
|
22
30
|
export function getExistingRepoNames(credentials) {
|
|
23
31
|
return get("/open_api/v1/component_usages/repo_names", credentials);
|
|
24
32
|
}
|
|
33
|
+
export async function getAuthDetails(credentials) {
|
|
34
|
+
const response = await get("/open_api/v2/auth_tokens", credentials);
|
|
35
|
+
return response.data;
|
|
36
|
+
}
|
|
25
37
|
async function get(path, credentials) {
|
|
26
38
|
return request(path, credentials, {
|
|
27
39
|
method: "GET",
|
|
@@ -42,7 +54,7 @@ async function request(path, credentials, init) {
|
|
|
42
54
|
const response = await fetch(url, {
|
|
43
55
|
...init,
|
|
44
56
|
headers: {
|
|
45
|
-
"X-API-CLIENT-NAME":
|
|
57
|
+
"X-API-CLIENT-NAME": "cli",
|
|
46
58
|
"X-API-CLIENT": credentials.client,
|
|
47
59
|
"X-API-KEY": credentials.token,
|
|
48
60
|
"Content-Type": "application/json",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,7 +8,7 @@ import NoCredentialsOnboarding from "../auth/no-credentials-onboarding.js";
|
|
|
8
8
|
import RepoNamePrompt from "../repo-name-prompt.js";
|
|
9
9
|
import UsageTable from "../usage-table.js";
|
|
10
10
|
import { configPath, writeConfig, readConfig, } from "../../common/config.js";
|
|
11
|
-
import { getZeroheightURL, submitUsageData } from "../../common/api.js";
|
|
11
|
+
import { getAuthDetails, getZeroheightURL, submitUsageData, } from "../../common/api.js";
|
|
12
12
|
import { calculateNumberOfComponents } from "../../commands/analyze.utils.js";
|
|
13
13
|
export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
14
14
|
const { exit } = useApp();
|
|
@@ -49,8 +49,12 @@ export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
|
49
49
|
setIsSendingData(true);
|
|
50
50
|
try {
|
|
51
51
|
await submitUsageData(result, repoName, credentials);
|
|
52
|
+
const authDetails = await getAuthDetails(credentials);
|
|
53
|
+
const projectId = authDetails.project?.id;
|
|
52
54
|
const resourceURL = getZeroheightURL();
|
|
53
|
-
resourceURL.pathname =
|
|
55
|
+
resourceURL.pathname = projectId
|
|
56
|
+
? `/project/${projectId}/adoption/`
|
|
57
|
+
: "/adoption/";
|
|
54
58
|
setResourceURL(resourceURL);
|
|
55
59
|
}
|
|
56
60
|
catch (e) {
|
|
@@ -94,6 +98,15 @@ export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
|
94
98
|
setShowContinue(true);
|
|
95
99
|
}
|
|
96
100
|
}
|
|
101
|
+
async function handleCheckCredentials(newClient, newToken) {
|
|
102
|
+
try {
|
|
103
|
+
await getAuthDetails({ token: newToken, client: newClient });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
setErrorList(["Auth credentials are not valid"]);
|
|
107
|
+
return exit();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
97
110
|
if (errorList.length > 0) {
|
|
98
111
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
99
112
|
React.createElement(Text, { color: "red" }, "Error:"),
|
|
@@ -102,7 +115,7 @@ export default function Analyze({ onAnalyzeFiles, dryRun, repoName, }) {
|
|
|
102
115
|
e))))));
|
|
103
116
|
}
|
|
104
117
|
if (!promptRepo && !credentials && !dryRun) {
|
|
105
|
-
return (React.createElement(NoCredentialsOnboarding, { configPath: configPath(), onSaveCredentials: async (persist, newClient, newToken) => {
|
|
118
|
+
return (React.createElement(NoCredentialsOnboarding, { configPath: configPath(), onCheckCredentials: handleCheckCredentials, onSaveCredentials: async (persist, newClient, newToken) => {
|
|
106
119
|
const newCredentials = { token: newToken, client: newClient };
|
|
107
120
|
setCredentials(newCredentials);
|
|
108
121
|
if (persist) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Text } from "ink";
|
|
1
|
+
import { Box, Text, useApp } from "ink";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import ConfirmInput from "../ui/confirm-input.js";
|
|
4
4
|
import NoCredentialsOnboarding from "./no-credentials-onboarding.js";
|
|
@@ -6,6 +6,7 @@ import { configPath, readConfig, writeConfig, } from "../../common/config.js";
|
|
|
6
6
|
import CredentialsAlreadyExists from "./credentials-already-exists.js";
|
|
7
7
|
import CredentialsPreview from "./credentials-preview.js";
|
|
8
8
|
import Spinner from "ink-spinner";
|
|
9
|
+
import { getAuthDetails } from "../../common/api.js";
|
|
9
10
|
var Step;
|
|
10
11
|
(function (Step) {
|
|
11
12
|
Step[Step["LOADING_CONFIG"] = 0] = "LOADING_CONFIG";
|
|
@@ -14,8 +15,10 @@ var Step;
|
|
|
14
15
|
Step[Step["DONE_WRITE_FAIL"] = 3] = "DONE_WRITE_FAIL";
|
|
15
16
|
Step[Step["DONE_WRITE_SUCCESS"] = 4] = "DONE_WRITE_SUCCESS";
|
|
16
17
|
Step[Step["DONE_NO_CHANGE"] = 5] = "DONE_NO_CHANGE";
|
|
18
|
+
Step[Step["DONE_AUTH_FAIL"] = 6] = "DONE_AUTH_FAIL";
|
|
17
19
|
})(Step || (Step = {}));
|
|
18
20
|
export default function Auth({ token, client }) {
|
|
21
|
+
const { exit } = useApp();
|
|
19
22
|
const [shouldOverwriteAnswer, setShouldOverwriteAnswer] = React.useState("");
|
|
20
23
|
const [existingConfig, setExistingConfig] = React.useState(null);
|
|
21
24
|
const [currentStep, setCurrentStep] = React.useState(Step.LOADING_CONFIG);
|
|
@@ -64,6 +67,15 @@ export default function Auth({ token, client }) {
|
|
|
64
67
|
setCurrentStep(Step.DONE_NO_CHANGE);
|
|
65
68
|
}
|
|
66
69
|
}
|
|
70
|
+
async function handleCheckCredentials(newClient, newToken) {
|
|
71
|
+
try {
|
|
72
|
+
await getAuthDetails({ token: newToken, client: newClient });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
setCurrentStep(Step.DONE_AUTH_FAIL);
|
|
76
|
+
return exit();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
67
79
|
async function handleSaveCredentials(persist, newClient, newToken) {
|
|
68
80
|
if (persist) {
|
|
69
81
|
await mergeAndWriteConfig(existingConfig, newClient, newToken);
|
|
@@ -83,7 +95,7 @@ export default function Auth({ token, client }) {
|
|
|
83
95
|
React.createElement(Text, { dimColor: true }, "(y/N)")),
|
|
84
96
|
React.createElement(ConfirmInput, { isChecked: false, value: shouldOverwriteAnswer, onChange: setShouldOverwriteAnswer, onSubmit: handleSubmitOverwrite }))));
|
|
85
97
|
case Step.PROMPT_NEW_CREDENTIALS:
|
|
86
|
-
return (React.createElement(NoCredentialsOnboarding, { onSaveCredentials: handleSaveCredentials, configPath: configPath() }));
|
|
98
|
+
return (React.createElement(NoCredentialsOnboarding, { onCheckCredentials: handleCheckCredentials, onSaveCredentials: handleSaveCredentials, configPath: configPath() }));
|
|
87
99
|
case Step.DONE_WRITE_SUCCESS:
|
|
88
100
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
89
101
|
React.createElement(Text, { color: "green" }, "Updated credentials"),
|
|
@@ -94,6 +106,8 @@ export default function Auth({ token, client }) {
|
|
|
94
106
|
existingConfig?.client && existingConfig?.token && (React.createElement(CredentialsPreview, { client: existingConfig.client, token: existingConfig.token }))));
|
|
95
107
|
case Step.DONE_WRITE_FAIL:
|
|
96
108
|
return React.createElement(Text, { color: "yellow" }, "Something went wrong, try again.");
|
|
109
|
+
case Step.DONE_AUTH_FAIL:
|
|
110
|
+
return (React.createElement(Text, { color: "red" }, "Unable to authenticate. Please re-run the command to try again."));
|
|
97
111
|
case Step.LOADING_CONFIG:
|
|
98
112
|
return (React.createElement(Text, null,
|
|
99
113
|
React.createElement(Text, { color: "green" },
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
interface NoCredentialsOnboardingProps {
|
|
3
|
+
onCheckCredentials: (client: string, token: string) => Promise<void>;
|
|
3
4
|
onSaveCredentials: (storeCredentials: boolean, client: string, token: string) => Promise<void>;
|
|
4
5
|
configPath: string;
|
|
5
6
|
}
|
|
6
|
-
export default function NoCredentialsOnboarding({ onSaveCredentials, configPath, }: NoCredentialsOnboardingProps): React.JSX.Element;
|
|
7
|
+
export default function NoCredentialsOnboarding({ onCheckCredentials, onSaveCredentials, configPath, }: NoCredentialsOnboardingProps): React.JSX.Element;
|
|
7
8
|
export {};
|