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
16 changes: 15 additions & 1 deletion content/get-started/start-your-journey/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Start your journey
intro: 'Learn the basics of {% data variables.product.github %}.'
intro: "Brand new to {% data variables.product.github %}? Learn the basics here."
versions:
fpt: '*'
ghes: '*'
Expand All @@ -22,4 +22,18 @@ children:
redirect_from:
- /github/getting-started-with-github/quickstart
- /get-started/quickstart
layout: journey-landing
journeyTracks:
- id: 'learn_the_basics'
title: 'Get started'
description: 'Master the fundamentals of {% data variables.product.github %} and Git.'
guides:
- '/get-started/start-your-journey/about-github-and-git'
- '/get-started/start-your-journey/creating-an-account-on-github'
- '/get-started/start-your-journey/hello-world'
- '/get-started/start-your-journey/setting-up-your-profile'
- '/get-started/start-your-journey/finding-inspiration-on-github'
- '/get-started/start-your-journey/downloading-files-from-github'
- '/get-started/start-your-journey/uploading-a-project-to-github'
- '/get-started/start-your-journey/git-and-github-learning-resources'
---
2 changes: 1 addition & 1 deletion data/features/dependabot-option-cooldown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
versions:
fpt: '*'
ghec: '*'
ghes: '>3.19'
ghes: '>3.18'
18 changes: 13 additions & 5 deletions src/article-api/middleware/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,19 @@ function incrementArticleLookup(

// logs the source of the request, if it's for hovercards it'll have the header X-Request-Source.
// see src/links/components/LinkPreviewPopover.tsx
const source =
req.get('X-Request-Source') ||
(req.get('Referer')
? `external-${new URL(req.get('Referer') || '').hostname || 'unknown'}`
: 'external')
let source = req.get('X-Request-Source')
if (!source) {
const referer = req.get('Referer')
if (referer) {
try {
source = `external-${new URL(referer).hostname || 'unknown'}`
} catch {
source = 'external'
}
} else {
source = 'external'
}
}

const tags = [
// According to https://docs.datadoghq.com/getting_started/tagging/#define-tags
Expand Down
9 changes: 9 additions & 0 deletions src/article-api/tests/article-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,13 @@ describe('article body api', () => {
const { error } = JSON.parse(res.body)
expect(error).toContain("isn't yet available in markdown")
})

test('invalid Referer header does not crash', async () => {
const res = await get(makeURL('/en/get-started/start-your-journey/hello-world'), {
headers: {
Referer: 'invalid-url',
},
})
expect(res.statusCode).toBe(200)
})
})
2 changes: 1 addition & 1 deletion src/codeql-cli/scripts/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async function main() {
includeBasePath: true,
globs: ['**/*.md'],
})
const cliMarkdownContents: Record<string, { data: any; content: string }> = {}
const cliMarkdownContents: Record<string, { data: Record<string, unknown>; content: string }> = {}

for (const file of markdownFiles) {
const sourceContent = await readFile(file, 'utf8')
Expand Down
7 changes: 4 additions & 3 deletions src/content-linter/lib/linting-rules/ctas-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,16 @@ export const ctasSchema: Rule = {
for (const error of errors) {
let message = ''
if (error.keyword === 'required') {
message = `Missing required parameter: ${(error.params as any)?.missingProperty}`
message = `Missing required parameter: ${(error.params as { missingProperty?: string })?.missingProperty}`
} else if (error.keyword === 'enum') {
const paramName = error.instancePath.substring(1)
// Get the actual invalid value from refParams and allowed values from params
const invalidValue = refParams[paramName]
const allowedValues = (error.params as any)?.allowedValues || []
const allowedValues =
(error.params as { allowedValues?: unknown[] })?.allowedValues || []
message = `Invalid value for ${paramName}: "${invalidValue}". Valid values are: ${allowedValues.join(', ')}`
} else if (error.keyword === 'additionalProperties') {
message = `Unexpected parameter: ${(error.params as any)?.additionalProperty}`
message = `Unexpected parameter: ${(error.params as { additionalProperty?: string })?.additionalProperty}`
} else {
message = `CTA URL validation error: ${error.message}`
}
Expand Down
20 changes: 14 additions & 6 deletions src/content-linter/lib/linting-rules/link-punctuation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,39 @@ import type { RuleParams, RuleErrorCallback, Rule } from '../../types'

import { doesStringEndWithPeriod, getRange, isStringQuoted } from '../helpers/utils'

// Minimal type for markdownit tokens used in this rule
interface MarkdownToken {
children?: MarkdownToken[]
line?: string
type?: string
content?: string
lineNumber?: number
}

export const linkPunctuation: Rule = {
names: ['GHD001', 'link-punctuation'],
description: 'Internal link titles must not contain punctuation',
tags: ['links', 'url'],
parser: 'markdownit',
function: (params: RuleParams, onError: RuleErrorCallback) => {
// Using 'any' type for token as markdownlint-rule-helpers doesn't provide TypeScript types
filterTokens(params, 'inline', (token: any) => {
filterTokens(params, 'inline', (token: MarkdownToken) => {
const { children, line } = token
let inLink = false
for (const child of children) {
for (const child of children || []) {
if (child.type === 'link_open') {
inLink = true
} else if (child.type === 'link_close') {
inLink = false
} else if (inLink && child.type === 'text') {
} else if (inLink && child.type === 'text' && child.content) {
const content = child.content.trim()
const hasPeriod = doesStringEndWithPeriod(content)
const hasQuotes = isStringQuoted(content)

if (hasPeriod || hasQuotes) {
const range = getRange(line, content)
const range = line ? getRange(line, content) : []
addError(
onError,
child.lineNumber,
child.lineNumber || 1,
'Remove quotes and/or period punctuation from the link title.',
child.content,
range,
Expand Down
11 changes: 7 additions & 4 deletions src/content-render/liquid/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import { getDataByLanguage } from '@/data-directory/lib/get-data'
const Syntax = /([a-z0-9/\\_.\-[\]]+)/i
const SyntaxHelp = "Syntax Error in 'data' - Valid syntax: data [path]"

// Using any for scope because it has custom environments property not in Liquid's Scope type
// Using unknown for scope because it has custom environments property not in Liquid's Scope type
interface CustomScope {
environments: any
[key: string]: any
environments: {
currentLanguage?: string
[key: string]: unknown
}
[key: string]: unknown
}

interface DataTag {
Expand All @@ -32,7 +35,7 @@ export default {
},

async render(scope: CustomScope) {
let text = getDataByLanguage(this.path, scope.environments.currentLanguage)
let text = getDataByLanguage(this.path, scope.environments.currentLanguage || '')
if (text === undefined) {
if (scope.environments.currentLanguage === 'en') {
const message = `Can't find the key 'data ${this.path}' in the scope.`
Expand Down
29 changes: 17 additions & 12 deletions src/fixtures/tests/internal-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('autotitle', () => {
test('internal links with AUTOTITLE resolves', async () => {
const $: cheerio.Root = await getDOM('/get-started/foo/autotitling')
const links = $('#article-contents a[href]')
links.each((i: number, element: any) => {
links.each((i: number, element: cheerio.Element) => {
if ($(element).attr('href')?.includes('/get-started/start-your-journey/hello-world')) {
expect($(element).text()).toBe('Hello World')
}
Expand Down Expand Up @@ -49,13 +49,14 @@ describe('cross-version-links', () => {

// Tests that the hardcoded prefix is always removed
const firstLink = links.filter(
(i: number, element: any) => $(element).text() === 'Hello world always in free-pro-team',
(i: number, element: cheerio.Element) =>
$(element).text() === 'Hello world always in free-pro-team',
)
expect(firstLink.attr('href')).toBe('/en/get-started/start-your-journey/hello-world')

// Tests that the second link always goes to [email protected]
const secondLink = links.filter(
(i: number, element: any) =>
(i: number, element: cheerio.Element) =>
$(element).text() === 'Autotitling page always in enterprise-server latest',
)
expect(secondLink.attr('href')).toBe(
Expand All @@ -72,29 +73,33 @@ describe('link-rewriting', () => {

{
const link = links.filter(
(i: number, element: any) => $(element).text() === 'Cross Version Linking',
(i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
)
expect(link.attr('href')).toMatch('/en/get-started/')
}

// Some links are left untouched

{
const link = links.filter((i: number, element: any) =>
const link = links.filter((i: number, element: cheerio.Element) =>
$(element).text().includes('Enterprise 11.10'),
)
expect(link.attr('href')).toMatch('/en/enterprise/')
}
{
const link = links.filter((i: number, element: any) => $(element).text().includes('peterbe'))
const link = links.filter((i: number, element: cheerio.Element) =>
$(element).text().includes('peterbe'),
)
expect(link.attr('href')).toMatch(/^https:/)
}
{
const link = links.filter((i: number, element: any) => $(element).text().includes('Picture'))
const link = links.filter((i: number, element: cheerio.Element) =>
$(element).text().includes('Picture'),
)
expect(link.attr('href')).toMatch(/^\/assets\//)
}
{
const link = links.filter((i: number, element: any) =>
const link = links.filter((i: number, element: cheerio.Element) =>
$(element).text().includes('GraphQL Schema'),
)
expect(link.attr('href')).toMatch(/^\/public\//)
Expand All @@ -108,7 +113,7 @@ describe('link-rewriting', () => {
const links = $('#article-contents a[href]')

const link = links.filter(
(i: number, element: any) => $(element).text() === 'Cross Version Linking',
(i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
)
expect(link.attr('href')).toMatch('/en/enterprise-cloud@latest/get-started/')
})
Expand All @@ -121,7 +126,7 @@ describe('link-rewriting', () => {
const links = $('#article-contents a[href]')

const link = links.filter(
(i: number, element: any) => $(element).text() === 'Cross Version Linking',
(i: number, element: cheerio.Element) => $(element).text() === 'Cross Version Linking',
)
expect(link.attr('href')).toMatch(
`/en/enterprise-server@${enterpriseServerReleases.latestStable}/get-started/`,
Expand All @@ -133,14 +138,14 @@ describe('subcategory links', () => {
test('no free-pro-team prefix', async () => {
const $: cheerio.Root = await getDOM('/rest/actions')
const links = $('[data-testid="table-of-contents"] a[href]')
links.each((i: number, element: any) => {
links.each((i: number, element: cheerio.Element) => {
expect($(element).attr('href')).not.toContain('/free-pro-team@latest')
})
})
test('enterprise-server prefix', async () => {
const $: cheerio.Root = await getDOM('/enterprise-server@latest/rest/actions')
const links = $('[data-testid="table-of-contents"] a[href]')
links.each((i: number, element: any) => {
links.each((i: number, element: cheerio.Element) => {
expect($(element).attr('href')).toMatch(/\/enterprise-server@\d/)
})
})
Expand Down
Loading