@herodevs/cli 2.0.0-beta.13 → 2.0.0-beta.15
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 +192 -20
- package/dist/api/apollo.client.d.ts +3 -0
- package/dist/api/apollo.client.js +53 -0
- package/dist/api/ci-token.client.d.ts +26 -0
- package/dist/api/ci-token.client.js +95 -0
- package/dist/api/errors.d.ts +8 -0
- package/dist/api/errors.js +13 -0
- package/dist/api/gql-operations.d.ts +3 -0
- package/dist/api/gql-operations.js +36 -1
- package/dist/api/graphql-errors.d.ts +6 -0
- package/dist/api/graphql-errors.js +22 -0
- package/dist/api/nes.client.d.ts +1 -2
- package/dist/api/nes.client.js +31 -20
- package/dist/api/user-setup.client.d.ts +15 -0
- package/dist/api/user-setup.client.js +92 -0
- package/dist/commands/auth/login.d.ts +14 -0
- package/dist/commands/auth/login.js +225 -0
- package/dist/commands/auth/logout.d.ts +5 -0
- package/dist/commands/auth/logout.js +27 -0
- package/dist/commands/auth/provision-ci-token.d.ts +5 -0
- package/dist/commands/auth/provision-ci-token.js +62 -0
- package/dist/commands/report/committers.d.ts +11 -7
- package/dist/commands/report/committers.js +144 -76
- package/dist/commands/scan/eol.d.ts +2 -0
- package/dist/commands/scan/eol.js +34 -4
- package/dist/commands/tracker/init.d.ts +14 -0
- package/dist/commands/tracker/init.js +84 -0
- package/dist/commands/tracker/run.d.ts +15 -0
- package/dist/commands/tracker/run.js +183 -0
- package/dist/config/constants.d.ts +14 -0
- package/dist/config/constants.js +15 -0
- package/dist/config/tracker.config.d.ts +16 -0
- package/dist/config/tracker.config.js +16 -0
- package/dist/hooks/finally/finally.js +10 -4
- package/dist/hooks/init/01_initialize_amplitude.js +20 -9
- package/dist/service/analytics.svc.d.ts +10 -3
- package/dist/service/analytics.svc.js +180 -18
- package/dist/service/auth-config.svc.d.ts +5 -0
- package/dist/service/auth-config.svc.js +20 -0
- package/dist/service/auth-refresh.svc.d.ts +8 -0
- package/dist/service/auth-refresh.svc.js +45 -0
- package/dist/service/auth-token.svc.d.ts +11 -0
- package/dist/service/auth-token.svc.js +48 -0
- package/dist/service/auth.svc.d.ts +27 -0
- package/dist/service/auth.svc.js +88 -0
- package/dist/service/ci-auth.svc.d.ts +6 -0
- package/dist/service/ci-auth.svc.js +32 -0
- package/dist/service/ci-token.svc.d.ts +6 -0
- package/dist/service/ci-token.svc.js +75 -0
- package/dist/service/committers.svc.d.ts +46 -58
- package/dist/service/committers.svc.js +55 -173
- package/dist/service/jwt.svc.d.ts +1 -0
- package/dist/service/jwt.svc.js +19 -0
- package/dist/service/tracker.svc.d.ts +58 -0
- package/dist/service/tracker.svc.js +101 -0
- package/dist/types/auth.d.ts +9 -0
- package/dist/types/auth.js +1 -0
- package/dist/utils/open-in-browser.d.ts +1 -0
- package/dist/utils/open-in-browser.js +21 -0
- package/dist/utils/retry.d.ts +11 -0
- package/dist/utils/retry.js +29 -0
- package/dist/utils/strip-typename.js +2 -1
- package/package.json +38 -19
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function decodeJwtPayload(token) {
|
|
2
|
+
if (!token) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
try {
|
|
6
|
+
const parts = token.split('.');
|
|
7
|
+
if (parts.length < 2 || !parts[1]) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
11
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
return payload;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type TrackerCategoryDefinition, type TrackerConfig } from '../config/tracker.config.ts';
|
|
2
|
+
export type GitLastCommit = {
|
|
3
|
+
hash: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
author: string;
|
|
6
|
+
};
|
|
7
|
+
export type FilesStats = {
|
|
8
|
+
total: number;
|
|
9
|
+
source: number;
|
|
10
|
+
comment: number;
|
|
11
|
+
single: number;
|
|
12
|
+
block: number;
|
|
13
|
+
mixed: number;
|
|
14
|
+
empty: number;
|
|
15
|
+
todo: number;
|
|
16
|
+
blockEmpty: number;
|
|
17
|
+
[k: string]: number | string | boolean;
|
|
18
|
+
};
|
|
19
|
+
export type CategoryStatsResult = {
|
|
20
|
+
name: string;
|
|
21
|
+
totals: FilesStats;
|
|
22
|
+
errors: string[];
|
|
23
|
+
fileTypes: string[];
|
|
24
|
+
};
|
|
25
|
+
export type CategorySavedResult = {
|
|
26
|
+
timestamp: string;
|
|
27
|
+
hash: string;
|
|
28
|
+
categories: CategoryStatsResult[];
|
|
29
|
+
};
|
|
30
|
+
export declare const INITIAL_FILES_STATS: FilesStats;
|
|
31
|
+
export declare const getRootDir: (path: string) => string;
|
|
32
|
+
export declare const getConfiguration: (path: string, folderName: string, fileName: string) => TrackerConfig;
|
|
33
|
+
export declare const createTrackerConfig: (rootPath: string, config: TrackerConfig, overwrite?: boolean) => Promise<void>;
|
|
34
|
+
export declare const getFilesFromCategory: (category: TrackerCategoryDefinition, options: {
|
|
35
|
+
rootDir: string;
|
|
36
|
+
ignorePatterns?: string[];
|
|
37
|
+
}) => string[];
|
|
38
|
+
export declare const getFileStats: (path: string, options: {
|
|
39
|
+
rootDir: string;
|
|
40
|
+
}) => {
|
|
41
|
+
source: number;
|
|
42
|
+
total: number;
|
|
43
|
+
comment: number;
|
|
44
|
+
single: number;
|
|
45
|
+
block: number;
|
|
46
|
+
mixed: number;
|
|
47
|
+
empty: number;
|
|
48
|
+
todo: number;
|
|
49
|
+
blockEmpty: number;
|
|
50
|
+
path: string;
|
|
51
|
+
fileType: string;
|
|
52
|
+
error?: undefined;
|
|
53
|
+
} | {
|
|
54
|
+
path: string;
|
|
55
|
+
fileType: string;
|
|
56
|
+
error: boolean;
|
|
57
|
+
};
|
|
58
|
+
export declare const saveResults: (categoriesResult: CategoryStatsResult[], rootDir: string, outputDir: string, git: GitLastCommit) => string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { extname, join, resolve } from 'node:path';
|
|
4
|
+
import { globSync } from 'glob'; // replace with node:fs globSync as soon as v22 is minimum supported version
|
|
5
|
+
import sloc from 'sloc';
|
|
6
|
+
import { DEFAULT_TRACKER_RUN_DATA_FILE } from '../config/constants.js';
|
|
7
|
+
import { TRACKER_ROOT_FILE } from "../config/tracker.config.js";
|
|
8
|
+
export const INITIAL_FILES_STATS = {
|
|
9
|
+
total: 0,
|
|
10
|
+
block: 0,
|
|
11
|
+
blockEmpty: 0,
|
|
12
|
+
comment: 0,
|
|
13
|
+
empty: 0,
|
|
14
|
+
mixed: 0,
|
|
15
|
+
single: 0,
|
|
16
|
+
source: 0,
|
|
17
|
+
todo: 0,
|
|
18
|
+
};
|
|
19
|
+
export const getRootDir = (path) => {
|
|
20
|
+
if (existsSync(join(path, TRACKER_ROOT_FILE))) {
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
else if (path === join(path, '..')) {
|
|
24
|
+
throw new Error(`Couldn't find root directory for the project`);
|
|
25
|
+
}
|
|
26
|
+
return getRootDir(resolve(join(path, '..')));
|
|
27
|
+
};
|
|
28
|
+
export const getConfiguration = (path, folderName, fileName) => {
|
|
29
|
+
const filePath = join(path, folderName, fileName);
|
|
30
|
+
if (!existsSync(filePath)) {
|
|
31
|
+
throw new Error(`Couldn't find configuration ${fileName} file in ${path}. If you haven't, run tracker init command to create the configuration file. If you have a custom folder and configuration file, use the flags -d (directory) and -f (filename) to specify it`);
|
|
32
|
+
}
|
|
33
|
+
const stringConfiguration = readFileSync(filePath, {
|
|
34
|
+
encoding: 'utf-8',
|
|
35
|
+
});
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(stringConfiguration);
|
|
38
|
+
}
|
|
39
|
+
catch (_err) {
|
|
40
|
+
throw new Error(`A configuration file was found, but it's contents are not valid. Review your configuration file and fix any errors, or run tracker init -o to overwrite the file`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export const createTrackerConfig = async (rootPath, config, overwrite = false) => {
|
|
44
|
+
const { outputDir } = config;
|
|
45
|
+
const configDir = join(rootPath, outputDir);
|
|
46
|
+
const configFile = join(configDir, config.configFile);
|
|
47
|
+
const doesConfigFileExists = existsSync(configFile);
|
|
48
|
+
if (!existsSync(configDir)) {
|
|
49
|
+
mkdirSync(configDir);
|
|
50
|
+
}
|
|
51
|
+
if (doesConfigFileExists && !overwrite) {
|
|
52
|
+
throw new Error(`Configuration file already exists for this repo. If you want to overwrite it, run the command again with the --overwrite flag`);
|
|
53
|
+
}
|
|
54
|
+
await writeFile(join(configDir, config.configFile), JSON.stringify(config, null, 2));
|
|
55
|
+
};
|
|
56
|
+
export const getFilesFromCategory = (category, options) => {
|
|
57
|
+
const { fileTypes, includes } = category;
|
|
58
|
+
const { rootDir, ignorePatterns } = options;
|
|
59
|
+
// if no includes folder or no specific file type is set, we ignore the category
|
|
60
|
+
if (fileTypes.length === 0 || includes.length === 0) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const patterns = includes.flatMap((include) => fileTypes.map((type) => `*${include.replace('./', '')}/**/*.${type}`));
|
|
64
|
+
return globSync(patterns, {
|
|
65
|
+
cwd: rootDir,
|
|
66
|
+
ignore: ignorePatterns?.map((ignore) => `${ignore}`) ?? [],
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
export const getFileStats = (path, options) => {
|
|
70
|
+
const fileType = extname(path).replace(/\./g, '');
|
|
71
|
+
try {
|
|
72
|
+
const stats = sloc(readFileSync(join(options.rootDir, path), 'utf8'), fileType);
|
|
73
|
+
return {
|
|
74
|
+
path,
|
|
75
|
+
fileType,
|
|
76
|
+
...stats,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (_err) {
|
|
80
|
+
return {
|
|
81
|
+
path,
|
|
82
|
+
fileType,
|
|
83
|
+
error: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
export const saveResults = (categoriesResult, rootDir, outputDir, git) => {
|
|
88
|
+
const dataResults = [];
|
|
89
|
+
const dataFile = join(rootDir, outputDir, DEFAULT_TRACKER_RUN_DATA_FILE);
|
|
90
|
+
try {
|
|
91
|
+
const savedOutput = readFileSync(dataFile).toString('utf8');
|
|
92
|
+
dataResults.push(...JSON.parse(savedOutput));
|
|
93
|
+
}
|
|
94
|
+
catch (_err) { }
|
|
95
|
+
dataResults.push({
|
|
96
|
+
...git,
|
|
97
|
+
categories: categoriesResult,
|
|
98
|
+
});
|
|
99
|
+
writeFileSync(dataFile, JSON.stringify(dataResults, null, 2));
|
|
100
|
+
return `${dataFile}`;
|
|
101
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function openInBrowser(url: string): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
export function openInBrowser(url) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const escapedUrl = `"${url.replace(/"/g, '\\"')}"`;
|
|
6
|
+
const command = (() => {
|
|
7
|
+
const plat = platform();
|
|
8
|
+
if (plat === 'darwin')
|
|
9
|
+
return `open ${escapedUrl}`; // macOS
|
|
10
|
+
if (plat === 'win32')
|
|
11
|
+
return `start "" ${escapedUrl}`; // Windows
|
|
12
|
+
return `xdg-open ${escapedUrl}`; // Linux
|
|
13
|
+
})();
|
|
14
|
+
exec(command, (err) => {
|
|
15
|
+
if (err)
|
|
16
|
+
reject(new Error(`Failed to open browser: ${err.message}`));
|
|
17
|
+
else
|
|
18
|
+
resolve();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type RetryOptions = {
|
|
2
|
+
attempts: number;
|
|
3
|
+
baseDelayMs: number;
|
|
4
|
+
onRetry?: (info: {
|
|
5
|
+
attempt: number;
|
|
6
|
+
delayMs: number;
|
|
7
|
+
error: unknown;
|
|
8
|
+
}) => void;
|
|
9
|
+
finalErrorMessage?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function withRetries<T>(operation: string, fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { debugLogger } from "../service/log.svc.js";
|
|
2
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
export async function withRetries(operation, fn, options) {
|
|
4
|
+
const { attempts, baseDelayMs, onRetry, finalErrorMessage } = options;
|
|
5
|
+
let lastError;
|
|
6
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
7
|
+
try {
|
|
8
|
+
return await fn();
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
lastError = error;
|
|
12
|
+
if (attempt === attempts) {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
const delayMs = baseDelayMs * attempt;
|
|
16
|
+
if (onRetry) {
|
|
17
|
+
onRetry({ attempt, delayMs, error });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
debugLogger('Retry (%s) attempt %d/%d after %dms: %o', operation, attempt, attempts, delayMs, error);
|
|
21
|
+
}
|
|
22
|
+
await sleep(delayMs);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const message = finalErrorMessage ??
|
|
26
|
+
(lastError instanceof Error ? lastError.message : null) ??
|
|
27
|
+
'Please contact support@herodevs.com.';
|
|
28
|
+
throw new Error(message);
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herodevs/cli",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.15",
|
|
4
4
|
"author": "HeroDevs, Inc",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hd": "./bin/run.js"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"url": "https://github.com/herodevs/cli"
|
|
12
12
|
},
|
|
13
13
|
"homepage": "https://github.com/herodevs/cli",
|
|
14
|
-
"bugs": "https://github.com
|
|
14
|
+
"bugs": "https://github.com/herodevs/cli/issues",
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "shx rm -rf dist && tsc -b",
|
|
17
17
|
"ci": "biome ci",
|
|
@@ -20,15 +20,18 @@
|
|
|
20
20
|
"clean:files": "shx rm -f herodevs.**.csv herodevs.**.json herodevs.**.txt",
|
|
21
21
|
"dev": "npm run build && ./bin/dev.js",
|
|
22
22
|
"dev:debug": "npm run build && DEBUG=oclif:* ./bin/dev.js",
|
|
23
|
+
"dev:auth:local": "OAUTH_CONNECT_URL='http://localhost:6040/realms/herodevs_local/protocol/openid-connect' npm run dev auth login",
|
|
24
|
+
"dev:auth:logout": "npm run dev auth logout",
|
|
23
25
|
"format": "biome format --write",
|
|
24
26
|
"lint": "biome lint --write",
|
|
25
27
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
26
28
|
"prepare": "shx test -d dist || npm run build",
|
|
27
29
|
"prepack": "oclif manifest",
|
|
28
30
|
"pretest": "npm run lint && npm run typecheck",
|
|
29
|
-
"readme": "npm run ci:fix && npm run build && oclif readme",
|
|
30
|
-
"test": "
|
|
31
|
-
"test:
|
|
31
|
+
"readme": "npm run ci:fix && npm run build && oclif readme && sed -i '' 's|/plugin-help/blob/v|/plugin-help/blob/|; s|/plugin-update/blob/v|/plugin-update/blob/|' README.md",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest watch",
|
|
34
|
+
"test:e2e": "globstar -- node --import tsx --import ./e2e/setup/register-mock-auth.mjs --test \"e2e/**/*.test.ts\"",
|
|
32
35
|
"typecheck": "tsc --noEmit",
|
|
33
36
|
"version": "oclif manifest",
|
|
34
37
|
"postversion": "node scripts/update-install-script-version.js && git add README.md"
|
|
@@ -39,33 +42,50 @@
|
|
|
39
42
|
"herodevs cli"
|
|
40
43
|
],
|
|
41
44
|
"dependencies": {
|
|
42
|
-
"@amplitude/analytics-node": "^1.5.
|
|
43
|
-
"@apollo/client": "^
|
|
44
|
-
"@cyclonedx/cdxgen": "^
|
|
45
|
-
"@herodevs/eol-shared": "github:herodevs/eol-shared#v0.1.
|
|
45
|
+
"@amplitude/analytics-node": "^1.5.26",
|
|
46
|
+
"@apollo/client": "^4.0.9",
|
|
47
|
+
"@cyclonedx/cdxgen": "^12.1.1",
|
|
48
|
+
"@herodevs/eol-shared": "github:herodevs/eol-shared#v0.1.17",
|
|
49
|
+
"@inquirer/prompts": "^8.0.2",
|
|
50
|
+
"@napi-rs/keyring": "^1.2.0",
|
|
46
51
|
"@oclif/core": "^4.8.0",
|
|
47
52
|
"@oclif/plugin-help": "^6.2.32",
|
|
48
|
-
"@oclif/plugin-update": "^4.7.
|
|
53
|
+
"@oclif/plugin-update": "^4.7.16",
|
|
54
|
+
"@oclif/table": "^0.5.1",
|
|
55
|
+
"cli-progress": "^3.12.0",
|
|
56
|
+
"conf": "^15.1.0",
|
|
57
|
+
"date-fns": "^4.1.0",
|
|
58
|
+
"glob": "^13.0.0",
|
|
59
|
+
"graphql": "^16.11.0",
|
|
49
60
|
"node-machine-id": "^1.1.12",
|
|
50
61
|
"ora": "^9.0.0",
|
|
51
62
|
"packageurl-js": "^2.0.1",
|
|
63
|
+
"sloc": "^0.3.2",
|
|
52
64
|
"terminal-link": "^5.0.0",
|
|
53
65
|
"update-notifier": "^7.3.1"
|
|
54
66
|
},
|
|
55
67
|
"devDependencies": {
|
|
56
|
-
"@biomejs/biome": "^2.3.
|
|
57
|
-
"@oclif/test": "^4.1.
|
|
68
|
+
"@biomejs/biome": "^2.3.8",
|
|
69
|
+
"@oclif/test": "^4.1.15",
|
|
70
|
+
"@types/cli-progress": "^3.11.6",
|
|
71
|
+
"@types/debug": "^4.1.12",
|
|
58
72
|
"@types/inquirer": "^9.0.9",
|
|
59
|
-
"@types/
|
|
60
|
-
"@types/
|
|
73
|
+
"@types/mock-fs": "^4.13.4",
|
|
74
|
+
"@types/node": "^24.10.1",
|
|
75
|
+
"@types/ora": "^3.1.0",
|
|
76
|
+
"@types/sinon": "^21.0.0",
|
|
77
|
+
"@types/sloc": "^0.2.3",
|
|
78
|
+
"@types/terminal-link": "^1.1.0",
|
|
61
79
|
"@types/update-notifier": "^6.0.8",
|
|
62
80
|
"globstar": "^1.0.0",
|
|
63
|
-
"
|
|
81
|
+
"mock-fs": "^5.5.0",
|
|
82
|
+
"oclif": "^4.22.47",
|
|
64
83
|
"shx": "^0.4.0",
|
|
65
84
|
"sinon": "^21.0.0",
|
|
66
85
|
"ts-node": "^10.9.2",
|
|
67
|
-
"tsx": "^4.
|
|
68
|
-
"typescript": "^5.9.3"
|
|
86
|
+
"tsx": "^4.21.0",
|
|
87
|
+
"typescript": "^5.9.3",
|
|
88
|
+
"vitest": "^4.0.16"
|
|
69
89
|
},
|
|
70
90
|
"engines": {
|
|
71
91
|
"node": ">=20.0.0"
|
|
@@ -83,7 +103,6 @@
|
|
|
83
103
|
"commands": "./dist/commands",
|
|
84
104
|
"plugins": [
|
|
85
105
|
"@oclif/plugin-help",
|
|
86
|
-
"@oclif/plugin-plugins",
|
|
87
106
|
"@oclif/plugin-update"
|
|
88
107
|
],
|
|
89
108
|
"hooks": {
|
|
@@ -110,4 +129,4 @@
|
|
|
110
129
|
}
|
|
111
130
|
},
|
|
112
131
|
"types": "dist/index.d.ts"
|
|
113
|
-
}
|
|
132
|
+
}
|