Skip to content

Bug: dry-run skips embedded default-config extraction and breaks config loading #277

Description

@dmcilvaney

Bug Description

Running azldev with --dry-run (-n) skips extraction of the embedded default
configuration into azldev's temporary directory. Project configuration loading then
immediately tries to read the skipped defaults.toml file and fails.

This is a global dry-run/config-loading issue, not specific to a particular command.
Commands that require a valid project fail before they can run.

Reproduction Steps

From a valid azldev project that uses the built-in default configuration, run:

azldev -n -O json config dump

The same initialization error occurs for component commands:

azldev -n -O json component list -a

Expected Behavior

Dry-run should suppress changes to the target project and external systems, while still
allowing internal temporary setup required to calculate and report what the command
would do. Both commands should load the resolved project configuration without writing
to the project.

Actual Behavior

The embedded defaults copy is skipped and configuration loading reads a path that was
never populated:

INF Dry run; would recursively copy dir source=/content dest=/tmp/azldev-tmp-.../azl-defconfigs-...
ERR Error loading configuration, execution may fail later; err="error loading project configuration:
failed to load project configuration:
failed to open project config file at '/tmp/azldev-tmp-.../azl-defconfigs-.../defaults.toml':
open /tmp/azldev-tmp-.../azl-defconfigs-.../defaults.toml: no such file or directory"

Observed command behavior:

  • azldev -n -O json config dump exits nonzero because it requires a valid config.
  • azldev -n -O json component list -a fails with the same initialization error.

Root Cause

internal/projectconfig.LoadProjectConfig creates a temporary destination and calls
defaultconfigs.CopyTo with the command's opctx.DryRunnable. CopyTo delegates to
fileutils.CopyDirRecursiveCrossFS, which returns successfully without copying when
DryRun() is true. LoadProjectConfig then appends the returned, nonexistent
defaults.toml path and attempts to load it.

There is also a contract mismatch in internal/utils/fileutils/copy.go:

  • CopyDirRecursive is documented as dry-run-aware.
  • CopyDirRecursiveCrossFS is documented as not dry-run-aware, but currently contains
    the dry-run check.

The affected path dates back to commit b41e1cd4, where embedded default extraction and
the current dry-run behavior were introduced together.

Proposed Fix

Treat embedded default extraction as internal temporary setup, not a user-visible
mutation:

  1. Move the dry-run guard and "would recursively copy" log from
    CopyDirRecursiveCrossFS into the same-filesystem CopyDirRecursive wrapper.
  2. Remove the DryRunnable parameter from CopyDirRecursiveCrossFS; it should always
    perform the cross-filesystem copy, matching its existing documentation.
  3. Remove the DryRunnable parameter from defaultconfigs.CopyTo and use the
    unconditional cross-filesystem copy for embedded defaults.
  4. Update LoadProjectConfig to call the simplified defaultconfigs.CopyTo.

This preserves dry-run behavior for ordinary filesystem copies while allowing azldev to
materialize read-only inputs in its own temporary directory.

Acceptance Criteria

  • azldev -n -O json config dump succeeds in a valid project that uses built-in defaults.
  • azldev -n -O json component list -a succeeds without a configuration-loading error.
  • Dry-run writes nothing to the target project.
  • Internal default files may be written only under azldev's temporary directory and are
    cleaned up through the existing temp-directory lifecycle.
  • CopyDirRecursive still skips normal same-filesystem copies during dry-run.
  • A regression test loads project configuration with DryRun() == true and built-in
    defaults enabled.

Workaround

--no-default-config avoids this code path, but is only valid for projects that do not
depend on the built-in defaults and is not a general solution.

Metadata

Metadata

Labels

bugSomething isn't working

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