@codifycli/plugin-core 1.2.6 → 1.2.7-beta.1

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.
@@ -207,10 +207,23 @@ Brew can be installed using Codify:
207
207
  if (isAptInstalled.status === SpawnStatus.SUCCESS) {
208
208
  await $.spawn('apt-get update', { requiresRoot: true });
209
209
  const flagStr = extraFlags.length > 0 ? `${extraFlags.join(' ')} ` : '';
210
- const { status, data } = await $.spawnSafe(`apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${flagStr}${packageName}`, {
211
- requiresRoot: true,
212
- env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }
213
- });
210
+ let status = SpawnStatus.ERROR;
211
+ let data = '';
212
+ const maxLockRetries = 5;
213
+ for (let attempt = 0; attempt <= maxLockRetries; attempt++) {
214
+ ({ status, data } = await $.spawnSafe(`apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${flagStr}${packageName}`, {
215
+ requiresRoot: true,
216
+ env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }
217
+ }));
218
+ // dpkg/apt lock is held by another process (e.g. unattended-upgrades on a fresh VM).
219
+ // This isn't a broken-dependency condition, so back off and retry rather than falling
220
+ // through to `apt-get install -f`, which will just hit the same lock and fail too.
221
+ const isLockContention = status === SpawnStatus.ERROR
222
+ && (data.includes('Could not get lock') || data.includes('dpkg frontend lock'));
223
+ if (!isLockContention || attempt === maxLockRetries)
224
+ break;
225
+ await new Promise((resolve) => setTimeout(resolve, 5000 * (attempt + 1)));
226
+ }
214
227
  if (status === SpawnStatus.ERROR && data.includes('E: dpkg was interrupted, you must manually run \'sudo dpkg --configure -a\' to correct the problem.')) {
215
228
  await $.spawn('dpkg --configure -a', { requiresRoot: true });
216
229
  await $.spawn(`apt-get -y install ${flagStr}${packageName}`, {
@@ -219,6 +232,11 @@ Brew can be installed using Codify:
219
232
  });
220
233
  return;
221
234
  }
235
+ const isLockContention = status === SpawnStatus.ERROR
236
+ && (data.includes('Could not get lock') || data.includes('dpkg frontend lock'));
237
+ if (isLockContention) {
238
+ throw new Error(`Failed to install package ${packageName} via apt: dpkg/apt lock held by another process after ${maxLockRetries} retries: ${data}`);
239
+ }
222
240
  if (status === SpawnStatus.ERROR) {
223
241
  // Attempt to fix broken dependencies then retry
224
242
  const fixResult = await $.spawnSafe('apt-get install -f -y -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codifycli/plugin-core",
3
- "version": "1.2.6",
3
+ "version": "1.2.7-beta.1",
4
4
  "description": "TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -4,7 +4,7 @@ import * as fs from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
6
 
7
- import { getPty, SpawnStatus } from '../pty/index.js';
7
+ import { getPty, SpawnResult, SpawnStatus } from '../pty/index.js';
8
8
 
9
9
  export function isDebug(): boolean {
10
10
  return process.env.DEBUG != null && process.env.DEBUG.includes('codify'); // TODO: replace with debug library
@@ -274,10 +274,25 @@ Brew can be installed using Codify:
274
274
  if (isAptInstalled.status === SpawnStatus.SUCCESS) {
275
275
  await $.spawn('apt-get update', { requiresRoot: true });
276
276
  const flagStr = extraFlags.length > 0 ? `${extraFlags.join(' ')} ` : '';
277
- const { status, data } = await $.spawnSafe(`apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${flagStr}${packageName}`, {
278
- requiresRoot: true,
279
- env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }
280
- });
277
+
278
+ let status: SpawnResult['status'] = SpawnStatus.ERROR;
279
+ let data = '';
280
+ const maxLockRetries = 5;
281
+ for (let attempt = 0; attempt <= maxLockRetries; attempt++) {
282
+ ({ status, data } = await $.spawnSafe(`apt-get -y -qq install -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0 ${flagStr}${packageName}`, {
283
+ requiresRoot: true,
284
+ env: { DEBIAN_FRONTEND: 'noninteractive', NEEDRESTART_MODE: 'a' }
285
+ }));
286
+
287
+ // dpkg/apt lock is held by another process (e.g. unattended-upgrades on a fresh VM).
288
+ // This isn't a broken-dependency condition, so back off and retry rather than falling
289
+ // through to `apt-get install -f`, which will just hit the same lock and fail too.
290
+ const isLockContention = status === SpawnStatus.ERROR
291
+ && (data.includes('Could not get lock') || data.includes('dpkg frontend lock'));
292
+ if (!isLockContention || attempt === maxLockRetries) break;
293
+
294
+ await new Promise((resolve) => setTimeout(resolve, 5000 * (attempt + 1)));
295
+ }
281
296
 
282
297
  if (status === SpawnStatus.ERROR && data.includes('E: dpkg was interrupted, you must manually run \'sudo dpkg --configure -a\' to correct the problem.')) {
283
298
  await $.spawn('dpkg --configure -a', { requiresRoot: true });
@@ -288,6 +303,12 @@ Brew can be installed using Codify:
288
303
  return;
289
304
  }
290
305
 
306
+ const isLockContention = status === SpawnStatus.ERROR
307
+ && (data.includes('Could not get lock') || data.includes('dpkg frontend lock'));
308
+ if (isLockContention) {
309
+ throw new Error(`Failed to install package ${packageName} via apt: dpkg/apt lock held by another process after ${maxLockRetries} retries: ${data}`);
310
+ }
311
+
291
312
  if (status === SpawnStatus.ERROR) {
292
313
  // Attempt to fix broken dependencies then retry
293
314
  const fixResult = await $.spawnSafe('apt-get install -f -y -o Dpkg::Use-Pty=0 -o Dpkg::Progress-Fancy=0', {