Add more info

This commit is contained in:
Shreyaschorge 2025-07-11 03:44:09 +05:30
parent e0ca42169b
commit 8ff7080e84
No known key found for this signature in database

View File

@ -1,33 +1,33 @@
import { execSync, spawn } from "child_process";
import fs from "fs";
import path from "path";
import os from "os";
import { fileURLToPath } from "url";
import inquirer from "inquirer";
import dotenv from "dotenv";
import crypto from "crypto";
import { Vercel } from "@vercel/sdk";
import { execSync, spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { fileURLToPath } from 'url';
import inquirer from 'inquirer';
import dotenv from 'dotenv';
import crypto from 'crypto';
import { Vercel } from '@vercel/sdk';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const projectRoot = path.join(__dirname, "..");
const projectRoot = path.join(__dirname, '..');
// Load environment variables in specific order
dotenv.config({ path: ".env" });
dotenv.config({ path: '.env' });
async function generateFarcasterMetadata(domain, webhookUrl) {
const trimmedDomain = domain.trim();
const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(",");
const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(',');
return {
frame: {
version: "1",
version: '1',
name: process.env.NEXT_PUBLIC_MINI_APP_NAME,
iconUrl: `https://${trimmedDomain}/icon.png`,
homeUrl: `https://${trimmedDomain}`,
imageUrl: `https://${trimmedDomain}/api/opengraph-image`,
buttonTitle: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT,
splashImageUrl: `https://${trimmedDomain}/splash.png`,
splashBackgroundColor: "#f7f7f7",
splashBackgroundColor: '#f7f7f7',
webhookUrl: webhookUrl?.trim(),
description: process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION,
primaryCategory: process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY,
@ -38,36 +38,36 @@ async function generateFarcasterMetadata(domain, webhookUrl) {
async function loadEnvLocal() {
try {
if (fs.existsSync(".env.local")) {
if (fs.existsSync('.env.local')) {
const { loadLocal } = await inquirer.prompt([
{
type: "confirm",
name: "loadLocal",
type: 'confirm',
name: 'loadLocal',
message:
"Found .env.local - would you like to load its values in addition to .env values?",
'Found .env.local - would you like to load its values in addition to .env values?',
default: true,
},
]);
if (loadLocal) {
console.log("Loading values from .env.local...");
const localEnv = dotenv.parse(fs.readFileSync(".env.local"));
console.log('Loading values from .env.local...');
const localEnv = dotenv.parse(fs.readFileSync('.env.local'));
const allowedVars = [
"NEXT_PUBLIC_MINI_APP_NAME",
"NEXT_PUBLIC_MINI_APP_DESCRIPTION",
"NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY",
"NEXT_PUBLIC_MINI_APP_TAGS",
"NEXT_PUBLIC_MINI_APP_BUTTON_TEXT",
"NEXT_PUBLIC_ANALYTICS_ENABLED",
"NEYNAR_API_KEY",
"NEYNAR_CLIENT_ID",
"SPONSOR_SIGNER",
'NEXT_PUBLIC_MINI_APP_NAME',
'NEXT_PUBLIC_MINI_APP_DESCRIPTION',
'NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY',
'NEXT_PUBLIC_MINI_APP_TAGS',
'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT',
'NEXT_PUBLIC_ANALYTICS_ENABLED',
'NEYNAR_API_KEY',
'NEYNAR_CLIENT_ID',
'SPONSOR_SIGNER',
];
const envContent = fs.existsSync(".env")
? fs.readFileSync(".env", "utf8") + "\n"
: "";
const envContent = fs.existsSync('.env')
? fs.readFileSync('.env', 'utf8') + '\n'
: '';
let newEnvContent = envContent;
for (const [key, value] of Object.entries(localEnv)) {
@ -79,35 +79,35 @@ async function loadEnvLocal() {
}
}
fs.writeFileSync(".env", newEnvContent);
console.log("✅ Values from .env.local have been written to .env");
fs.writeFileSync('.env', newEnvContent);
console.log('✅ Values from .env.local have been written to .env');
}
}
} catch (error) {
console.log("Note: No .env.local file found");
console.log('Note: No .env.local file found');
}
}
async function checkRequiredEnvVars() {
console.log("\n📝 Checking environment variables...");
console.log("Loading values from .env...");
console.log('\n📝 Checking environment variables...');
console.log('Loading values from .env...');
await loadEnvLocal();
const requiredVars = [
{
name: "NEXT_PUBLIC_MINI_APP_NAME",
message: "Enter the name for your frame (e.g., My Cool Mini App):",
name: 'NEXT_PUBLIC_MINI_APP_NAME',
message: 'Enter the name for your frame (e.g., My Cool Mini App):',
default: process.env.NEXT_PUBLIC_MINI_APP_NAME,
validate: (input) =>
input.trim() !== "" || "Mini app name cannot be empty",
input.trim() !== '' || 'Mini app name cannot be empty',
},
{
name: "NEXT_PUBLIC_MINI_APP_BUTTON_TEXT",
message: "Enter the text for your frame button:",
name: 'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT',
message: 'Enter the text for your frame button:',
default:
process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT ?? "Launch Mini App",
validate: (input) => input.trim() !== "" || "Button text cannot be empty",
process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT ?? 'Launch Mini App',
validate: (input) => input.trim() !== '' || 'Button text cannot be empty',
},
];
@ -120,8 +120,8 @@ async function checkRequiredEnvVars() {
for (const varConfig of missingVars) {
const { value } = await inquirer.prompt([
{
type: "input",
name: "value",
type: 'input',
name: 'value',
message: varConfig.message,
default: varConfig.default,
validate: varConfig.validate,
@ -130,14 +130,14 @@ async function checkRequiredEnvVars() {
process.env[varConfig.name] = value;
const envContent = fs.existsSync(".env")
? fs.readFileSync(".env", "utf8")
: "";
const envContent = fs.existsSync('.env')
? fs.readFileSync('.env', 'utf8')
: '';
if (!envContent.includes(`${varConfig.name}=`)) {
const newLine = envContent ? "\n" : "";
const newLine = envContent ? '\n' : '';
fs.appendFileSync(
".env",
'.env',
`${newLine}${varConfig.name}="${value.trim()}"`
);
}
@ -150,15 +150,19 @@ async function checkRequiredEnvVars() {
name: 'sponsorSigner',
message:
'Do you want to sponsor the signer? (This will be used in Sign In With Neynar)\n' +
'Note: If you choose to sponsor the signer, Neynar will sponsor it for you and you will be charged in CUs.',
'Note: If you choose to sponsor the signer, Neynar will sponsor it for you and you will be charged in CUs.\n' +
'For more information, see https://docs.neynar.com/docs/two-ways-to-sponsor-a-farcaster-signer-via-neynar#sponsor-signers',
default: false,
},
]);
process.env.SPONSOR_SIGNER = sponsorSigner.toString();
if (storeSeedPhrase) {
fs.appendFileSync('.env.local', `\nSPONSOR_SIGNER="${sponsorSigner}"`);
fs.appendFileSync(
'.env.local',
`\nSPONSOR_SIGNER="${sponsorSigner}"`
);
console.log('✅ Sponsor signer preference stored in .env.local');
}
}
@ -166,7 +170,11 @@ async function checkRequiredEnvVars() {
}
// Load SPONSOR_SIGNER from .env.local if SEED_PHRASE exists but SPONSOR_SIGNER doesn't
if (process.env.SEED_PHRASE && !process.env.SPONSOR_SIGNER && fs.existsSync('.env.local')) {
if (
process.env.SEED_PHRASE &&
!process.env.SPONSOR_SIGNER &&
fs.existsSync('.env.local')
) {
const localEnv = dotenv.parse(fs.readFileSync('.env.local'));
if (localEnv.SPONSOR_SIGNER) {
process.env.SPONSOR_SIGNER = localEnv.SPONSOR_SIGNER;
@ -176,9 +184,9 @@ async function checkRequiredEnvVars() {
async function getGitRemote() {
try {
const remoteUrl = execSync("git remote get-url origin", {
const remoteUrl = execSync('git remote get-url origin', {
cwd: projectRoot,
encoding: "utf8",
encoding: 'utf8',
}).trim();
return remoteUrl;
} catch (error) {
@ -188,9 +196,9 @@ async function getGitRemote() {
async function checkVercelCLI() {
try {
execSync("vercel --version", {
stdio: "ignore",
shell: process.platform === "win32",
execSync('vercel --version', {
stdio: 'ignore',
shell: process.platform === 'win32',
});
return true;
} catch (error) {
@ -199,23 +207,23 @@ async function checkVercelCLI() {
}
async function installVercelCLI() {
console.log("Installing Vercel CLI...");
execSync("npm install -g vercel", {
stdio: "inherit",
shell: process.platform === "win32",
console.log('Installing Vercel CLI...');
execSync('npm install -g vercel', {
stdio: 'inherit',
shell: process.platform === 'win32',
});
}
async function getVercelToken() {
try {
// Try to get token from Vercel CLI config
const configPath = path.join(os.homedir(), ".vercel", "auth.json");
const configPath = path.join(os.homedir(), '.vercel', 'auth.json');
if (fs.existsSync(configPath)) {
const authConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
const authConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
return authConfig.token;
}
} catch (error) {
console.warn("Could not read Vercel token from config file");
console.warn('Could not read Vercel token from config file');
}
// Try environment variable
@ -225,75 +233,75 @@ async function getVercelToken() {
// Try to extract from vercel whoami
try {
const whoamiOutput = execSync("vercel whoami", {
encoding: "utf8",
stdio: "pipe",
const whoamiOutput = execSync('vercel whoami', {
encoding: 'utf8',
stdio: 'pipe',
});
// If we can get whoami, we're logged in, but we need the actual token
// The token isn't directly exposed, so we'll need to use CLI for some operations
console.log("✅ Verified Vercel CLI authentication");
console.log('✅ Verified Vercel CLI authentication');
return null; // We'll fall back to CLI operations
} catch (error) {
throw new Error(
"Not logged in to Vercel CLI. Please run this script again to login."
'Not logged in to Vercel CLI. Please run this script again to login.'
);
}
}
async function loginToVercel() {
console.log("\n🔑 Vercel Login");
console.log("You can either:");
console.log("1. Log in to an existing Vercel account");
console.log("2. Create a new Vercel account during login\n");
console.log("If creating a new account:");
console.log('\n🔑 Vercel Login');
console.log('You can either:');
console.log('1. Log in to an existing Vercel account');
console.log('2. Create a new Vercel account during login\n');
console.log('If creating a new account:');
console.log('1. Click "Continue with GitHub"');
console.log("2. Authorize GitHub access");
console.log("3. Complete the Vercel account setup in your browser");
console.log("4. Return here once your Vercel account is created\n");
console.log('2. Authorize GitHub access');
console.log('3. Complete the Vercel account setup in your browser');
console.log('4. Return here once your Vercel account is created\n');
console.log(
"\nNote: you may need to cancel this script with ctrl+c and run it again if creating a new vercel account"
'\nNote: you may need to cancel this script with ctrl+c and run it again if creating a new vercel account'
);
const child = spawn("vercel", ["login"], {
stdio: "inherit",
const child = spawn('vercel', ['login'], {
stdio: 'inherit',
});
await new Promise((resolve, reject) => {
child.on("close", (code) => {
child.on('close', (code) => {
resolve();
});
});
console.log("\n📱 Waiting for login to complete...");
console.log('\n📱 Waiting for login to complete...');
console.log(
"If you're creating a new account, please complete the Vercel account setup in your browser first."
);
for (let i = 0; i < 150; i++) {
try {
execSync("vercel whoami", { stdio: "ignore" });
console.log("✅ Successfully logged in to Vercel!");
execSync('vercel whoami', { stdio: 'ignore' });
console.log('✅ Successfully logged in to Vercel!');
return true;
} catch (error) {
if (error.message.includes("Account not found")) {
console.log(" Waiting for Vercel account setup to complete...");
if (error.message.includes('Account not found')) {
console.log(' Waiting for Vercel account setup to complete...');
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
}
console.error("\n❌ Login timed out. Please ensure you have:");
console.error("1. Completed the Vercel account setup in your browser");
console.error("2. Authorized the GitHub integration");
console.error("Then try running this script again.");
console.error('\n❌ Login timed out. Please ensure you have:');
console.error('1. Completed the Vercel account setup in your browser');
console.error('2. Authorized the GitHub integration');
console.error('Then try running this script again.');
return false;
}
async function setVercelEnvVarSDK(vercelClient, projectId, key, value) {
try {
let processedValue;
if (typeof value === "object") {
if (typeof value === 'object') {
processedValue = JSON.stringify(value);
} else {
processedValue = value.toString();
@ -305,7 +313,7 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) {
});
const existingVar = existingVars.envs?.find(
(env) => env.key === key && env.target?.includes("production")
(env) => env.key === key && env.target?.includes('production')
);
if (existingVar) {
@ -315,7 +323,7 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) {
id: existingVar.id,
requestBody: {
value: processedValue,
target: ["production"],
target: ['production'],
},
});
console.log(`✅ Updated environment variable: ${key}`);
@ -326,8 +334,8 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) {
requestBody: {
key: key,
value: processedValue,
type: "encrypted",
target: ["production"],
type: 'encrypted',
target: ['production'],
},
});
console.log(`✅ Created environment variable: ${key}`);
@ -349,7 +357,7 @@ async function setVercelEnvVarCLI(key, value, projectRoot) {
try {
execSync(`vercel env rm ${key} production -y`, {
cwd: projectRoot,
stdio: "ignore",
stdio: 'ignore',
env: process.env,
});
} catch (error) {
@ -357,7 +365,7 @@ async function setVercelEnvVarCLI(key, value, projectRoot) {
}
let processedValue;
if (typeof value === "object") {
if (typeof value === 'object') {
processedValue = JSON.stringify(value);
} else {
processedValue = value.toString();
@ -365,11 +373,11 @@ async function setVercelEnvVarCLI(key, value, projectRoot) {
// Create temporary file
const tempFilePath = path.join(projectRoot, `${key}_temp.txt`);
fs.writeFileSync(tempFilePath, processedValue, "utf8");
fs.writeFileSync(tempFilePath, processedValue, 'utf8');
// Use appropriate command based on platform
let command;
if (process.platform === "win32") {
if (process.platform === 'win32') {
command = `type "${tempFilePath}" | vercel env add ${key} production`;
} else {
command = `cat "${tempFilePath}" | vercel env add ${key} production`;
@ -377,7 +385,7 @@ async function setVercelEnvVarCLI(key, value, projectRoot) {
execSync(command, {
cwd: projectRoot,
stdio: "pipe", // Changed from 'inherit' to avoid interactive prompts
stdio: 'pipe', // Changed from 'inherit' to avoid interactive prompts
shell: true,
env: process.env,
});
@ -404,7 +412,7 @@ async function setEnvironmentVariables(
envVars,
projectRoot
) {
console.log("\n📝 Setting up environment variables...");
console.log('\n📝 Setting up environment variables...');
const results = [];
@ -432,28 +440,33 @@ async function setEnvironmentVariables(
console.warn(`\n⚠️ Failed to set ${failed.length} environment variables:`);
failed.forEach((r) => console.warn(` - ${r.key}`));
console.warn(
"\nYou may need to set these manually in the Vercel dashboard."
'\nYou may need to set these manually in the Vercel dashboard.'
);
}
return results;
}
async function waitForDeployment(vercelClient, projectId, maxWaitTime = 300000) { // 5 minutes
async function waitForDeployment(
vercelClient,
projectId,
maxWaitTime = 300000
) {
// 5 minutes
console.log('\n⏳ Waiting for deployment to complete...');
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
try {
const deployments = await vercelClient.deployments.list({
projectId: projectId,
limit: 1
limit: 1,
});
if (deployments.deployments?.[0]) {
const deployment = deployments.deployments[0];
console.log(`📊 Deployment status: ${deployment.state}`);
if (deployment.state === 'READY') {
console.log('✅ Deployment completed successfully!');
return deployment;
@ -462,36 +475,36 @@ async function waitForDeployment(vercelClient, projectId, maxWaitTime = 300000)
} else if (deployment.state === 'CANCELED') {
throw new Error('Deployment was canceled');
}
// Still building, wait and check again
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds
} else {
console.log('⏳ No deployment found yet, waiting...');
await new Promise(resolve => setTimeout(resolve, 5000));
await new Promise((resolve) => setTimeout(resolve, 5000));
}
} catch (error) {
console.warn('⚠️ Could not check deployment status:', error.message);
await new Promise(resolve => setTimeout(resolve, 5000));
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
throw new Error('Deployment timed out after 5 minutes');
}
async function deployToVercel(useGitHub = false) {
try {
console.log("\n🚀 Deploying to Vercel...");
console.log('\n🚀 Deploying to Vercel...');
// Ensure vercel.json exists
const vercelConfigPath = path.join(projectRoot, "vercel.json");
const vercelConfigPath = path.join(projectRoot, 'vercel.json');
if (!fs.existsSync(vercelConfigPath)) {
console.log("📝 Creating vercel.json configuration...");
console.log('📝 Creating vercel.json configuration...');
fs.writeFileSync(
vercelConfigPath,
JSON.stringify(
{
buildCommand: "next build",
framework: "nextjs",
buildCommand: 'next build',
framework: 'nextjs',
},
null,
2
@ -501,15 +514,19 @@ async function deployToVercel(useGitHub = false) {
// Set up Vercel project
console.log('\n📦 Setting up Vercel project...');
console.log('An initial deployment is required to get an assigned domain that can be used in the mini app manifest\n');
console.log('\n⚠ Note: choosing a longer, more unique project name will help avoid conflicts with other existing domains\n');
console.log(
'An initial deployment is required to get an assigned domain that can be used in the mini app manifest\n'
);
console.log(
'\n⚠ Note: choosing a longer, more unique project name will help avoid conflicts with other existing domains\n'
);
// Use spawn instead of execSync for better error handling
const { spawn } = await import('child_process');
const vercelSetup = spawn('vercel', [], {
const vercelSetup = spawn('vercel', [], {
cwd: projectRoot,
stdio: "inherit",
shell: process.platform === "win32",
stdio: 'inherit',
shell: process.platform === 'win32',
});
await new Promise((resolve, reject) => {
@ -522,7 +539,7 @@ async function deployToVercel(useGitHub = false) {
resolve(); // Don't reject, as this is often expected
}
});
vercelSetup.on('error', (error) => {
console.log('⚠️ Vercel setup command completed (this is normal)');
resolve(); // Don't reject, as this is often expected
@ -530,15 +547,19 @@ async function deployToVercel(useGitHub = false) {
});
// Wait a moment for project files to be written
await new Promise(resolve => setTimeout(resolve, 2000));
await new Promise((resolve) => setTimeout(resolve, 2000));
// Load project info
let projectId;
try {
const projectJson = JSON.parse(fs.readFileSync('.vercel/project.json', 'utf8'));
const projectJson = JSON.parse(
fs.readFileSync('.vercel/project.json', 'utf8')
);
projectId = projectJson.projectId;
} catch (error) {
throw new Error('Failed to load project info. Please ensure the Vercel project was created successfully.');
throw new Error(
'Failed to load project info. Please ensure the Vercel project was created successfully.'
);
}
// Get Vercel token and initialize SDK client
@ -549,16 +570,16 @@ async function deployToVercel(useGitHub = false) {
vercelClient = new Vercel({
bearerToken: token,
});
console.log("✅ Initialized Vercel SDK client");
console.log('✅ Initialized Vercel SDK client');
}
} catch (error) {
console.warn(
"⚠️ Could not initialize Vercel SDK, falling back to CLI operations"
'⚠️ Could not initialize Vercel SDK, falling back to CLI operations'
);
}
// Get project details
console.log("\n🔍 Getting project details...");
console.log('\n🔍 Getting project details...');
let domain;
let projectName;
@ -569,10 +590,10 @@ async function deployToVercel(useGitHub = false) {
});
projectName = project.name;
domain = `${projectName}.vercel.app`;
console.log("🌐 Using project name for domain:", domain);
console.log('🌐 Using project name for domain:', domain);
} catch (error) {
console.warn(
"⚠️ Could not get project details via SDK, using CLI fallback"
'⚠️ Could not get project details via SDK, using CLI fallback'
);
}
}
@ -580,16 +601,19 @@ async function deployToVercel(useGitHub = false) {
// Fallback to CLI method if SDK failed
if (!domain) {
try {
const inspectOutput = execSync(`vercel project inspect ${projectId} 2>&1`, {
cwd: projectRoot,
encoding: 'utf8'
});
const inspectOutput = execSync(
`vercel project inspect ${projectId} 2>&1`,
{
cwd: projectRoot,
encoding: 'utf8',
}
);
const nameMatch = inspectOutput.match(/Name\s+([^\n]+)/);
if (nameMatch) {
projectName = nameMatch[1].trim();
domain = `${projectName}.vercel.app`;
console.log("🌐 Using project name for domain:", domain);
console.log('🌐 Using project name for domain:', domain);
} else {
const altMatch = inspectOutput.match(/Found Project [^/]+\/([^\n]+)/);
if (altMatch) {
@ -597,7 +621,9 @@ async function deployToVercel(useGitHub = false) {
domain = `${projectName}.vercel.app`;
console.log('🌐 Using project name for domain:', domain);
} else {
console.warn('⚠️ Could not determine project name from inspection, using fallback');
console.warn(
'⚠️ Could not determine project name from inspection, using fallback'
);
// Use a fallback domain based on project ID
domain = `project-${projectId.slice(-8)}.vercel.app`;
console.log('🌐 Using fallback domain:', domain);
@ -612,7 +638,7 @@ async function deployToVercel(useGitHub = false) {
}
// Generate mini app metadata
console.log("\n🔨 Generating mini app metadata...");
console.log('\n🔨 Generating mini app metadata...');
const webhookUrl =
process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID
@ -620,11 +646,11 @@ async function deployToVercel(useGitHub = false) {
: `https://${domain}/api/webhook`;
const miniAppMetadata = await generateFarcasterMetadata(domain, webhookUrl);
console.log("✅ Mini app metadata generated");
console.log('✅ Mini app metadata generated');
// Prepare environment variables
const nextAuthSecret =
process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("hex");
process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString('hex');
const vercelEnv = {
NEXTAUTH_SECRET: nextAuthSecret,
AUTH_SECRET: nextAuthSecret,
@ -637,12 +663,14 @@ async function deployToVercel(useGitHub = false) {
...(process.env.NEYNAR_CLIENT_ID && {
NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID,
}),
...(process.env.SPONSOR_SIGNER && { SPONSOR_SIGNER: process.env.SPONSOR_SIGNER }),
...(process.env.SPONSOR_SIGNER && {
SPONSOR_SIGNER: process.env.SPONSOR_SIGNER,
}),
...(miniAppMetadata && { MINI_APP_METADATA: miniAppMetadata }),
...Object.fromEntries(
Object.entries(process.env).filter(([key]) =>
key.startsWith("NEXT_PUBLIC_")
key.startsWith('NEXT_PUBLIC_')
)
),
};
@ -657,21 +685,21 @@ async function deployToVercel(useGitHub = false) {
// Deploy the project
if (useGitHub) {
console.log("\nSetting up GitHub integration...");
execSync("vercel link", {
console.log('\nSetting up GitHub integration...');
execSync('vercel link', {
cwd: projectRoot,
stdio: "inherit",
stdio: 'inherit',
env: process.env,
});
console.log("\n📦 Deploying with GitHub integration...");
console.log('\n📦 Deploying with GitHub integration...');
} else {
console.log("\n📦 Deploying local code directly...");
console.log('\n📦 Deploying local code directly...');
}
// Use spawn for better control over the deployment process
const vercelDeploy = spawn('vercel', ['deploy', '--prod'], {
const vercelDeploy = spawn('vercel', ['deploy', '--prod'], {
cwd: projectRoot,
stdio: "inherit",
stdio: 'inherit',
env: process.env,
});
@ -685,7 +713,7 @@ async function deployToVercel(useGitHub = false) {
reject(new Error(`Vercel deployment failed with exit code: ${code}`));
}
});
vercelDeploy.on('error', (error) => {
console.error('❌ Vercel deployment error:', error.message);
reject(error);
@ -698,13 +726,16 @@ async function deployToVercel(useGitHub = false) {
try {
deployment = await waitForDeployment(vercelClient, projectId);
} catch (error) {
console.warn('⚠️ Could not verify deployment completion:', error.message);
console.warn(
'⚠️ Could not verify deployment completion:',
error.message
);
console.log(' Proceeding with domain verification...');
}
}
// Verify actual domain after deployment
console.log("\n🔍 Verifying deployment domain...");
console.log('\n🔍 Verifying deployment domain...');
let actualDomain = domain;
if (vercelClient && deployment) {
@ -713,14 +744,14 @@ async function deployToVercel(useGitHub = false) {
console.log('🌐 Verified actual domain:', actualDomain);
} catch (error) {
console.warn(
"⚠️ Could not verify domain via SDK, using assumed domain"
'⚠️ Could not verify domain via SDK, using assumed domain'
);
}
}
// Update environment variables if domain changed
if (actualDomain !== domain) {
console.log("🔄 Updating environment variables with correct domain...");
console.log('🔄 Updating environment variables with correct domain...');
const webhookUrl =
process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID
@ -733,16 +764,27 @@ async function deployToVercel(useGitHub = false) {
};
if (miniAppMetadata) {
const updatedMetadata = await generateFarcasterMetadata(actualDomain, fid, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl);
const updatedMetadata = await generateFarcasterMetadata(
actualDomain,
fid,
await validateSeedPhrase(process.env.SEED_PHRASE),
process.env.SEED_PHRASE,
webhookUrl
);
updatedEnv.MINI_APP_METADATA = updatedMetadata;
}
await setEnvironmentVariables(vercelClient, projectId, updatedEnv, projectRoot);
await setEnvironmentVariables(
vercelClient,
projectId,
updatedEnv,
projectRoot
);
console.log('\n📦 Redeploying with correct domain...');
const vercelRedeploy = spawn('vercel', ['deploy', '--prod'], {
const vercelRedeploy = spawn('vercel', ['deploy', '--prod'], {
cwd: projectRoot,
stdio: "inherit",
stdio: 'inherit',
env: process.env,
});
@ -756,49 +798,49 @@ async function deployToVercel(useGitHub = false) {
reject(new Error(`Redeployment failed with exit code: ${code}`));
}
});
vercelRedeploy.on('error', (error) => {
console.error('❌ Redeployment error:', error.message);
reject(error);
});
});
domain = actualDomain;
}
console.log("\n✨ Deployment complete! Your mini app is now live at:");
console.log('\n✨ Deployment complete! Your mini app is now live at:');
console.log(`🌐 https://${domain}`);
console.log(
"\n📝 You can manage your project at https://vercel.com/dashboard"
'\n📝 You can manage your project at https://vercel.com/dashboard'
);
} catch (error) {
console.error("\n❌ Deployment failed:", error.message);
console.error('\n❌ Deployment failed:', error.message);
process.exit(1);
}
}
async function main() {
try {
console.log("🚀 Vercel Mini App Deployment (SDK Edition)");
console.log('🚀 Vercel Mini App Deployment (SDK Edition)');
console.log(
"This script will deploy your mini app to Vercel using the Vercel SDK."
'This script will deploy your mini app to Vercel using the Vercel SDK.'
);
console.log("\nThe script will:");
console.log("1. Check for required environment variables");
console.log("2. Set up a Vercel project (new or existing)");
console.log("3. Configure environment variables in Vercel using SDK");
console.log("4. Deploy and build your mini app\n");
console.log('\nThe script will:');
console.log('1. Check for required environment variables');
console.log('2. Set up a Vercel project (new or existing)');
console.log('3. Configure environment variables in Vercel using SDK');
console.log('4. Deploy and build your mini app\n');
// Check if @vercel/sdk is installed
try {
await import("@vercel/sdk");
await import('@vercel/sdk');
} catch (error) {
console.log("📦 Installing @vercel/sdk...");
execSync("npm install @vercel/sdk", {
console.log('📦 Installing @vercel/sdk...');
execSync('npm install @vercel/sdk', {
cwd: projectRoot,
stdio: "inherit",
stdio: 'inherit',
});
console.log("✅ @vercel/sdk installed successfully");
console.log('✅ @vercel/sdk installed successfully');
}
await checkRequiredEnvVars();
@ -807,55 +849,55 @@ async function main() {
let useGitHub = false;
if (remoteUrl) {
console.log("\n📦 Found GitHub repository:", remoteUrl);
console.log('\n📦 Found GitHub repository:', remoteUrl);
const { useGitHubDeploy } = await inquirer.prompt([
{
type: "confirm",
name: "useGitHubDeploy",
message: "Would you like to deploy from the GitHub repository?",
type: 'confirm',
name: 'useGitHubDeploy',
message: 'Would you like to deploy from the GitHub repository?',
default: true,
},
]);
useGitHub = useGitHubDeploy;
} else {
console.log("\n⚠ No GitHub repository found.");
console.log('\n⚠ No GitHub repository found.');
const { action } = await inquirer.prompt([
{
type: "list",
name: "action",
message: "What would you like to do?",
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
{ name: "Deploy local code directly", value: "deploy" },
{ name: "Set up GitHub repository first", value: "setup" },
{ name: 'Deploy local code directly', value: 'deploy' },
{ name: 'Set up GitHub repository first', value: 'setup' },
],
default: "deploy",
default: 'deploy',
},
]);
if (action === "setup") {
console.log("\n👋 Please set up your GitHub repository first:");
console.log("1. Create a new repository on GitHub");
console.log("2. Run these commands:");
console.log(" git remote add origin <your-repo-url>");
console.log(" git push -u origin main");
console.log("\nThen run this script again to deploy.");
if (action === 'setup') {
console.log('\n👋 Please set up your GitHub repository first:');
console.log('1. Create a new repository on GitHub');
console.log('2. Run these commands:');
console.log(' git remote add origin <your-repo-url>');
console.log(' git push -u origin main');
console.log('\nThen run this script again to deploy.');
process.exit(0);
}
}
if (!(await checkVercelCLI())) {
console.log("Vercel CLI not found. Installing...");
console.log('Vercel CLI not found. Installing...');
await installVercelCLI();
}
if (!(await loginToVercel())) {
console.error("\n❌ Failed to log in to Vercel. Please try again.");
console.error('\n❌ Failed to log in to Vercel. Please try again.');
process.exit(1);
}
await deployToVercel(useGitHub);
} catch (error) {
console.error("\n❌ Error:", error.message);
console.error('\n❌ Error:', error.message);
process.exit(1);
}
}