Files
supabase/apps/learn/scripts/sync-internal-content.mts
Terry Sutton dda0b526ac Feat/learn (#41566)
wip

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

# Release Notes

* **New Features**
* Added a new Learn application offering foundational Supabase courses
with interactive documentation
* Courses include Architecture, Authentication, Data Fundamentals,
Security, Storage, Realtime, and Edge Functions
  * Chapter tracking and progress indicators for course completions
  * Responsive sidebar navigation with search/command menu
  * Theme switching support (light, dark, classic dark modes)
  * Mobile-friendly course interface

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alan Daniel <stylesshjs@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 21:36:24 -03:30

79 lines
2.8 KiB
TypeScript

import { config } from 'dotenv'
import { existsSync, rmSync, mkdirSync, renameSync } from 'node:fs'
import { execSync } from 'node:child_process'
import path from 'node:path'
const envLocalPath = path.resolve(process.cwd(), '.env.local')
if (existsSync(envLocalPath)) {
config({ path: envLocalPath })
}
const repo = process.env.INTERNAL_CONTENT_REPO
const token = process.env.INTERNAL_CONTENT_GITHUB_TOKEN
const branch = process.env.INTERNAL_CONTENT_BRANCH || 'main'
const targetDir = path.resolve(process.cwd(), 'content/internal')
const tmpDir = path.resolve(process.cwd(), '.tmp-internal-content')
if (!token) {
console.log('INTERNAL_CONTENT_GITHUB_TOKEN not set; skipping internal content sync.')
process.exit(0)
}
if (!repo) {
console.error('INTERNAL_CONTENT_REPO not set; cannot sync internal content.')
process.exit(1)
}
const repoUrl = `https://${token}@github.com/${repo}.git`
const safeRepo = `https://github.com/${repo}.git`
// Run command with output captured to prevent token leakage
const run = (cmd: string) => {
try {
const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' })
return output
} catch (error: any) {
// Sanitize error messages by replacing token with [REDACTED]
const sanitizedMessage = error.message?.replace(new RegExp(token, 'g'), '[REDACTED]') ?? 'Unknown error'
const sanitizedStderr = error.stderr?.toString().replace(new RegExp(token, 'g'), '[REDACTED]') ?? ''
const sanitizedStdout = error.stdout?.toString().replace(new RegExp(token, 'g'), '[REDACTED]') ?? ''
const sanitizedError = new Error(sanitizedMessage)
;(sanitizedError as any).stderr = sanitizedStderr
;(sanitizedError as any).stdout = sanitizedStdout
;(sanitizedError as any).status = error.status
throw sanitizedError
}
}
try {
// Clean up any existing content
rmSync(targetDir, { recursive: true, force: true })
rmSync(tmpDir, { recursive: true, force: true })
// Clone to temp directory (using token URL but errors will be sanitized)
console.log(`Cloning ${safeRepo} branch ${branch}...`)
run(`git clone --depth 1 --branch ${branch} ${repoUrl} ${tmpDir}`)
// Remove git folder so targetDir is not a git repo
rmSync(path.join(tmpDir, '.git'), { recursive: true, force: true })
// Ensure parent directory exists
mkdirSync(path.dirname(targetDir), { recursive: true })
// Move temp folder into place (Node, not shell)
renameSync(tmpDir, targetDir)
console.log('Internal content synced successfully')
} catch (e: any) {
// Sanitize error message to prevent token leakage
const sanitizedMessage = (e?.message ?? String(e)).replace(new RegExp(token, 'g'), '[REDACTED]')
console.error('Failed to sync internal content:', sanitizedMessage)
if (e?.stderr) {
console.error('Error details:', e.stderr)
}
rmSync(tmpDir, { recursive: true, force: true })
process.exit(1)
}