Skip to content

Commit 4717987

Browse files
authored
Merge branch 'newrelic:develop' into develop
2 parents a652e20 + d823478 commit 4717987

File tree

2,935 files changed

+287218
-60807
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,935 files changed

+287218
-60807
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,6 @@ src/images/infrastructure_screenshot_full_sonarqube-dashboard.webp
9191

9292
# Local Netlify folder
9393
.netlify
94+
95+
# Claude Code documentation (local only)
96+
.claude/

gatsby-browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { updateCookieConsentMessage } = require('./scripts/utils/updateCookieConsentMessage');
2+
require('katex/dist/katex.min.css');
23

34
export { default as onRouteUpdate } from './gatsby/on-route-update';
45

gatsby-config.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,10 @@ module.exports = {
162162
// https://github.com/mdx-js/mdx/issues/1283
163163
//
164164
// If this is addressed in MDX v2, we can safely remove this.
165-
remarkPlugins: [],
165+
remarkPlugins: [require('remark-math')],
166166
rehypePlugins: [
167-
[
168-
require('./rehype-plugins/gatsby-inline-images'),
169-
{ spacing: '0.5rem' },
170-
],
167+
require('rehype-katex'),
168+
[require('./rehype-plugins/gatsby-inline-images'), { spacing: '0.5rem' }],
171169
],
172170
gatsbyRemarkPlugins: [
173171
{

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"@emotion/styled": "^11.3.0",
1111
"@mdx-js/mdx": "2.0.0-next.8",
1212
"@mdx-js/react": "2.0.0-next.8",
13-
"@newrelic/gatsby-theme-newrelic": "9.12.4",
13+
"@newrelic/gatsby-theme-newrelic": "9.14.0",
1414
"@splitsoftware/splitio-react": "^1.2.4",
1515
"ansi-colors": "^4.1.3",
1616
"cockatiel": "^3.0.0-beta.0",
@@ -41,6 +41,7 @@
4141
"hast-util-select": "^4.0.2",
4242
"hast-util-to-mdast": "^7.1.3",
4343
"jsdom": "^16.5.0",
44+
"katex": "^0.16.11",
4445
"lodash": "^4.17.21",
4546
"markdown-link-check": "^3.12.2",
4647
"parse-link-header": "^2.0.0",
@@ -57,9 +58,11 @@
5758
"react-visibility-sensor": "^5.1.1",
5859
"rehype-add-classes": "^1.0.0",
5960
"rehype-format": "^5.0.0",
61+
"rehype-katex": "^5.0.0",
6062
"rehype-parse": "^7.0.1",
6163
"rehype-remark": "^8.0.0",
6264
"remark-gfm": "^3.0.1",
65+
"remark-math": "^3.0.1",
6366
"remark-rehype": "^8.0.0",
6467
"rss": "^1.2.2",
6568
"sequelize": "^6.21.3",
@@ -228,9 +231,11 @@
228231
"trigger-i18n-merge": "node scripts/actions/trigger-i18n-merge.js",
229232
"update-attribute-dictionary-json": "node scripts/actions/update-attribute-dictionary-json.mjs",
230233
"verify-install-page": "node scripts/verifyInstallPage.js",
234+
"mdx:freshness": "node scripts/listMdxFreshness.mjs",
231235
"verify-mdx": "node scripts/verify_mdx.js",
232236
"webdriver-desktop": "node scripts/actions/webdriver-desktop.mjs",
233-
"webdriver-mobile": "node scripts/actions/webdriver-mobile.mjs"
237+
"webdriver-mobile": "node scripts/actions/webdriver-mobile.mjs",
238+
"measure-retention": "python3 scripts/measure_content_retention.py"
234239
},
235240
"repository": {
236241
"type": "git",

scripts/actions/utils/handlers.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,42 @@ export default {
427427
identifyComponent: false,
428428
}),
429429
},
430+
p: {
431+
deserialize: deserializeComponent,
432+
serialize: (state, node) =>
433+
serializeComponent(state, node, {
434+
tagName: 'p',
435+
wrapChildren: false,
436+
identifyComponent: false,
437+
}),
438+
},
439+
ul: {
440+
deserialize: deserializeComponent,
441+
serialize: (state, node) =>
442+
serializeComponent(state, node, {
443+
tagName: 'ul',
444+
wrapChildren: false,
445+
identifyComponent: false,
446+
}),
447+
},
448+
ol: {
449+
deserialize: deserializeComponent,
450+
serialize: (state, node) =>
451+
serializeComponent(state, node, {
452+
tagName: 'ol',
453+
wrapChildren: false,
454+
identifyComponent: false,
455+
}),
456+
},
457+
li: {
458+
deserialize: deserializeComponent,
459+
serialize: (state, node) =>
460+
serializeComponent(state, node, {
461+
tagName: 'li',
462+
wrapChildren: false,
463+
identifyComponent: false,
464+
}),
465+
},
430466
var: {
431467
deserialize: (state, node) =>
432468
deserializeComponent(state, node, { type: 'mdxJsxTextElement' }),

scripts/listMdxFreshness.mjs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { execSync } from 'node:child_process';
2+
import { writeFileSync } from 'node:fs';
3+
4+
// Add or remove entries here; ALWAYS include trailing slash for directories.
5+
// Example: 'src/content/docs/experimental/' to exclude that subdir.
6+
const EXCLUDE_PREFIXES = [
7+
'src/content/docs/release-notes/',
8+
'src/content/docs/style-guide/',
9+
];
10+
11+
function isExcluded(relPath) {
12+
return EXCLUDE_PREFIXES.some(pref => relPath.startsWith(pref));
13+
}
14+
15+
function sh(cmd) {
16+
try { return execSync(cmd, { encoding: 'utf8' }).trim(); } catch { return ''; }
17+
}
18+
19+
function listMdxFiles() {
20+
console.log('Collecting tracked MDX files...');
21+
const all = sh('git ls-files src/content/docs');
22+
const tracked = all ? all.split('\n').filter(f => f.endsWith('.mdx') && !isExcluded(f)) : [];
23+
console.log(`Found ${tracked.length} tracked MDX files...`);
24+
return tracked;
25+
}
26+
27+
function lastCommitISO(path) {
28+
return sh(`git log -1 --format=%cI -- "${path}"`) || null;
29+
}
30+
31+
function daysBetween(iso) {
32+
const then = Date.parse(iso);
33+
const now = Date.now();
34+
return (now - then) / (1000 * 60 * 60 * 24);
35+
}
36+
37+
function monthsBetween(iso) {
38+
return daysBetween(iso) / 30;
39+
}
40+
41+
function main() {
42+
43+
console.log('Starting MDX tracking scan...');
44+
const files = listMdxFiles();
45+
46+
console.log('Querying last commit for each file...');
47+
const rows = [];
48+
let processed = 0;
49+
50+
for (const f of files) {
51+
const iso = lastCommitISO(f);
52+
processed++;
53+
if (processed % 500 === 0) console.log(`Processed ${processed} files...`);
54+
if (!iso) continue; // skip if no commit
55+
const ageDays = daysBetween(iso);
56+
const ageMonths = monthsBetween(iso);
57+
rows.push({ path: f, lastCommit: iso, ageDays, ageMonths });
58+
}
59+
console.log(`Collected commit data for ${rows.length} files`);
60+
61+
// Sort by age (oldest first)
62+
rows.sort((a,b) => b.ageDays - a.ageDays);
63+
console.log('Sorted files by age (descending)');
64+
65+
// Determine age bucket for each row
66+
function getAgeBucket(ageMonths) {
67+
if (ageMonths >= 24) return '24+ months';
68+
if (ageMonths >= 18) return '18-24 months';
69+
if (ageMonths >= 12) return '12-18 months';
70+
if (ageMonths >= 6) return '6-12 months';
71+
return '0-6 months';
72+
}
73+
74+
// Escape CSV field if needed (contains comma, quote, or newline)
75+
function escapeCSV(field) {
76+
if (field.includes(',') || field.includes('"') || field.includes('\n')) {
77+
return `"${field.replace(/"/g, '""')}"`;
78+
}
79+
return field;
80+
}
81+
82+
// Generate CSV output
83+
const csvLines = [];
84+
csvLines.push('Path,Last Commit,Age (months),Age (days),Age Bucket');
85+
86+
for (const r of rows) {
87+
const bucket = getAgeBucket(r.ageMonths);
88+
const line = [
89+
escapeCSV(r.path),
90+
r.lastCommit,
91+
r.ageMonths.toFixed(1),
92+
r.ageDays.toFixed(0),
93+
bucket
94+
].join(',');
95+
csvLines.push(line);
96+
}
97+
98+
const outputCSV = csvLines.join('\n');
99+
const outArg = process.argv.find(a => a.startsWith('--out='));
100+
const outPath = outArg ? outArg.split('=')[1] : 'mdx-freshness.csv';
101+
try {
102+
writeFileSync(outPath, outputCSV, 'utf8');
103+
console.log(`Wrote report to ${outPath}`);
104+
} catch (e) {
105+
console.log('Failed writing file, falling back to stdout');
106+
console.log(outputCSV);
107+
}
108+
console.log('Done');
109+
}
110+
111+
main();
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import sys
2+
import subprocess
3+
import difflib
4+
5+
def run_git_command(command):
6+
"""A helper function to run a git command and return the output."""
7+
try:
8+
result = subprocess.run(command, capture_output=True, text=True, check=True, encoding='utf-8')
9+
10+
return result.stdout.strip()
11+
except subprocess.CalledProcessError as e:
12+
print(f"Error executing command '{' '.join(command)}': {e.stderr}")
13+
sys.exit(1)
14+
except FileNotFoundError:
15+
print("Error: 'git' command not found. Is Git installed and in your PATH?")
16+
sys.exit(1)
17+
18+
def get_initial_commit(file_path):
19+
"""Finds the very first commit hash for a given file."""
20+
command = ['git', 'log', '--reverse', '--pretty=format:%H', '--', file_path]
21+
all_commits = run_git_command(command)
22+
if not all_commits:
23+
return None
24+
initial_commit = all_commits.split('\n')[0]
25+
return initial_commit
26+
27+
def get_latest_commit(file_path):
28+
"""Finds the most recent commit hash for a given file."""
29+
command = ['git', 'log', '-1', '--pretty=format:%H', '--', file_path]
30+
return run_git_command(command)
31+
32+
def get_file_content(commit_hash, file_path):
33+
"""Extracts the content of a file from a specific git commit."""
34+
command = ['git', 'show', f'{commit_hash}:{file_path}']
35+
return run_git_command(command)
36+
37+
def analyze_character_diff(original_text, new_text):
38+
"""
39+
Analyzes character-level differences and returns the percentage of
40+
unmodified characters from the original text.
41+
"""
42+
if not original_text:
43+
return 0.0, 0.0
44+
45+
unmodified_chars = 0
46+
matcher = difflib.SequenceMatcher(None, original_text, new_text, autojunk=False)
47+
48+
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
49+
if tag == 'equal':
50+
unmodified_chars += (i2 - i1)
51+
52+
total_original_chars = len(original_text)
53+
unmodified_percentage = (unmodified_chars / total_original_chars) * 100
54+
modified_percentage = 100 - unmodified_percentage
55+
56+
return modified_percentage, unmodified_percentage
57+
58+
if __name__ == "__main__":
59+
60+
if len(sys.argv) == 2:
61+
# Mode 1: Automatic detection
62+
file_path = sys.argv[1]
63+
print(f"🔎 Automatically finding commits for: {file_path}...")
64+
old_commit = get_initial_commit(file_path)
65+
new_commit = get_latest_commit(file_path)
66+
if not old_commit:
67+
print(f"Error: Could not find any commits for '{file_path}'. Does the file exist and is it tracked by Git?")
68+
sys.exit(1)
69+
70+
elif len(sys.argv) == 4:
71+
# Mode 2: Manual commit entry
72+
file_path = sys.argv[1]
73+
old_commit = sys.argv[2]
74+
new_commit = sys.argv[3]
75+
print(f"✅ Using provided commits for: {file_path}...")
76+
77+
else:
78+
print("Usage (automatic): python diff_chars.py <file_path>")
79+
print("Usage (manual): python diff_chars.py <file_path> <old_commit> <new_commit>")
80+
sys.exit(1)
81+
82+
print(f" Old commit: {old_commit[:7]}")
83+
print(f" New commit: {new_commit[:7]}")
84+
print("--------------------------------------------------")
85+
86+
original_content = get_file_content(old_commit, file_path)
87+
new_content = get_file_content(new_commit, file_path)
88+
89+
modified_pct, unmodified_pct = analyze_character_diff(original_content, new_content)
90+
91+
print(f"🔬 Character-level analysis for: {file_path}")
92+
print("--------------------------------------------------")
93+
print(f"Total characters in original (commit {old_commit[:7]}): {len(original_content)}")
94+
print(f"\nPercentage of original characters MODIFIED: {modified_pct:.2f} %")
95+
print(f"Percentage of original characters UNMODIFIED: {unmodified_pct:.2f} %")

src/content/docs/accounts/accounts-billing/account-structure/factors-affecting-access-features-data.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ Your [user type](/docs/accounts/accounts-billing/new-relic-one-user-management/u
3838

3939
To learn more about assigned roles, see [How groups and roles control access to New Relic](/docs/accounts/accounts-billing/new-relic-one-user-management/user-management-concepts/#understand-concepts).
4040

41+
### Organization-level feature controls [#feature-controls]
42+
43+
Your organization administrators can control access to Intelligent Observability features using the [Feature control manager](/docs/accounts/accounts-billing/new-relic-one-user-management/feature-control-manager). If a feature appears unavailable, check with your admin about whether it's been activated for your account.
44+
4145
### Pricing edition [#pricing]
4246

4347
We have four pricing editions: Free, Standard, Pro, and Enterprise. Your edition affects your access to some features. For more on this, see [Editions](https://newrelic.com/pricing).

src/content/docs/accounts/accounts-billing/general-account-settings/intro-account-settings.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Here are some of the most popular topics related to New Relic administration:
3939

4040
* [Manage billing](/docs/accounts/accounts-billing/new-relic-one-pricing-billing/new-relic-one-pricing-billing#usage-ui)
4141
* [Manage users](/docs/accounts/accounts-billing/new-relic-one-user-management/introduction-managing-users)
42+
* [Manage Intelligent Observability features](/docs/accounts/accounts-billing/new-relic-one-user-management/feature-control-manager)
4243
* [Add an account to your organization](/docs/accounts/accounts-billing/account-structure/add-accounts)
4344
* [Manage data](/docs/data-apis/manage-data/manage-your-data)
4445
* [Manage API keys](/docs/apis/intro-apis/new-relic-api-keys)

src/content/docs/accounts/accounts-billing/new-relic-one-pricing-billing/compute-budgets/overview.mdx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,12 @@ Compute Budgets provides a mechanism to monitor and alert on your Compute Consum
3232

3333
To use **Compute Budgets**, you must:
3434

35-
- [Sign up](https://newrelic.com/signup/) for a New Relic account.
36-
- Be on at least one compute SKU and have a single organisation, non-POA structure.
37-
- Have a minimum commitment amount defined on the Compute SKU.
35+
- [Sign up](https://newrelic.com/signup/) for a New Relic account.
36+
- Be a single-tenant customer with a contract commitment on either Core or Legacy Compute Capacity Units (CCUs).
37+
- Have a minimum commitment amount defined on the Compute SKU.
3838

3939
## Access controls
4040

41-
- Users with the `organization_manager` role can **create**, **update**, or **delete** `Org Budget` and `account budgets`, provided they also have access to the telemetry account.
42-
43-
- Users with the `organization_read_only` role can **view** `Org Budget` and `account budgets`, provided they also have access to the telemetry account.
44-
45-
- Users who don't have access to the telemetry account cannot view the `Org budget`, but they can view `account budgets`, if they do have access to them.
41+
- Users with the `organization_manager` role can **create**, **update**, or **delete** `Org Budget` and `account budgets`, provided they also have access to the telemetry account.
42+
- Users with the `organization_read_only` role can **view** `Org Budget` and `account budgets`, provided they also have access to the telemetry account.
43+
- Users who don't have access to the telemetry account cannot view the `Org budget`, but they can view `account budgets`, if they do have access to them.

0 commit comments

Comments
 (0)