@akash-chowdhury-24/deployhub 1.0.12 → 1.0.14
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/package.json
CHANGED
package/src/core/config.js
CHANGED
|
@@ -21,6 +21,7 @@ const EnvironmentSchema = z.object({
|
|
|
21
21
|
accountId: z.string().optional(),
|
|
22
22
|
appId: z.string().optional(),
|
|
23
23
|
region: z.string().optional(),
|
|
24
|
+
branch: z.string().optional(),
|
|
24
25
|
githubConnected: z.boolean().optional(),
|
|
25
26
|
resourceName: z.string().optional(),
|
|
26
27
|
projectId: z.string().optional(),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
-
import
|
|
3
|
+
import archiver from 'archiver';
|
|
4
|
+
import axios from 'axios';
|
|
4
5
|
import { createLogger } from '../../../logger/index.js';
|
|
5
6
|
import {
|
|
6
7
|
runCli,
|
|
@@ -9,6 +10,37 @@ import {
|
|
|
9
10
|
checkUrlHealth,
|
|
10
11
|
} from './_shared.js';
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} sourceDir
|
|
15
|
+
* @param {string} zipPath
|
|
16
|
+
*/
|
|
17
|
+
async function createRootZip(sourceDir, zipPath) {
|
|
18
|
+
await fs.ensureDir(path.dirname(zipPath));
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const output = fs.createWriteStream(zipPath);
|
|
21
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
22
|
+
|
|
23
|
+
output.on('close', resolve);
|
|
24
|
+
archive.on('error', reject);
|
|
25
|
+
|
|
26
|
+
archive.pipe(output);
|
|
27
|
+
archive.directory(sourceDir, false);
|
|
28
|
+
archive.finalize();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} zipPath
|
|
34
|
+
* @param {string} uploadUrl
|
|
35
|
+
*/
|
|
36
|
+
async function uploadZipToAmplify(zipPath, uploadUrl) {
|
|
37
|
+
await axios.put(uploadUrl, fs.createReadStream(zipPath), {
|
|
38
|
+
headers: { 'Content-Type': 'application/zip' },
|
|
39
|
+
maxBodyLength: Infinity,
|
|
40
|
+
maxContentLength: Infinity,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
/**
|
|
13
45
|
* @param {import('../../../core/config.js').DeployHubConfig} config
|
|
14
46
|
* @param {string} envName
|
|
@@ -19,21 +51,68 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
19
51
|
const log = createLogger('aws-amplify');
|
|
20
52
|
const appId = environment?.appId || env.AMPLIFY_APP_ID;
|
|
21
53
|
const region = environment?.region || env.AWS_REGION || 'us-east-1';
|
|
54
|
+
const branch = environment?.branch || env.AMPLIFY_BRANCH || 'main';
|
|
22
55
|
const githubConnected = environment?.githubConnected ?? false;
|
|
23
56
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const awsEnv = {
|
|
57
|
+
function getAwsEnv() {
|
|
58
|
+
return {
|
|
28
59
|
AWS_ACCESS_KEY_ID: env.AWS_ACCESS_KEY_ID || '',
|
|
29
60
|
AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY || '',
|
|
30
61
|
AWS_DEFAULT_REGION: region,
|
|
31
62
|
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} stage
|
|
67
|
+
*/
|
|
68
|
+
function resolveAmplifyStage(stage) {
|
|
69
|
+
const normalized = stage.toLowerCase();
|
|
70
|
+
if (normalized === 'production') return 'PRODUCTION';
|
|
71
|
+
if (normalized === 'staging') return 'BETA';
|
|
72
|
+
return 'DEVELOPMENT';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function branchExists(awsEnv) {
|
|
76
|
+
const result = await runCli(
|
|
77
|
+
`aws amplify get-branch --app-id ${appId} --branch-name ${branch}`,
|
|
78
|
+
process.cwd(),
|
|
79
|
+
awsEnv
|
|
80
|
+
);
|
|
81
|
+
return result.exitCode === 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function ensureBranchExists(awsEnv) {
|
|
85
|
+
if (await branchExists(awsEnv)) return;
|
|
86
|
+
|
|
87
|
+
if (githubConnected) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Amplify branch "${branch}" not found. Connect your GitHub repo in the Amplify console or choose a branch that already exists.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const stage = resolveAmplifyStage(envName);
|
|
94
|
+
log.info(`Creating Amplify branch "${branch}" for manual zip deploys...`);
|
|
95
|
+
const createResult = await runCli(
|
|
96
|
+
`aws amplify create-branch --app-id ${appId} --branch-name ${branch} --stage ${stage} --no-enable-auto-build --description "Created by DeployHub for manual deployments"`,
|
|
97
|
+
process.cwd(),
|
|
98
|
+
awsEnv
|
|
99
|
+
);
|
|
100
|
+
if (createResult.exitCode !== 0) {
|
|
101
|
+
throw new Error(`Amplify create-branch failed: ${createResult.stderr || createResult.stdout}`);
|
|
102
|
+
}
|
|
103
|
+
log.success(`Amplify branch "${branch}" ready`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function deploy(artifactDir) {
|
|
107
|
+
if (!appId) throw new Error('AMPLIFY_APP_ID is required');
|
|
108
|
+
|
|
109
|
+
const awsEnv = getAwsEnv();
|
|
32
110
|
|
|
33
111
|
if (githubConnected) {
|
|
34
|
-
|
|
112
|
+
await ensureBranchExists(awsEnv);
|
|
113
|
+
log.info(`Triggering Amplify release job on branch ${branch} (GitHub connected)...`);
|
|
35
114
|
const result = await runCli(
|
|
36
|
-
`aws amplify start-job --app-id ${appId} --branch-name
|
|
115
|
+
`aws amplify start-job --app-id ${appId} --branch-name ${branch} --job-type RELEASE`,
|
|
37
116
|
process.cwd(),
|
|
38
117
|
awsEnv
|
|
39
118
|
);
|
|
@@ -50,6 +129,7 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
50
129
|
platform: 'aws-amplify',
|
|
51
130
|
deploymentId: jobId,
|
|
52
131
|
appId,
|
|
132
|
+
branch,
|
|
53
133
|
method: 'github',
|
|
54
134
|
},
|
|
55
135
|
envName
|
|
@@ -58,14 +138,22 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
58
138
|
return;
|
|
59
139
|
}
|
|
60
140
|
|
|
61
|
-
log.info(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
141
|
+
log.info(`Uploading build output to Amplify branch ${branch}...`);
|
|
142
|
+
await ensureBranchExists(awsEnv);
|
|
143
|
+
|
|
144
|
+
const buildOutput = config.frontend?.buildOutput || config.buildOutput || 'dist';
|
|
145
|
+
const buildDir = path.join(process.cwd(), buildOutput);
|
|
146
|
+
if (!(await fs.pathExists(buildDir))) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Build output not found at ${buildOutput}. Amplify needs the built static files, not artifact.zip with a nested folder.`
|
|
149
|
+
);
|
|
65
150
|
}
|
|
66
151
|
|
|
152
|
+
const amplifyZipPath = path.join(artifactDir, 'amplify-deploy.zip');
|
|
153
|
+
await createRootZip(buildDir, amplifyZipPath);
|
|
154
|
+
|
|
67
155
|
const createResult = await runCli(
|
|
68
|
-
`aws amplify create-deployment --app-id ${appId} --branch-name
|
|
156
|
+
`aws amplify create-deployment --app-id ${appId} --branch-name ${branch} --output json`,
|
|
69
157
|
process.cwd(),
|
|
70
158
|
awsEnv
|
|
71
159
|
);
|
|
@@ -77,14 +165,11 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
77
165
|
const jobId = deployment.jobId;
|
|
78
166
|
const zipUploadUrl = deployment.zipUploadUrl;
|
|
79
167
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
stdio: 'inherit',
|
|
83
|
-
shell: true,
|
|
84
|
-
});
|
|
168
|
+
log.info('Uploading zip to Amplify...');
|
|
169
|
+
await uploadZipToAmplify(amplifyZipPath, zipUploadUrl);
|
|
85
170
|
|
|
86
171
|
const startResult = await runCli(
|
|
87
|
-
`aws amplify start-deployment --app-id ${appId} --branch-name
|
|
172
|
+
`aws amplify start-deployment --app-id ${appId} --branch-name ${branch} --job-id ${jobId}`,
|
|
88
173
|
process.cwd(),
|
|
89
174
|
awsEnv
|
|
90
175
|
);
|
|
@@ -92,12 +177,15 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
92
177
|
throw new Error(`Amplify start-deployment failed: ${startResult.stderr}`);
|
|
93
178
|
}
|
|
94
179
|
|
|
180
|
+
await fs.remove(amplifyZipPath);
|
|
181
|
+
|
|
95
182
|
await saveDeploymentRecord(
|
|
96
183
|
artifactDir,
|
|
97
184
|
{
|
|
98
185
|
platform: 'aws-amplify',
|
|
99
186
|
deploymentId: jobId,
|
|
100
187
|
appId,
|
|
188
|
+
branch,
|
|
101
189
|
method: 'zip',
|
|
102
190
|
},
|
|
103
191
|
envName
|
|
@@ -114,21 +202,18 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
114
202
|
throw new Error('No previous Amplify job ID found for rollback');
|
|
115
203
|
}
|
|
116
204
|
|
|
117
|
-
const awsEnv =
|
|
118
|
-
|
|
119
|
-
AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY || '',
|
|
120
|
-
AWS_DEFAULT_REGION: region,
|
|
121
|
-
};
|
|
205
|
+
const awsEnv = getAwsEnv();
|
|
206
|
+
const rollbackBranch = previous?.branch || branch;
|
|
122
207
|
|
|
123
|
-
log.info(`Re-triggering Amplify job ${jobId}...`);
|
|
208
|
+
log.info(`Re-triggering Amplify job ${jobId} on branch ${rollbackBranch}...`);
|
|
124
209
|
const result = await runCli(
|
|
125
|
-
`aws amplify start-job --app-id ${appId} --branch-name
|
|
210
|
+
`aws amplify start-job --app-id ${appId} --branch-name ${rollbackBranch} --job-id ${jobId} --job-type RETRY`,
|
|
126
211
|
process.cwd(),
|
|
127
212
|
awsEnv
|
|
128
213
|
);
|
|
129
214
|
if (result.exitCode !== 0) {
|
|
130
215
|
await runCli(
|
|
131
|
-
`aws amplify start-job --app-id ${appId} --branch-name
|
|
216
|
+
`aws amplify start-job --app-id ${appId} --branch-name ${rollbackBranch} --job-type RELEASE`,
|
|
132
217
|
process.cwd(),
|
|
133
218
|
awsEnv
|
|
134
219
|
);
|
|
@@ -144,18 +229,28 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
|
|
|
144
229
|
|
|
145
230
|
async function testConnection() {
|
|
146
231
|
if (!appId) throw new Error('AMPLIFY_APP_ID is required');
|
|
147
|
-
|
|
232
|
+
|
|
233
|
+
const awsEnv = getAwsEnv();
|
|
234
|
+
const appResult = await runCli(
|
|
148
235
|
`aws amplify get-app --app-id ${appId}`,
|
|
149
236
|
process.cwd(),
|
|
150
|
-
|
|
151
|
-
AWS_ACCESS_KEY_ID: env.AWS_ACCESS_KEY_ID || '',
|
|
152
|
-
AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY || '',
|
|
153
|
-
AWS_DEFAULT_REGION: region,
|
|
154
|
-
}
|
|
237
|
+
awsEnv
|
|
155
238
|
);
|
|
156
|
-
if (
|
|
239
|
+
if (appResult.exitCode !== 0) {
|
|
157
240
|
throw new Error('Could not find Amplify app — check AMPLIFY_APP_ID and AWS credentials');
|
|
158
241
|
}
|
|
242
|
+
|
|
243
|
+
if (await branchExists(awsEnv)) return;
|
|
244
|
+
|
|
245
|
+
if (githubConnected) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`Amplify branch "${branch}" not found. Connect your GitHub repo in the Amplify console or choose a branch that already exists.`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
log.info(
|
|
252
|
+
`Amplify branch "${branch}" will be created automatically on the first deploy`
|
|
253
|
+
);
|
|
159
254
|
}
|
|
160
255
|
|
|
161
256
|
return { deploy, rollback, healthCheck, testConnection };
|
|
@@ -104,6 +104,12 @@ export async function promptPlatformQuestions(
|
|
|
104
104
|
message: 'AWS region:',
|
|
105
105
|
default: 'us-east-1',
|
|
106
106
|
},
|
|
107
|
+
{
|
|
108
|
+
type: 'input',
|
|
109
|
+
name: 'branch',
|
|
110
|
+
message: 'Amplify branch name (auto-created for manual zip deploys):',
|
|
111
|
+
default: 'main',
|
|
112
|
+
},
|
|
107
113
|
{
|
|
108
114
|
type: 'confirm',
|
|
109
115
|
name: 'githubConnected',
|
|
@@ -113,6 +119,7 @@ export async function promptPlatformQuestions(
|
|
|
113
119
|
]);
|
|
114
120
|
config.appId = answers.appId;
|
|
115
121
|
config.region = answers.region;
|
|
122
|
+
config.branch = answers.branch;
|
|
116
123
|
config.githubConnected = answers.githubConnected;
|
|
117
124
|
break;
|
|
118
125
|
}
|