@akash-chowdhury-24/deployhub 1.0.14 → 1.0.16

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.14",
3
+ "version": "1.0.16",
4
4
  "description": "Zero-configuration deployment and artifact manager",
5
5
  "type": "module",
6
6
  "main": "./src/cli/index.js",
@@ -1,7 +1,6 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs-extra';
3
3
  import archiver from 'archiver';
4
- import axios from 'axios';
5
4
  import { createLogger } from '../../../logger/index.js';
6
5
  import {
7
6
  runCli,
@@ -10,6 +9,21 @@ import {
10
9
  checkUrlHealth,
11
10
  } from './_shared.js';
12
11
 
12
+ const BLOCKING_AMPLIFY_JOB_STATUSES = new Set([
13
+ 'CREATED',
14
+ 'PENDING',
15
+ 'PROVISIONING',
16
+ 'RUNNING',
17
+ 'CANCELLING',
18
+ ]);
19
+
20
+ /**
21
+ * @param {number} ms
22
+ */
23
+ function sleep(ms) {
24
+ return new Promise((resolve) => setTimeout(resolve, ms));
25
+ }
26
+
13
27
  /**
14
28
  * @param {string} sourceDir
15
29
  * @param {string} zipPath
@@ -34,11 +48,18 @@ async function createRootZip(sourceDir, zipPath) {
34
48
  * @param {string} uploadUrl
35
49
  */
36
50
  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,
51
+ const body = await fs.readFile(zipPath);
52
+ const response = await fetch(uploadUrl, {
53
+ method: 'PUT',
54
+ body,
41
55
  });
56
+
57
+ if (!response.ok) {
58
+ const detail = await response.text().catch(() => '');
59
+ throw new Error(
60
+ `Amplify zip upload failed: HTTP ${response.status}${detail ? ` — ${detail}` : ''}`
61
+ );
62
+ }
42
63
  }
43
64
 
44
65
  /**
@@ -103,6 +124,102 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
103
124
  log.success(`Amplify branch "${branch}" ready`);
104
125
  }
105
126
 
127
+ /**
128
+ * @param {Record<string, string>} awsEnv
129
+ * @returns {Promise<{ jobId?: string, status?: string }[]>}
130
+ */
131
+ async function listBranchJobs(awsEnv) {
132
+ const result = await runCli(
133
+ `aws amplify list-jobs --app-id ${appId} --branch-name ${branch} --max-items 10 --output json`,
134
+ process.cwd(),
135
+ awsEnv
136
+ );
137
+ if (result.exitCode !== 0) return [];
138
+
139
+ try {
140
+ const data = JSON.parse(result.stdout);
141
+ return data.jobSummaries || [];
142
+ } catch {
143
+ return [];
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Cancel stuck jobs left by failed uploads so create-deployment can proceed.
149
+ * @param {Record<string, string>} awsEnv
150
+ */
151
+ async function clearBlockingAmplifyJobs(awsEnv) {
152
+ const jobs = await listBranchJobs(awsEnv);
153
+ const blocking = jobs.filter((job) =>
154
+ BLOCKING_AMPLIFY_JOB_STATUSES.has(job.status || '')
155
+ );
156
+
157
+ if (blocking.length === 0) return;
158
+
159
+ for (const job of blocking) {
160
+ log.info(`Stopping in-progress Amplify job ${job.jobId} (${job.status})...`);
161
+ const stopResult = await runCli(
162
+ `aws amplify stop-job --app-id ${appId} --branch-name ${branch} --job-id ${job.jobId}`,
163
+ process.cwd(),
164
+ awsEnv
165
+ );
166
+ if (stopResult.exitCode !== 0) {
167
+ log.warn(
168
+ `Could not stop Amplify job ${job.jobId}: ${stopResult.stderr || stopResult.stdout}`
169
+ );
170
+ }
171
+ }
172
+
173
+ for (let attempt = 0; attempt < 12; attempt += 1) {
174
+ await sleep(5000);
175
+ const current = await listBranchJobs(awsEnv);
176
+ const stillBlocking = current.filter((job) =>
177
+ BLOCKING_AMPLIFY_JOB_STATUSES.has(job.status || '')
178
+ );
179
+ if (stillBlocking.length === 0) {
180
+ log.info('Amplify branch is ready for a new deployment');
181
+ return;
182
+ }
183
+ }
184
+
185
+ throw new Error(
186
+ 'Timed out waiting for in-progress Amplify jobs to finish. Stop them in the AWS console and retry.'
187
+ );
188
+ }
189
+
190
+ /**
191
+ * @param {Record<string, string>} awsEnv
192
+ * @returns {Promise<Record<string, unknown>>}
193
+ */
194
+ async function createManualDeployment(awsEnv) {
195
+ await clearBlockingAmplifyJobs(awsEnv);
196
+
197
+ const createResult = await runCli(
198
+ `aws amplify create-deployment --app-id ${appId} --branch-name ${branch} --output json`,
199
+ process.cwd(),
200
+ awsEnv
201
+ );
202
+ if (createResult.exitCode !== 0) {
203
+ const errorText = `${createResult.stderr || ''}${createResult.stdout || ''}`;
204
+ if (/was not finished/i.test(errorText)) {
205
+ log.warn('Amplify has a stuck deployment — clearing it and retrying...');
206
+ await clearBlockingAmplifyJobs(awsEnv);
207
+ const retryResult = await runCli(
208
+ `aws amplify create-deployment --app-id ${appId} --branch-name ${branch} --output json`,
209
+ process.cwd(),
210
+ awsEnv
211
+ );
212
+ if (retryResult.exitCode !== 0) {
213
+ throw new Error(`Amplify create-deployment failed: ${retryResult.stderr}`);
214
+ }
215
+ return JSON.parse(retryResult.stdout);
216
+ }
217
+ throw new Error(`Amplify create-deployment failed: ${createResult.stderr}`);
218
+ }
219
+
220
+ return JSON.parse(createResult.stdout);
221
+ }
222
+
106
223
  async function deploy(artifactDir) {
107
224
  if (!appId) throw new Error('AMPLIFY_APP_ID is required');
108
225
 
@@ -152,16 +269,7 @@ export function createAwsAmplifyProvider(config, envName, env = process.env) {
152
269
  const amplifyZipPath = path.join(artifactDir, 'amplify-deploy.zip');
153
270
  await createRootZip(buildDir, amplifyZipPath);
154
271
 
155
- const createResult = await runCli(
156
- `aws amplify create-deployment --app-id ${appId} --branch-name ${branch} --output json`,
157
- process.cwd(),
158
- awsEnv
159
- );
160
- if (createResult.exitCode !== 0) {
161
- throw new Error(`Amplify create-deployment failed: ${createResult.stderr}`);
162
- }
163
-
164
- const deployment = JSON.parse(createResult.stdout);
272
+ const deployment = await createManualDeployment(awsEnv);
165
273
  const jobId = deployment.jobId;
166
274
  const zipUploadUrl = deployment.zipUploadUrl;
167
275