Skip to content

Commit 68f247a

Browse files
authored
Merge pull request #40326 from github/repo-sync
Repo sync
2 parents d9b6b79 + 2a29f59 commit 68f247a

File tree

17 files changed

+409
-22
lines changed

17 files changed

+409
-22
lines changed

content/admin/administering-your-instance/administering-your-instance-from-the-web-ui/managing-search-indices-for-your-instance.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ For more information about search for {% data variables.product.prodname_ghe_ser
2121

2222
{% data variables.product.prodname_ghe_server %} reconciles the state of the search index with data on the instance automatically and regularly, including:
2323

24-
* Issues, pull requests, repositories, and users in the database
24+
* Issues,{% ifversion ghes > 3.17 %} projects,{% endif %} pull requests, repositories, and users in the database
2525
* Git repositories (source code) on disk
2626

2727
In normal use, enterprise owners do not need to create new indices or schedule repair jobs. For troubleshooting or other support purposes, {% data variables.contact.github_support %} may instruct you to run a repair job.

content/code-security/secret-scanning/introduction/supported-secret-scanning-patterns.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ In addition to these generic non-provider patterns, {% data variables.product.pr
101101

102102
Service providers update the patterns used to generate tokens periodically and may support more than one version of a token. Push protection only supports the most recent token versions that {% data variables.product.prodname_secret_scanning %} can identify with confidence. This avoids push protection blocking commits unnecessarily when a result may be a false positive, which is more likely to happen with legacy tokens.<!-- markdownlint-disable-line MD053 -->
103103

104+
#### Multi-part secrets
105+
106+
<a name="multi-part-secrets"></a>
107+
108+
By default, {% data variables.product.prodname_secret_scanning %} supports validation for pair-matched access keys and key IDs.
109+
110+
{% data variables.product.prodname_secret_scanning_caps %} also supports validation for individual key IDs for Amazon AWS Access Key IDs, in addition to existing pair matching.
111+
112+
A key ID will show as active if {% data variables.product.prodname_secret_scanning %} confirms the key ID exists, regardless of whether or not a corresponding access key is found. The key ID will show as `inactive` if it's invalid (for example, if it is not a real key ID).
113+
114+
Where a valid pair is found, the {% data variables.product.prodname_secret_scanning %} alerts will be linked.<!-- markdownlint-disable-line MD053 -->
115+
104116
## Further reading
105117

106118
* [AUTOTITLE](/code-security/secret-scanning/managing-alerts-from-secret-scanning/about-alerts)

data/code-languages.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ bash:
77
bicep:
88
name: Bicep
99
comment: slash
10+
copilot:
11+
name: Copilot Chat prompt
12+
comment: none
1013
csharp:
1114
name: C#
1215
comment: slash

package-lock.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@
243243
"tcp-port-used": "1.0.2",
244244
"tsx": "^4.19.4",
245245
"unified": "^11.0.5",
246+
"unist-util-find": "^3.0.0",
246247
"unist-util-visit": "^5.0.0",
247248
"url-template": "^3.1.1",
248249
"walk-sync": "^4.0.1"

src/content-render/liquid/engine.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Octicon from './octicon'
66
import Ifversion from './ifversion'
77
import { Tool, tags as toolTags } from './tool'
88
import { Spotlight, tags as spotlightTags } from './spotlight'
9+
import { Prompt } from './prompt'
910

1011
export const engine = new Liquid({
1112
extname: '.html',
@@ -25,6 +26,8 @@ for (const tag in spotlightTags) {
2526
engine.registerTag(tag, Spotlight)
2627
}
2728

29+
engine.registerTag('prompt', Prompt)
30+
2831
/**
2932
* Like the `size` filter, but specifically for
3033
* getting the number of keys in an object
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// src/content-render/liquid/prompt.js
2+
// Defines {% prompt %}…{% endprompt %} to wrap its content in <code> and append the Copilot icon.
3+
4+
import octicons from '@primer/octicons'
5+
6+
export const Prompt = {
7+
type: 'block',
8+
9+
// Collect everything until {% endprompt %}
10+
parse(tagToken, remainTokens) {
11+
this.templates = []
12+
const stream = this.liquid.parser.parseStream(remainTokens)
13+
stream
14+
.on('template', (tpl) => this.templates.push(tpl))
15+
.on('tag:endprompt', () => stream.stop())
16+
.on('end', () => {
17+
throw new Error(`{% prompt %} tag not closed`)
18+
})
19+
stream.start()
20+
},
21+
22+
// Render the inner Markdown, wrap in <code>, then append the SVG
23+
render: function* (scope) {
24+
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
25+
26+
// build a URL with the prompt text encoded as query parameter
27+
const promptParam = encodeURIComponent(content)
28+
const href = `https://github.com/copilot?prompt=${promptParam}`
29+
return `<code>${content}</code><a href="${href}" target="_blank" class="tooltipped tooltipped-nw ml-1" aria-label="Run this prompt in Copilot Chat" style="text-decoration:none;">${octicons.copilot.toSVG()}</a>`
30+
},
31+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
import { renderContent } from '@/content-render/index'
3+
4+
describe('code-header plugin', () => {
5+
describe('copilot language code blocks', () => {
6+
it('should render basic copilot code block without header (no copy meta)', async () => {
7+
const markdown = '```copilot\nImprove the variable names in this function\n```'
8+
9+
const html = await renderContent(markdown)
10+
11+
// Should keep copilot as the language (not convert to text without copy meta)
12+
expect(html).toContain('language-copilot')
13+
// Should NOT wrap in code-example div since no copy meta
14+
expect(html).not.toContain('code-example')
15+
// Should NOT have header since no copy meta
16+
expect(html).not.toContain('<header')
17+
})
18+
19+
it('should render copilot code block with copy button when copy meta is present', async () => {
20+
const markdown = '```copilot copy\nImprove the variable names in this function\n```'
21+
22+
const html = await renderContent(markdown)
23+
24+
// Should be wrapped in code-example div
25+
expect(html).toContain('code-example')
26+
// Should have header with copy button
27+
expect(html).toContain('<header')
28+
expect(html).toContain('js-btn-copy')
29+
expect(html).toContain('language-copilot')
30+
// Should NOT have prompt button (no prompt meta)
31+
expect(html).not.toContain('https://github.com/copilot?prompt=')
32+
})
33+
34+
it('should render copilot code block with prompt button only (no copy meta)', async () => {
35+
const markdown = '```copilot prompt\nImprove the variable names in this function\n```'
36+
37+
const html = await renderContent(markdown)
38+
39+
// Should be wrapped in code-example div
40+
expect(html).toContain('code-example')
41+
// Should have header
42+
expect(html).toContain('<header')
43+
// Should have prompt button
44+
expect(html).toContain('https://github.com/copilot?prompt=')
45+
expect(html).toContain('language-copilot')
46+
// Should NOT have copy button
47+
expect(html).not.toContain('js-btn-copy')
48+
})
49+
50+
it('should render copilot code block with both copy and prompt buttons when prompt meta is present', async () => {
51+
const markdown = '```copilot copy prompt\nImprove the variable names in this function\n```'
52+
53+
const html = await renderContent(markdown)
54+
55+
// Should be wrapped in code-example div
56+
expect(html).toContain('code-example')
57+
// Should have header with copy button
58+
expect(html).toContain('<header')
59+
expect(html).toContain('js-btn-copy')
60+
// Should have prompt button with encoded URL
61+
expect(html).toContain('https://github.com/copilot?prompt=')
62+
expect(html).toContain('Improve%20the%20variable%20names%20in%20this%20function')
63+
// Should have Copilot icon button
64+
expect(html).toContain('aria-label="Run this prompt in Copilot Chat"')
65+
expect(html).toContain('language-copilot')
66+
})
67+
68+
it('should render copilot code block with context reference when ref meta is present', async () => {
69+
const markdown = `
70+
\`\`\`javascript id=js-age
71+
function logPersonsAge(a, b, c) {
72+
if (c) {
73+
console.log(a + " is " + b + " years old.");
74+
} else {
75+
console.log(a + " does not want to reveal their age.");
76+
}
77+
}
78+
\`\`\`
79+
80+
\`\`\`copilot copy prompt ref=js-age
81+
Improve the variable names in this function
82+
\`\`\`
83+
`
84+
85+
const html = await renderContent(markdown)
86+
87+
// Should have prompt button with both code blocks in URL
88+
expect(html).toContain('https://github.com/copilot?prompt=')
89+
// Should contain encoded content from both the referenced code and the prompt
90+
expect(html).toContain('function%20logPersonsAge')
91+
expect(html).toContain('Improve%20the%20variable%20names')
92+
// Should have different aria-label indicating context
93+
expect(html).toContain('aria-label="Run this prompt with context in Copilot Chat"')
94+
})
95+
96+
it('should render copilot code block with prompt and ref only (no copy meta)', async () => {
97+
const markdown = `
98+
\`\`\`javascript id=js-age
99+
function logPersonsAge(a, b, c) {
100+
if (c) {
101+
console.log(a + " is " + b + " years old.");
102+
} else {
103+
console.log(a + " does not want to reveal their age.");
104+
}
105+
}
106+
\`\`\`
107+
108+
\`\`\`copilot prompt ref=js-age
109+
Improve the variable names in this function
110+
\`\`\`
111+
`
112+
113+
const html = await renderContent(markdown)
114+
115+
// Should have prompt button with both code blocks in URL
116+
expect(html).toContain('https://github.com/copilot?prompt=')
117+
// Should contain encoded content from both the referenced code and the prompt
118+
expect(html).toContain('function%20logPersonsAge')
119+
expect(html).toContain('Improve%20the%20variable%20names')
120+
// Should have different aria-label indicating context
121+
expect(html).toContain('aria-label="Run this prompt with context in Copilot Chat"')
122+
// Should NOT have copy button
123+
expect(html).not.toContain('js-btn-copy')
124+
})
125+
})
126+
127+
describe('edge cases', () => {
128+
it('should handle missing reference gracefully and fall back to current code only', async () => {
129+
// Mock console.warn to capture warning
130+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
131+
132+
const markdown =
133+
'```copilot copy prompt ref=nonexistent-id\nImprove the variable names in this function\n```'
134+
135+
const html = await renderContent(markdown)
136+
137+
// Should warn about missing reference
138+
expect(consoleWarnSpy).toHaveBeenCalledWith(
139+
expect.stringContaining("Can't find referenced code block with id=nonexistent-id"),
140+
)
141+
142+
// Should still render with prompt button using current code only
143+
expect(html).toContain('https://github.com/copilot?prompt=')
144+
expect(html).toContain('Improve%20the%20variable%20names%20in%20this%20function')
145+
// Should NOT contain any referenced code since none was found
146+
expect(html).not.toContain('function%20logPersonsAge')
147+
// Should have standard aria-label (not context version)
148+
expect(html).toContain('aria-label="Run this prompt in Copilot Chat"')
149+
// Should not crash or fail
150+
expect(html).toContain('code-example')
151+
152+
// Restore console.warn
153+
consoleWarnSpy.mockRestore()
154+
})
155+
156+
it('should not process annotated code blocks', async () => {
157+
const markdown = `\`\`\`javascript copy annotate
158+
// This is an annotation
159+
function test() {}
160+
\`\`\``
161+
162+
const html = await renderContent(markdown)
163+
164+
// Should NOT wrap in code-example div (annotated blocks are excluded)
165+
expect(html).not.toContain('code-example')
166+
})
167+
168+
it('should handle regular code blocks with copy', async () => {
169+
const markdown = '```javascript copy\nfunction test() {}\n```'
170+
171+
const html = await renderContent(markdown)
172+
173+
// Should render with copy button
174+
expect(html).toContain('code-example')
175+
expect(html).toContain('js-btn-copy')
176+
expect(html).toContain('language-javascript')
177+
})
178+
})
179+
180+
describe('URL encoding', () => {
181+
it('should properly encode special characters in prompt URLs', async () => {
182+
const markdown = '```copilot copy prompt\nHow do I handle "quotes" and & symbols?\n```'
183+
184+
const html = await renderContent(markdown)
185+
186+
// Should encode quotes and ampersands properly
187+
expect(html).toContain('%22quotes%22')
188+
expect(html).toContain('%26%20symbols')
189+
})
190+
191+
it('should handle multiline prompts correctly', async () => {
192+
const markdown = `\`\`\`copilot copy prompt
193+
This is line 1
194+
This is line 2
195+
\`\`\``
196+
197+
const html = await renderContent(markdown)
198+
199+
// Should encode newlines properly
200+
expect(html).toContain('This%20is%20line%201%0AThis%20is%20line%202')
201+
})
202+
})
203+
})

src/content-render/tests/prompt.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { describe, expect, test } from 'vitest'
2+
import { renderContent } from '@/content-render/index'
3+
4+
describe('prompt tag', () => {
5+
test('wraps content in <code> and appends svg', async () => {
6+
const input = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
7+
const output = await renderContent(input)
8+
expect(output).toContain('<code>example prompt text</code><a')
9+
expect(output).toContain('<svg')
10+
})
11+
})

0 commit comments

Comments
 (0)