-
-
Notifications
You must be signed in to change notification settings - Fork 131
chore(supabase): stop publishing unused Capgo cloud edge functions #2781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { readdirSync } from 'node:fs' | ||
| import { join } from 'node:path' | ||
| import process from 'node:process' | ||
|
|
||
| /** | ||
| * Capgo cloud (prod/preprod/alpha) only publishes Supabase Edge Functions that | ||
| * still receive traffic from Postgres (pg_net) or other non-Cloudflare callers. | ||
| * | ||
| * Public API, plugin, private, and files traffic runs on Cloudflare Workers. | ||
| * Self-hosted installs keep deploying every function under supabase/functions/. | ||
| */ | ||
| export const CAPGO_CLOUD_SUPABASE_FUNCTIONS = [ | ||
| 'triggers', | ||
| ] as const | ||
|
|
||
| export type CapgoCloudSupabaseFunction = typeof CAPGO_CLOUD_SUPABASE_FUNCTIONS[number] | ||
|
|
||
| const SKIP_FUNCTION_DIRS = new Set([ | ||
| '_backend', | ||
| 'shared', | ||
| 'plugin_runtime', | ||
| ]) | ||
|
|
||
| export function listLocalSupabaseFunctions(functionsDir = join(process.cwd(), 'supabase', 'functions')): string[] { | ||
| return readdirSync(functionsDir, { withFileTypes: true }) | ||
| .filter(entry => entry.isDirectory() && !entry.name.startsWith('.') && !SKIP_FUNCTION_DIRS.has(entry.name)) | ||
| .map(entry => entry.name) | ||
| .sort() | ||
| } | ||
|
|
||
| export function listCapgoCloudSkippedSupabaseFunctions(localFunctions = listLocalSupabaseFunctions()): string[] { | ||
| const keep = new Set<string>(CAPGO_CLOUD_SUPABASE_FUNCTIONS) | ||
| return localFunctions.filter(name => !keep.has(name)) | ||
| } | ||
|
|
||
| export function buildCapgoCloudSupabaseDeployArgs( | ||
| functions: readonly string[] = CAPGO_CLOUD_SUPABASE_FUNCTIONS, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The deploy-argument helper can emit arbitrary function names when a caller supplies its optional argument, so a future caller can bypass the cloud allowlist while still using the allowlist-named API. Typing this parameter as Prompt for AI agents |
||
| ): string[] { | ||
| if (functions.length === 0) | ||
| throw new Error('CAPGO_CLOUD_SUPABASE_FUNCTIONS must not be empty') | ||
| return [...functions] | ||
| } | ||
|
|
||
| if (import.meta.main) { | ||
| const mode = process.argv[2] ?? 'deploy-args' | ||
| if (mode === 'list') { | ||
| for (const name of CAPGO_CLOUD_SUPABASE_FUNCTIONS) | ||
| console.log(name) | ||
| process.exit(0) | ||
| } | ||
| if (mode === 'skip-list') { | ||
| for (const name of listCapgoCloudSkippedSupabaseFunctions()) | ||
| console.log(name) | ||
| process.exit(0) | ||
| } | ||
| if (mode === 'deploy-args') { | ||
| console.log(buildCapgoCloudSupabaseDeployArgs().join(' ')) | ||
| process.exit(0) | ||
| } | ||
| console.error(`Unknown mode: ${mode}. Use deploy-args | list | skip-list`) | ||
| process.exit(1) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import { | ||
| buildCapgoCloudSupabaseDeployArgs, | ||
| CAPGO_CLOUD_SUPABASE_FUNCTIONS, | ||
| listCapgoCloudSkippedSupabaseFunctions, | ||
| listLocalSupabaseFunctions, | ||
| } from '../scripts/supabase-cloud-functions.ts' | ||
|
|
||
| describe('supabase cloud function allowlist', () => { | ||
| it('keeps only triggers for Capgo cloud deploys', () => { | ||
| expect([...CAPGO_CLOUD_SUPABASE_FUNCTIONS]).toEqual(['triggers']) | ||
| expect(buildCapgoCloudSupabaseDeployArgs()).toEqual(['triggers']) | ||
| }) | ||
|
|
||
| it('rejects an empty explicit deploy list', () => { | ||
| expect(() => buildCapgoCloudSupabaseDeployArgs([])).toThrow(/must not be empty/) | ||
| }) | ||
|
|
||
| it('passes through multiple explicit deploy targets', () => { | ||
| expect(buildCapgoCloudSupabaseDeployArgs(['triggers', 'ok'])).toEqual(['triggers', 'ok']) | ||
| }) | ||
|
|
||
| it('skips every local function except the Capgo cloud allowlist', () => { | ||
| const local = listLocalSupabaseFunctions() | ||
| expect(local).toContain('triggers') | ||
| expect(local).toContain('updates') | ||
| expect(local).toContain('stats') | ||
| expect(local).toContain('private') | ||
|
|
||
| const skipped = listCapgoCloudSkippedSupabaseFunctions(local) | ||
| expect(skipped).not.toContain('triggers') | ||
| expect(skipped).toContain('updates') | ||
| expect(skipped).toContain('stats') | ||
| expect(skipped).toContain('channel_self') | ||
| expect(skipped).toContain('private') | ||
| expect(skipped).toContain('files') | ||
| expect(skipped.length).toBe(local.length - CAPGO_CLOUD_SUPABASE_FUNCTIONS.length) | ||
| }) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: If the script fails, FUNCTIONS will be empty and
supabase functions deploywill silently deploy ALL functions, defeating the allowlist. Add a guard:if [ ${#FUNCTIONS[@]} -eq 0 ]; then ... exit 1; fiPrompt for AI agents