@akash-chowdhury-24/deployhub 1.0.12 → 1.0.13

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akash-chowdhury-24/deployhub",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Zero-configuration deployment and artifact manager",
5
5
  "type": "module",
6
6
  "main": "./src/cli/index.js",
@@ -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,5 +1,6 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs-extra';
3
+ import archiver from 'archiver';
3
4
  import { execa } from 'execa';
4
5
  import { createLogger } from '../../../logger/index.js';
5
6
  import {
@@ -9,6 +10,25 @@ 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
+
12
32
  /**
13
33
  * @param {import('../../../core/config.js').DeployHubConfig} config
14
34
  * @param {string} envName
@@ -19,21 +39,68 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
19
39
  const log = createLogger('aws-amplify');
20
40
  const appId = environment?.appId || env.AMPLIFY_APP_ID;
21
41
  const region = environment?.region || env.AWS_REGION || 'us-east-1';
42
+ const branch = environment?.branch || env.AMPLIFY_BRANCH || 'main';
22
43
  const githubConnected = environment?.githubConnected ?? false;
23
44
 
24
- async function deploy(artifactDir) {
25
- if (!appId) throw new Error('AMPLIFY_APP_ID is required');
26
-
27
- const awsEnv = {
45
+ function getAwsEnv() {
46
+ return {
28
47
  AWS_ACCESS_KEY_ID: env.AWS_ACCESS_KEY_ID || '',
29
48
  AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY || '',
30
49
  AWS_DEFAULT_REGION: region,
31
50
  };
51
+ }
52
+
53
+ /**
54
+ * @param {string} stage
55
+ */
56
+ function resolveAmplifyStage(stage) {
57
+ const normalized = stage.toLowerCase();
58
+ if (normalized === 'production') return 'PRODUCTION';
59
+ if (normalized === 'staging') return 'BETA';
60
+ return 'DEVELOPMENT';
61
+ }
62
+
63
+ async function branchExists(awsEnv) {
64
+ const result = await runCli(
65
+ `aws amplify get-branch --app-id ${appId} --branch-name ${branch}`,
66
+ process.cwd(),
67
+ awsEnv
68
+ );
69
+ return result.exitCode === 0;
70
+ }
71
+
72
+ async function ensureBranchExists(awsEnv) {
73
+ if (await branchExists(awsEnv)) return;
74
+
75
+ if (githubConnected) {
76
+ throw new Error(
77
+ `Amplify branch "${branch}" not found. Connect your GitHub repo in the Amplify console or choose a branch that already exists.`
78
+ );
79
+ }
80
+
81
+ const stage = resolveAmplifyStage(envName);
82
+ log.info(`Creating Amplify branch "${branch}" for manual zip deploys...`);
83
+ const createResult = await runCli(
84
+ `aws amplify create-branch --app-id ${appId} --branch-name ${branch} --stage ${stage} --no-enable-auto-build --description "Created by DeployHub for manual deployments"`,
85
+ process.cwd(),
86
+ awsEnv
87
+ );
88
+ if (createResult.exitCode !== 0) {
89
+ throw new Error(`Amplify create-branch failed: ${createResult.stderr || createResult.stdout}`);
90
+ }
91
+ log.success(`Amplify branch "${branch}" ready`);
92
+ }
93
+
94
+ async function deploy(artifactDir) {
95
+ if (!appId) throw new Error('AMPLIFY_APP_ID is required');
96
+
97
+ const awsEnv = getAwsEnv();
32
98
 
33
99
  if (githubConnected) {
34
- log.info('Triggering Amplify release job (GitHub connected)...');
100
+ await ensureBranchExists(awsEnv);
101
+ log.info(`Triggering Amplify release job on branch ${branch} (GitHub connected)...`);
35
102
  const result = await runCli(
36
- `aws amplify start-job --app-id ${appId} --branch-name main --job-type RELEASE`,
103
+ `aws amplify start-job --app-id ${appId} --branch-name ${branch} --job-type RELEASE`,
37
104
  process.cwd(),
38
105
  awsEnv
39
106
  );
@@ -50,6 +117,7 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
50
117
  platform: 'aws-amplify',
51
118
  deploymentId: jobId,
52
119
  appId,
120
+ branch,
53
121
  method: 'github',
54
122
  },
55
123
  envName
@@ -58,14 +126,22 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
58
126
  return;
59
127
  }
60
128
 
61
- log.info('Uploading artifact zip to Amplify...');
62
- const zipPath = path.join(artifactDir, 'artifact.zip');
63
- if (!(await fs.pathExists(zipPath))) {
64
- throw new Error('artifact.zip not found for Amplify upload');
129
+ log.info(`Uploading build output to Amplify branch ${branch}...`);
130
+ await ensureBranchExists(awsEnv);
131
+
132
+ const buildOutput = config.frontend?.buildOutput || config.buildOutput || 'dist';
133
+ const buildDir = path.join(process.cwd(), buildOutput);
134
+ if (!(await fs.pathExists(buildDir))) {
135
+ throw new Error(
136
+ `Build output not found at ${buildOutput}. Amplify needs the built static files, not artifact.zip with a nested folder.`
137
+ );
65
138
  }
66
139
 
140
+ const amplifyZipPath = path.join(artifactDir, 'amplify-deploy.zip');
141
+ await createRootZip(buildDir, amplifyZipPath);
142
+
67
143
  const createResult = await runCli(
68
- `aws amplify create-deployment --app-id ${appId} --branch-name main --output json`,
144
+ `aws amplify create-deployment --app-id ${appId} --branch-name ${branch} --output json`,
69
145
  process.cwd(),
70
146
  awsEnv
71
147
  );
@@ -77,14 +153,14 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
77
153
  const jobId = deployment.jobId;
78
154
  const zipUploadUrl = deployment.zipUploadUrl;
79
155
 
80
- await execa('curl', ['-T', zipPath, zipUploadUrl], {
156
+ await execa('curl', ['-T', amplifyZipPath, zipUploadUrl], {
81
157
  env: { ...process.env, ...awsEnv },
82
158
  stdio: 'inherit',
83
159
  shell: true,
84
160
  });
85
161
 
86
162
  const startResult = await runCli(
87
- `aws amplify start-deployment --app-id ${appId} --branch-name main --job-id ${jobId}`,
163
+ `aws amplify start-deployment --app-id ${appId} --branch-name ${branch} --job-id ${jobId}`,
88
164
  process.cwd(),
89
165
  awsEnv
90
166
  );
@@ -92,12 +168,15 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
92
168
  throw new Error(`Amplify start-deployment failed: ${startResult.stderr}`);
93
169
  }
94
170
 
171
+ await fs.remove(amplifyZipPath);
172
+
95
173
  await saveDeploymentRecord(
96
174
  artifactDir,
97
175
  {
98
176
  platform: 'aws-amplify',
99
177
  deploymentId: jobId,
100
178
  appId,
179
+ branch,
101
180
  method: 'zip',
102
181
  },
103
182
  envName
@@ -114,21 +193,18 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
114
193
  throw new Error('No previous Amplify job ID found for rollback');
115
194
  }
116
195
 
117
- const awsEnv = {
118
- AWS_ACCESS_KEY_ID: env.AWS_ACCESS_KEY_ID || '',
119
- AWS_SECRET_ACCESS_KEY: env.AWS_SECRET_ACCESS_KEY || '',
120
- AWS_DEFAULT_REGION: region,
121
- };
196
+ const awsEnv = getAwsEnv();
197
+ const rollbackBranch = previous?.branch || branch;
122
198
 
123
- log.info(`Re-triggering Amplify job ${jobId}...`);
199
+ log.info(`Re-triggering Amplify job ${jobId} on branch ${rollbackBranch}...`);
124
200
  const result = await runCli(
125
- `aws amplify start-job --app-id ${appId} --branch-name main --job-id ${jobId} --job-type RETRY`,
201
+ `aws amplify start-job --app-id ${appId} --branch-name ${rollbackBranch} --job-id ${jobId} --job-type RETRY`,
126
202
  process.cwd(),
127
203
  awsEnv
128
204
  );
129
205
  if (result.exitCode !== 0) {
130
206
  await runCli(
131
- `aws amplify start-job --app-id ${appId} --branch-name main --job-type RELEASE`,
207
+ `aws amplify start-job --app-id ${appId} --branch-name ${rollbackBranch} --job-type RELEASE`,
132
208
  process.cwd(),
133
209
  awsEnv
134
210
  );
@@ -144,18 +220,28 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
144
220
 
145
221
  async function testConnection() {
146
222
  if (!appId) throw new Error('AMPLIFY_APP_ID is required');
147
- const result = await runCli(
223
+
224
+ const awsEnv = getAwsEnv();
225
+ const appResult = await runCli(
148
226
  `aws amplify get-app --app-id ${appId}`,
149
227
  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
- }
228
+ awsEnv
155
229
  );
156
- if (result.exitCode !== 0) {
230
+ if (appResult.exitCode !== 0) {
157
231
  throw new Error('Could not find Amplify app — check AMPLIFY_APP_ID and AWS credentials');
158
232
  }
233
+
234
+ if (await branchExists(awsEnv)) return;
235
+
236
+ if (githubConnected) {
237
+ throw new Error(
238
+ `Amplify branch "${branch}" not found. Connect your GitHub repo in the Amplify console or choose a branch that already exists.`
239
+ );
240
+ }
241
+
242
+ log.info(
243
+ `Amplify branch "${branch}" will be created automatically on the first deploy`
244
+ );
159
245
  }
160
246
 
161
247
  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
  }