Skip to content

Instantly share code, notes, and snippets.

@WomB0ComB0
Created January 22, 2025 10:00
Show Gist options
  • Save WomB0ComB0/6a2a004a482ffbd82aaf8492c80e7d21 to your computer and use it in GitHub Desktop.
Save WomB0ComB0/6a2a004a482ffbd82aaf8492c80e7d21 to your computer and use it in GitHub Desktop.
Prisma x Supabase script to execute on package.json scripts for easy db updates
'use server';
import { exec } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { promisify } from 'node:util';
import { logger } from '@/utils';
const execPromise = promisify(exec);
const retry = async (fn: () => Promise<any>, retries = 3, delay = 1000) => {
try {
return await fn();
} catch (error) {
if (retries > 0) {
logger.warn(`Retrying... (${retries} attempts left)`, { error });
await new Promise((resolve) => setTimeout(resolve, delay));
return retry(fn, retries - 1, delay);
}
throw error;
}
};
const updateShouldUseSupabase = (shouldUseSupabase: boolean) => {
const filePath = path.join(process.cwd(), 'src/config/supabase.ts');
const fileContent = fs.readFileSync(filePath, 'utf8').split('\n');
const updatedContent = fileContent.map((line) => {
if (line.includes('export const SHOULD_USE_SUPABASE =')) {
return `export const SHOULD_USE_SUPABASE = ${shouldUseSupabase};`;
}
return line;
});
fs.writeFileSync(filePath, updatedContent.join('\n'));
logger.info(`Updated SHOULD_USE_SUPABASE to ${shouldUseSupabase} in src/config/supabase.ts`);
};
(async () => {
const projectRef = process.env.NEXT_PUBLIC_SUPABASE_URL?.split('//')[1]?.split('.')[0];
if (!process.env.SUPABASE_ACCESS_TOKEN) {
logger.error('SUPABASE_ACCESS_TOKEN environment variable is not set');
process.exit(1);
}
const isDev = process.env.NODE_ENV !== 'production';
try {
logger.info('Starting database setup...', { env: process.env.NODE_ENV, projectRef });
logger.info('Logging in to Supabase CLI...');
const { stdout: loginOutput, stderr: loginError } = await retry(() =>
execPromise(`bunx supabase login --token ${process.env.SUPABASE_ACCESS_TOKEN}`),
);
if (loginError) {
logger.warn('Supabase login produced warnings:', { error: loginError });
}
logger.info('Supabase CLI login completed:', { output: loginOutput });
logger.info('Generating Prisma client...');
const { stdout: generateOutput, stderr: generateError } = await retry(() =>
execPromise('bunx prisma generate'),
);
if (generateError) {
logger.warn('Prisma generate produced warnings:', { error: generateError });
}
logger.info('Prisma client generated:', { output: generateOutput });
if (isDev) {
logger.info('Pushing schema changes (development only)...');
try {
const { stdout: pushOutput, stderr: pushError } = await retry(() =>
execPromise('bunx prisma db push --accept-data-loss --skip-generate'),
);
if (pushError) {
logger.warn('Schema push produced warnings:', { error: pushError });
}
logger.info('Schema push completed:', { output: pushOutput });
} catch (pushError) {
logger.warn('Schema push failed, continuing with migrations:', { error: pushError });
}
}
logger.info('Applying migrations...');
const { stdout: migrateOutput, stderr: migrateError } = await retry(() =>
execPromise('bunx prisma migrate deploy'),
);
if (migrateError) {
logger.warn('Migration produced warnings:', { error: migrateError });
}
logger.info('Migrations completed:', { output: migrateOutput });
logger.info('Generating Supabase types...');
const supabaseCommand = `npx supabase gen types --lang=typescript --project-id "${projectRef}" --schema public,auth,storage,next_auth > src/types/supabase.ts`;
const { stdout: supabaseOutput, stderr: supabaseError } = await retry(() =>
execPromise(supabaseCommand),
);
if (supabaseError) {
logger.warn('Supabase types generation produced warnings:', { error: supabaseError });
}
logger.info('Supabase types generated successfully:', { output: supabaseOutput });
logger.info('Database setup completed successfully!');
updateShouldUseSupabase(true);
logger.info('Updated SHOULD_USE_SUPABASE to true in src/config/supabase.ts');
} catch (error) {
logger.error('Failed to complete database setup:', { error });
updateShouldUseSupabase(false);
logger.info('Updated SHOULD_USE_SUPABASE to false in src/config/supabase.ts');
if (error instanceof Error) {
logger.error('Error details:', {
message: error.message,
stack: error.stack,
// @ts-ignore
code: error.code,
});
}
process.exit(1);
}
})();
@WomB0ComB0
Copy link
Author

This is my logger if you were wondering

import { Logger } from "tslog";

export const logger = new Logger({
  minLevel: Number.parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
  maskValuesOfKeys: ["password", "passwordConfirmation", "credentials", "credential"],
  prettyLogTimeZone: process.env.NODE_ENV === "production" ? "UTC" : "local",
  prettyErrorStackTemplate: "  • {{fileName}}\t{{method}}\n\t{{filePathWithLine}}", // default
  prettyErrorTemplate: "\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}", // default
  prettyLogTemplate: "{{hh}}:{{MM}}:{{ss}}:{{ms}} [{{logLevelName}}] ", // default with exclusion of `{{filePathWithLine}}`
  stylePrettyLogs: true,
  prettyLogStyles: {
    name: "yellow",
    dateIsoStr: "blue",
  },
});

export default logger;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment