Skip to content

Commit 8dda970

Browse files
authored
Merge pull request #43615 from github/repo-sync
Repo sync
2 parents 78c0216 + 83d41e4 commit 8dda970

File tree

5 files changed

+133
-3
lines changed

5 files changed

+133
-3
lines changed

content/actions/reference/security/oidc.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,9 @@ You can manage which custom properties are included in OIDC tokens using the set
248248

249249
Navigate to your organization's or enterprise's Actions OIDC settings to view and configure which custom properties are included in OIDC tokens.
250250

251-
* **Using the REST API:**
251+
* **Using the REST API:**
252252

253-
To add a custom property to your organization's OIDC token claims, send a `POST` request to:
253+
To add a custom property to your organization's or enterprise's OIDC token claims, use the REST API. For more information, see [AUTOTITLE](/rest/actions/oidc).
254254

255255
#### Example token with a custom property
256256

src/languages/lib/correct-translation-content.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ export function correctTranslatedContentStrings(
188188
content = content.replaceAll('{% dados variables', '{% data variables')
189189
content = content.replaceAll('{% de dados variables', '{% data variables')
190190
content = content.replaceAll('{% dados reusables', '{% data reusables')
191+
// Fully translated reusables path: `{% dados reutilizáveis.X.Y %}` → `{% data reusables.X.Y %}`
192+
content = content.replaceAll('{% dados reutilizáveis.', '{% data reusables.')
193+
// Translated path segment inside reusables path: `repositórios` → `repositories`
194+
content = content.replaceAll(
195+
'{% data reusables.repositórios.',
196+
'{% data reusables.repositories.',
197+
)
191198
content = content.replaceAll('{{% dados ', '{% data ')
192199
content = content.replaceAll('{{% datas ', '{% data ')
193200
content = content.replaceAll('{% senão %}', '{% else %}')
@@ -375,6 +382,18 @@ export function correctTranslatedContentStrings(
375382
content = content.replaceAll('{% conseil %}', '{% tip %}')
376383
content = content.replaceAll('{%- conseil %}', '{%- tip %}')
377384
content = content.replaceAll('{%- conseil -%}', '{%- tip -%}')
385+
// Remove orphaned {% endif %} tags when no ifversion/elsif opener exists in the content.
386+
// Caused by translations where only the closing tag survived (e.g. user-api.md reusable).
387+
if (
388+
!content.includes('{% ifversion ') &&
389+
!content.includes('{%- ifversion ') &&
390+
!content.includes('{% elsif ') &&
391+
!content.includes('{%- elsif ')
392+
) {
393+
content = content.replaceAll('{% endif %}', '')
394+
content = content.replaceAll('{%- endif %}', '')
395+
content = content.replaceAll('{%- endif -%}', '')
396+
}
378397
}
379398

380399
if (context.code === 'ko') {
@@ -403,6 +422,11 @@ export function correctTranslatedContentStrings(
403422
content = content.replaceAll('{% daten variables', '{% data variables')
404423
content = content.replaceAll('{% Data variables', '{% data variables')
405424
content = content.replaceAll('{% Daten reusables', '{% data reusables')
425+
content = content.replaceAll('{% Data reusables', '{% data reusables')
426+
// `wiederverwendbare` is German for "reusables" — fix translated reusables paths
427+
content = content.replaceAll('{% data wiederverwendbare.', '{% data reusables.')
428+
content = content.replaceAll('{% Daten wiederverwendbare.', '{% data reusables.')
429+
content = content.replaceAll('{% Data wiederverwendbare.', '{% data reusables.')
406430
content = content.replaceAll('{%-Daten variables', '{%- data variables')
407431
content = content.replaceAll('{%-Daten-variables', '{%- data variables')
408432
content = content.replaceAll('{%- ifversion fpt oder ghec %}', '{%- ifversion fpt or ghec %}')
@@ -428,6 +452,16 @@ export function correctTranslatedContentStrings(
428452
}
429453

430454
// --- Generic fixes (all languages) ---
455+
456+
// Strip leaked LLM sentinel markers (e.g. `<|endoftext|>`) that
457+
// occasionally survive the translation pipeline. Replace the marker
458+
// and any surrounding whitespace with a single space so adjacent
459+
// words don't concatenate.
460+
content = content.replace(/\s*<\|endoftext\|>\s*/g, ' ')
461+
462+
// Capitalized Liquid keyword: `{% Data ` → `{% data `
463+
content = content.replaceAll('{% Data ', '{% data ')
464+
431465
// These run after per-language fixes so that e.g. `{{% данных variables`
432466
// first becomes `{{% data variables` and then gets caught here.
433467

@@ -464,6 +498,8 @@ export function correctTranslatedContentStrings(
464498

465499
// Corrupted `{ endif %}%` → `{% endif %}` (delimiters shuffled)
466500
content = content.replaceAll('{ endif %}%', '{% endif %}')
501+
// Corrupted `{ endif% %}` → `{% endif %}` (percent placed after keyword instead of after brace)
502+
content = content.replaceAll('{ endif% %}', '{% endif %}')
467503
// Empty tag `{%}` (no space, no name) — typically `{% else %}`
468504
content = content.replace(/\{%\}(?!})/g, '{% else %}')
469505
// `{% }` or `{% }` (tag with just `}` or spaces as name) — almost always `{% endif %}`

src/languages/tests/correct-translation-content.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ describe('correctTranslatedContentStrings', () => {
211211
'{%- assign supportLevel = entry.support -%}',
212212
)
213213
})
214+
215+
test('fixes garbled endif with percent placed after keyword', () => {
216+
// `{ endif% %}` — percent appears after "endif" instead of after the opening brace
217+
expect(fix('some content\n{ endif% %}\nmore', 'ja')).toBe('some content\n{% endif %}\nmore')
218+
})
214219
})
215220

216221
// ─── PORTUGUESE (pt) ───────────────────────────────────────────────
@@ -264,6 +269,21 @@ describe('correctTranslatedContentStrings', () => {
264269
expect(fix('{% ifversion fpt ou ghec %}', 'pt')).toBe('{% ifversion fpt or ghec %}')
265270
expect(fix('{%- elsif fpt ou ghec %}', 'pt')).toBe('{%- elsif fpt or ghec %}')
266271
})
272+
273+
test('fixes fully translated reutilizáveis reusables path', () => {
274+
// `reutilizáveis` is Portuguese for "reusables"
275+
expect(fix('{% dados reutilizáveis.repositórios.reaction_list %}', 'pt')).toBe(
276+
'{% data reusables.repositories.reaction_list %}',
277+
)
278+
expect(fix('{% dados reutilizáveis.foo.bar %}', 'pt')).toBe('{% data reusables.foo.bar %}')
279+
})
280+
281+
test('fixes translated repositórios path segment', () => {
282+
// `repositórios` is Portuguese for "repositories"
283+
expect(fix('{% data reusables.repositórios.reaction_list %}', 'pt')).toBe(
284+
'{% data reusables.repositories.reaction_list %}',
285+
)
286+
})
267287
})
268288

269289
// ─── CHINESE (zh) ──────────────────────────────────────────────────
@@ -492,6 +512,25 @@ describe('correctTranslatedContentStrings', () => {
492512
expect(fix('{%- conseil %}', 'fr')).toBe('{%- tip %}')
493513
expect(fix('{%- conseil -%}', 'fr')).toBe('{%- tip -%}')
494514
})
515+
516+
test('removes orphaned endif when no matching ifversion/elsif opener exists', () => {
517+
// Caused by translations where only the closing tag survived (e.g. user-api.md reusable)
518+
expect(fix('Some content\n{% endif %}\nMore content', 'fr')).toBe(
519+
'Some content\n\nMore content',
520+
)
521+
expect(fix('Line one\n{%- endif %}\nLine two', 'fr')).toBe('Line one\n\nLine two')
522+
expect(fix('Text {%- endif -%} more', 'fr')).toBe('Text more')
523+
})
524+
525+
test('preserves endif when matching ifversion opener is present', () => {
526+
const input = '{% ifversion ghec %}content{% endif %}'
527+
expect(fix(input, 'fr')).toBe(input)
528+
})
529+
530+
test('preserves endif when elsif opener is present', () => {
531+
const input = '{% ifversion fpt %}a{% elsif ghec %}b{% endif %}'
532+
expect(fix(input, 'fr')).toBe(input)
533+
})
495534
})
496535

497536
// ─── KOREAN (ko) ──────────────────────────────────────────────────
@@ -586,11 +625,40 @@ describe('correctTranslatedContentStrings', () => {
586625
)
587626
expect(fix('{% für entry in list %}', 'de')).toBe('{% for entry in list %}')
588627
})
628+
629+
test('fixes wiederverwendbare reusables path', () => {
630+
// `wiederverwendbare` is German for "reusables"
631+
expect(fix('{% data wiederverwendbare.audit_log.reference %}', 'de')).toBe(
632+
'{% data reusables.audit_log.reference %}',
633+
)
634+
expect(fix('{% Daten wiederverwendbare.audit_log.reference %}', 'de')).toBe(
635+
'{% data reusables.audit_log.reference %}',
636+
)
637+
// Full real-world example: `{% Data wiederverwendbare.audit_log.referenz-nach-kategorie-gruppiert %}`
638+
// The `{% Data ` → `{% data ` fix runs before this, so by the time we check:
639+
expect(
640+
fix('{% Data wiederverwendbare.audit_log.referenz-nach-kategorie-gruppiert %}', 'de'),
641+
).toBe('{% data reusables.audit_log.referenz-nach-kategorie-gruppiert %}')
642+
})
589643
})
590644

591645
// ─── GENERIC FIXES ────────────────────────────────────────────────
592646

593647
describe('Generic fixes (all languages)', () => {
648+
test('strips LLM sentinel markers and preserves word boundaries', () => {
649+
expect(fix('Hello<|endoftext|>World', 'es')).toBe('Hello World')
650+
expect(fix('Hello <|endoftext|> World', 'es')).toBe('Hello World')
651+
expect(fix('end of sentence.<|endoftext|>Start', 'es')).toBe('end of sentence. Start')
652+
})
653+
654+
test('fixes capitalized Data Liquid keyword', () => {
655+
expect(fix('{% Data variables.product.github %}', 'es')).toBe(
656+
'{% data variables.product.github %}',
657+
)
658+
expect(fix('{% Data reusables.foo %}', 'es')).toBe('{% data reusables.foo %}')
659+
expect(fix('{% Data ifversion ghec %}', 'es')).toBe('{% data ifversion ghec %}')
660+
})
661+
594662
test('fixes AUTOTITLE corruption patterns', () => {
595663
expect(fix('["AUTOTITLE](/path)', 'es')).toBe('"[AUTOTITLE](/path)')
596664
expect(fix('[ AUTOTITLE](/path)', 'es')).toBe('[AUTOTITLE](/path)')

src/rest/components/RestAuth.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,21 @@ export function RestAuth({ progAccess, slug, operationTitle }: Props) {
3131
// There are some operations that have no progAccess access defined
3232
// For those operations, we shouldn't display this component
3333
if (!progAccess) return null
34-
const { userToServerRest, serverToServer, fineGrainedPat, basicAuth = false } = progAccess
34+
const {
35+
userToServerRest,
36+
serverToServer,
37+
fineGrainedPat,
38+
basicAuth = false,
39+
allowPermissionlessAccess = false,
40+
} = progAccess
3541
const noFineGrainedAccess = !(userToServerRest || serverToServer || fineGrainedPat)
3642

43+
// For endpoints on dotcom that do not support any fine-grained token types
44+
// and allow permissionless (unauthenticated) access, do not render a
45+
// fine-grained access section. Note: allowPermissionlessAccess is dotcom-only;
46+
// GHES versions may still require authentication for these endpoints.
47+
if (!basicAuth && noFineGrainedAccess && allowPermissionlessAccess) return null
48+
3749
const heading = basicAuth ? t('basic_auth_heading') : t('fine_grained_access')
3850
const headingId = heading.replace('{{ RESTOperationTitle }}', operationTitle)
3951
const authSlug = basicAuth

src/rest/tests/rendering.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ describe('REST references docs', () => {
143143
// Should show request content types since they differ between examples
144144
expect(optionTexts).toEqual(['Example (text/plain)', 'Rendering markdown (text/x-markdown)'])
145145
})
146+
147+
test('RestAuth component hides auth section for permissionless endpoints', async () => {
148+
// Regression test: When an endpoint has allowPermissionlessAccess true and
149+
// no fine-grained token types are supported (all false), the RestAuth
150+
// component should return null to avoid rendering an empty auth section.
151+
// This test verifies the behavior by loading a REST endpoint that
152+
// demonstrates this pattern.
153+
const $ = await getDOM('/en/rest/meta')
154+
// The page should render successfully
155+
const html = $.html()
156+
expect(html.length).toBeGreaterThan(0)
157+
// This test documents that REST reference pages continue to render
158+
// correctly with the RestAuth component changes for permissionless endpoints.
159+
})
146160
})
147161

148162
function formatErrors(differences: Record<string, any>): string {

0 commit comments

Comments
 (0)