Skip to content

[Refactoring] Split generateDockerCompose (309-line function) in src/compose-generator.ts into focused phases #5727

Description

@github-actions

Refactoring Opportunity

Summary

  • File: src/compose-generator.ts
  • Current size: 355 lines
  • Function of concern: generateDockerCompose spans lines 23–331 (309 lines in a single function)
  • Responsibilities identified: 5 distinct concerns mixed into one function body

Evidence

The generateDockerCompose function (lines 23–331) is 309 lines long and mixes five distinct concerns:

1. Filesystem side effect (lines 58–62): Creates the init-signal/ directory directly inside the compose-generation function:

const initSignalDir = path.join(config.workDir, 'init-signal');
if (!fs.existsSync(initSignalDir)) {
  fs.mkdirSync(initSignalDir, { recursive: true });
}

This is a setup phase that does not belong in a function named "generate".

2. Environment mutation before service definitions (lines 88–101): Pre-sets AWF_API_PROXY_IP and AWF_CLI_PROXY_IP in the environment map before the init container object is constructed — because object literals capture values at definition time. This coupling is a footgun and the comment explains the fragility:

// Pre-set API proxy IP in environment before the init container definition.
// The init container's environment object captures values at definition time,
// so AWF_API_PROXY_IP must be set before the init container is defined.

3. Sysroot volume post-processing (lines 120–135): After buildAgentVolumes(...) returns, the function immediately mutates the returned array to strip out volumes that conflict with the sysroot staged volume:

const filteredVolumes = agentVolumes.filter(volume => {
  const target = volume.split(':')[1];
  return !sysrootShadowedTargets.has(target);
});
agentVolumes.length = 0;
agentVolumes.push(...filteredVolumes);

4. Optional service insertion + dependency graph wiring (lines 168–266): Four separate if blocks (sysroot-stage, iptables-init, api-proxy, doh-proxy, cli-proxy) each build an optional service and mutate agentService.depends_on — 100 lines of conditional orchestration inline in the main function.

5. Dual network topology assembly (lines 268–330): Two distinct code paths — networkIsolation returns a topology compose; otherwise returns a standard external-network compose — each with their own composeResult object.

Proposed Split

src/compose-generator.ts (355 lines, one 309-line function) could be refactored as:

  • src/compose-workdir-setup.ts — Filesystem side effects (create init-signal/ dir). Already partially modelled by src/workdir-setup.ts; this fits naturally there (~10 lines, possibly inlined into writeConfigs in config-writer.ts).

  • src/services/optional-services.tsassembleOptionalServices(services, agentService, environment, config, networkConfig, imageConfig, logPaths): inserts the conditional sidecars (api-proxy, doh-proxy, cli-proxy, sysroot-stage, iptables-init) and wires depends_on edges (~130 lines extracted, making each optional service a self-contained block).

  • src/compose-network.tsbuildComposeNetworks(services, config, networkConfig, namedVolumes): encapsulates the two network topology code paths (isolation vs. external) (~50 lines).

  • src/compose-generator.ts — Orchestrating facade reduced to ~60 lines: resolve image config, call service builders, call assembleOptionalServices, call buildComposeNetworks, return result.

Affected Callers

src/config-writer.ts:10:   import { generateDockerCompose, redactDockerComposeSecrets } from './compose-generator';
src/config-writer.ts:278:  const dockerCompose = generateDockerCompose(config, networkConfig, sslConfig, squidConfig);

The public API of generateDockerCompose does not need to change — only internal extraction. Test utilities (src/services/service-test-setup.test-utils.ts) import generateDockerCompose directly and will not be affected.

Effort Estimate

Medium (no API surface change; pure internal restructuring with test coverage already in place via src/compose-generator.test.ts)

Benefits

  • Navigation: A developer working on CLI proxy sidecar config navigates to assembleOptionalServices, not a 309-line function
  • Testability: The assembleOptionalServices extraction is independently testable without constructing a full compose
  • Reduced footgun: The environment-mutation-before-init-container ordering constraint can be documented and enforced in a smaller scope
  • Filesystem side effects isolated: generateDockerCompose becomes a pure function (or near-pure), making it easier to test without fs stubs

Detected by Refactoring Scanner workflow. Run date: 2026-06-30

Generated by Refactoring Opportunity Scanner · 97.6 AIC · ⊞ 7K ·

  • expires on Aug 29, 2026, 3:51 PM UTC

Metadata

Metadata

Assignees

Type

No type

Fields

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