-
Notifications
You must be signed in to change notification settings - Fork 992
chore: remove Fly.io infrastructure, use Electric Cloud #1717
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 all 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 |
|---|---|---|
|
|
@@ -103,8 +103,8 @@ jobs: | |
| QSTASH_TOKEN: ${{ secrets.QSTASH_TOKEN }} | ||
| QSTASH_CURRENT_SIGNING_KEY: ${{ secrets.QSTASH_CURRENT_SIGNING_KEY }} | ||
| QSTASH_NEXT_SIGNING_KEY: ${{ secrets.QSTASH_NEXT_SIGNING_KEY }} | ||
| ELECTRIC_URL: ${{ secrets.ELECTRIC_URL }} | ||
| ELECTRIC_SECRET: ${{ secrets.ELECTRIC_SECRET }} | ||
| ELECTRIC_SOURCE_ID: ${{ secrets.ELECTRIC_SOURCE_ID }} | ||
| ELECTRIC_SOURCE_SECRET: ${{ secrets.ELECTRIC_SOURCE_SECRET }} | ||
|
Comment on lines
+106
to
+107
Contributor
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. Pre-stage The PR description lists adding these secrets as a post-merge step. However, the workflow triggers on every push to The fix is to add both secrets to the production GitHub environment before this PR is merged, not after. Also applies to: 155-156 🤖 Prompt for AI Agents |
||
| DURABLE_STREAMS_URL: ${{ secrets.DURABLE_STREAMS_URL }} | ||
| DURABLE_STREAMS_SECRET: ${{ secrets.DURABLE_STREAMS_SECRET }} | ||
| STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }} | ||
|
|
@@ -152,8 +152,8 @@ jobs: | |
| --env QSTASH_TOKEN=$QSTASH_TOKEN \ | ||
| --env QSTASH_CURRENT_SIGNING_KEY=$QSTASH_CURRENT_SIGNING_KEY \ | ||
| --env QSTASH_NEXT_SIGNING_KEY=$QSTASH_NEXT_SIGNING_KEY \ | ||
| --env ELECTRIC_URL=$ELECTRIC_URL \ | ||
| --env ELECTRIC_SECRET=$ELECTRIC_SECRET \ | ||
| --env ELECTRIC_SOURCE_ID=$ELECTRIC_SOURCE_ID \ | ||
| --env ELECTRIC_SOURCE_SECRET=$ELECTRIC_SOURCE_SECRET \ | ||
| --env DURABLE_STREAMS_URL=$DURABLE_STREAMS_URL \ | ||
| --env DURABLE_STREAMS_SECRET=$DURABLE_STREAMS_SECRET \ | ||
| --env STRIPE_SECRET_KEY=$STRIPE_SECRET_KEY \ | ||
|
|
@@ -418,33 +418,6 @@ jobs: | |
| --env SECRETS_ENCRYPTION_KEY=$SECRETS_ENCRYPTION_KEY \ | ||
| --env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY | ||
|
|
||
| deploy-electric: | ||
| name: Deploy Electric to Fly.io | ||
| runs-on: ubuntu-latest | ||
| environment: production | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Fly CLI | ||
| uses: superfly/flyctl-actions/setup-flyctl@master | ||
|
|
||
| - name: Stage secrets | ||
| env: | ||
| FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | ||
| run: | | ||
| flyctl secrets set \ | ||
| DATABASE_URL="${{ secrets.DATABASE_URL_UNPOOLED }}" \ | ||
| ELECTRIC_SECRET="${{ secrets.ELECTRIC_SECRET }}" \ | ||
| --app superset-electric \ | ||
| --stage | ||
|
|
||
| - name: Deploy to Fly.io | ||
| env: | ||
| FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | ||
| run: flyctl deploy . --config fly.toml --remote-only | ||
|
|
||
| deploy-docs: | ||
| name: Deploy Docs to Vercel | ||
| runs-on: ubuntu-latest | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,22 +24,26 @@ export async function GET(request: Request): Promise<Response> { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Response("Not a member of this organization", { status: 403 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useCloud = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request.headers.get("x-electric-backend") === "cloud" && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env.ELECTRIC_SOURCE_ID && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env.ELECTRIC_SOURCE_SECRET; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const originUrl = useCloud | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new URL("/v1/shape", "https://api.electric-sql.cloud") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new URL(env.ELECTRIC_URL); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (useCloud) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // biome-ignore lint/style/noNonNullAssertion: guarded by useCloud check above | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("source_id", env.ELECTRIC_SOURCE_ID!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // biome-ignore lint/style/noNonNullAssertion: guarded by useCloud check above | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("source_secret", env.ELECTRIC_SOURCE_SECRET!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELECTRIC_SOURCE_ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELECTRIC_SOURCE_SECRET, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELECTRIC_URL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ELECTRIC_SECRET, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = env; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let originUrl: URL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ELECTRIC_SOURCE_ID && ELECTRIC_SOURCE_SECRET) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl = new URL("/v1/shape", "https://api.electric-sql.cloud"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("source_id", ELECTRIC_SOURCE_ID); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("source_secret", ELECTRIC_SOURCE_SECRET); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (ELECTRIC_URL && ELECTRIC_SECRET) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl = new URL(ELECTRIC_URL); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("secret", ELECTRIC_SECRET); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| originUrl.searchParams.set("secret", env.ELECTRIC_SECRET); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Response( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Missing Electric config: set ELECTRIC_SOURCE_ID/SECRET or ELECTRIC_URL/SECRET", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { status: 500 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
47
Contributor
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. Local Electric URL is missing the The cloud branch (line 36) explicitly constructs Electric SQL's official auth guide constructs the upstream URL as The fix aligns the local branch with both the cloud branch and Electric SQL's documented pattern: 🐛 Proposed fix- } else if (ELECTRIC_URL && ELECTRIC_SECRET) {
- originUrl = new URL(ELECTRIC_URL);
- originUrl.searchParams.set("secret", ELECTRIC_SECRET);
+ } else if (ELECTRIC_URL && ELECTRIC_SECRET) {
+ originUrl = new URL("/v1/shape", ELECTRIC_URL);
+ originUrl.searchParams.set("secret", ELECTRIC_SECRET);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url.searchParams.forEach((value, key) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -85,7 +89,7 @@ export async function GET(request: Request): Promise<Response> { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await fetch(originUrl.toString()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const headers = new Headers(response.headers); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers.append("Vary", "Authorization, X-Electric-Backend"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers.append("Vary", "Authorization"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (headers.get("content-encoding")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers.delete("content-encoding"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,10 +10,10 @@ export const env = createEnv({ | |||||||||
| server: { | ||||||||||
| DATABASE_URL: z.string(), | ||||||||||
| DATABASE_URL_UNPOOLED: z.string(), | ||||||||||
| ELECTRIC_URL: z.string().url(), | ||||||||||
| ELECTRIC_SECRET: z.string().min(16), | ||||||||||
| ELECTRIC_SOURCE_ID: z.string().optional(), | ||||||||||
| ELECTRIC_SOURCE_SECRET: z.string().optional(), | ||||||||||
| ELECTRIC_URL: z.string().url().optional(), | ||||||||||
| ELECTRIC_SECRET: z.string().optional(), | ||||||||||
|
Comment on lines
+15
to
+16
Contributor
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.
The previous 🛡️ Proposed fix- ELECTRIC_SECRET: z.string().optional(),
+ ELECTRIC_SECRET: z.string().min(16).optional(),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||
| BLOB_READ_WRITE_TOKEN: z.string(), | ||||||||||
| GOOGLE_CLIENT_ID: z.string().min(1), | ||||||||||
| GOOGLE_CLIENT_SECRET: z.string().min(1), | ||||||||||
|
|
||||||||||
This file was deleted.
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.
ELECTRIC_SOURCE_ID/ELECTRIC_SOURCE_SECRETmust also be added to thepreviewGitHub environment.The
deploy-apijob runs underenvironment: preview(Line 80). GitHub Actions resolves${{ secrets.* }}from the environment that the job is scoped to, so these two secrets must exist in both thepreviewandproductionenvironments.The post-merge instructions in the PR description only say:
If only
productionis updated, every preview-PR deploy will receive empty values forELECTRIC_SOURCE_IDandELECTRIC_SOURCE_SECRET, silently breaking Electric sync for all preview environments after this merges. The same secrets (pointing at the same Electric Cloud source, or a dedicated one) need to be configured in thepreviewenvironment too.🤖 Prompt for AI Agents