Skip to content

Commit e7b6146

Browse files
authored
docstat quiet option (#58812)
1 parent 53f5acf commit e7b6146

File tree

5 files changed

+130
-130
lines changed

5 files changed

+130
-130
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"debug": "cross-env NODE_ENV=development ENABLED_LANGUAGES=en nodemon --inspect src/frame/server.ts",
3131
"delete-orphan-translation-files": "tsx src/workflows/delete-orphan-translation-files.ts",
3232
"docsaudit": "tsx src/metrics/scripts/docsaudit.ts",
33-
"docstat": "tsx src/metrics/scripts/docstat.ts",
33+
"docstat": "tsx --disable-warning=DEP0190 src/metrics/scripts/docstat.ts",
3434
"deleted-assets-pr-comment": "tsx src/assets/scripts/deleted-assets-pr-comment.ts",
3535
"deleted-features-pr-comment": "tsx src/data-directory/scripts/deleted-features-pr-comment.ts",
3636
"deprecate-ghes": "tsx src/ghes-releases/scripts/deprecate/index.ts",

src/metrics/README.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,66 @@ CLI tools to fetch data from the Kusto API.
1111
1. Enter your `<username>@githubazure.com` credentials.
1212
* These will get cached for future logins.
1313
1. At the prompt in Terminal asking which subscription you want to use, just press Enter to choose the default.
14-
1. Open or create an `.env` file in the root directory of your checkout (this file is already in `.gitignore`).
15-
1. Add the `KUSTO_CLUSTER` and `KUSTO_DATABASE` values to the `.env`.
14+
1. Open or create an `.env` file in the root directory of your checkout (this file is already in `.gitignore` so it won't be tracked by Git).
15+
1. Add the `KUSTO_CLUSTER` and `KUSTO_DATABASE` values to the `.env` (_these values are pinned in slack_):
1616
```
1717
KUSTO_CLUSTER='<value>'
1818
KUSTO_DATABASE='<value>'
19-
```
19+
```
20+
21+
## docstat usage
22+
23+
Run `npm run docstat -- <URL>` on any GitHub Docs URL to gather a set of default metrics about it, including 30d views, users, view duration, bounces, helpfulness score, and exits to support.
24+
25+
Notes:
26+
* If the URL doesn't include a version, `docstat` will return data that includes **all versions** (so FPT, Cloud, Server, etc.).
27+
* If you want data for FPT only, pass the `--fptOnly` option.
28+
* `docstat` only accepts URLs with an `en` language code or no language code, and it only fetches English data.
29+
30+
To see all the options:
31+
```
32+
npm run docstat -- --help
33+
```
34+
You can combine options like this:
35+
```
36+
npm run docstat -- https://docs.github.com/copilot/tutorials/modernize-legacy-code --compare --range 60
37+
```
38+
Use `--redirects` to include `redirect_from` frontmatter paths in the queries (this is helpful if the article may have moved recently):
39+
```
40+
npm run docstat -- https://docs.github.com/copilot/tutorials/modernize-legacy-code --redirects
41+
```
42+
Use the `--json` (or `-j`) option to output JSON:
43+
```
44+
npm run docstat -- https://docs.github.com/copilot/tutorials/modernize-legacy-code --json
45+
```
46+
If you want to pass the results of the JSON to `jq`, you need to use `silent` mode:
47+
```
48+
npm run --silent docstat -- https://docs.github.com/copilot/tutorials/modernize-legacy-code --json | jq .data.users
49+
```
50+
51+
## docsaudit usage
52+
53+
Run `npm run docsaudit` on a top-level content directory to gather data about its files—including title, path, versions, 30d views, and 30d users—and output it to a CSV file.
54+
55+
To see all the options:
56+
```
57+
npm run docsaudit -- --help
58+
```
59+
Run the script on any top-level content directory:
60+
```
61+
npm run docsaudit -- <content directory name>
62+
```
63+
For example:
64+
```
65+
npm run docsaudit -- actions
66+
```
67+
68+
## Future development
69+
70+
Applies to all scripts:
71+
72+
* The date range option only accepts a start date (via `-r <number>`, where the number means "`<number>` days ago"). The end date will always be the current date.
73+
* In the future, we can add an option to set a custom end date.
74+
75+
* The only Kusto queries available are hardcoded in the `kusto/queries` directory.
76+
* In the future, we can hardcode more queries, add the ability to send custom queries, or perhaps create pre-defined sets of queries.

src/metrics/lib/kusto-client.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import {
44
KustoResultTable,
55
} from 'azure-kusto-data'
66

7-
import dotenv from 'dotenv'
8-
9-
dotenv.config()
7+
import 'dotenv/config'
108

119
if (!(process.env.KUSTO_CLUSTER || process.env.KUSTO_DATABASE)) {
1210
console.error(`Add KUSTO_CLUSTER and KUSTO_DATABASE to your .env file`)

src/metrics/scripts/README.md

Lines changed: 0 additions & 102 deletions
This file was deleted.

src/metrics/scripts/docstat.ts

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ interface CliOptions {
4242
defaultToAll?: boolean
4343
showDocset?: boolean
4444
allVersions?: boolean
45+
quiet?: boolean
4546
}
4647

4748
interface JsonOutput {
@@ -103,10 +104,16 @@ program
103104
'Get data for free-pro-team@latest only (default: all versions if URL is versionless)',
104105
)
105106
.option('--verbose', 'Display Kusto queries being executed')
107+
.option('-q, --quiet', 'Suppress all output except results (automatically enabled with --json)')
106108
.parse(process.argv)
107109

108110
const options = program.opts<CliOptions>()
109111

112+
// Auto-enable quiet mode when JSON output is requested
113+
if (options.json) {
114+
options.quiet = true
115+
}
116+
110117
// If specific options are not provided, default to all
111118
options.defaultToAll = !(
112119
options.views ||
@@ -141,26 +148,34 @@ const usingFptOnly = !!options.fptOnly
141148
if (version === FREE_PRO_TEAM) {
142149
if (usingFptOnly) {
143150
// User explicitly wants only free-pro-team@latest
144-
console.log(
145-
'\nFetching data for free-pro-team@latest only. To get all versions, omit the --fptOnly flag.\n',
146-
)
151+
if (!options.quiet) {
152+
console.log(
153+
'\nFetching data for free-pro-team@latest only. To get all versions, omit the --fptOnly flag.\n',
154+
)
155+
}
147156
} else {
148157
// Default: all versions
149158
version = null
150-
console.log(
151-
'\nFetching data for all versions (no version specified in URL). To get only free-pro-team@latest, pass "--fptOnly".\n',
152-
)
159+
if (!options.quiet) {
160+
console.log(
161+
'\nFetching data for all versions (no version specified in URL). To get only free-pro-team@latest, pass "--fptOnly".\n',
162+
)
163+
}
153164
}
154165
} else {
155166
// Version is specified in the URL (e.g. enterprise-server@)
156-
console.log(
157-
`\nFetching data for version "${version}" as specified in the URL. To get data for all versions, remove the version segment from the URL.\n`,
158-
)
159-
if (usingFptOnly) {
167+
if (!options.quiet) {
160168
console.log(
161-
`You specified a version in the URL (${version}), but also passed --fptOnly. Only the version in the URL will be used.\n`,
169+
`\nFetching data for version "${version}" as specified in the URL. To get data for all versions, remove the version segment from the URL.\n`,
162170
)
163171
}
172+
if (usingFptOnly) {
173+
if (!options.quiet) {
174+
console.log(
175+
`You specified a version in the URL (${version}), but also passed --fptOnly. Only the version in the URL will be used.\n`,
176+
)
177+
}
178+
}
164179
// Always use the version from the URL
165180
}
166181

@@ -194,21 +209,22 @@ const queryPaths = [cleanPath].concat(redirects)
194209
const dates: DateRange = getDates(options.range)
195210

196211
async function main(): Promise<void> {
197-
const spinner = ora('Connecting to Kusto...').start()
212+
const spinner = options.quiet ? null : ora('Connecting to Kusto...').start()
198213

199214
try {
200215
const client = getKustoClient()
201216

202217
if (!client) {
203-
spinner.fail('Failed to connect to Kusto')
218+
if (spinner) spinner.fail('Failed to connect to Kusto')
219+
else if (!options.quiet) console.error('Failed to connect to Kusto')
204220
process.exit(1)
205221
}
206222

207-
spinner.text = 'Connected! Querying Kusto...'
223+
if (spinner) spinner.text = 'Connected! Querying Kusto...'
208224

209225
// Only show docset stats if option is passed AND if the given path is not already a docset.
210226
options.showDocset = !(cleanPath === docsetPath) && options.compare
211-
if (options.compare && cleanPath === docsetPath) {
227+
if (options.compare && cleanPath === docsetPath && !options.quiet) {
212228
console.log(`\n\nSkipping comparison, since '${cleanPath}' is already a docset.\n`)
213229
}
214230

@@ -272,7 +288,7 @@ async function main(): Promise<void> {
272288
: undefined,
273289
])
274290

275-
spinner.succeed('Data retrieved successfully!\n')
291+
if (spinner) spinner.succeed('Data retrieved successfully!\n')
276292

277293
// Output JSON and exit
278294
if (options.json) {
@@ -399,17 +415,48 @@ async function main(): Promise<void> {
399415

400416
console.log(green('-------------------------------------------'))
401417
} catch (error) {
402-
spinner.fail('Error getting data')
403-
console.error(red('Error details:'))
404-
console.error(error)
418+
if (spinner) spinner.fail('Error getting data')
419+
420+
if (options.json) {
421+
// Output error in JSON format for consistent parsing
422+
console.log(
423+
JSON.stringify(
424+
{
425+
error: true,
426+
message: 'Error getting data',
427+
details: error instanceof Error ? error.message : String(error),
428+
},
429+
null,
430+
2,
431+
),
432+
)
433+
} else {
434+
console.error(red('Error details:'))
435+
console.error(error)
436+
}
405437
}
406438
}
407439

408440
try {
409441
await main()
410442
} catch (error) {
411-
console.error(red('Unexpected error:'))
412-
console.error(error)
443+
if (options.json) {
444+
// Output error in JSON format for consistent parsing
445+
console.log(
446+
JSON.stringify(
447+
{
448+
error: true,
449+
message: 'Unexpected error',
450+
details: error instanceof Error ? error.message : String(error),
451+
},
452+
null,
453+
2,
454+
),
455+
)
456+
} else {
457+
console.error(red('Unexpected error:'))
458+
console.error(error)
459+
}
413460
process.exit(1)
414461
}
415462

0 commit comments

Comments
 (0)