Skip to content

Commit da41e51

Browse files
authored
feat: migrate tracker run command from 0.3.1 (#436)
1 parent f9e431f commit da41e51

8 files changed

Lines changed: 1106 additions & 2120 deletions

File tree

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ USAGE
8585
* [`hd report committers`](#hd-report-committers)
8686
* [`hd scan eol`](#hd-scan-eol)
8787
* [`hd tracker init`](#hd-tracker-init)
88+
* [`hd tracker run`](#hd-tracker-run)
8889
* [`hd update [CHANNEL]`](#hd-update-channel)
8990
* **NOTE:** Only applies to [binary installation method](#binary-installation). NPM users should use [`npm install`](#global-npm-installation) to update to the latest version.
9091

@@ -233,6 +234,31 @@ EXAMPLES
233234

234235
_See code: [src/commands/tracker/init.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.14/src/commands/tracker/init.ts)_
235236

237+
## `hd tracker run`
238+
239+
Run the tracker
240+
241+
```
242+
USAGE
243+
$ hd tracker run [-d <value>] [-f <value>]
244+
245+
FLAGS
246+
-d, --configDir=<value> [default: hd-tracker] Directory where the tracker configuration file resides
247+
-f, --configFile=<value> [default: config.json] Filename for the tracker configuration file
248+
249+
DESCRIPTION
250+
Run the tracker
251+
252+
EXAMPLES
253+
$ hd tracker run
254+
255+
$ hd tracker run -d tracker-configuration
256+
257+
$ hd tracker run -d tracker -f settings.json
258+
```
259+
260+
_See code: [src/commands/tracker/run.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.14/src/commands/tracker/run.ts)_
261+
236262
## `hd update [CHANNEL]`
237263

238264
update the hd CLI

package-lock.json

Lines changed: 352 additions & 2103 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,28 @@
5252
"@oclif/plugin-help": "^6.2.32",
5353
"@oclif/plugin-update": "^4.7.14",
5454
"@oclif/table": "^0.5.1",
55+
"cli-progress": "^3.12.0",
5556
"date-fns": "^4.1.0",
57+
"glob": "^13.0.0",
5658
"graphql": "^16.11.0",
5759
"node-machine-id": "^1.1.12",
5860
"ora": "^9.0.0",
5961
"packageurl-js": "^2.0.1",
62+
"sloc": "^0.3.2",
6063
"terminal-link": "^5.0.0",
6164
"update-notifier": "^7.3.1"
6265
},
6366
"devDependencies": {
6467
"@biomejs/biome": "^2.3.4",
6568
"@oclif/test": "^4.1.13",
69+
"@types/cli-progress": "^3.11.6",
6670
"@types/inquirer": "^9.0.9",
6771
"@types/mock-fs": "^4.13.4",
6872
"@types/node": "^24.10.0",
73+
"@types/ora": "^3.1.0",
6974
"@types/sinon": "^17.0.4",
75+
"@types/sloc": "^0.2.3",
76+
"@types/terminal-link": "^1.1.0",
7077
"@types/update-notifier": "^6.0.8",
7178
"globstar": "^1.0.0",
7279
"mock-fs": "^5.5.0",
@@ -121,4 +128,4 @@
121128
}
122129
},
123130
"types": "dist/index.d.ts"
124-
}
131+
}

src/commands/tracker/run.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { spawnSync } from 'node:child_process';
2+
import { cwd } from 'node:process';
3+
import { Command, Flags, ux } from '@oclif/core';
4+
import { Presets, SingleBar } from 'cli-progress';
5+
import ora from 'ora';
6+
import terminalLink from 'terminal-link';
7+
import { TRACKER_GIT_OUTPUT_FORMAT } from '../../config/constants.js';
8+
import { getErrorMessage, isErrnoException } from '../../service/error.svc.js';
9+
import {
10+
type CategoryStatsResult,
11+
type FilesStats,
12+
type GitLastCommit,
13+
getConfiguration,
14+
getFileStats,
15+
getFilesFromCategory,
16+
getRootDir,
17+
INITIAL_FILES_STATS,
18+
saveResults,
19+
} from '../../service/tracker.svc.js';
20+
21+
export default class Run extends Command {
22+
static override description = 'Run the tracker';
23+
static enableJsonFlag = false;
24+
static override examples = [
25+
'<%= config.bin %> <%= command.id %>',
26+
'<%= config.bin %> <%= command.id %> -d tracker-configuration',
27+
'<%= config.bin %> <%= command.id %> -d tracker -f settings.json',
28+
];
29+
30+
static override flags = {
31+
configDir: Flags.string({
32+
char: 'd',
33+
description: 'Directory where the tracker configuration file resides',
34+
default: 'hd-tracker',
35+
}),
36+
configFile: Flags.string({
37+
char: 'f',
38+
description: 'Filename for the tracker configuration file',
39+
default: 'config.json',
40+
}),
41+
};
42+
43+
public async run(): Promise<void> {
44+
const { flags } = await this.parse(Run);
45+
const { configDir, configFile } = flags;
46+
47+
try {
48+
const rootDir = getRootDir(cwd());
49+
const confSpinner = ora('Searching for configuration file').start();
50+
const { categories, ignorePatterns, outputDir } = getConfiguration(rootDir, configDir, configFile);
51+
52+
confSpinner.text = `Configuration file ${configFile} found in ${rootDir}/${configDir}`;
53+
54+
const categoriesTotal = Object.keys(categories).length;
55+
if (categoriesTotal > 0) {
56+
confSpinner.stopAndPersist({
57+
text: ux.colorize('green', `Found ${categoriesTotal} categor${categoriesTotal === 1 ? 'y' : 'ies'}`),
58+
symbol: ux.colorize('green', `\u2714`),
59+
});
60+
} else {
61+
confSpinner.stopAndPersist({
62+
text: ux.colorize('red', `No categories found, please check your configuration file`),
63+
symbol: ux.colorize('red', `\u2716`),
64+
});
65+
return;
66+
}
67+
this.log('');
68+
const results = Object.entries(categories).reduce((acc: CategoryStatsResult[], [name, category]) => {
69+
const loadingFilesSpinner = ora(`[${ux.colorize('blueBright', name)}] Getting files`).start();
70+
const fileProgress = new SingleBar(
71+
{
72+
format: `${ux.colorize('green', '{bar}')} | {value}/{total} | {name}`,
73+
clearOnComplete: false,
74+
fps: 100,
75+
hideCursor: true,
76+
},
77+
Presets.shades_grey,
78+
);
79+
80+
const fileTypes: Set<string> = new Set();
81+
const categoryFilesWithError: string[] = [];
82+
83+
const files = getFilesFromCategory(category, {
84+
rootDir,
85+
ignorePatterns,
86+
});
87+
88+
if (files.length === 0) {
89+
loadingFilesSpinner.stopAndPersist({
90+
text: ux.colorize('yellow', `[${ux.colorize('yellowBright', name)}] Found 0 files`),
91+
symbol: ux.colorize('yellowBright', `\u26A0`),
92+
});
93+
this.log(
94+
ux.colorize(
95+
'yellow',
96+
`Please check your configuration [includes] property so it matches folders in your project directory`,
97+
),
98+
);
99+
this.log('');
100+
return acc;
101+
}
102+
103+
loadingFilesSpinner.stopAndPersist({
104+
text: ux.colorize('green', `[${ux.colorize('blueBright', name)}] Found ${files.length} files`),
105+
symbol: ux.colorize('green', `\u2714`),
106+
});
107+
fileProgress.start(files.length, 1);
108+
109+
const fileResults = files.reduce((result: FilesStats, file, currentIndex, array) => {
110+
const fileStats = getFileStats(file, {
111+
rootDir,
112+
});
113+
if (currentIndex === array.length - 1) {
114+
fileProgress.update({
115+
name: ux.colorize('green', 'All files were processed successfully'),
116+
});
117+
fileProgress.stop();
118+
} else {
119+
fileProgress.increment({
120+
name: file,
121+
});
122+
}
123+
124+
if ('error' in fileStats) {
125+
categoryFilesWithError.push(file);
126+
fileProgress.increment();
127+
return result;
128+
} else {
129+
fileTypes.add(fileStats.fileType);
130+
return {
131+
total: fileStats.total + result.total,
132+
block: fileStats.block + result.block,
133+
blockEmpty: fileStats.blockEmpty + result.blockEmpty,
134+
comment: fileStats.comment + result.comment,
135+
empty: fileStats.empty + result.empty,
136+
mixed: fileStats.mixed + result.mixed,
137+
single: fileStats.single + result.single,
138+
source: fileStats.source + result.source,
139+
todo: fileStats.todo + result.todo,
140+
};
141+
}
142+
}, INITIAL_FILES_STATS);
143+
144+
this.log('');
145+
acc.push({
146+
name,
147+
totals: fileResults,
148+
errors: categoryFilesWithError,
149+
fileTypes: Array.from(fileTypes),
150+
});
151+
return acc;
152+
}, []);
153+
154+
this.log('');
155+
const spinner = ora('Saving results').start();
156+
const resultsLink = saveResults(results, rootDir, outputDir, this.fetchGitLastCommit(rootDir));
157+
spinner.stopAndPersist({
158+
text: ux.colorize('green', 'Tracker results saved!\n'),
159+
symbol: ux.colorize('green', '\u2713'),
160+
});
161+
162+
this.log(`${ux.colorize('blueBright', terminalLink(`Open Tracker Results`, `file://${resultsLink}`))}\n`);
163+
} catch (err) {
164+
if (err instanceof Error) {
165+
this.error(ux.colorize('red', err.message));
166+
}
167+
}
168+
}
169+
170+
/**
171+
* Fetches Git last commit
172+
*/
173+
private fetchGitLastCommit(rootDir?: string): GitLastCommit {
174+
const logParameters = ['log', `-1`, `--format=${TRACKER_GIT_OUTPUT_FORMAT}`, ...(rootDir ? ['--', rootDir] : [])];
175+
176+
const logProcess = spawnSync('git', logParameters, {
177+
encoding: 'utf-8',
178+
});
179+
180+
if (logProcess.error) {
181+
if (isErrnoException(logProcess.error)) {
182+
if (logProcess.error.code === 'ENOENT') {
183+
this.error('Git command not found. Please ensure git is installed and available in your PATH.');
184+
}
185+
this.error(`Git command failed: ${getErrorMessage(logProcess.error)}`);
186+
}
187+
this.error(`Git command failed: ${getErrorMessage(logProcess.error)}`);
188+
}
189+
190+
if (logProcess.status !== 0) {
191+
this.error(`Git command failed with status ${logProcess.status}: ${logProcess.stderr}`);
192+
}
193+
194+
if (!logProcess.stdout) {
195+
return {
196+
hash: '',
197+
timestamp: '',
198+
author: '',
199+
};
200+
}
201+
202+
return logProcess.stdout
203+
.split('\n')
204+
.filter(Boolean)
205+
.reduce(
206+
(_acc, curr) => {
207+
const [hash, author, timestamp] = curr.replace(/^"(.*)"$/, '$1').split('|');
208+
return {
209+
timestamp,
210+
hash,
211+
author,
212+
};
213+
},
214+
{
215+
hash: '',
216+
timestamp: '',
217+
author: '',
218+
},
219+
);
220+
}
221+
}

src/config/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ const toBoolean = (value: string | undefined): boolean | undefined => {
1818
return undefined;
1919
};
2020

21+
// Trackers - Constants
22+
export const DEFAULT_TRACKER_RUN_DATA_FILE = 'data.json';
23+
export const TRACKER_GIT_OUTPUT_FORMAT = `"${['%H', '%an', '%ad'].join('|')}"`;
24+
2125
let concurrentPageRequests = CONCURRENT_PAGE_REQUESTS;
2226
const parsed = Number.parseInt(process.env.CONCURRENT_PAGE_REQUESTS ?? '0', 10);
2327
if (parsed > 0) {

src/config/tracker.config.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ export const TRACKER_DEFAULT_CONFIG: TrackerConfig = {
1919
legacy: {
2020
fileTypes: ['js', 'ts', 'html', 'css', 'scss', 'less'],
2121
includes: ['./legacy'],
22-
jsTsPairs: 'js',
2322
},
2423
modern: {
2524
fileTypes: ['ts', 'html', 'css', 'scss', 'less'],
2625
includes: ['./modern'],
27-
jsTsPairs: 'ts',
2826
},
2927
},
30-
ignorePatterns: ['node_modules'],
28+
ignorePatterns: ['**/node_modules/**'],
3129
outputDir: 'hd-tracker',
3230
configFile: 'config.json',
3331
};

0 commit comments

Comments
 (0)