mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Bug fix. Complete App configurations produce the same auth options as before. ## What is the current behavior? Without the docs GitHub App private key, two things fail for a contributor: - `pnpm run embeddings` aborts before doing any work. The lint warnings source throws, and every source shares one `Promise.all` in [`fetchAllSources()`](https://github.com/supabase/supabase/blob/master/apps/docs/scripts/search/sources/index.ts). - `pnpm --filter docs build` exits 1 in prebuild, so the `npm run build` pre-flight CONTRIBUTING.md asks for cannot run either: ``` Error: DOCS_GITHUB_APP_PRIVATE_KEY environment variable is required at octokit (apps/docs/lib/octokit.ts:21:13) at fetchAiSkills (apps/docs/scripts/federated-content/fetch-federated-content.ts:258:36) ``` Both read public content, so this is a rate-limit guard rather than access control: App auth landed in #43015 because unauthenticated calls (60 req/hr per IP) went flaky on shared runners. ## What is the new behavior? `apps/docs/lib/octokit.auth.ts` adds one rung below the App: a token from `GH_TOKEN`, then `GITHUB_TOKEN` (the precedence [`gh help environment`](https://cli.github.com/manual/gh_help_environment) documents), so `export GH_TOKEN=$(gh auth token)` is enough to build locally. Still authenticated, so #43015's fix holds, and still an authenticated Octokit client, so #44274 holds. A partially configured App is now an error naming the missing vars, rather than falling through to a token. Used by the lint warnings loader and `lib/octokit.ts`. The two token vars are declared in `apps/docs/turbo.jsonc` for `turbo/no-undeclared-env-vars`. ## Additional context With only `GH_TOKEN` set, `turbo run build --filter=docs --force` passes 4/4 and search-index source loading completes. `pnpm test` passes (20 files, 164 tests), and `tsc --noEmit` plus `pnpm run lint` match `origin/master`. For a complete App config the auth options are identical to before. Happy to post the fuller verification as a comment. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added flexible GitHub authentication for documentation services, supporting GitHub App credentials or personal access tokens. - GitHub App authentication is preferred when fully configured, with token-based fallback when unavailable. - Added support for both `GH_TOKEN` and `GITHUB_TOKEN`, with clear precedence rules. - **Bug Fixes** - Improved configuration validation with clear errors for missing or incomplete authentication settings. - Standardized authentication across GitHub content and lint-warning retrieval. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
import { Octokit } from '@octokit/core'
|
|
import { retry } from '@octokit/plugin-retry'
|
|
import { createHash } from 'node:crypto'
|
|
import { githubAuthOptions } from '../../../lib/octokit.auth.js'
|
|
import { OCTOKIT_RETRY_OPTIONS } from '../../../lib/octokit.constants.js'
|
|
import { BaseLoader, BaseSource } from './base.js'
|
|
|
|
const RetryOctokit = Octokit.plugin(retry)
|
|
|
|
const getBasename = (path: string) => path.split('/').at(-1)!.replace(/\.md$/, '')
|
|
|
|
export class LintWarningsGuideLoader extends BaseLoader {
|
|
type = 'markdown' as const
|
|
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
public org: string,
|
|
public repo: string,
|
|
public branch: string,
|
|
public docsDir: string
|
|
) {
|
|
super(source, path)
|
|
}
|
|
|
|
async load() {
|
|
const octokit = new RetryOctokit(githubAuthOptions())
|
|
|
|
const response = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
|
|
owner: this.org,
|
|
repo: this.repo,
|
|
path: this.docsDir,
|
|
ref: this.branch,
|
|
request: OCTOKIT_RETRY_OPTIONS,
|
|
headers: {
|
|
'X-GitHub-Api-Version': '2022-11-28',
|
|
},
|
|
})
|
|
|
|
if (response.status >= 400) {
|
|
throw Error(`Could not get contents of repo ${this.org}/${this.repo}`)
|
|
}
|
|
|
|
if (!Array.isArray(response.data)) {
|
|
throw Error(
|
|
'Reading a directory, not a file. Should not reach this, solely to appease Typescript.'
|
|
)
|
|
}
|
|
|
|
const lintsList = response.data.filter(({ path }) => /docs\/\d+.+\.md$/.test(path))
|
|
|
|
// Fetch all lint files and combine them into a single guide
|
|
const lints = await Promise.all(
|
|
lintsList.map(async ({ path }) => {
|
|
const fileResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
|
|
owner: this.org,
|
|
repo: this.repo,
|
|
path,
|
|
ref: this.branch,
|
|
request: OCTOKIT_RETRY_OPTIONS,
|
|
})
|
|
|
|
if (!('content' in fileResponse.data) || fileResponse.data.type !== 'file') {
|
|
throw Error(`Could not get contents of file ${this.org}/${this.repo}/${path}`)
|
|
}
|
|
|
|
const content = Buffer.from(fileResponse.data.content, 'base64').toString('utf-8')
|
|
const basename = getBasename(path)
|
|
|
|
return {
|
|
path: basename,
|
|
content,
|
|
originalPath: path,
|
|
}
|
|
})
|
|
)
|
|
|
|
// Create a separate source for each lint file
|
|
return lints.map(
|
|
(lint) =>
|
|
new LintWarningsGuideSource(
|
|
this.source,
|
|
`${this.path}?queryGroups=lint&lint=${lint.path}`,
|
|
lint
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
export class LintWarningsGuideSource extends BaseSource {
|
|
type = 'markdown' as const
|
|
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
public lint: {
|
|
path: string
|
|
content: string
|
|
originalPath: string
|
|
}
|
|
) {
|
|
super(source, path)
|
|
}
|
|
|
|
async process() {
|
|
this.checksum = createHash('sha256').update(this.lint.content).digest('base64')
|
|
|
|
this.meta = {
|
|
title: `Database Advisor: Lint ${this.lint.path}`,
|
|
}
|
|
|
|
this.sections = [
|
|
{
|
|
content: this.lint.content,
|
|
},
|
|
]
|
|
|
|
return { checksum: this.checksum, meta: this.meta, sections: this.sections }
|
|
}
|
|
|
|
extractIndexedContent(): string {
|
|
const sections = this.sections ?? []
|
|
const sectionText = sections.map(({ content }) => content).join('\n\n')
|
|
return `# Database Advisor: Lint ${this.lint.path}\n\nThis is a database lint rule for Supabase, targeting the lint ID ${this.lint.path}. Lint rules help enforce performance and security best practices for your Supabase database.\n\n${sectionText}`
|
|
}
|
|
}
|