Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# ---------------------------------------------------------------
# To update the sha:
# https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250529-200944-g9be5a274f AS base
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250616-220726-g8823b63b3 AS base

# Install curl for Node install and determining the early access branch
# Install git for cloning docs-early-access & translations repos
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/coding-agent/assign-to-copilot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/coding-agent/firewall-warning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/coding-agent/issue-link-to-pr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/coding-agent/log-stop-session.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/coding-agent/log-view-session.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/sdlc-guide/agent-mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/sdlc-guide/agent-pr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/sdlc-guide/autofix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/sdlc-guide/issue-creation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/sdlc-guide/model-compare.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/tell-me-about-repo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/copilot/vsc-mcp-server-restart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/dependabot/dependabot-alert-fix-summary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/dependabot/dependabot-alert-timeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/dependabot/dependabot-vnet-logs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/issues/issues-create-saved-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/models/github-models-commit-changes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/models/github-models-compare-toggle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/images/help/models/github-models-datasets.png
Binary file modified assets/images/help/models/github-models-system-prompt.png
Binary file modified assets/images/social-cards/actions.png
Binary file modified assets/images/social-cards/code-security.png
Binary file modified assets/images/social-cards/copilot.png
Binary file modified assets/images/social-cards/default.png
Binary file modified assets/images/social-cards/issues.png
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ We track Appeals and Reinstatements in our [Transparency Center](https://transpa

### Legal Rights

If you believe that a final decision made on Appeal is still incorrect you may, in certain circumstances, have additional rights to seek review of the decision under your local law. For example, if you are located in the European Union, it might be possible to access an out-of-court dispute settlement process under the [Digital Services Act](https://eur-lex.europa.eu/eli/reg/2022/2065/oj#d1e2819-1-1).
If you believe that a final decision made on Appeal is still incorrect you may, in certain circumstances, have additional rights to seek review of the decision under your local law. For example, if you are located in the European Union, it might be possible to access an out-of-court dispute settlement process under the [Digital Services Act](https://eur-lex.europa.eu/eli/reg/2022/2065/oj#d1e2819-1-1). This process reflects GitHub's commitment to internationally recognized human rights set out in the United Nations Guiding Principles on Business and Human Rights (UNGPs), privacy, and free expression.
7 changes: 5 additions & 2 deletions data/ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ search:
references: Copilot Sources
loading_status_message: Loading Copilot response...
done_loading_status_message: Done loading Copilot response
copy_answer: Copy answer
copied_announcement: Copied!
share_answer: Copy answer URL
share_copied_announcement: Copied share URL!
thumbs_up: This answer was helpful
thumbs_down: This answer was not helpful
thumbs_announcement: Thank you for your feedback!
Expand All @@ -61,6 +61,9 @@ search:
query_too_large: Sorry, your question is too long. Please try shortening it and asking again.
asked_too_many_times: Sorry, you've asked too many questions in a short time period. Please wait a few minutes and try again.
invalid_query: Sorry, I'm unable to answer that question. Please try asking a different question.
response:
copy_code: Copy code to clipboard
copied_code: Copied!
failure:
general_title: There was an error loading search results.
ai_title: There was an error loading Copilot.
Expand Down
2 changes: 2 additions & 0 deletions src/content-linter/lib/linting-rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { liquidTagWhitespace } from './liquid-tag-whitespace.js'
import { linkQuotation } from './link-quotation.js'
import { octiconAriaLabels } from './octicon-aria-labels.js'
import { liquidIfversionVersions } from './liquid-ifversion-versions.js'
import { noteWarningFormatting } from './note-warning-formatting.js'

const noDefaultAltText = markdownlintGitHub.find((elem) =>
elem.names.includes('no-default-alt-text'),
Expand Down Expand Up @@ -84,5 +85,6 @@ export const gitHubDocsMarkdownlint = {
liquidTagWhitespace,
linkQuotation,
octiconAriaLabels,
noteWarningFormatting,
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export const listFirstWordCapitalization = {
description: 'First word of list item should be capitalized',
tags: ['ul', 'ol'],
function: (params, onError) => {
// Skip site-policy directory as these are legal documents with specific formatting requirements
if (params.name && params.name.includes('content/site-policy/')) return

// We're going to look for a sequence of 3 tokens. If the markdown
// is a really small string, it might not even have that many tokens
// in it. Can bail early.
Expand Down
220 changes: 220 additions & 0 deletions src/content-linter/lib/linting-rules/note-warning-formatting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { addError } from 'markdownlint-rule-helpers'
import { getRange } from '../helpers/utils.js'
import frontmatter from '#src/frame/lib/read-frontmatter.js'

export const noteWarningFormatting = {
names: ['GHD049', 'note-warning-formatting'],
description: 'Note and warning tags should be formatted according to style guide',
tags: ['formatting', 'callouts', 'notes', 'warnings', 'style'],
severity: 'warning',
function: (params, onError) => {
// Skip autogenerated files
const frontmatterString = params.frontMatterLines.join('\n')
const fm = frontmatter(frontmatterString).data
if (fm && fm.autogenerated) return

const lines = params.lines
let inLegacyNote = false
let noteStartLine = null
let noteContent = []

for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const lineNumber = i + 1

// Check for legacy {% note %} tags
if (line.trim() === '{% note %}') {
inLegacyNote = true
noteStartLine = lineNumber
noteContent = []

// Check for missing line break before {% note %}
const prevLine = i > 0 ? lines[i - 1] : ''
if (prevLine.trim() !== '') {
const range = getRange(line, '{% note %}')
addError(onError, lineNumber, 'Add a blank line before {% note %} tag', line, range, {
editColumn: 1,
deleteCount: 0,
insertText: '\n',
})
}
continue
}

// Check for end of legacy note
if (line.trim() === '{% endnote %}') {
if (inLegacyNote) {
inLegacyNote = false

// Check for missing line break after {% endnote %}
const nextLine = i < lines.length - 1 ? lines[i + 1] : ''
if (nextLine.trim() !== '') {
const range = getRange(line, '{% endnote %}')
addError(onError, lineNumber, 'Add a blank line after {% endnote %} tag', line, range, {
editColumn: line.length + 1,
deleteCount: 0,
insertText: '\n',
})
}

// Check note content formatting
validateNoteContent(noteContent, noteStartLine, onError)
}
continue
}

// Collect content inside legacy notes
if (inLegacyNote) {
noteContent.push({ text: line, lineNumber: lineNumber })
continue
}

// Check for new-style callouts > [!NOTE], > [!WARNING], > [!DANGER]
const calloutMatch = line.match(/^>\s*\[!(NOTE|WARNING|DANGER)\]\s*$/)
if (calloutMatch) {
const calloutType = calloutMatch[1]

// Check for missing line break before callout
const prevLine = i > 0 ? lines[i - 1] : ''
if (prevLine.trim() !== '') {
const range = getRange(line, line.trim())
addError(
onError,
lineNumber,
`Add a blank line before > [!${calloutType}] callout`,
line,
range,
{
editColumn: 1,
deleteCount: 0,
insertText: '\n',
},
)
}

// Find the end of this callout block and validate content
const calloutContent = []
let j = i + 1
while (j < lines.length && lines[j].startsWith('>')) {
if (lines[j].trim() !== '>') {
calloutContent.push({ text: lines[j], lineNumber: j + 1 })
}
j++
}

// Check for missing line break after callout
if (j < lines.length && lines[j].trim() !== '') {
const range = getRange(lines[j], lines[j].trim())
addError(
onError,
j + 1,
`Add a blank line after > [!${calloutType}] callout block`,
lines[j],
range,
{
editColumn: 1,
deleteCount: 0,
insertText: '\n',
},
)
}

validateCalloutContent(calloutContent, calloutType, lineNumber, onError)
i = j - 1 // Skip to end of callout block
continue
}

// Check for orphaned **Note:**/**Warning:**/**Danger:** outside callouts
const orphanedPrefixMatch = line.match(/\*\*(Note|Warning|Danger):\*\*/)
if (orphanedPrefixMatch && !inLegacyNote && !line.startsWith('>')) {
const range = getRange(line, orphanedPrefixMatch[0])
addError(
onError,
lineNumber,
`${orphanedPrefixMatch[1]} prefix should be inside a callout block`,
line,
range,
null, // No auto-fix as this requires human decision
)
}
}
},
}

/**
* Validate content inside legacy {% note %} blocks
*/
function validateNoteContent(noteContent, noteStartLine, onError) {
if (noteContent.length === 0) return

const contentLines = noteContent.filter((item) => item.text.trim() !== '')
if (contentLines.length === 0) return

// Count bullet points
const bulletLines = contentLines.filter((item) => item.text.trim().match(/^[*\-+]\s/))
if (bulletLines.length > 2) {
const range = getRange(bulletLines[2].text, bulletLines[2].text.trim())
addError(
onError,
bulletLines[2].lineNumber,
'Do not include more than 2 bullet points inside a callout',
bulletLines[2].text,
range,
null, // No auto-fix as this requires content restructuring
)
}

// Check for missing prefix (only if it looks like a traditional note)
const firstContentLine = contentLines[0]
const allContent = contentLines.map((line) => line.text).join(' ')
const hasButtons =
allContent.includes('<a href=') || allContent.includes('btn') || allContent.includes('class=')

if (
!hasButtons &&
!firstContentLine.text.includes('**Note:**') &&
!firstContentLine.text.includes('**Warning:**') &&
!firstContentLine.text.includes('**Danger:**')
) {
const range = getRange(firstContentLine.text, firstContentLine.text.trim())
addError(
onError,
firstContentLine.lineNumber,
'Note content should start with **Note:**, **Warning:**, or **Danger:**',
firstContentLine.text,
range,
{
editColumn: firstContentLine.text.indexOf(firstContentLine.text.trim()) + 1,
deleteCount: 0,
insertText: '**Note:** ',
},
)
}
}

/**
* Validate content inside new-style callouts
*/
function validateCalloutContent(calloutContent, calloutType, calloutStartLine, onError) {
if (calloutContent.length === 0) return

const contentLines = calloutContent.filter((item) => item.text.trim() !== '>')
if (contentLines.length === 0) return

// Count bullet points
const bulletLines = contentLines.filter((item) => item.text.match(/^>\s*[*\-+]\s/))
if (bulletLines.length > 2) {
const range = getRange(bulletLines[2].text, bulletLines[2].text.trim())
addError(
onError,
bulletLines[2].lineNumber,
'Do not include more than 2 bullet points inside a callout',
bulletLines[2].text,
range,
null, // No auto-fix as this requires content restructuring
)
}

// For new-style callouts, the prefix is handled by the [!NOTE] syntax itself
// so we don't need to check for manual **Note:** prefixes
}
21 changes: 21 additions & 0 deletions src/content-linter/tests/unit/list-first-word-captitalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,25 @@ describe(listFirstWordCapitalization.names.join(' - '), () => {
const errors = result.markdown
expect(errors.length).toBe(0)
})

test('skips site-policy directory files', async () => {
const markdown = [
'- list item should normally be flagged',
'- another uncapitalized item',
'- a. this is alphabetic numbering',
'- b. this is also alphabetic numbering',
].join('\n')

// Test normal behavior (should flag errors)
const normalResult = await runRule(listFirstWordCapitalization, { strings: { markdown } })
expect(normalResult.markdown.length).toBeGreaterThan(0)

// Test site-policy exclusion (should skip all errors)
const sitePolicyResult = await runRule(listFirstWordCapitalization, {
strings: {
'content/site-policy/some-policy.md': markdown,
},
})
expect(sitePolicyResult['content/site-policy/some-policy.md'].length).toBe(0)
})
})
Loading
Loading