From bd525361e3b465b0ecdff5045743056afc34f7b8 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Wed, 16 Jul 2025 17:47:58 -0700 Subject: [PATCH 1/3] Add content linter rule for versions frontmatter whitespace validation (GHD051) (#56131) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../contributing/content-linter-rules.md | 3 +- .../frontmatter-versions-whitespace.js | 94 ++++++ src/content-linter/lib/linting-rules/index.js | 2 + src/content-linter/style/github-docs.js | 6 + .../unit/frontmatter-versions-whitespace.js | 276 ++++++++++++++++++ 5 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.js create mode 100644 src/content-linter/tests/unit/frontmatter-versions-whitespace.js diff --git a/data/reusables/contributing/content-linter-rules.md b/data/reusables/contributing/content-linter-rules.md index b2033dc5d99d..5f861a8dc84d 100644 --- a/data/reusables/contributing/content-linter-rules.md +++ b/data/reusables/contributing/content-linter-rules.md @@ -72,4 +72,5 @@ | GHD046 | outdated-release-phase-terminology | Outdated release phase terminology should be replaced with current GitHub terminology | warning | terminology, consistency, release-phases | | GHD048 | british-english-quotes | Periods and commas should be placed inside quotation marks (American English style) | warning | punctuation, quotes, style, consistency | | GHD050 | multiple-emphasis-patterns | Do not use more than one emphasis/strong, italics, or uppercase for a string | warning | formatting, emphasis, style | -| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style | \ No newline at end of file +| GHD049 | note-warning-formatting | Note and warning tags should be formatted according to style guide | warning | formatting, callouts, notes, warnings, style | +| GHD051 | frontmatter-versions-whitespace | Versions frontmatter should not contain unnecessary whitespace | warning | frontmatter, versions | \ No newline at end of file diff --git a/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.js b/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.js new file mode 100644 index 000000000000..befc1d5ee882 --- /dev/null +++ b/src/content-linter/lib/linting-rules/frontmatter-versions-whitespace.js @@ -0,0 +1,94 @@ +import { addError } from 'markdownlint-rule-helpers' +import { getFrontmatter } from '@/content-linter/lib/helpers/utils' + +export const frontmatterVersionsWhitespace = { + names: ['GHD051', 'frontmatter-versions-whitespace'], + description: 'Versions frontmatter should not contain unnecessary whitespace', + tags: ['frontmatter', 'versions'], + function: (params, onError) => { + const fm = getFrontmatter(params.lines) + if (!fm || !fm.versions) return + + const versionsObj = fm.versions + if (typeof versionsObj !== 'object') return + + // Find the frontmatter section in the file + const fmStartIndex = params.lines.findIndex((line) => line.trim() === '---') + if (fmStartIndex === -1) return + + // Check each version entry for whitespace issues + Object.entries(versionsObj).forEach(([key, value]) => { + if (typeof value !== 'string') return + + const hasUnwantedWhitespace = checkForUnwantedWhitespace(value) + if (hasUnwantedWhitespace) { + // Find the line containing this version key + const versionLineIndex = params.lines.findIndex((line, index) => { + return index > fmStartIndex && line.trim().startsWith(`${key}:`) && line.includes(value) + }) + + if (versionLineIndex !== -1) { + const line = params.lines[versionLineIndex] + const lineNumber = versionLineIndex + 1 + const cleanedValue = getCleanedValue(value) + + // Create fix info to remove unwanted whitespace + const fixInfo = { + editColumn: line.indexOf(value) + 1, + deleteCount: value.length, + insertText: cleanedValue, + } + + addError( + onError, + lineNumber, + `Versions frontmatter should not contain leading or trailing whitespace. Found: '${value}', expected: '${cleanedValue}'`, + line, + [line.indexOf(value) + 1, value.length], + fixInfo, + ) + } + } + }) + }, +} + +/** + * Check if a version string has unwanted whitespace + * Allows whitespace in complex expressions like '<3.6 >3.8' + * but disallows leading/trailing whitespace + */ +function checkForUnwantedWhitespace(value) { + // Don't flag if the value is just whitespace or empty + if (!value || value.trim() === '') return false + + // Check for leading or trailing whitespace + if (value !== value.trim()) return true + + // Allow whitespace around operators in complex expressions + // This regex matches patterns like '<3.6 >3.8', '>=2.19', etc. + const hasOperators = /[<>=]/.test(value) + if (hasOperators) { + // For operator expressions, we're more lenient about internal whitespace + // Only flag if there's leading/trailing whitespace (already checked above) + return false + } + + // For simple version strings (like 'fpt', 'ghec'), no internal whitespace should be allowed + // This catches cases like 'f pt' where there's whitespace in the middle + return /\s/.test(value) +} + +/** + * Get the cleaned version of a value by removing appropriate whitespace + */ +function getCleanedValue(value) { + // For values with operators, just trim leading/trailing whitespace + const hasOperators = /[<>=]/.test(value) + if (hasOperators) { + return value.trim() + } + + // For simple version strings, remove all whitespace + return value.replace(/\s/g, '') +} diff --git a/src/content-linter/lib/linting-rules/index.js b/src/content-linter/lib/linting-rules/index.js index b31c2a68c251..918e76924d06 100644 --- a/src/content-linter/lib/linting-rules/index.js +++ b/src/content-linter/lib/linting-rules/index.js @@ -50,6 +50,7 @@ import { outdatedReleasePhaseTerminology } from '@/content-linter/lib/linting-ru import { britishEnglishQuotes } from '@/content-linter/lib/linting-rules/british-english-quotes' import { multipleEmphasisPatterns } from '@/content-linter/lib/linting-rules/multiple-emphasis-patterns' import { noteWarningFormatting } from '@/content-linter/lib/linting-rules/note-warning-formatting' +import { frontmatterVersionsWhitespace } from '@/content-linter/lib/linting-rules/frontmatter-versions-whitespace' const noDefaultAltText = markdownlintGitHub.find((elem) => elem.names.includes('no-default-alt-text'), @@ -105,5 +106,6 @@ export const gitHubDocsMarkdownlint = { britishEnglishQuotes, multipleEmphasisPatterns, noteWarningFormatting, + frontmatterVersionsWhitespace, ], } diff --git a/src/content-linter/style/github-docs.js b/src/content-linter/style/github-docs.js index 0d51ff84f8a7..19b3d3e4c650 100644 --- a/src/content-linter/style/github-docs.js +++ b/src/content-linter/style/github-docs.js @@ -286,6 +286,12 @@ export const githubDocsFrontmatterConfig = { severity: 'error', 'partial-markdown-files': false, }, + 'frontmatter-versions-whitespace': { + // GHD051 + severity: 'warning', + 'partial-markdown-files': false, + 'yml-files': false, + }, } // Configures rules from the `github/markdownlint-github` repo diff --git a/src/content-linter/tests/unit/frontmatter-versions-whitespace.js b/src/content-linter/tests/unit/frontmatter-versions-whitespace.js new file mode 100644 index 000000000000..ac2b7c22d99e --- /dev/null +++ b/src/content-linter/tests/unit/frontmatter-versions-whitespace.js @@ -0,0 +1,276 @@ +import { describe, expect, test } from 'vitest' + +import { runRule } from '@/content-linter/lib/init-test' +import { frontmatterVersionsWhitespace } from '@/content-linter/lib/linting-rules/frontmatter-versions-whitespace' + +// Configure the test fixture to not split frontmatter and content +const fmOptions = { markdownlintOptions: { frontMatter: null } } + +// Valid cases - should pass +const validCases = [ + { + name: 'valid-simple-versions', + content: `--- +title: Test +versions: + fpt: '*' + ghec: '*' + ghes: '>=3.0' +--- + +# Test + +This is a test. +`, + }, + { + name: 'valid-complex-operators', + content: `--- +title: Test +versions: + ghes: '<3.6 >3.8' + fpt: '*' +--- + +# Test + +This is a test. +`, + }, + { + name: 'valid-no-versions', + content: `--- +title: Test +type: tutorial +--- + +# Test + +This is a test. +`, + }, +] + +// Invalid cases - should fail +const invalidCases = [ + { + name: 'trailing-whitespace', + content: `--- +title: Test +versions: + fpt: '* ' + ghec: '*' +--- + +# Test + +This is a test. +`, + expectedErrors: 1, + expectedMessage: + "Versions frontmatter should not contain leading or trailing whitespace. Found: '* ', expected: '*'", + }, + { + name: 'leading-whitespace', + content: `--- +title: Test +versions: + fpt: ' *' + ghec: '*' +--- + +# Test + +This is a test. +`, + expectedErrors: 1, + expectedMessage: + "Versions frontmatter should not contain leading or trailing whitespace. Found: ' *', expected: '*'", + }, + { + name: 'both-leading-and-trailing', + content: `--- +title: Test +versions: + fpt: ' * ' + ghec: '*' +--- + +# Test + +This is a test. +`, + expectedErrors: 1, + expectedMessage: + "Versions frontmatter should not contain leading or trailing whitespace. Found: ' * ', expected: '*'", + }, + { + name: 'multiple-version-whitespace-issues', + content: `--- +title: Test +versions: + fpt: '* ' + ghec: ' *' + ghes: '>=3.0' +--- + +# Test + +This is a test. +`, + expectedErrors: 2, + }, + { + name: 'internal-whitespace-simple-version', + content: `--- +title: Test +versions: + fpt: 'f pt' + ghec: '*' +--- + +# Test + +This is a test. +`, + expectedErrors: 1, + expectedMessage: + "Versions frontmatter should not contain leading or trailing whitespace. Found: 'f pt', expected: 'fpt'", + }, +] + +describe(frontmatterVersionsWhitespace.names.join(' - '), () => { + describe('valid cases', () => { + validCases.forEach(({ name, content }) => { + test(`${name} should pass`, async () => { + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(0) + }) + }) + }) + + describe('invalid cases', () => { + invalidCases.forEach(({ name, content, expectedErrors, expectedMessage }) => { + test(`${name} should fail`, async () => { + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(expectedErrors) + + if (expectedMessage) { + expect(result.content[0].errorDetail).toBe(expectedMessage) + } + }) + }) + }) + + describe('fixable errors', () => { + test('should provide fix information for trailing whitespace', async () => { + const content = `--- +title: Test +versions: + fpt: '* ' +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(1) + expect(result.content[0].fixInfo).toBeDefined() + expect(result.content[0].fixInfo.insertText).toBe('*') + }) + + test('should provide fix information for leading whitespace', async () => { + const content = `--- +title: Test +versions: + fpt: ' *' +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(1) + expect(result.content[0].fixInfo).toBeDefined() + expect(result.content[0].fixInfo.insertText).toBe('*') + }) + }) + + describe('edge cases', () => { + test('should handle non-string version values', async () => { + const content = `--- +title: Test +versions: + fpt: '*' + feature: ['foo', 'bar'] +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(0) + }) + + test('should handle missing versions frontmatter', async () => { + const content = `--- +title: Test +type: tutorial +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(0) + }) + + test('should allow complex operator expressions with spaces', async () => { + const content = `--- +title: Test +versions: + ghes: '<3.6 >3.8' + fpt: '*' +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(0) + }) + + test('should allow operator expressions with spaces around operators', async () => { + const content = `--- +title: Test +versions: + ghes: '>= 3.0' + fpt: '*' +--- + +# Test +` + const result = await runRule(frontmatterVersionsWhitespace, { + strings: { content }, + ...fmOptions, + }) + expect(result.content.length).toBe(0) + }) + }) +}) From 2cadd3a90e69131303d0cc3952bf36dccd5f76a5 Mon Sep 17 00:00:00 2001 From: Ricardo Kreyhsig <2385700+rickreyhsig@users.noreply.github.com> Date: Thu, 17 Jul 2025 05:19:18 -0400 Subject: [PATCH 2/3] Add helm-registry in docs page for :dependabot: private registries (#56682) Co-authored-by: mchammer01 <42146119+mchammer01@users.noreply.github.com> --- .../configuring-access-to-private-registries-for-dependabot.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot.md b/content/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot.md index ddce43061e35..4bb01dabc320 100644 --- a/content/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot.md +++ b/content/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot.md @@ -160,7 +160,8 @@ Examples of how to configure access to the private registries supported by {% da * [`cargo-registry`](#cargo-registry) * [`composer-repository`](#composer-repository) * [`docker-registry`](#docker-registry) -* [`git`](#git) +* [`git`](#git){% ifversion dependabot-helm-support %} +* [`helm-registry`](#helm-registry){% endif %} * [`hex-organization`](#hex-organization) * [`hex-repository`](#hex-repository) * [`maven-repository`](#maven-repository) From 70f9fe3f5fcea363b361c8d1f043460f3d0098c4 Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:28:50 +0100 Subject: [PATCH 3/3] [EDI - Drivers] Align "Setting up Copilot in your organization/enterprise" (#56617) Co-authored-by: Kevin Heis Co-authored-by: Isaac Brown <101839405+isaacmbrown@users.noreply.github.com> --- ...ng-up-github-copilot-for-your-enterprise.md | 4 ++-- ...-up-github-copilot-for-your-organization.md | 18 +++--------------- data/reusables/copilot/setup-next-steps.md | 4 ++++ 3 files changed, 9 insertions(+), 17 deletions(-) create mode 100644 data/reusables/copilot/setup-next-steps.md diff --git a/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-enterprise.md b/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-enterprise.md index 656b987a0a17..ee3104489333 100644 --- a/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-enterprise.md +++ b/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-enterprise.md @@ -41,6 +41,6 @@ Enable {% data variables.product.prodname_copilot_short %} for some or all organ If your enterprise is on {% data variables.enterprise.data_residency_site %}, users must perform some additional setup to authenticate to their account from their development environment. See [AUTOTITLE](/copilot/managing-copilot/configure-personal-settings/using-github-copilot-with-an-account-on-ghecom). -## 5. Drive {% data variables.product.prodname_copilot_short %} adoption +## Next steps -Planning and implementing an effective enablement process is essential to drive adoption of {% data variables.product.prodname_copilot_short %} in your enterprise. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/driving-copilot-adoption-in-your-company). +{% data reusables.copilot.setup-next-steps %} diff --git a/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-organization.md b/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-organization.md index ce0a4ac9aac9..2e2b8fb94bff 100644 --- a/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-organization.md +++ b/content/copilot/how-tos/set-up/setting-up-github-copilot-for-your-organization.md @@ -35,22 +35,10 @@ You may also need to install custom SSL certificates on your members' machines. ## 4. Grant access to members -Enable {% data variables.product.prodname_copilot_short %} for some or all members of your organization. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/granting-access-to-copilot-for-members-of-your-organization). - -To help drive adoption of {% data variables.product.prodname_copilot_short %} in your organization, think about what teams or members are most excited about {% data variables.product.prodname_copilot_short %} or could benefit the most from {% data variables.product.prodname_copilot_short %}. You may want to enable {% data variables.product.prodname_copilot_short %} for those members before enabling {% data variables.product.prodname_copilot_short %} for your whole organization. This can help you discover blockers, demonstrate early success, and set your organization up for a successful {% data variables.product.prodname_copilot_short %} rollout. - -{% data reusables.copilot.self-serve-license-link %} +Enable {% data variables.product.prodname_copilot_short %} for some or all members of your organization. You may want to identify teams or members who are most likely to benefit from {% data variables.product.prodname_copilot_short %} and enable {% data variables.product.prodname_copilot_short %} for them first to discover potential blockers and demonstrate early success. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/granting-access-to-copilot-for-members-of-your-organization). If your organization is part of an enterprise on {% data variables.enterprise.data_residency_site %}, users must perform some additional setup to authenticate to their account from their development environment. See [AUTOTITLE](/copilot/managing-copilot/configure-personal-settings/using-github-copilot-with-an-account-on-ghecom). -## 5. Drive {% data variables.product.prodname_copilot_short %} adoption - -Planning and implementing an effective enablement process is essential to drive adoption of {% data variables.product.prodname_copilot_short %} in your organization. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/driving-copilot-adoption-in-your-company). - -## 6. Enhance the {% data variables.product.prodname_copilot_short %} experience - -Enhance the {% data variables.product.prodname_copilot_short %} experience for your organization by: +## Next steps -* **Setting up knowledge bases** for use with {% data variables.copilot.copilot_chat_short %} _({% data variables.copilot.copilot_enterprise_short %} only)_. See [AUTOTITLE](/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-github-copilot-features-in-your-organization/managing-copilot-knowledge-bases). -* **Installing {% data variables.copilot.copilot_extensions_short %}** to integrate other tools with {% data variables.copilot.copilot_chat_short %}. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-github-copilot-features-in-your-organization/installing-github-copilot-extensions-for-your-organization). -* **Adding {% data variables.copilot.copilot_coding_agent %}** as a team member to work asynchronously on issues. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/enabling-developers/using-copilot-coding-agent-in-org). +{% data reusables.copilot.setup-next-steps %} diff --git a/data/reusables/copilot/setup-next-steps.md b/data/reusables/copilot/setup-next-steps.md new file mode 100644 index 000000000000..7bb6f25d2d08 --- /dev/null +++ b/data/reusables/copilot/setup-next-steps.md @@ -0,0 +1,4 @@ +* **Explore self-service license management options**. Many successful rollouts use a self-service model where developers can claim a license without approval. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/setting-up-a-self-serve-process-for-github-copilot-licenses). +* **Learn how to plan and implement an effective enablement process to drive {% data variables.product.prodname_copilot_short %} adoption**. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/driving-copilot-adoption-in-your-company). +* **Enhance the development experience by enabling and training developers on the latest features**. For example, share context with {% data variables.copilot.copilot_spaces %}, enable {% data variables.copilot.copilot_code-review_short %} on pull requests, and allow developers to experiment with prompts using {% data variables.product.prodname_github_models %}. For an example showing how these features fit together, see [AUTOTITLE](/copilot/tutorials/rolling-out-github-copilot-at-scale/enabling-developers/integrating-agentic-ai). +* **Add {% data variables.copilot.copilot_coding_agent %} as a team member for asynchronous issue work**. See [AUTOTITLE](/copilot/rolling-out-github-copilot-at-scale/enabling-developers/using-copilot-coding-agent-in-org).