feat: vercel deployment with frame manifest

This commit is contained in:
lucas-neynar 2025-03-21 14:31:08 -07:00
parent e01d091c66
commit 8ed98b604a
No known key found for this signature in database
2 changed files with 60 additions and 77 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "create-neynar-farcaster-frame", "name": "create-neynar-farcaster-frame",
"version": "1.1.7", "version": "1.2.0",
"type": "module", "type": "module",
"files": [ "files": [
"bin/index.js" "bin/index.js"

View File

@ -43,7 +43,7 @@ async function generateFarcasterMetadata(domain, accountAddress, seedPhrase, web
}); });
const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url');
const metadata = { return {
accountAssociation: { accountAssociation: {
header: encodedHeader, header: encodedHeader,
payload: encodedPayload, payload: encodedPayload,
@ -51,19 +51,16 @@ async function generateFarcasterMetadata(domain, accountAddress, seedPhrase, web
}, },
frame: { frame: {
version: "1", version: "1",
name: process.env.NEXT_PUBLIC_FRAME_NAME, name: process.env.NEXT_PUBLIC_FRAME_NAME?.trim(),
iconUrl: `https://${trimmedDomain}/icon.png`, iconUrl: `https://${trimmedDomain}/icon.png`,
homeUrl: trimmedDomain, homeUrl: trimmedDomain,
imageUrl: `https://${trimmedDomain}/opengraph-image`, imageUrl: `https://${trimmedDomain}/opengraph-image`,
buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT, buttonTitle: process.env.NEXT_PUBLIC_FRAME_BUTTON_TEXT?.trim(),
splashImageUrl: `https://${trimmedDomain}/splash.png`, splashImageUrl: `https://${trimmedDomain}/splash.png`,
splashBackgroundColor: "#f7f7f7", splashBackgroundColor: "#f7f7f7",
webhookUrl, webhookUrl: webhookUrl?.trim(),
}, },
}; };
// Return stringified metadata to ensure proper JSON formatting
return JSON.stringify(metadata);
} }
async function loadEnvLocal() { async function loadEnvLocal() {
@ -287,6 +284,54 @@ async function loginToVercel() {
return false; return false;
} }
async function setVercelEnvVar(key, value, projectRoot) {
try {
// First try to remove the existing env var if it exists
try {
execSync(`vercel env rm ${key} production -y`, {
cwd: projectRoot,
stdio: 'ignore',
env: process.env
});
} catch (error) {
// Ignore errors from removal (var might not exist)
}
// For complex objects like frameMetadata, use a temporary file approach
if (typeof value === 'object') {
const tempFilePath = path.join(projectRoot, `${key}_temp.json`);
// Write the value to a temporary file with proper JSON formatting
fs.writeFileSync(tempFilePath, JSON.stringify(value));
// Use the file to add the environment variable
execSync(`vercel env add ${key} production < "${tempFilePath}"`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
// Clean up the temporary file
fs.unlinkSync(tempFilePath);
} else {
// For simple string values, use a different approach to avoid shell interpretation issues
const tempFilePath = path.join(projectRoot, `${key}_temp.txt`);
fs.writeFileSync(tempFilePath, value.toString());
execSync(`vercel env add ${key} production < "${tempFilePath}"`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
fs.unlinkSync(tempFilePath);
}
return true;
} catch (error) {
console.warn(`⚠️ Warning: Failed to set environment variable ${key}:`, error.message);
return false;
}
}
async function deployToVercel(useGitHub = false) { async function deployToVercel(useGitHub = false) {
try { try {
console.log('\n🚀 Deploying to Vercel...'); console.log('\n🚀 Deploying to Vercel...');
@ -371,7 +416,7 @@ async function deployToVercel(useGitHub = false) {
...(process.env.NEYNAR_API_KEY && { NEYNAR_API_KEY: process.env.NEYNAR_API_KEY }), ...(process.env.NEYNAR_API_KEY && { NEYNAR_API_KEY: process.env.NEYNAR_API_KEY }),
...(process.env.NEYNAR_CLIENT_ID && { NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID }), ...(process.env.NEYNAR_CLIENT_ID && { NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID }),
// Frame metadata // Frame metadata - don't stringify here
...(frameMetadata && { FRAME_METADATA: frameMetadata }), ...(frameMetadata && { FRAME_METADATA: frameMetadata }),
// Public vars // Public vars
@ -385,27 +430,7 @@ async function deployToVercel(useGitHub = false) {
console.log('\n📝 Setting up environment variables...'); console.log('\n📝 Setting up environment variables...');
for (const [key, value] of Object.entries(vercelEnv)) { for (const [key, value] of Object.entries(vercelEnv)) {
if (value) { if (value) {
try { await setVercelEnvVar(key, value, projectRoot);
// First try to remove the existing env var if it exists
try {
execSync(`vercel env rm ${key} production -y`, {
cwd: projectRoot,
stdio: 'ignore',
env: process.env
});
} catch (error) {
// Ignore errors from removal (var might not exist)
}
// Add the new env var without newline
execSync(`printf "%s" "${value}" | vercel env add ${key} production`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
} catch (error) {
console.warn(`⚠️ Warning: Failed to set environment variable ${key}`);
}
} }
} }
@ -442,7 +467,6 @@ async function deployToVercel(useGitHub = false) {
// Read the output file // Read the output file
const projectOutput = fs.readFileSync(tempOutputFile, 'utf8'); const projectOutput = fs.readFileSync(tempOutputFile, 'utf8');
console.log('Raw project output:', projectOutput);
// Process the output // Process the output
const projectLines = projectOutput const projectLines = projectOutput
@ -454,8 +478,6 @@ async function deployToVercel(useGitHub = false) {
line.includes(projectName) line.includes(projectName)
); );
console.log('Current project line:', currentProject);
if (currentProject) { if (currentProject) {
// Extract the domain from the line // Extract the domain from the line
const domainMatch = currentProject.match(/https:\/\/([^\s]+)/); const domainMatch = currentProject.match(/https:\/\/([^\s]+)/);
@ -472,54 +494,15 @@ async function deployToVercel(useGitHub = false) {
if (frameMetadata) { if (frameMetadata) {
frameMetadata = await generateFarcasterMetadata(actualDomain, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl); frameMetadata = await generateFarcasterMetadata(actualDomain, await validateSeedPhrase(process.env.SEED_PHRASE), process.env.SEED_PHRASE, webhookUrl);
// Update FRAME_METADATA env var // Update FRAME_METADATA env var using the new function
try { await setVercelEnvVar('FRAME_METADATA', frameMetadata, projectRoot);
execSync(`vercel env rm FRAME_METADATA production -y`, {
cwd: projectRoot,
stdio: 'ignore',
env: process.env
});
execSync(`printf "%s" "${frameMetadata}" | vercel env add FRAME_METADATA production`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
} catch (error) {
console.warn('⚠️ Warning: Failed to update FRAME_METADATA with correct domain');
}
} }
// Update NEXTAUTH_URL // Update NEXTAUTH_URL
try { await setVercelEnvVar('NEXTAUTH_URL', `https://${actualDomain}`, projectRoot);
execSync(`vercel env rm NEXTAUTH_URL production -y`, {
cwd: projectRoot,
stdio: 'ignore',
env: process.env
});
execSync(`printf "%s" "https://${actualDomain}" | vercel env add NEXTAUTH_URL production`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
} catch (error) {
console.warn('⚠️ Warning: Failed to update NEXTAUTH_URL with correct domain');
}
// Update NEXT_PUBLIC_URL // Update NEXT_PUBLIC_URL
try { await setVercelEnvVar('NEXT_PUBLIC_URL', `https://${actualDomain}`, projectRoot);
execSync(`vercel env rm NEXT_PUBLIC_URL production -y`, {
cwd: projectRoot,
stdio: 'ignore',
env: process.env
});
execSync(`printf "%s" "https://${actualDomain}" | vercel env add NEXT_PUBLIC_URL production`, {
cwd: projectRoot,
stdio: 'inherit',
env: process.env
});
} catch (error) {
console.warn('⚠️ Warning: Failed to update NEXT_PUBLIC_URL with correct domain');
}
// Redeploy with updated environment variables // Redeploy with updated environment variables
console.log('\n📦 Redeploying with correct domain...'); console.log('\n📦 Redeploying with correct domain...');