From 87b1c342722450002f7347a964d02a8f54f4ff24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Che=C5=82miniak?= Date: Fri, 8 Nov 2024 15:05:57 +0100 Subject: [PATCH 1/3] feat: add gh cli installation prompt (#24) --- .../installSteps/github/ghInstaller.ts | 1 - .../installSteps/github/install.ts | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/core/installMachine/installSteps/github/ghInstaller.ts b/packages/core/installMachine/installSteps/github/ghInstaller.ts index 7378b42..ac9ad7c 100644 --- a/packages/core/installMachine/installSteps/github/ghInstaller.ts +++ b/packages/core/installMachine/installSteps/github/ghInstaller.ts @@ -49,7 +49,6 @@ export const installGitHubCLI = (): boolean => { logWithColoredPrefix('github', 'Installing GitHub CLI...'); try { execSync(installCommand, { stdio: 'inherit' }); - logWithColoredPrefix('github', 'GitHub CLI installed successfully.'); return true; } catch (error) { console.error('Failed to install GitHub CLI.'); diff --git a/packages/core/installMachine/installSteps/github/install.ts b/packages/core/installMachine/installSteps/github/install.ts index 99b143d..e3deca2 100644 --- a/packages/core/installMachine/installSteps/github/install.ts +++ b/packages/core/installMachine/installSteps/github/install.ts @@ -1,3 +1,4 @@ +import inquirer from 'inquirer'; import { logWithColoredPrefix } from '../../../utils/logWithColoredPrefix'; import { installGitHubCLI, isGitHubCLIInstalled } from './ghInstaller'; import { @@ -14,13 +15,27 @@ interface ProjectRepositoryOptions { } // Helper function to check if GitHub CLI is installed -const checkGitHubCLI = () => { +const checkGitHubCLI = async () => { logWithColoredPrefix('github', 'Checking if GitHub CLI is installed...'); if (!isGitHubCLIInstalled()) { logWithColoredPrefix('github', 'GitHub CLI is not installed.'); - const installed = installGitHubCLI(); - if (!installed) { - console.error('GitHub CLI installation failed. Exiting...'); + const { shouldInstallGitHubCLI } = await inquirer.prompt([ + { + type: 'confirm', + name: 'shouldInstallGitHubCLI', + message: 'Would you like us to install GitHub CLI?', + default: true, + }, + ]); + + if (shouldInstallGitHubCLI) { + const installed = installGitHubCLI(); + if (!installed) { + console.error('GitHub CLI installation failed. Exiting...'); + process.exit(1); + } + } else { + console.error('GitHub CLI is not installed. Please install GitHub CLI and try again.'); process.exit(1); } } From a2752d87fea01c66d526cb516c97ff9cd514774e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Che=C5=82miniak?= Date: Fri, 8 Nov 2024 15:08:43 +0100 Subject: [PATCH 2/3] feat: remove createEnvFile and updateEnvFile (#25) --- packages/core/installMachine/index.ts | 29 +---------- .../installSteps/env/createEnvFile.ts | 29 ----------- .../installSteps/supabase/connectProject.ts | 13 ----- packages/core/types.ts | 1 - packages/core/utils/rcFileManager/index.ts | 2 +- packages/core/utils/updateEnvFile.ts | 51 ------------------- 6 files changed, 2 insertions(+), 123 deletions(-) delete mode 100644 packages/core/installMachine/installSteps/env/createEnvFile.ts delete mode 100644 packages/core/utils/updateEnvFile.ts diff --git a/packages/core/installMachine/index.ts b/packages/core/installMachine/index.ts index 468a352..ae1a73f 100644 --- a/packages/core/installMachine/index.ts +++ b/packages/core/installMachine/index.ts @@ -1,6 +1,5 @@ import { createMachine, fromPromise, ActorLogic, AnyEventObject, PromiseSnapshot, createActor, and, not } from 'xstate'; -import { createEnvFile } from './installSteps/env/createEnvFile'; import { initializeRepository } from './installSteps/github/install'; import { preparePayload } from './installSteps/payload/install'; import { prettify } from './installSteps/prettier/prettify'; @@ -59,25 +58,11 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { always: [ { guard: isStepCompleted('initializeProject'), - target: 'createEnvFile', - }, - ], - invoke: { - src: 'initializeProjectActor', - input: ({ context }) => context, - onDone: 'createEnvFile', - onError: 'failed', - }, - }, - createEnvFile: { - always: [ - { - guard: isStepCompleted('createEnvFile'), target: 'installSupabase', }, ], invoke: { - src: 'createEnvFileActor', + src: 'initializeProjectActor', input: ({ context }) => context, onDone: 'installSupabase', onError: 'failed', @@ -288,18 +273,6 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { } }), ), - createEnvFileActor: createStepMachine( - fromPromise(async ({ input }) => { - try { - createEnvFile(input.projectDir); - input.stateData.stepsCompleted.createEnvFile = true; - saveStateToRcFile(input.stateData, input.projectDir); - } catch (error) { - console.error('Error in createEnvFileActor:', error); - throw error; - } - }), - ), installSupabaseActor: createStepMachine( fromPromise(async ({ input }) => { try { diff --git a/packages/core/installMachine/installSteps/env/createEnvFile.ts b/packages/core/installMachine/installSteps/env/createEnvFile.ts deleted file mode 100644 index dc47709..0000000 --- a/packages/core/installMachine/installSteps/env/createEnvFile.ts +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { logWithColoredPrefix } from '../../../utils/logWithColoredPrefix'; - -const requiredEnvVariables: Record = { - NEXT_PUBLIC_SUPABASE_URL: 'required', - NEXT_PUBLIC_SUPABASE_ANON_KEY: 'required', - SUPABASE_URL: 'required', - SUPABASE_ANON_KEY: 'required', - SUPABASE_JWT_SECRET: 'required', - SUPABASE_SERVICE_ROLE_KEY: 'required', - // DATABASE_URI:"required", // this is created by Payload in web app directory - // PAYLOAD_SECRET:"required", // this is created by Payload in web app directory -}; - -// Function to create .env file with empty fields -export const createEnvFile = (destinationDirectory: string) => { - logWithColoredPrefix('stapler', 'Creating .env file...'); - let envTemplate = ''; - for (const [key, status] of Object.entries(requiredEnvVariables)) { - envTemplate += `${key}=\n`; - } - - if (destinationDirectory) { - fs.writeFileSync(path.resolve(destinationDirectory, '.env'), envTemplate); - } else { - throw new Error(`Directory does not exist: ${destinationDirectory}`); - } -}; diff --git a/packages/core/installMachine/installSteps/supabase/connectProject.ts b/packages/core/installMachine/installSteps/supabase/connectProject.ts index f5943e6..af093eb 100644 --- a/packages/core/installMachine/installSteps/supabase/connectProject.ts +++ b/packages/core/installMachine/installSteps/supabase/connectProject.ts @@ -3,7 +3,6 @@ import inquirer from 'inquirer'; import { promisify } from 'util'; import chalk from 'chalk'; import { continueOnAnyKeypress } from '../../../utils/continueOnKeypress'; -import { updateEnvFile } from '../../../utils/updateEnvFile'; import { getSupabaseKeys, parseProjectsList } from './utils'; import { logWithColoredPrefix } from '../../../utils/logWithColoredPrefix'; @@ -33,18 +32,6 @@ export const connectSupabaseProject = async (projectName: string, currentDir: st throw new Error('Failed to retrieve Supabase API keys. Please check your project configuration.'); } - const SUPABASE_URL = `https://${newProject.refId}.supabase.co/`; - - logWithColoredPrefix('supabase', `Saving keys to .env...`); - await updateEnvFile({ - currentDir, - pairs: [ - ['SUPABASE_ANON_KEY', anonKey], - ['SUPABASE_SERVICE_ROLE_KEY', serviceRoleKey], - ['SUPABASE_URL', SUPABASE_URL], - ], - }); - logWithColoredPrefix('supabase', 'Linking project...'); execSync(`npx supabase link --project-ref ${newProject.refId}`, { stdio: 'inherit' }); diff --git a/packages/core/types.ts b/packages/core/types.ts index 2b2d834..745e947 100644 --- a/packages/core/types.ts +++ b/packages/core/types.ts @@ -5,7 +5,6 @@ export interface ProjectOptions { export interface StepsCompleted { initializeProject: boolean; - createEnvFile: boolean; installSupabase: boolean; setupDatabaseWithDocker: boolean; installPayload: boolean; diff --git a/packages/core/utils/rcFileManager/index.ts b/packages/core/utils/rcFileManager/index.ts index e9076d1..a8d57cb 100644 --- a/packages/core/utils/rcFileManager/index.ts +++ b/packages/core/utils/rcFileManager/index.ts @@ -16,9 +16,9 @@ export const initializeRcFile = (projectDir: string, name: string, usePayload: b projectName: name, stepsCompleted: { initializeProject: false, - createEnvFile: false, installPayload: false, installSupabase: false, + setupDatabaseWithDocker: false, prettifyCode: false, createDocFiles: false, createSupabaseProject: false, diff --git a/packages/core/utils/updateEnvFile.ts b/packages/core/utils/updateEnvFile.ts deleted file mode 100644 index 19e6947..0000000 --- a/packages/core/utils/updateEnvFile.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as util from 'util'; - -const readFile = util.promisify(fs.readFile); -const writeFile = util.promisify(fs.writeFile); -const access = util.promisify(fs.access); - -type EnvPair = [string, string]; - -interface EnvUpdateConfig { - currentDir: string; - pairs: EnvPair[]; -} - -// Function to update a single line -const updateLine = (line: string, key: string, value: string): string => { - if (line.startsWith(`${key}=`)) { - return `${key}=${value}`; - } - return line; -}; - -export const updateEnvFile = async ({ currentDir, pairs }: EnvUpdateConfig): Promise => { - const envFilePath = path.join(currentDir, '.env'); - try { - // Check if the .env file exists - try { - await access(envFilePath); // Check if file exists - } catch { - // If it doesn't exist, create it with default values - await writeFile(envFilePath, '', 'utf8'); - } - - const data = await readFile(envFilePath, 'utf8'); - let lines = data.split('\n'); - - lines = lines.map((line) => { - for (const [key, value] of pairs) { - line = updateLine(line, key, value); - } - return line; - }); - - const updatedContent = lines.join('\n'); - await writeFile(envFilePath, updatedContent, 'utf8'); - } catch (error) { - console.error('Error updating .env file:', error); - throw error; // Re-throw the error for the caller to handle - } -}; From 329277778dc9eaaa2a7cb10c2ed812a2625af7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Che=C5=82miniak?= Date: Tue, 12 Nov 2024 10:35:45 +0100 Subject: [PATCH 3/3] Feat/replace local database with supabase (#26) --- .../supabase/files/docker-compose.yml | 15 --------- .../cli/templates/supabase/installConfig.ts | 2 +- packages/core/installMachine/index.ts | 33 ++----------------- .../installSteps/payload/install.ts | 15 +++++---- .../installSteps/payload/utils/loadEnvFile.ts | 11 +++++++ .../installSteps/supabase/install.ts | 30 +++++++++++++++++ .../supabase/setupDatabaseWithDocker.ts | 24 -------------- .../supabase/files/docker-compose.yml | 15 --------- .../core/templates/supabase/installConfig.ts | 2 +- packages/core/types.ts | 1 - packages/core/utils/rcFileManager/index.ts | 1 - 11 files changed, 55 insertions(+), 94 deletions(-) delete mode 100644 packages/cli/templates/supabase/files/docker-compose.yml create mode 100644 packages/core/installMachine/installSteps/payload/utils/loadEnvFile.ts delete mode 100644 packages/core/installMachine/installSteps/supabase/setupDatabaseWithDocker.ts delete mode 100644 packages/core/templates/supabase/files/docker-compose.yml diff --git a/packages/cli/templates/supabase/files/docker-compose.yml b/packages/cli/templates/supabase/files/docker-compose.yml deleted file mode 100644 index 9ae8fd8..0000000 --- a/packages/cli/templates/supabase/files/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -services: - postgres: - image: postgres:17 - container_name: stapler-postgres - environment: - POSTGRES_USER: user - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - '5432:5432' - volumes: - - pg_data:/var/lib/postgresql/data - -volumes: - pg_data: diff --git a/packages/cli/templates/supabase/installConfig.ts b/packages/cli/templates/supabase/installConfig.ts index ee432e8..b78e264 100644 --- a/packages/cli/templates/supabase/installConfig.ts +++ b/packages/cli/templates/supabase/installConfig.ts @@ -1,7 +1,7 @@ export const supabaseFiles = [ { path: 'supabase/src/', - files: ['client.ts', 'index.ts', 'middleware.ts', 'server.ts', 'types.ts', 'docker-compose.yml'], + files: ['client.ts', 'index.ts', 'middleware.ts', 'server.ts', 'types.ts'], }, { path: 'supabase/', diff --git a/packages/core/installMachine/index.ts b/packages/core/installMachine/index.ts index ae1a73f..414341d 100644 --- a/packages/core/installMachine/index.ts +++ b/packages/core/installMachine/index.ts @@ -14,7 +14,6 @@ import { createDocFiles } from './installSteps/docs/create'; import { pushToGitHub } from './installSteps/github/repositoryManager'; import { InstallMachineContext, StepsCompleted } from '../types'; import { saveStateToRcFile } from '../utils/rcFileManager'; -import { setupDatabaseWithDocker } from './installSteps/supabase/setupDatabaseWithDocker'; const isStepCompleted = (stepName: keyof StepsCompleted) => { return ({ context }: { context: InstallMachineContext; event: AnyEventObject }) => { @@ -71,31 +70,17 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { installSupabase: { always: [ { - guard: isStepCompleted('installSupabase'), - target: 'setupDatabaseWithDocker', - }, - ], - invoke: { - input: ({ context }) => context, - src: 'installSupabaseActor', - onDone: 'setupDatabaseWithDocker', - onError: 'failed', - }, - }, - setupDatabaseWithDocker: { - always: [ - { - guard: and([isStepCompleted('setupDatabaseWithDocker'), 'shouldInstallPayload']), + guard: and([isStepCompleted('installSupabase'), 'shouldInstallPayload']), target: 'installPayload', }, { - guard: isStepCompleted('setupDatabaseWithDocker'), + guard: isStepCompleted('installSupabase'), target: 'createDocFiles', }, ], invoke: { input: ({ context }) => context, - src: 'setupDatabaseWithDockerActor', + src: 'installSupabaseActor', onDone: [{ guard: 'shouldInstallPayload', target: 'installPayload' }, { target: 'createDocFiles' }], onError: 'failed', }, @@ -286,18 +271,6 @@ const createInstallMachine = (initialContext: InstallMachineContext) => { } }), ), - setupDatabaseWithDockerActor: createStepMachine( - fromPromise(async ({ input }) => { - try { - setupDatabaseWithDocker(); - input.stateData.stepsCompleted.setupDatabaseWithDocker = true; - saveStateToRcFile(input.stateData, input.projectDir); - } catch (error) { - console.error('Error in setupDatabaseWithDockerActor:', error); - throw error; - } - }), - ), installPayloadActor: createStepMachine( fromPromise(async ({ input }) => { try { diff --git a/packages/core/installMachine/installSteps/payload/install.ts b/packages/core/installMachine/installSteps/payload/install.ts index 2cbb5b4..ccad6a4 100644 --- a/packages/core/installMachine/installSteps/payload/install.ts +++ b/packages/core/installMachine/installSteps/payload/install.ts @@ -1,12 +1,13 @@ import { execSync } from 'child_process'; import { existsSync } from 'fs'; -import { join } from 'path'; +import path, { join } from 'path'; +import chalk from 'chalk'; import { preparePayloadConfig } from './preparePayloadConfig'; import { prepareTsConfig } from './prepareTsConfig'; import { removeTurboFlag } from './removeTurboFlag'; import { updatePackages } from './updatePackages'; import { logWithColoredPrefix } from '../../../utils/logWithColoredPrefix'; -import chalk from 'chalk'; +import { loadEnvFile } from './utils/loadEnvFile'; export const preparePayload = async () => { logWithColoredPrefix('payload', 'Initializing...'); @@ -24,10 +25,12 @@ export const preparePayload = async () => { ); logWithColoredPrefix('payload', 'Installing to Next.js...'); - logWithColoredPrefix( - 'postgres', - `Local connection string: ${chalk.cyan('postgresql://user:password@localhost:5432/postgres')}`, - ); + + // Show the local Supabase connection string + loadEnvFile(path.resolve('../../supabase/.env')); + logWithColoredPrefix('postgres', `Local connection string: ${chalk.cyan(process.env.DB_URL)}`); + + // Install Payload execSync('npx create-payload-app@beta --db postgres', { stdio: 'inherit' }); // Payload doesn't work with Turbopack yet diff --git a/packages/core/installMachine/installSteps/payload/utils/loadEnvFile.ts b/packages/core/installMachine/installSteps/payload/utils/loadEnvFile.ts new file mode 100644 index 0000000..d06539a --- /dev/null +++ b/packages/core/installMachine/installSteps/payload/utils/loadEnvFile.ts @@ -0,0 +1,11 @@ +import fs from 'fs'; + +export const loadEnvFile = (filePath: fs.PathOrFileDescriptor) => { + const envData = fs.readFileSync(filePath, 'utf-8'); + envData.split('\n').forEach((line) => { + const [key, value] = line.split('='); + if (key && value) { + process.env[key.trim()] = value.trim(); + } + }); +}; diff --git a/packages/core/installMachine/installSteps/supabase/install.ts b/packages/core/installMachine/installSteps/supabase/install.ts index d9478f6..bc18127 100644 --- a/packages/core/installMachine/installSteps/supabase/install.ts +++ b/packages/core/installMachine/installSteps/supabase/install.ts @@ -1,6 +1,7 @@ import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; +import chalk from 'chalk'; import { supabaseFiles } from '../../../templates/supabase/installConfig'; import { templateGenerator } from '../../../utils/generator/generator'; import { getTemplateDirectory } from '../../../utils/getTemplateDirectory'; @@ -80,5 +81,34 @@ export const installSupabase = async (destinationDirectory: string) => { execSync('pnpm i --reporter silent', { stdio: 'inherit' }); + logWithColoredPrefix('supabase', 'Starting local database...'); + + try { + execSync('npx supabase start', { stdio: 'ignore' }); + } catch (error) { + console.error( + `Failed to start local database. Is your ${chalk.hex('#0db7ed')('Docker')} daemon running?`, + `\n${error}`, + ); + process.exit(1); + } + + logWithColoredPrefix('supabase', 'Writing local variables to .env file...'); + + const output = execSync('npx supabase status --output json', { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'ignore'], + }); + // Parse the JSON output + const jsonData = JSON.parse(output); + + // Convert JSON data to .env format + const envData = Object.entries(jsonData) + .map(([key, value]) => `${key}=${value}`) + .join('\n'); + + // Write the formatted data to .env file + fs.writeFileSync('.env', envData, 'utf8'); + process.chdir('..'); }; diff --git a/packages/core/installMachine/installSteps/supabase/setupDatabaseWithDocker.ts b/packages/core/installMachine/installSteps/supabase/setupDatabaseWithDocker.ts deleted file mode 100644 index aea0ad5..0000000 --- a/packages/core/installMachine/installSteps/supabase/setupDatabaseWithDocker.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { execSync } from 'child_process'; -import { logWithColoredPrefix } from '../../../utils/logWithColoredPrefix'; - -export const setupDatabaseWithDocker = () => { - logWithColoredPrefix('docker', 'Starting local Postgres...'); - - try { - execSync('docker -v', { stdio: 'ignore' }); - } catch (error) { - console.error('Docker is not installed. Please install Docker and try again.'); - process.exit(1); - } - - try { - execSync('docker compose -f ./supabase/src/docker-compose.yml -p stapler up -d', { stdio: 'inherit' }); - } catch (error) { - console.error( - 'Failed to start Docker Postgres container. Ensure Docker is running.', - '\nDocker error message:', - `\n${error}`, - ); - process.exit(1); - } -}; diff --git a/packages/core/templates/supabase/files/docker-compose.yml b/packages/core/templates/supabase/files/docker-compose.yml deleted file mode 100644 index 9ae8fd8..0000000 --- a/packages/core/templates/supabase/files/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -services: - postgres: - image: postgres:17 - container_name: stapler-postgres - environment: - POSTGRES_USER: user - POSTGRES_PASSWORD: password - POSTGRES_DB: postgres - ports: - - '5432:5432' - volumes: - - pg_data:/var/lib/postgresql/data - -volumes: - pg_data: diff --git a/packages/core/templates/supabase/installConfig.ts b/packages/core/templates/supabase/installConfig.ts index ee432e8..b78e264 100644 --- a/packages/core/templates/supabase/installConfig.ts +++ b/packages/core/templates/supabase/installConfig.ts @@ -1,7 +1,7 @@ export const supabaseFiles = [ { path: 'supabase/src/', - files: ['client.ts', 'index.ts', 'middleware.ts', 'server.ts', 'types.ts', 'docker-compose.yml'], + files: ['client.ts', 'index.ts', 'middleware.ts', 'server.ts', 'types.ts'], }, { path: 'supabase/', diff --git a/packages/core/types.ts b/packages/core/types.ts index 745e947..b7cbd73 100644 --- a/packages/core/types.ts +++ b/packages/core/types.ts @@ -6,7 +6,6 @@ export interface ProjectOptions { export interface StepsCompleted { initializeProject: boolean; installSupabase: boolean; - setupDatabaseWithDocker: boolean; installPayload: boolean; createDocFiles: boolean; prettifyCode: boolean; diff --git a/packages/core/utils/rcFileManager/index.ts b/packages/core/utils/rcFileManager/index.ts index a8d57cb..3779acd 100644 --- a/packages/core/utils/rcFileManager/index.ts +++ b/packages/core/utils/rcFileManager/index.ts @@ -18,7 +18,6 @@ export const initializeRcFile = (projectDir: string, name: string, usePayload: b initializeProject: false, installPayload: false, installSupabase: false, - setupDatabaseWithDocker: false, prettifyCode: false, createDocFiles: false, createSupabaseProject: false,