Skip to content

[Duplicate Code] startup-failure detection functions duplicated twice in container-lifecycle.ts #4332

@github-actions

Description

@github-actions

Duplicate Code Opportunity

Summary

  • Pattern: Two pairs of nearly-identical startup-failure detection functions differ only in the container name constant they reference: isApiProxyStartupFailureError / isSquidStartupFailureError (4 lines each) and didApiProxyFailStartup / didSquidFailStartup (~24 lines each)
  • Locations: src/container-lifecycle.ts lines 92–131 (api-proxy pair) and 136–170 (squid pair)
  • Impact: ~50 lines of near-identical logic; any bug fix or change to startup-failure detection must be applied in two places

Evidence

isApiProxyStartupFailureError (lines 92–98) vs isSquidStartupFailureError (lines 136–142) — identical logic, only container name differs:

// api-proxy version
function isApiProxyStartupFailureError(errorMsg: string): boolean {
  if (!errorMsg.includes(API_PROXY_CONTAINER_NAME)) {
    return false;
  }
  return errorMsg.includes('is unhealthy') || errorMsg.includes('exited (1)');
}

// squid version — identical except constant
function isSquidStartupFailureError(errorMsg: string): boolean {
  if (!errorMsg.includes(SQUID_CONTAINER_NAME)) {
    return false;
  }
  return errorMsg.includes('is unhealthy') || errorMsg.includes('exited (1)');
}

didApiProxyFailStartup (lines 103–130) vs didSquidFailStartup (lines 147–170) — identical logic, only container name and log message differ:

// api-proxy version
async function didApiProxyFailStartup(errorMsg: string): Promise<boolean> {
  if (isApiProxyStartupFailureError(errorMsg)) { return true; }
  try {
    const result = await execa(
      'docker',
      ['inspect', API_PROXY_CONTAINER_NAME, '--format', '{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}'],
      { reject: false, env: getLocalDockerEnv() }
    );
    if (result.exitCode !== 0) return false;
    const [containerStatus = '', healthStatus = ''] = result.stdout.trim().split('|');
    return containerStatus === 'exited' || healthStatus === 'unhealthy';
  } catch (error) {
    logger.debug(`Could not inspect ${API_PROXY_CONTAINER_NAME} after startup failure:`, error);
    return false;
  }
}

// squid version — identical except constant name
async function didSquidFailStartup(errorMsg: string): Promise<boolean> {
  if (isSquidStartupFailureError(errorMsg)) { return true; }
  try {
    const result = await execa(
      'docker',
      ['inspect', SQUID_CONTAINER_NAME, '--format', '{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}'],
      { reject: false, env: getLocalDockerEnv() }
    );
    if (result.exitCode !== 0) return false;
    const [containerStatus = '', healthStatus = ''] = result.stdout.trim().split('|');
    return containerStatus === 'exited' || healthStatus === 'unhealthy';
  } catch (error) {
    logger.debug(`Could not inspect ${SQUID_CONTAINER_NAME} after startup failure:`, error);
    return false;
  }
}

Suggested Refactoring

Collapse the four functions into two generic helpers parameterised on containerName:

function isContainerStartupFailureError(errorMsg: string, containerName: string): boolean {
  if (!errorMsg.includes(containerName)) return false;
  return errorMsg.includes('is unhealthy') || errorMsg.includes('exited (1)');
}

async function didContainerFailStartup(errorMsg: string, containerName: string): Promise<boolean> {
  if (isContainerStartupFailureError(errorMsg, containerName)) return true;
  try {
    const result = await execa(
      'docker',
      ['inspect', containerName, '--format', '{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}'],
      { reject: false, env: getLocalDockerEnv() }
    );
    if (result.exitCode !== 0) return false;
    const [containerStatus = '', healthStatus = ''] = result.stdout.trim().split('|');
    return containerStatus === 'exited' || healthStatus === 'unhealthy';
  } catch (error) {
    logger.debug(`Could not inspect ${containerName} after startup failure:`, error);
    return false;
  }
}

Call sites become:

if (await didContainerFailStartup(errorMsg, API_PROXY_CONTAINER_NAME)) ...
if (await didContainerFailStartup(errorMsg, SQUID_CONTAINER_NAME)) ...

Affected Files

  • src/container-lifecycle.ts — lines 92–170

Effort Estimate

Low


Detected by Duplicate Code Detector workflow. Run date: 2026-06-04

Generated by Duplicate Code Detector · sonnet46 2.2M ·

  • expires on Jul 4, 2026, 10:15 PM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions