@akash-chowdhury-24/deployhub 1.0.13 → 1.0.15

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