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
22 changes: 4 additions & 18 deletions apps/docs/app/guides/ai/python/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import rehypeSlug from 'rehype-slug'

import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { getGitHubFileContents } from '~/lib/octokit'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
Expand Down Expand Up @@ -83,23 +83,9 @@ const getContent = async ({ slug }: Params) => {

const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)

let response: Response
try {
response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
)
} catch (err) {
throw new Error(`Failed to fetch Python vecs docs from GitHub (network error): ${err}`)
}

if (!response.ok) {
throw new Error(
`Failed to fetch Python vecs docs from GitHub: ${response.status} ${response.statusText}`
)
}

let content = await response.text()
content = removeRedundantH1(content)
const content = removeRedundantH1(
await getGitHubFileContents({ org, repo, path: `${docsDir}/${remoteFile}`, branch })
)

return {
pathname: `/guides/ai/python/${slug}` satisfies `/${string}`,
Expand Down
49 changes: 11 additions & 38 deletions apps/docs/app/guides/database/database-advisors/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Octokit } from '@octokit/core'
import { capitalize } from 'lodash-es'
import rehypeSlug from 'rehype-slug'
import { Heading } from 'ui'
Expand All @@ -7,7 +6,7 @@ import { Admonition } from 'ui-patterns'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { MDXRemoteBase } from '~/features/docs/MdxBase'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { getGitHubFileContents, octokit } from '~/lib/octokit'
import { TabPanel, Tabs } from '~/features/ui/Tabs'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
Expand Down Expand Up @@ -144,24 +143,15 @@ const urlTransform: (lints: Array<{ path: string }>) => UrlTransformFunction = (
* Fetch lint remediation Markdown from external repo
*/
const getLints = async () => {
const octokit = new Octokit({ request: { fetch: fetchRevalidatePerDay } })

let response: Awaited<ReturnType<typeof octokit.request>>
try {
response = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: org,
repo: repo,
path: docsDir,
ref: branch,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
})
} catch (err) {
throw new Error(
`Failed to fetch database advisors lint list from GitHub (network error): ${err}`
)
}
const response = await octokit().request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: org,
repo: repo,
path: docsDir,
ref: branch,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
})

if (response.status >= 400) {
throw new Error(
Expand All @@ -179,24 +169,7 @@ const getLints = async () => {

const lints = await Promise.all(
lintsList.map(async ({ path }) => {
let fileResponse: Response
try {
fileResponse = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${path}`
)
} catch (err) {
throw new Error(
`Failed to fetch database advisors lint file ${path} from GitHub (network error): ${err}`
)
}

if (response.status >= 400) {
throw new Error(
`Failed to fetch ${org}/${repo}/${branch}/${path} docs from GitHub: ${response.status}`
)
}

const content = await fileResponse.text()
const content = await getGitHubFileContents({ org, repo, path, branch })

return {
path: getBasename(path),
Expand Down
21 changes: 4 additions & 17 deletions apps/docs/app/guides/deployment/ci/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import rehypeSlug from 'rehype-slug'

import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { getGitHubFileContents } from '~/lib/octokit'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
Expand Down Expand Up @@ -82,22 +82,9 @@ const getContent = async ({ slug }: Params) => {

const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)

let response: Response
try {
response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
)
} catch (err) {
throw new Error(`Failed to fetch CI docs from GitHub (network error): ${err}`)
}

if (!response.ok) {
throw new Error(
`Failed to fetch CI docs from GitHub: ${response.status} ${response.statusText}`
)
}

const content = removeRedundantH1(await response.text())
const content = removeRedundantH1(
await getGitHubFileContents({ org, repo, path: `${docsDir}/${remoteFile}`, branch })
)

return {
pathname: `/guides/cli/github-action/${slug}` satisfies `/${string}`,
Expand Down
24 changes: 7 additions & 17 deletions apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import rehypeSlug from 'rehype-slug'

import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { getGitHubFileContents } from '~/lib/octokit'
import { isValidGuideFrontmatter } from '~/lib/docs'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
Expand Down Expand Up @@ -115,22 +115,12 @@ const getContent = async ({ slug }: Params) => {
`${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)

let response: Response
try {
response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)
} catch (err) {
throw new Error(`Failed to fetch Terraform docs from GitHub (network error): ${err}`)
}

if (!response.ok) {
throw new Error(
`Failed to fetch Terraform docs from GitHub: ${response.status} ${response.statusText}`
)
}

let rawContent = await response.text()
let rawContent = await getGitHubFileContents({
org: terraformDocsOrg,
repo: terraformDocsRepo,
path: useRoot ? remoteFile : `${terraformDocsDocsDir}/${remoteFile}`,
branch: terraformDocsBranch,
})
// Strip out HTML comments
rawContent = rawContent.replace(/<!--.*?-->/, '')
let { content, data } = matter(rawContent)
Expand Down
25 changes: 9 additions & 16 deletions apps/docs/app/guides/deployment/terraform/reference/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {

import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { getGitHubFileContents } from '~/lib/octokit'
import { TabPanel, Tabs } from '~/features/ui/Tabs'
import {
terraformDocsBranch,
Expand Down Expand Up @@ -394,21 +394,14 @@ const TerraformReferencePage = async () => {
* Fetch JSON schema from external repo
*/
const getSchema = async () => {
let response: Response
try {
response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${terraformDocsDocsDir}/schema.json`
)
} catch (err) {
throw new Error(`Failed to fetch Terraform JSON schema from GitHub (network error): ${err}`)
}

if (!response.ok)
throw Error(
`Failed to fetch Terraform JSON schema from GitHub: ${response.status} ${response.statusText}`
)

const schema = await response.json()
const schema = JSON.parse(
await getGitHubFileContents({
org: terraformDocsOrg,
repo: terraformDocsRepo,
path: `${terraformDocsDocsDir}/schema.json`,
branch: terraformDocsBranch,
})
)

return {
schema,
Expand Down
61 changes: 61 additions & 0 deletions apps/docs/app/guides/getting-started/ai-skills/AiSkills.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import matter from 'gray-matter'
import { cache } from 'react'

import { getGitHubFileContents, octokit } from '~/lib/octokit'

const SKILLS_REPO = {
org: 'supabase',
repo: 'agent-skills',
branch: 'main',
path: 'skills',
}

interface SkillMetadata {
name?: string
title?: string
description?: string
}

interface SkillSummary {
name: string
description: string
installCommand: string
}

async function getAiSkillsImpl(): Promise<SkillSummary[]> {
const { data: contents } = await octokit().request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: SKILLS_REPO.org,
repo: SKILLS_REPO.repo,
path: SKILLS_REPO.path,
ref: SKILLS_REPO.branch,
})

if (!Array.isArray(contents)) {
throw new Error('Expected directory listing from GitHub agent skills repo')
}

const skillDirs = contents.filter((item) => item.type === 'dir')

const skills = await Promise.all(
skillDirs.map(async (item) => {
const skillPath = `${SKILLS_REPO.path}/${item.name}/SKILL.md`
const rawContent = await getGitHubFileContents({
org: SKILLS_REPO.org,
repo: SKILLS_REPO.repo,
branch: SKILLS_REPO.branch,
path: skillPath,
})
const { data } = matter(rawContent) as { data: SkillMetadata }

return {
name: item.name,
description: data.description || '',
installCommand: `npx skills add supabase/agent-skills --skill ${item.name}`,
}
})
)

return skills.sort((a, b) => a.name.localeCompare(b.name))
}

export const getAiSkills = cache(getAiSkillsImpl)
60 changes: 60 additions & 0 deletions apps/docs/app/guides/getting-started/ai-skills/AiSkillsIndex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { getAiSkills } from './AiSkills.utils'
import { CopyButton } from './CopyButton'

export async function AiSkillsIndex() {
let skills: Awaited<ReturnType<typeof getAiSkills>> = []

try {
skills = await getAiSkills()
} catch {
// Swallow errors from getAiSkills to keep the page usable
}

if (!skills.length) {
return (
<div className="not-prose text-sm text-foreground-lighter">
Unable to load AI skills at the moment.
</div>
)
}
return (
<div className="not-prose overflow-x-auto">
<table className="w-full text-sm border-collapse">
<thead>
<tr className="border-b border-default">
<th className="text-left py-2 pr-4 text-foreground-lighter font-medium">Skill</th>
<th className="text-left py-2 pr-4 text-foreground-lighter font-medium">Description</th>
<th className="text-left py-2 text-foreground-lighter font-medium">Install command</th>
</tr>
</thead>
<tbody>
{skills.map((skill) => (
<tr key={skill.name} className="border-b border-default">
<td className="py-3 pr-4 font-mono text-xs whitespace-nowrap">
<a
href={`https://github.com/supabase/agent-skills/tree/main/skills/${skill.name}`}
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:text-brand transition-colors"
>
{skill.name}
</a>
</td>
<td className="py-3 pr-4 text-foreground-lighter">{skill.description}</td>
<td className="w-px p-0">
<div className="h-full max-w-xs overflow-x-auto flex items-center py-3">
<div className="flex items-center gap-2 whitespace-nowrap">
<CopyButton text={skill.installCommand} />
<code className="font-mono text-xs text-foreground-lighter">
{skill.installCommand}
</code>
</div>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
29 changes: 29 additions & 0 deletions apps/docs/app/guides/getting-started/ai-skills/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client'

import { useState } from 'react'
import { Check, Copy } from 'lucide-react'
import { cn } from 'ui'

export function CopyButton({ text }: { text: string }) {
const [copied, setCopied] = useState(false)

const handleCopy = async () => {
await navigator.clipboard.writeText(text)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}

return (
<button
type="button"
onClick={handleCopy}
className={cn(
'inline-flex items-center rounded-md border p-1.5 transition-colors',
'border-default bg-surface-100 text-foreground-lighter hover:bg-surface-200 hover:text-foreground'
)}
title={copied ? 'Copied!' : 'Copy to clipboard'}
>
{copied ? <Check size={14} /> : <Copy size={14} />}
</button>
)
}
4 changes: 3 additions & 1 deletion apps/docs/app/guides/getting-started/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import Layout from '~/layouts/guides'
import { getAiPrompts } from '../getting-started/ai-prompts/[slug]/AiPrompts.utils'

export default async function GettingStartedLayout({ children }: { children: React.ReactNode }) {
const additionalNavItems = { prompts: await getPrompts() }
const additionalNavItems = {
prompts: await getPrompts(),
}

return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
}
Expand Down
Loading
Loading