@akash-chowdhury-24/deployhub 1.0.15 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -55
- package/package.json +1 -1
- package/src/commands/doctor.js +1 -141
- package/src/commands/init.js +25 -156
- package/src/core/config.js +2 -13
- package/src/core/stages.js +0 -11
- package/src/deployment/index.js +0 -72
- package/src/rollback/engine.js +6 -39
- package/src/utils/github-actions.js +6 -96
- package/src/deployment/providers/platforms/_shared.js +0 -167
- package/src/deployment/providers/platforms/aws-amplify.js +0 -361
- package/src/deployment/providers/platforms/azure-static-web-apps.js +0 -68
- package/src/deployment/providers/platforms/cloudflare-pages.js +0 -103
- package/src/deployment/providers/platforms/firebase-app-hosting.js +0 -95
- package/src/deployment/providers/platforms/firebase-hosting.js +0 -99
- package/src/deployment/providers/platforms/index.js +0 -44
- package/src/deployment/providers/platforms/netlify.js +0 -102
- package/src/deployment/providers/platforms/vercel.js +0 -92
- package/src/utils/firebase-config-generator.js +0 -35
- package/src/utils/init-platform.js +0 -236
- package/src/utils/platform-env.js +0 -132
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { createLogger } from '../../../logger/index.js';
|
|
2
|
-
import { ensureFirebaseJson } from '../../../utils/firebase-config-generator.js';
|
|
3
|
-
import {
|
|
4
|
-
runCli,
|
|
5
|
-
saveDeploymentRecord,
|
|
6
|
-
readPreviousPlatformDeployment,
|
|
7
|
-
checkUrlHealth,
|
|
8
|
-
getBuildOutputDir,
|
|
9
|
-
} from './_shared.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {import('../../../core/config.js').DeployHubConfig} config
|
|
13
|
-
* @param {string} envName
|
|
14
|
-
* @param {Record<string, string>} [env]
|
|
15
|
-
*/
|
|
16
|
-
export function createFirebaseHostingProvider(config, envName, env = process.env) {
|
|
17
|
-
const environment = config.environments[envName];
|
|
18
|
-
const log = createLogger('firebase-hosting');
|
|
19
|
-
const token = env.FIREBASE_TOKEN;
|
|
20
|
-
const projectId = environment?.projectId || env.FIREBASE_PROJECT_ID;
|
|
21
|
-
|
|
22
|
-
async function deploy(artifactDir) {
|
|
23
|
-
if (!token || !projectId) {
|
|
24
|
-
throw new Error('FIREBASE_TOKEN and FIREBASE_PROJECT_ID are required');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const cwd = process.cwd();
|
|
28
|
-
const buildOutput = getBuildOutputDir(config);
|
|
29
|
-
await ensureFirebaseJson(buildOutput, cwd);
|
|
30
|
-
|
|
31
|
-
log.info(`Deploying to Firebase Hosting (${projectId})...`);
|
|
32
|
-
const result = await runCli(
|
|
33
|
-
`firebase deploy --only hosting --token=${token} --project=${projectId} --non-interactive`,
|
|
34
|
-
cwd
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (result.exitCode !== 0) {
|
|
38
|
-
throw new Error(`Firebase deploy failed: ${result.stderr || result.stdout}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const urlMatch = (result.stdout || '').match(/Hosting URL:\s*(https:\/\/\S+)/i);
|
|
42
|
-
const deploymentUrl = urlMatch?.[1] || config.healthCheck?.url || '';
|
|
43
|
-
|
|
44
|
-
await saveDeploymentRecord(
|
|
45
|
-
artifactDir,
|
|
46
|
-
{
|
|
47
|
-
platform: 'firebase-hosting',
|
|
48
|
-
deploymentUrl,
|
|
49
|
-
deploymentId: deploymentUrl,
|
|
50
|
-
projectId,
|
|
51
|
-
},
|
|
52
|
-
envName
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
log.success(`Deployed to Firebase Hosting: ${deploymentUrl || projectId}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function rollback(artifactDir) {
|
|
59
|
-
if (!token || !projectId) {
|
|
60
|
-
throw new Error('FIREBASE_TOKEN and FIREBASE_PROJECT_ID are required');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
log.info('Rolling back Firebase Hosting...');
|
|
64
|
-
const result = await runCli(
|
|
65
|
-
`firebase hosting:rollback --token=${token} --project=${projectId} --non-interactive`,
|
|
66
|
-
process.cwd()
|
|
67
|
-
);
|
|
68
|
-
if (result.exitCode !== 0) {
|
|
69
|
-
throw new Error(`Firebase rollback failed: ${result.stderr || result.stdout}`);
|
|
70
|
-
}
|
|
71
|
-
log.success('Firebase Hosting rollback complete');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function healthCheck() {
|
|
75
|
-
const url = config.healthCheck?.url;
|
|
76
|
-
if (!url) return true;
|
|
77
|
-
return checkUrlHealth(url);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function testConnection() {
|
|
81
|
-
if (!token || !projectId) {
|
|
82
|
-
throw new Error('FIREBASE_TOKEN and FIREBASE_PROJECT_ID are required');
|
|
83
|
-
}
|
|
84
|
-
const result = await runCli(
|
|
85
|
-
`firebase projects:list --token=${token} --non-interactive`,
|
|
86
|
-
process.cwd()
|
|
87
|
-
);
|
|
88
|
-
if (result.exitCode !== 0) {
|
|
89
|
-
throw new Error('Invalid FIREBASE_TOKEN');
|
|
90
|
-
}
|
|
91
|
-
if (!result.stdout.includes(projectId)) {
|
|
92
|
-
throw new Error(`Firebase project "${projectId}" not accessible`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { deploy, rollback, healthCheck, testConnection };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export default { createFirebaseHostingProvider };
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { createVercelProvider } from './vercel.js';
|
|
2
|
-
import { createNetlifyProvider } from './netlify.js';
|
|
3
|
-
import { createCloudflarePagesProvider } from './cloudflare-pages.js';
|
|
4
|
-
import { createAwsAmplifyProvider } from './aws-amplify.js';
|
|
5
|
-
import { createAzureStaticWebAppsProvider } from './azure-static-web-apps.js';
|
|
6
|
-
import { createFirebaseHostingProvider } from './firebase-hosting.js';
|
|
7
|
-
import { createFirebaseAppHostingProvider } from './firebase-app-hosting.js';
|
|
8
|
-
|
|
9
|
-
/** @type {Record<string, Function>} */
|
|
10
|
-
const PLATFORM_FACTORIES = {
|
|
11
|
-
vercel: createVercelProvider,
|
|
12
|
-
netlify: createNetlifyProvider,
|
|
13
|
-
'cloudflare-pages': createCloudflarePagesProvider,
|
|
14
|
-
'aws-amplify': createAwsAmplifyProvider,
|
|
15
|
-
'azure-static-web-apps': createAzureStaticWebAppsProvider,
|
|
16
|
-
'firebase-hosting': createFirebaseHostingProvider,
|
|
17
|
-
'firebase-app-hosting': createFirebaseAppHostingProvider,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {string} platform
|
|
22
|
-
* @param {import('../../../core/config.js').DeployHubConfig} config
|
|
23
|
-
* @param {string} envName
|
|
24
|
-
* @param {Record<string, string>} [env]
|
|
25
|
-
*/
|
|
26
|
-
export function createPlatformProvider(platform, config, envName, env = process.env) {
|
|
27
|
-
const factory = PLATFORM_FACTORIES[platform];
|
|
28
|
-
if (!factory) {
|
|
29
|
-
throw new Error(`Unknown platform: ${platform}`);
|
|
30
|
-
}
|
|
31
|
-
return factory(config, envName, env);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export {
|
|
35
|
-
createVercelProvider,
|
|
36
|
-
createNetlifyProvider,
|
|
37
|
-
createCloudflarePagesProvider,
|
|
38
|
-
createAwsAmplifyProvider,
|
|
39
|
-
createAzureStaticWebAppsProvider,
|
|
40
|
-
createFirebaseHostingProvider,
|
|
41
|
-
createFirebaseAppHostingProvider,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default { createPlatformProvider, PLATFORM_FACTORIES };
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import { createLogger } from '../../../logger/index.js';
|
|
3
|
-
import {
|
|
4
|
-
runCli,
|
|
5
|
-
saveDeploymentRecord,
|
|
6
|
-
readPreviousPlatformDeployment,
|
|
7
|
-
checkUrlHealth,
|
|
8
|
-
resolveBuildOutputPath,
|
|
9
|
-
} from './_shared.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @param {import('../../../core/config.js').DeployHubConfig} config
|
|
13
|
-
* @param {string} envName
|
|
14
|
-
* @param {Record<string, string>} [env]
|
|
15
|
-
*/
|
|
16
|
-
export function createNetlifyProvider(config, envName, env = process.env) {
|
|
17
|
-
const environment = config.environments[envName];
|
|
18
|
-
const log = createLogger('netlify');
|
|
19
|
-
const authToken = env.NETLIFY_AUTH_TOKEN;
|
|
20
|
-
const siteId = environment?.siteId || env.NETLIFY_SITE_ID;
|
|
21
|
-
|
|
22
|
-
async function deploy(artifactDir) {
|
|
23
|
-
if (!authToken || !siteId) {
|
|
24
|
-
throw new Error('NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID are required');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const buildDir = resolveBuildOutputPath(config);
|
|
28
|
-
log.info(`Deploying to Netlify from ${buildDir}...`);
|
|
29
|
-
|
|
30
|
-
const result = await runCli(
|
|
31
|
-
`netlify deploy --prod --dir="${buildDir}" --auth=${authToken} --site=${siteId}`,
|
|
32
|
-
process.cwd()
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
if (result.exitCode !== 0) {
|
|
36
|
-
throw new Error(`Netlify deploy failed: ${result.stderr || result.stdout}`);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const deployIdMatch = (result.stdout || '').match(/Deploy ID:\s*(\S+)/i);
|
|
40
|
-
const urlMatch = (result.stdout || '').match(/Website URL:\s*(https:\/\/\S+)/i);
|
|
41
|
-
const deployId = deployIdMatch?.[1] || '';
|
|
42
|
-
const deploymentUrl = urlMatch?.[1] || config.healthCheck?.url || '';
|
|
43
|
-
|
|
44
|
-
await saveDeploymentRecord(
|
|
45
|
-
artifactDir,
|
|
46
|
-
{
|
|
47
|
-
platform: 'netlify',
|
|
48
|
-
deploymentUrl,
|
|
49
|
-
deploymentId: deployId,
|
|
50
|
-
siteId,
|
|
51
|
-
},
|
|
52
|
-
envName
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
log.success(`Deployed to Netlify: ${deploymentUrl || siteId}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function rollback(artifactDir) {
|
|
59
|
-
if (!authToken || !siteId) {
|
|
60
|
-
throw new Error('NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID are required');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const previous = await readPreviousPlatformDeployment(artifactDir);
|
|
64
|
-
const deployId = previous?.deploymentId;
|
|
65
|
-
if (!deployId) {
|
|
66
|
-
throw new Error('No previous Netlify deployment ID found for rollback');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
log.info(`Restoring Netlify deploy ${deployId}...`);
|
|
70
|
-
await axios.post(
|
|
71
|
-
`https://api.netlify.com/api/v1/sites/${siteId}/deploys/${deployId}/restore`,
|
|
72
|
-
{},
|
|
73
|
-
{ headers: { Authorization: `Bearer ${authToken}` } }
|
|
74
|
-
);
|
|
75
|
-
log.success('Netlify rollback complete');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function healthCheck() {
|
|
79
|
-
const url = config.healthCheck?.url;
|
|
80
|
-
if (!url) return true;
|
|
81
|
-
return checkUrlHealth(url);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function testConnection() {
|
|
85
|
-
if (!authToken) throw new Error('NETLIFY_AUTH_TOKEN is required');
|
|
86
|
-
const response = await axios.get('https://api.netlify.com/api/v1/user', {
|
|
87
|
-
headers: { Authorization: `Bearer ${authToken}` },
|
|
88
|
-
});
|
|
89
|
-
if (!response.data?.email) {
|
|
90
|
-
throw new Error('Invalid NETLIFY_AUTH_TOKEN');
|
|
91
|
-
}
|
|
92
|
-
if (siteId) {
|
|
93
|
-
await axios.get(`https://api.netlify.com/api/v1/sites/${siteId}`, {
|
|
94
|
-
headers: { Authorization: `Bearer ${authToken}` },
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { deploy, rollback, healthCheck, testConnection };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export default { createNetlifyProvider };
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { createLogger } from '../../../logger/index.js';
|
|
2
|
-
import {
|
|
3
|
-
runCli,
|
|
4
|
-
saveDeploymentRecord,
|
|
5
|
-
readPreviousPlatformDeployment,
|
|
6
|
-
checkUrlHealth,
|
|
7
|
-
} from './_shared.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {import('../../../core/config.js').DeployHubConfig} config
|
|
11
|
-
* @param {string} envName
|
|
12
|
-
* @param {Record<string, string>} [env]
|
|
13
|
-
*/
|
|
14
|
-
export function createVercelProvider(config, envName, env = process.env) {
|
|
15
|
-
const log = createLogger('vercel');
|
|
16
|
-
const token = env.VERCEL_TOKEN;
|
|
17
|
-
const orgId = env.VERCEL_ORG_ID;
|
|
18
|
-
const projectId = env.VERCEL_PROJECT_ID;
|
|
19
|
-
|
|
20
|
-
async function deploy(artifactDir) {
|
|
21
|
-
if (!token) throw new Error('VERCEL_TOKEN is required');
|
|
22
|
-
|
|
23
|
-
log.info('Deploying to Vercel...');
|
|
24
|
-
const cwd = process.cwd();
|
|
25
|
-
const deployEnv = {
|
|
26
|
-
VERCEL_ORG_ID: orgId || '',
|
|
27
|
-
VERCEL_PROJECT_ID: projectId || '',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const result = await runCli(
|
|
31
|
-
`vercel deploy --prod --token=${token} --yes`,
|
|
32
|
-
cwd,
|
|
33
|
-
deployEnv
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
if (result.exitCode !== 0) {
|
|
37
|
-
throw new Error(`Vercel deploy failed: ${result.stderr || result.stdout}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const output = result.stdout || '';
|
|
41
|
-
const urlMatch = output.match(/https:\/\/[^\s]+\.vercel\.app/);
|
|
42
|
-
const deploymentUrl = urlMatch ? urlMatch[0] : config.healthCheck?.url || '';
|
|
43
|
-
|
|
44
|
-
await saveDeploymentRecord(
|
|
45
|
-
artifactDir,
|
|
46
|
-
{
|
|
47
|
-
platform: 'vercel',
|
|
48
|
-
deploymentUrl,
|
|
49
|
-
deploymentId: deploymentUrl,
|
|
50
|
-
},
|
|
51
|
-
envName
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
log.success(`Deployed to Vercel: ${deploymentUrl || 'production'}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function rollback(artifactDir) {
|
|
58
|
-
if (!token) throw new Error('VERCEL_TOKEN is required');
|
|
59
|
-
|
|
60
|
-
const previous = await readPreviousPlatformDeployment(artifactDir);
|
|
61
|
-
const deploymentUrl = previous?.deploymentUrl || previous?.deploymentId;
|
|
62
|
-
|
|
63
|
-
log.info('Rolling back on Vercel...');
|
|
64
|
-
const cmd = deploymentUrl
|
|
65
|
-
? `vercel rollback ${deploymentUrl} --token=${token} --yes`
|
|
66
|
-
: `vercel rollback --token=${token} --yes`;
|
|
67
|
-
|
|
68
|
-
const result = await runCli(cmd, process.cwd());
|
|
69
|
-
if (result.exitCode !== 0) {
|
|
70
|
-
throw new Error(`Vercel rollback failed: ${result.stderr || result.stdout}`);
|
|
71
|
-
}
|
|
72
|
-
log.success('Vercel rollback complete');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function healthCheck() {
|
|
76
|
-
const url = config.healthCheck?.url;
|
|
77
|
-
if (!url) return true;
|
|
78
|
-
return checkUrlHealth(url);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function testConnection() {
|
|
82
|
-
if (!token) throw new Error('VERCEL_TOKEN is required');
|
|
83
|
-
const result = await runCli(`vercel whoami --token=${token}`, process.cwd());
|
|
84
|
-
if (result.exitCode !== 0) {
|
|
85
|
-
throw new Error('Invalid VERCEL_TOKEN');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { deploy, rollback, healthCheck, testConnection };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export default { createVercelProvider };
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate firebase.json for Firebase Hosting if missing.
|
|
3
|
-
*
|
|
4
|
-
* @param {string} buildOutput
|
|
5
|
-
* @returns {object}
|
|
6
|
-
*/
|
|
7
|
-
export function generateFirebaseHostingConfig(buildOutput = 'dist') {
|
|
8
|
-
return {
|
|
9
|
-
hosting: {
|
|
10
|
-
public: buildOutput,
|
|
11
|
-
ignore: ['firebase.json', '**/.*', '**/node_modules/**'],
|
|
12
|
-
rewrites: [{ source: '**', destination: '/index.html' }],
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param {string} buildOutput
|
|
19
|
-
* @param {string} [cwd]
|
|
20
|
-
*/
|
|
21
|
-
export async function ensureFirebaseJson(buildOutput = 'dist', cwd = process.cwd()) {
|
|
22
|
-
const fs = (await import('fs-extra')).default;
|
|
23
|
-
const path = (await import('path')).default;
|
|
24
|
-
const firebasePath = path.join(cwd, 'firebase.json');
|
|
25
|
-
|
|
26
|
-
if (await fs.pathExists(firebasePath)) {
|
|
27
|
-
return firebasePath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const config = generateFirebaseHostingConfig(buildOutput);
|
|
31
|
-
await fs.writeJson(firebasePath, config, { spaces: 2 });
|
|
32
|
-
return firebasePath;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default { generateFirebaseHostingConfig, ensureFirebaseJson };
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import fs from 'fs-extra';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { execa } from 'execa';
|
|
6
|
-
import {
|
|
7
|
-
PLATFORM_CHOICES,
|
|
8
|
-
PLATFORM_COMPARISON,
|
|
9
|
-
getPlatformEnvExample,
|
|
10
|
-
} from './platform-env.js';
|
|
11
|
-
import { ensureFirebaseJson } from './firebase-config-generator.js';
|
|
12
|
-
import { appendEnv } from '../core/config.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {string} platform
|
|
16
|
-
* @param {string} projectName
|
|
17
|
-
* @param {string} buildOutput
|
|
18
|
-
* @param {string} [cwd]
|
|
19
|
-
* @returns {Promise<Record<string, unknown>>}
|
|
20
|
-
*/
|
|
21
|
-
export async function promptPlatformQuestions(
|
|
22
|
-
platform,
|
|
23
|
-
projectName,
|
|
24
|
-
buildOutput,
|
|
25
|
-
cwd = process.cwd()
|
|
26
|
-
) {
|
|
27
|
-
/** @type {Record<string, unknown>} */
|
|
28
|
-
const config = { platform };
|
|
29
|
-
|
|
30
|
-
switch (platform) {
|
|
31
|
-
case 'vercel': {
|
|
32
|
-
const answers = await inquirer.prompt([
|
|
33
|
-
{
|
|
34
|
-
type: 'input',
|
|
35
|
-
name: 'projectName',
|
|
36
|
-
message: 'Vercel project name:',
|
|
37
|
-
default: projectName,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
type: 'confirm',
|
|
41
|
-
name: 'vercelLinked',
|
|
42
|
-
message: 'Have you already linked this project with Vercel?',
|
|
43
|
-
default: fs.existsSync(path.join(cwd, '.vercel', 'project.json')),
|
|
44
|
-
},
|
|
45
|
-
]);
|
|
46
|
-
config.projectName = answers.projectName;
|
|
47
|
-
|
|
48
|
-
if (!answers.vercelLinked) {
|
|
49
|
-
console.log(chalk.gray('Running vercel link — follow the prompts...'));
|
|
50
|
-
try {
|
|
51
|
-
await execa('vercel', ['link'], { cwd, stdio: 'inherit' });
|
|
52
|
-
} catch {
|
|
53
|
-
console.log(
|
|
54
|
-
chalk.yellow(
|
|
55
|
-
'Could not run vercel link automatically. Run it manually, then add VERCEL_ORG_ID and VERCEL_PROJECT_ID to .env'
|
|
56
|
-
)
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
case 'netlify': {
|
|
64
|
-
const answers = await inquirer.prompt([
|
|
65
|
-
{
|
|
66
|
-
type: 'input',
|
|
67
|
-
name: 'siteId',
|
|
68
|
-
message: 'Netlify site ID (from Netlify dashboard):',
|
|
69
|
-
},
|
|
70
|
-
]);
|
|
71
|
-
config.siteId = answers.siteId;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
case 'cloudflare-pages': {
|
|
76
|
-
const answers = await inquirer.prompt([
|
|
77
|
-
{
|
|
78
|
-
type: 'input',
|
|
79
|
-
name: 'projectName',
|
|
80
|
-
message: 'Cloudflare Pages project name:',
|
|
81
|
-
default: projectName,
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
type: 'input',
|
|
85
|
-
name: 'accountId',
|
|
86
|
-
message: 'Cloudflare account ID (from dashboard):',
|
|
87
|
-
},
|
|
88
|
-
]);
|
|
89
|
-
config.projectName = answers.projectName;
|
|
90
|
-
config.accountId = answers.accountId;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
case 'aws-amplify': {
|
|
95
|
-
const answers = await inquirer.prompt([
|
|
96
|
-
{
|
|
97
|
-
type: 'input',
|
|
98
|
-
name: 'appId',
|
|
99
|
-
message: 'Amplify App ID (from AWS console):',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
type: 'input',
|
|
103
|
-
name: 'region',
|
|
104
|
-
message: 'AWS region:',
|
|
105
|
-
default: 'us-east-1',
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
type: 'input',
|
|
109
|
-
name: 'branch',
|
|
110
|
-
message: 'Amplify branch name (auto-created for manual zip deploys):',
|
|
111
|
-
default: 'main',
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
type: 'confirm',
|
|
115
|
-
name: 'githubConnected',
|
|
116
|
-
message: 'GitHub already connected to Amplify?',
|
|
117
|
-
default: true,
|
|
118
|
-
},
|
|
119
|
-
]);
|
|
120
|
-
config.appId = answers.appId;
|
|
121
|
-
config.region = answers.region;
|
|
122
|
-
config.branch = answers.branch;
|
|
123
|
-
config.githubConnected = answers.githubConnected;
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
case 'azure-static-web-apps': {
|
|
128
|
-
const answers = await inquirer.prompt([
|
|
129
|
-
{
|
|
130
|
-
type: 'input',
|
|
131
|
-
name: 'resourceName',
|
|
132
|
-
message: 'Resource name:',
|
|
133
|
-
default: projectName,
|
|
134
|
-
},
|
|
135
|
-
]);
|
|
136
|
-
config.resourceName = answers.resourceName;
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
case 'firebase-hosting': {
|
|
141
|
-
const answers = await inquirer.prompt([
|
|
142
|
-
{
|
|
143
|
-
type: 'input',
|
|
144
|
-
name: 'projectId',
|
|
145
|
-
message: 'Firebase project ID:',
|
|
146
|
-
default: projectName,
|
|
147
|
-
},
|
|
148
|
-
]);
|
|
149
|
-
config.projectId = answers.projectId;
|
|
150
|
-
await ensureFirebaseJson(buildOutput, cwd);
|
|
151
|
-
console.log(chalk.green('✓ firebase.json generated'));
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
case 'firebase-app-hosting': {
|
|
156
|
-
const answers = await inquirer.prompt([
|
|
157
|
-
{
|
|
158
|
-
type: 'input',
|
|
159
|
-
name: 'projectId',
|
|
160
|
-
message: 'Firebase project ID:',
|
|
161
|
-
default: projectName,
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
type: 'input',
|
|
165
|
-
name: 'backendName',
|
|
166
|
-
message: 'Backend name:',
|
|
167
|
-
default: `${projectName}-backend`,
|
|
168
|
-
},
|
|
169
|
-
]);
|
|
170
|
-
config.projectId = answers.projectId;
|
|
171
|
-
config.backendName = answers.backendName;
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
default:
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return config;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* @param {string} platform
|
|
184
|
-
* @param {string} [cwd]
|
|
185
|
-
*/
|
|
186
|
-
export async function appendPlatformEnvExample(platform, cwd = process.cwd()) {
|
|
187
|
-
const examples = getPlatformEnvExample(platform);
|
|
188
|
-
await appendEnv(examples, cwd);
|
|
189
|
-
|
|
190
|
-
const envExamplePath = path.join(cwd, '.env.example');
|
|
191
|
-
if (!(await fs.pathExists(envExamplePath))) return;
|
|
192
|
-
|
|
193
|
-
let content = await fs.readFile(envExamplePath, 'utf-8');
|
|
194
|
-
const sectionHeader = `\n# ${platform} deployment\n`;
|
|
195
|
-
const lines = Object.keys(examples)
|
|
196
|
-
.map((key) => `${key}=`)
|
|
197
|
-
.join('\n');
|
|
198
|
-
|
|
199
|
-
if (!content.includes(`${platform} deployment`)) {
|
|
200
|
-
content += `${sectionHeader}${lines}\n`;
|
|
201
|
-
await fs.writeFile(envExamplePath, content);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function showPlatformComparison() {
|
|
206
|
-
console.log('');
|
|
207
|
-
console.log(chalk.bold('Platform comparison:'));
|
|
208
|
-
console.log(chalk.gray(PLATFORM_COMPARISON));
|
|
209
|
-
console.log('');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* @param {string} platform
|
|
214
|
-
* @param {string} projectName
|
|
215
|
-
* @returns {string}
|
|
216
|
-
*/
|
|
217
|
-
export function suggestHealthUrl(platform, projectName) {
|
|
218
|
-
const urls = {
|
|
219
|
-
vercel: `https://${projectName}.vercel.app`,
|
|
220
|
-
netlify: `https://${projectName}.netlify.app`,
|
|
221
|
-
'cloudflare-pages': `https://${projectName}.pages.dev`,
|
|
222
|
-
'firebase-hosting': `https://${projectName}.web.app`,
|
|
223
|
-
'firebase-app-hosting': `https://${projectName}.web.app`,
|
|
224
|
-
};
|
|
225
|
-
return urls[platform] || '';
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export { PLATFORM_CHOICES };
|
|
229
|
-
|
|
230
|
-
export default {
|
|
231
|
-
promptPlatformQuestions,
|
|
232
|
-
appendPlatformEnvExample,
|
|
233
|
-
showPlatformComparison,
|
|
234
|
-
suggestHealthUrl,
|
|
235
|
-
PLATFORM_CHOICES,
|
|
236
|
-
};
|