Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions defaultconfigs/defaultconfigs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{},
Expand Down
4 changes: 1 addition & 3 deletions internal/app/azldev/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions internal/projectconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
}
Expand Down
22 changes: 18 additions & 4 deletions internal/projectconfig/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions internal/projectgen/projectgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
36 changes: 21 additions & 15 deletions internal/utils/fileutils/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -127,27 +135,26 @@ 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,
) (err error) {
// 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 {
Expand Down Expand Up @@ -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)
}
Expand All @@ -205,8 +212,7 @@ func CopyDirRecursiveCrossFS(
}
}

err = CopyFileCrossFS(
dryRunnable,
err = copyFileCrossFS(
sourceFS, sourcePath,
destFS, destPath,
options.CopyFileOptions,
Expand Down
15 changes: 15 additions & 0 deletions internal/utils/fileutils/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading