diff --git a/.github/PULL_REQUEST_TEMPLATE/eng_pr_template.md b/.github/PULL_REQUEST_TEMPLATE/eng_pr_template.md new file mode 100644 index 00000000000..2a5adde6b30 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/eng_pr_template.md @@ -0,0 +1,24 @@ +### ✨ Summary + + + +### 🖼️ Screenshots + + + +### 🔮 Engineering checklist + + + +- [ ] Relevant documentation including README's or Confluence are updated to reflect these changes +- [ ] Code changes are self-documenting or clearly commented/documented +- [ ] Changes that effect tech writers have been communicated to tech docs team +- [ ] New components created for use in MDX files have been serialized/deserialized +- [ ] New tests are added where applicable and total coverage remains >70% +- [ ] No new warnings or errors where added to the browser or build console +- All downstream dependencies for DaaS have been considered for negative impacts + - [ ] What's new JSON endpoints + - [ ] Release notes JSON & EOL pages +- [ ] The build preview has been checked for expected functionality on desktop browsers +- [ ] The build preview has been checked for expected functionality on mobile browsers +- [ ] Synthetics tests have been considered for any changes that might trigger false positives diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index 8feffbcc3cb..e905b71fa8f 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -6,8 +6,6 @@ reviewers: - paperclypse - rhetoric101 - homelessbirds - - nbaenam - - a-sassman - ally-sassman - akristen - jeff-colucci diff --git a/.github/styles/new-relic/ComplexWords.yml b/.github/styles/new-relic/ComplexWords.yml index 65b7a347215..a4d37b61c83 100644 --- a/.github/styles/new-relic/ComplexWords.yml +++ b/.github/styles/new-relic/ComplexWords.yml @@ -6,7 +6,7 @@ level: suggestion action: name: replace swap: - "approximate(?:ly)?": about + 'approximate(?:ly)?': about abundance: plenty accelerate: speed up accentuate: stress @@ -19,7 +19,7 @@ swap: acquiesce: agree acquire: get|buy additional: more|extra - address: discuss + '[^IP] address': discuss addressees: you adjacent to: next to adjustment: change diff --git a/.github/workflows/attribute-dictionary-comment.yml b/.github/workflows/attribute-dictionary-comment.yml new file mode 100644 index 00000000000..be112010756 --- /dev/null +++ b/.github/workflows/attribute-dictionary-comment.yml @@ -0,0 +1,33 @@ +name: Attribute dictionary updates + +on: + pull_request: + branches: + - develop + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + deny-attribute-dictionary-changes: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Prevent file change + uses: xalvarez/prevent-file-change-action@v1 + with: + githubToken: ${{ secrets.GITHUB_TOKEN }} + pattern: .*\attribute-dictionary.json + trustedAuthors: nr-opensource-bot + + - name: Comment if JSON file is modified + if: failure() + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + > [!WARNING] + > `attribute-dictionary.json` is an autogenerated file. + > + > Changes to attribute dictionary should be made through [source.datanerd.us/docs-eng/attribute-dictionary](https://source.datanerd.us/docs-eng/attribute-dictionary) diff --git a/.github/workflows/auto-assign-reviewer.yml b/.github/workflows/auto-assign-reviewer.yml index f602f7f05d5..0f8d5684a36 100644 --- a/.github/workflows/auto-assign-reviewer.yml +++ b/.github/workflows/auto-assign-reviewer.yml @@ -7,4 +7,4 @@ jobs: add-reviews: runs-on: ubuntu-latest steps: - - uses: kentaro-m/auto-assign-action@v1.2.0 + - uses: kentaro-m/auto-assign-action@v1.2.5 diff --git a/.github/workflows/check-for-keys.yml b/.github/workflows/check-for-keys.yml new file mode 100644 index 00000000000..3f8174ba6ed --- /dev/null +++ b/.github/workflows/check-for-keys.yml @@ -0,0 +1,22 @@ +name: Check for Keys + +on: + pull_request: + types: + - opened + branches: + - develop + +jobs: + check-for-keys: + name: Check for keys in files and commit history + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Check for keys + run: ./scripts/actions/check-for-keys.sh diff --git a/.github/workflows/check-images.yml b/.github/workflows/check-images.yml index 7563106438f..dd77627deed 100644 --- a/.github/workflows/check-images.yml +++ b/.github/workflows/check-images.yml @@ -33,9 +33,11 @@ jobs: - name: Convert images to WEBP run: yarn convert-to-webp -g + id: convert-images - name: Commit changes id: commit-changes + if: steps.convert-images.outputs.convertedImages != 0 run: | git config --local user.email "${{ env.BOT_EMAIL }}" git config --local user.name "${{ env.BOT_NAME }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7da2eefb2e4..50a83928c85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,12 +52,3 @@ jobs: - name: Set Release Version from Tag run: echo "RELEASE_VERSION=${{ env.NEW_TAG }}" >> $GITHUB_ENV - - - name: Create New Relic deployment marker - uses: newrelic/deployment-marker-action@v2.1.0 - with: - apiKey: ${{ secrets.NEW_RELIC_API_KEY_DEPLOY_MARKERS }} - guid: ${{ secrets.NEW_RELIC_DEPLOYMENT_ENTITY_GUID }} - region: 'staging' - version: '${{ env.RELEASE_VERSION }}' - user: '${{ github.actor }}' diff --git a/.github/workflows/vale-lint.yml b/.github/workflows/vale-lint.yml index 4a09629d5ef..c766c4b114b 100644 --- a/.github/workflows/vale-lint.yml +++ b/.github/workflows/vale-lint.yml @@ -13,9 +13,20 @@ jobs: uses: actions/checkout@v3 - name: Run vale - uses: errata-ai/vale-action@v1 + uses: errata-ai/vale-action@reviewdog env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} with: - files: __onlyModified - onlyAnnotateModifiedLines: true + reporter: github-check + fail_on_error: true + filter_mode: added + token: ${{secrets.GITHUB_TOKEN}} + + - name: Comment on Failure + if: failure() + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + > [!NOTE] + + > Our Vale automated writing feedback tool has suggestions for this PR. You can view these suggestions in the *Files changed* tab. If you're not a New Relic docs writer, we'll review any comments and you don't need to take any action (though feel free to make edits if you like!). diff --git a/.github/workflows/verify-mdx.yml b/.github/workflows/verify-mdx.yml new file mode 100644 index 00000000000..d41363ba479 --- /dev/null +++ b/.github/workflows/verify-mdx.yml @@ -0,0 +1,39 @@ +name: verify mdx files + +on: + pull_request: + branches: + - develop + - main + +env: + NODE_OPTIONS: '--max-old-space-size=4096' + CHOKIDAR_USEPOLLING: 1 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + verify-mdx: + name: run verify + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Cache dependencies + id: yarn-cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-modules-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install --frozen-lockfile + + - name: Run verify-mdx + run: yarn verify-mdx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3e3dd2de21..b39416791f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ yarn start Your site is now running at [`http://localhost:8000`](http://localhost:8000)! -The first time you build the site locally, it will take upwards of 20 minutes. This is normal, and will take signifigantly less time afterwards. +The first time you build the site locally, it will take upwards of 20 minutes. This is normal, and will take significantly less time afterward. ### Dependencies @@ -287,7 +287,7 @@ if you face issues in contributing. ### General Install, compile or build issues -Many of the common errors you'll face can be resolve by the following: +Many of the common errors you'll face can be resolved by the following: 1. Ensure you are using `yarn` not `npm` 2. Always run `git pull` whenever you intend to create a new working branch diff --git a/gatsby-config.js b/gatsby-config.js index d31e0caa2e5..209fe35d47e 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -20,13 +20,22 @@ const ignoreFolders = process.env.BUILD_FOLDERS ) .map((folder) => `${__dirname}/src/content/docs/${folder}/*`) : []; +const allI18nFolders = fs + .readdirSync(`${__dirname}/src/i18n/content`) + .filter((folder) => !folder.startsWith('.')); -const autoLinkHeaders = { - resolve: 'gatsby-remark-autolink-headers', - options: { - icon: - '', - }, +const ignoreI18nFolders = () => { + if (process.env.BUILD_LANG) { + return allI18nFolders + .map((folder) => { + if (folder !== process.env.BUILD_LANG) { + return `${__dirname}/src/i18n/content/${folder}`; + } + return null; + }) + .filter(Boolean); + } + return []; }; module.exports = { @@ -86,10 +95,7 @@ module.exports = { options: { name: 'translated-content', path: `${__dirname}/src/i18n/content`, - ignore: - process.env.BUILD_I18N === 'false' - ? [`${__dirname}/src/i18n/content/*`] - : [], + ignore: ignoreI18nFolders(), }, }, { @@ -125,7 +131,7 @@ module.exports = { resolve: 'gatsby-remark-images', options: { maxWidth: 850, - linkImagesToOriginal: true, + linkImagesToOriginal: false, backgroundColor: 'transparent', disableBgImageOnAlpha: true, }, @@ -173,7 +179,11 @@ module.exports = { disableBgImageOnAlpha: true, }, }, - autoLinkHeaders, + { + resolve: require.resolve( + './plugins/gatsby-remark-autolink-headers-custom' + ), + }, // This MUST come after `gatsby-remark-autolink-headers` to ensure the // link created for the icon has the proper id. // diff --git a/gatsby-node.js b/gatsby-node.js index 9077182af52..350329284dd 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -99,7 +99,6 @@ exports.createPages = async ({ actions, graphql, reporter }) => { type subject redirects - hideNavs } } } @@ -307,10 +306,10 @@ exports.createPages = async ({ actions, graphql, reporter }) => { }); }); - // Redirect for VSU page to new Introduction to APM doc + // Redirect for old VSU Introduction to APM .js doc createRedirect({ - fromPath: '/docs/apm/new-relic-apm/getting-started/introduction-apm/', - toPath: '/introduction-apm', + fromPath: '/introduction-apm', + toPath: '/docs/apm/new-relic-apm/getting-started/introduction-apm/', isPermanent: false, redirectInBrowser: true, }); @@ -352,7 +351,6 @@ exports.createSchemaCustomization = ( type Frontmatter { isFeatured: Boolean translationType: String - hideNavs: Boolean eolDate: String downloadLink: String signupBanner: SignupBanner @@ -456,10 +454,6 @@ exports.createResolvers = ({ createResolvers }) => { ? source.translationType : null, }, - hideNavs: { - resolve: (source) => - hasOwnProperty(source, 'hideNavs') ? source.hideNavs : null, - }, eolDate: { resolve: (source) => hasOwnProperty(source, 'eolDate') ? source.eolDate : null, @@ -501,20 +495,6 @@ exports.createResolvers = ({ createResolvers }) => { exports.onCreatePage = ({ page, actions }) => { const { createPage } = actions; - if (page.path === '/') { - page.context.quicklaunchSlug = - 'docs/new-relic-solutions/get-started/quick-launch-guide'; - page.context.layout = 'homepage'; - } - if (page.path === '/jp/') { - page.context.quicklaunchSlug = - 'jp/docs/new-relic-solutions/get-started/quick-launch-guide'; - } - if (page.path === '/kr/') { - page.context.quicklaunchSlug = - 'kr/docs/new-relic-solutions/get-started/quick-launch-guide'; - } - if (page.path.match(/404/)) { page.context.layout = 'basic'; } @@ -573,7 +553,7 @@ const createPageFromNode = ( defer = false ) => { const { - frontmatter: { subject: agentName, hideNavs }, + frontmatter: { subject: agentName }, fields: { fileRelativePath, slug }, } = node; @@ -621,7 +601,6 @@ const createPageFromNode = ( context: { ...context, fileRelativePath, - hideNavs, slug, slugRegex: `${slug}/.+/`, disableSwiftype, diff --git a/package.json b/package.json index be4604f6b59..bb31d8048fc 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "@emotion/styled": "^11.3.0", "@mdx-js/mdx": "2.0.0-next.8", "@mdx-js/react": "2.0.0-next.8", - "@newrelic/gatsby-theme-newrelic": "7.1.2", + "@newrelic/gatsby-theme-newrelic": "9.2.5", "@splitsoftware/splitio-react": "^1.2.4", + "ansi-colors": "^4.1.3", "cockatiel": "^3.0.0-beta.0", "compare-versions": "^6.0.0-rc.2", "date-fns": "^2.17.0", @@ -64,7 +65,8 @@ "turndown": "^6.0.0", "unist-util-filter": "^2.0.3", "unist-util-remove": "^2.0.1", - "use-boop": "^1.1.1" + "use-boop": "^1.1.1", + "uuid": "^9.0.1" }, "devDependencies": { "@actions/core": "^1.10.0", @@ -82,7 +84,9 @@ "aws-sdk": "^2.827.0", "babel-jest": "~27.4.6", "babel-preset-gatsby": "^1.3.0", + "cli-progress": "^3.12.0", "commander": "^8.3.0", + "csv-stringify": "^6.4.4", "deep-equal": "^2.2.2", "dotenv": "^8.2.0", "eslint": "^7.7.0", @@ -170,7 +174,6 @@ "build:production": "ENVIRONMENT=production yarn run build", "build": "NODE_OPTIONS='--max-old-space-size=5120' gatsby build", "check-for-outdated-translations": "node scripts/actions/check-for-outdated-translations.js", - "check-image-imports": "node scripts/checkForOrphanImports.js", "clean": "gatsby clean", "convert-to-webp": "node scripts/convertPNGs.mjs", "db:clean": "./scripts/actions/translation_workflow/testing/cleanup.sh", @@ -178,6 +181,7 @@ "debug": "yarn develop --inspect-brk", "deserialize": "./scripts/serde.js deserialize", "develop": "gatsby develop", + "docs-freshness": "./scripts/docsAndLastDateEdited.mjs", "extract-i18n": "i18next", "fetch-related-content": "node scripts/actions/get-related-resources.js", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", @@ -196,8 +200,8 @@ "remove-unused-images": " node scripts/removeUnusedImages.js", "serialize": "./scripts/serde.js serialize", "serve": "gatsby serve", - "start:full": "NODE_OPTIONS='--max-old-space-size=5120' BUILD_I18N=true yarn develop", - "start": "NODE_OPTIONS='--max-old-space-size=5120' BUILD_I18N=false yarn develop", + "start:full": "NODE_OPTIONS='--max-old-space-size=5120' yarn develop", + "start": "NODE_OPTIONS='--max-old-space-size=5120' BUILD_LANG=en yarn develop", "test": "jest -i", "update-attribute-dictionary-json": "node scripts/actions/update-attribute-dictionary-json.mjs", "verify-install-page": "node scripts/verifyInstallPage.js", diff --git a/plugins/gatsby-plugin-security-bulletins-rss/gatsby-node.js b/plugins/gatsby-plugin-security-bulletins-rss/gatsby-node.js index 9570a2833db..25a1a382d24 100644 --- a/plugins/gatsby-plugin-security-bulletins-rss/gatsby-node.js +++ b/plugins/gatsby-plugin-security-bulletins-rss/gatsby-node.js @@ -32,6 +32,7 @@ const securityBulletinsQuery = async (graphql) => { frontmatter { title metaDescription + releaseDate } slug mdxAST @@ -59,7 +60,7 @@ const securityBulletinsQuery = async (graphql) => { const getFeedItem = (node, siteMetadata, imageHashMap) => { const { frontmatter, slug, mdxAST } = node; - const { title, metaDescription } = frontmatter; + const { title, metaDescription, releaseDate } = frontmatter; const transformedAST = htmlGenerator.runSync(mdxAST); const html = htmlGenerator.stringify( @@ -72,7 +73,7 @@ const getFeedItem = (node, siteMetadata, imageHashMap) => { ); // time is necessary for RSS validity - const date = new Date(); + const date = new Date(releaseDate); const pubDate = `${format(date, 'EE, dd LLL yyyy')} 00:00:00 +0000`; const link = new URL(slug, siteMetadata.siteUrl).href; const id = Buffer.from(`${title}`).toString('base64'); diff --git a/plugins/gatsby-remark-autolink-headers-custom/README.md b/plugins/gatsby-remark-autolink-headers-custom/README.md new file mode 100644 index 00000000000..8242f3aa528 --- /dev/null +++ b/plugins/gatsby-remark-autolink-headers-custom/README.md @@ -0,0 +1 @@ +This plugin code was sourced from https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-autolink-headers and tweaked to fit our needs by adding the ability to copy the header links to the clipboard diff --git a/plugins/gatsby-remark-autolink-headers-custom/gatsby-browser.js b/plugins/gatsby-remark-autolink-headers-custom/gatsby-browser.js new file mode 100644 index 00000000000..722641bc68d --- /dev/null +++ b/plugins/gatsby-remark-autolink-headers-custom/gatsby-browser.js @@ -0,0 +1,48 @@ +let offsetY = 0; + +const getTargetOffset = (hash) => { + const id = window.decodeURI(hash.replace(`#`, ``)); + if (id !== ``) { + const element = document.getElementById(id); + if (element) { + const scrollTop = + window.pageYOffset || + document.documentElement.scrollTop || + document.body.scrollTop; + const clientTop = + document.documentElement.clientTop || document.body.clientTop || 0; + const computedStyles = window.getComputedStyle(element); + const scrollMarginTop = + computedStyles.getPropertyValue(`scroll-margin-top`) || + computedStyles.getPropertyValue(`scroll-snap-margin-top`) || + `0px`; + + return ( + element.getBoundingClientRect().top + + scrollTop - + parseInt(scrollMarginTop, 10) - + clientTop - + offsetY + ); + } + } + return null; +}; + +exports.onInitialClientRender = (_, pluginOptions) => { + if (pluginOptions.offsetY) { + offsetY = pluginOptions.offsetY; + } + + requestAnimationFrame(() => { + const offset = getTargetOffset(window.location.hash); + if (offset !== null) { + window.scrollTo(0, offset); + } + }); +}; + +exports.shouldUpdateScroll = ({ routerProps: { location } }) => { + const offset = getTargetOffset(location.hash); + return offset !== null ? [0, offset] : true; +}; diff --git a/plugins/gatsby-remark-autolink-headers-custom/gatsby-node.js b/plugins/gatsby-remark-autolink-headers-custom/gatsby-node.js new file mode 100644 index 00000000000..cf7fe7c843d --- /dev/null +++ b/plugins/gatsby-remark-autolink-headers-custom/gatsby-node.js @@ -0,0 +1,30 @@ +exports.pluginOptionsSchema = ({ Joi }) => + Joi.object({ + offsetY: Joi.number() + .integer() + .description(`Signed integer. Vertical offset value in pixels.`) + .default(0), + icon: Joi.alternatives() + .try(Joi.string(), Joi.boolean()) + .description( + `SVG shape inside a template literal or boolean 'false'. Set your own svg or disable icon.` + ), + className: Joi.string() + .description(`Set your own class for the anchor.`) + .default(`anchor`), + maintainCase: Joi.boolean().description( + `Maintains the case for markdown header.` + ), + removeAccents: Joi.boolean().description( + `Remove accents from generated headings IDs.` + ), + enableCustomId: Joi.boolean().description( + `Enable custom header IDs with \`{#id}\`` + ), + isIconAfterHeader: Joi.boolean().description( + `Enable the anchor icon to be inline at the end of the header text.` + ), + elements: Joi.array() + .items(Joi.string()) + .description(`Specify which type of header tags to link.`), + }); diff --git a/plugins/gatsby-remark-autolink-headers-custom/gatsby-ssr.js b/plugins/gatsby-remark-autolink-headers-custom/gatsby-ssr.js new file mode 100644 index 00000000000..0b6dfc938ae --- /dev/null +++ b/plugins/gatsby-remark-autolink-headers-custom/gatsby-ssr.js @@ -0,0 +1,87 @@ +import React from 'react'; + +const pluginDefaults = { + className: `anchor`, + icon: true, + offsetY: 0, +}; + +export const onRenderBody = ({ setHeadComponents }, pluginOptions) => { + const { className, icon, offsetY } = Object.assign( + pluginDefaults, + pluginOptions + ); + + const styles = ` + .${className}.before { + position: absolute; + top: 0; + left: 0; + transform: translateX(-100%); + padding-right: 4px; + } + .${className}.after { + display: inline-block; + padding-left: 4px; + } + h1 .${className} svg, + h2 .${className} svg, + h3 .${className} svg, + h4 .${className} svg, + h5 .${className} svg, + h6 .${className} svg { + visibility: hidden; + } + h1:hover .${className} svg, + h2:hover .${className} svg, + h3:hover .${className} svg, + h4:hover .${className} svg, + h5:hover .${className} svg, + h6:hover .${className} svg, + h1 .${className}:focus svg, + h2 .${className}:focus svg, + h3 .${className}:focus svg, + h4 .${className}:focus svg, + h5 .${className}:focus svg, + h6 .${className}:focus svg { + visibility: visible; + } + `; + + // This script used to have `let scrollTop` and `let clientTop` instead of + // current ones which are `var`. It is changed due to incompatibility with + // older browsers (that do not implement `let`). See: + // - https://github.com/gatsbyjs/gatsby/issues/21058 + // - https://github.com/gatsbyjs/gatsby/pull/21083 + const script = ` + document.addEventListener("DOMContentLoaded", function(event) { + var hash = window.decodeURI(location.hash.replace('#', '')) + if (hash !== '') { + var element = document.getElementById(hash) + if (element) { + var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop + var clientTop = document.documentElement.clientTop || document.body.clientTop || 0 + var offset = element.getBoundingClientRect().top + scrollTop - clientTop + // Wait for the browser to finish rendering before scrolling. + setTimeout((function() { + window.scrollTo(0, offset - ${offsetY}) + }), 0) + } + } + }) + `; + + const style = icon ? ( + + ) : undefined; + + return setHeadComponents([ + style, +