diff --git a/defaultconfigs/defaultconfigs.go b/defaultconfigs/defaultconfigs.go index 1445f1c12..8ff1db426 100644 --- a/defaultconfigs/defaultconfigs.go +++ b/defaultconfigs/defaultconfigs.go @@ -31,11 +31,10 @@ var content embed.FS // CopyTo recursively copies the contents of the [content] embedded filesystem to the specified // destination path in the provided filesystem. Returns the path to the root configuration file // to load from the copied directory. -func CopyTo(dryRunnable opctx.DryRunnable, fs opctx.FS, destPath string) (rootConfigFilePath string, err error) { +func CopyTo(fs opctx.FS, destPath string) (rootConfigFilePath string, err error) { sourceFS := fileutils.WrapEmbedFS(&content) err = fileutils.CopyDirRecursiveCrossFS( - dryRunnable, sourceFS, embedfsRootDir, fs, destPath, fileutils.CopyDirOptions{}, diff --git a/internal/app/azldev/app.go b/internal/app/azldev/app.go index 33d692e35..78d103057 100644 --- a/internal/app/azldev/app.go +++ b/internal/app/azldev/app.go @@ -404,7 +404,6 @@ func (a *App) initializeEnvOptions() *EnvOptions { // This is responsible for finding the project root, finding and processing the configuration file. func (a *App) initializeProjectConfig(envOptions *EnvOptions, earlyTempDirPath string) error { projectDir, config, err := a.findAndLoadConfig( - envOptions.DryRunnable, earlyTempDirPath, a.configFiles, ) @@ -516,7 +515,7 @@ func (a *App) handParsePrefixedFlags(arg string) { // Initializes the configuration for the azldev CLI. This includes finding the project. // loading configuration, etc. -func (a *App) findAndLoadConfig(dryRunnable opctx.DryRunnable, tempDirPath string, extraConfigFiles []string) ( +func (a *App) findAndLoadConfig(tempDirPath string, extraConfigFiles []string) ( projectDir string, config *projectconfig.ProjectConfig, err error, ) { // If no explicit project dir was specified, then fall back to the current working directory. @@ -531,7 +530,6 @@ func (a *App) findAndLoadConfig(dryRunnable opctx.DryRunnable, tempDirPath strin // Rely on projectconfig package to find all relevant configuration files (including defaults) and // load them into a single project configuration object. projectDir, config, err = projectconfig.LoadProjectConfig( - dryRunnable, a.fsFactory.FS(), a.osEnvFactory.OSEnv(), referenceDir, diff --git a/internal/projectconfig/config.go b/internal/projectconfig/config.go index b2f71e25b..a25cf2740 100644 --- a/internal/projectconfig/config.go +++ b/internal/projectconfig/config.go @@ -18,7 +18,6 @@ import ( // for cleaning it up -- but not until after it is done using the loaded configuration. The loaded // configuration may implicitly depend on the contents of the temporary directory. func LoadProjectConfig( - dryRunnable opctx.DryRunnable, fs opctx.FS, osEnv opctx.OSEnv, referenceDir string, @@ -43,7 +42,7 @@ func LoadProjectConfig( return "", nil, fmt.Errorf("failed to create temp dir for default config files:\n%w", err) } - defaultConfigFilePath, err := defaultconfigs.CopyTo(dryRunnable, fs, tempConfigDirPath) + defaultConfigFilePath, err := defaultconfigs.CopyTo(fs, tempConfigDirPath) if err != nil { return "", nil, fmt.Errorf("failed to copy default config files:\n%w", err) } diff --git a/internal/projectconfig/config_test.go b/internal/projectconfig/config_test.go index a31e1d719..d7f0ad0f8 100644 --- a/internal/projectconfig/config_test.go +++ b/internal/projectconfig/config_test.go @@ -51,13 +51,27 @@ description = "`+testProjectDesc+`" `) _, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, + ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, ) require.NoError(t, err) require.NotNil(t, config) assert.Equal(t, testProjectDesc, config.Project.Description) } +func TestLoadProjectConfig_WithDefaultConfig(t *testing.T) { + const tempDir = "/test/tmp" + + ctx := newTestCtxWithXDGConfigHome() + writeProjectConfig(t, ctx, "") + require.NoError(t, fileutils.MkdirAll(ctx.FS(), tempDir)) + + _, config, err := projectconfig.LoadProjectConfig( + ctx.FS(), ctx.OSEnv(), testProjectDir, false /*disableDefaultConfig*/, tempDir, nil, false, + ) + require.NoError(t, err) + require.NotNil(t, config) +} + // TestLoadProjectConfig_UserConfigOverridesProject verifies that values defined in the // user-level config file override those defined by the project config file. func TestLoadProjectConfig_UserConfigOverridesProject(t *testing.T) { @@ -79,7 +93,7 @@ output-dir = "/from/user/out" `), fileperms.PublicFile)) _, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, + ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, ) require.NoError(t, err) require.NotNil(t, config) @@ -120,7 +134,7 @@ description = "`+testUserDesc+`" `), fileperms.PublicFile)) _, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), + ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), []string{extraConfigPath}, false, ) require.NoError(t, err) @@ -151,7 +165,7 @@ description = "`+testUserDesc+`" `), fileperms.PublicFile)) _, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, + ctx.FS(), ctx.OSEnv(), testProjectDir, true /*disableDefaultConfig*/, t.TempDir(), nil, false, ) require.NoError(t, err) require.NotNil(t, config) diff --git a/internal/projectgen/projectgen_test.go b/internal/projectgen/projectgen_test.go index d9b82d820..e1200e5e3 100644 --- a/internal/projectgen/projectgen_test.go +++ b/internal/projectgen/projectgen_test.go @@ -29,7 +29,6 @@ func requireProjectHasValidDefaultConfig(t *testing.T, ctx opctx.Ctx, projectPat // Load the config. _, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), projectPath, @@ -67,7 +66,7 @@ default-distro = { name = "other", version = "42.42" } // Load the project. foundProjectDir, config, err := projectconfig.LoadProjectConfig( - ctx, ctx.FS(), ctx.OSEnv(), testProjectPath, false /*disable default config?*/, t.TempDir(), nil, false, + ctx.FS(), ctx.OSEnv(), testProjectPath, false /*disable default config?*/, t.TempDir(), nil, false, ) require.NoError(t, err) diff --git a/internal/utils/fileutils/copy.go b/internal/utils/fileutils/copy.go index 1f480117c..790822976 100644 --- a/internal/utils/fileutils/copy.go +++ b/internal/utils/fileutils/copy.go @@ -30,22 +30,30 @@ func CopyFile(dryRunnable opctx.DryRunnable, fs opctx.FS, sourcePath, destPath s // Copies the contents of an existing file to a new file at the given destination path, // potentially across two [opctx.FS] filesystem instances. May preserve mode bits if // "options" says so but does *not* preserve ownership, timestamps, or other metadata. -// *Does* follow symlinks. Is *not* enlightened to skip copying in dry-run mode. +// *Does* follow symlinks. Is enlightened to skip copying in dry-run mode. func CopyFileCrossFS( dryRunnable opctx.DryRunnable, sourceFS opctx.FS, sourcePath string, destFS opctx.FS, destPath string, options CopyFileOptions, ) (err error) { - // Use a default read/write mode; umask will further restrict this as appropriate. - const defaultPerms = os.FileMode(0o666) - if dryRunnable.DryRun() { slog.Info("Dry run; would copy file", "source", sourcePath, "dest", destPath) return nil } + return copyFileCrossFS(sourceFS, sourcePath, destFS, destPath, options) +} + +func copyFileCrossFS( + sourceFS opctx.FS, sourcePath string, + destFS opctx.FS, destPath string, + options CopyFileOptions, +) (err error) { + // Use a default read/write mode; umask will further restrict this as appropriate. + const defaultPerms = os.FileMode(0o666) + destFileMode := defaultPerms if options.PreserveFileMode { @@ -127,14 +135,19 @@ func CopyDirRecursive( sourceDirPath, destDirPath string, options CopyDirOptions, ) (err error) { - return CopyDirRecursiveCrossFS(dryRunnable, fs, sourceDirPath, fs, destDirPath, options) + if dryRunnable.DryRun() { + slog.Info("Dry run; would recursively copy dir", "source", sourceDirPath, "dest", destDirPath) + + return nil + } + + return CopyDirRecursiveCrossFS(fs, sourceDirPath, fs, destDirPath, options) } // Recursively copies a directory, potentially across two [opctx.FS] filesystem instances. May preserve // mode bits if "options" says so but does *not* preserve ownership, timestamps, or other metadata. // *Does* follow symlinks. Is *not* enlightened to skip copying in dry-run mode. func CopyDirRecursiveCrossFS( - dryRunnable opctx.DryRunnable, sourceFS opctx.FS, sourceDirPath string, destFS opctx.FS, destDirPath string, options CopyDirOptions, @@ -142,12 +155,6 @@ func CopyDirRecursiveCrossFS( // Use a default read/write/execute mode; umask will further restrict this as appropriate. const defaultDirPerms = os.FileMode(0o777) - if dryRunnable.DryRun() { - slog.Info("Dry run; would recursively copy dir", "source", sourceDirPath, "dest", destDirPath) - - return nil - } - var fileInfo os.FileInfo if fileInfo, err = sourceFS.Stat(sourceDirPath); err != nil { @@ -186,7 +193,7 @@ func CopyDirRecursiveCrossFS( destPath := filepath.Join(destDirPath, entry.Name()) if entry.IsDir() { - err = CopyDirRecursiveCrossFS(dryRunnable, sourceFS, sourcePath, destFS, destPath, options) + err = CopyDirRecursiveCrossFS(sourceFS, sourcePath, destFS, destPath, options) if err != nil { return fmt.Errorf("failed to recursively copy dir '%s' to '%s':\n%w", sourcePath, destPath, err) } @@ -205,8 +212,7 @@ func CopyDirRecursiveCrossFS( } } - err = CopyFileCrossFS( - dryRunnable, + err = copyFileCrossFS( sourceFS, sourcePath, destFS, destPath, options.CopyFileOptions, diff --git a/internal/utils/fileutils/copy_test.go b/internal/utils/fileutils/copy_test.go index 3c202d9f1..0febf757e 100644 --- a/internal/utils/fileutils/copy_test.go +++ b/internal/utils/fileutils/copy_test.go @@ -136,6 +136,21 @@ func TestCopyDirRecursive(t *testing.T) { } }) + t.Run("DryRunDoesNotCopy", func(t *testing.T) { + ctx := testctx.NewCtx() + ctx.DryRunValue = true + + sourceFilePath := filepath.Join(sourceDirPath, "file.txt") + require.NoError(t, fileutils.WriteFile(ctx.FS(), sourceFilePath, []byte(content1), fileperms.PrivateFile)) + + err := fileutils.CopyDirRecursive(ctx, ctx.FS(), sourceDirPath, destDirPath, fileutils.CopyDirOptions{}) + require.NoError(t, err) + + exists, err := fileutils.Exists(ctx.FS(), destDirPath) + require.NoError(t, err) + assert.False(t, exists) + }) + t.Run("ConfirmDoesNotPreserveDirMode", func(t *testing.T) { const ( extraDirPerms = os.ModeSticky