Compare commits
1
Commits
b8c699adee
...
2b692fad7c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b692fad7c |
@@ -287,6 +287,9 @@ are now tracked only in the [issues](https://git.eeqj.de/sneak/mfer/issues).
|
||||
only on the paths it is given and the files under them, so with the same
|
||||
`--seed` and an unchanged tree it writes the same bytes wherever the
|
||||
manifest goes. The file it writes to is never listed
|
||||
- never lists `index.mf` at the top of a directory it is given, or a file it
|
||||
is given by that name, wherever it writes the manifest, since `fetch`
|
||||
saves the manifest there; `index.mf` in a subdirectory is listed
|
||||
- `mfer check` / `mfer check .`
|
||||
- verifies checksums of all files in manifest, displaying error and exiting
|
||||
nonzero if any files are missing or corrupted, or have permission bits
|
||||
@@ -305,7 +308,8 @@ are now tracked only in the [issues](https://git.eeqj.de/sneak/mfer/issues).
|
||||
- rewrites `index.mf` to list the files now under the directory holding it,
|
||||
or under the one `--base` names, hashing only the files that are new or
|
||||
changed
|
||||
- lists regular files only, as gen does
|
||||
- lists regular files only, and never `index.mf` at the top of that
|
||||
directory, as gen does
|
||||
- leaves out hidden files unless given `--include-dotfiles`, and symlinks
|
||||
unless given `--follow-symlinks`, which lists each symlink to a regular
|
||||
file under its own name with the contents of the file it points to
|
||||
|
||||
@@ -1420,6 +1420,66 @@ func TestGenerateLeavesLeftoverTempFileOutOfListing(t *testing.T) {
|
||||
assert.Equal(t, []string{testFileTxt}, manifestPaths(t, fs, output))
|
||||
}
|
||||
|
||||
// TestGenerateLeavesTopIndexMFOutOfListing runs gen with --output outside
|
||||
// the paths it is given: index.mf at the top of each directory given, and
|
||||
// a file given by that name, are not listed, since fetch saves the manifest
|
||||
// as index.mf at the top of the tree, while sub/index.mf is listed. The
|
||||
// files are recognized by file identity, which needs the real filesystem.
|
||||
func TestGenerateLeavesTopIndexMFOutOfListing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Paths are relative to a temp dir holding tree, which holds file.txt,
|
||||
// index.mf and sub/index.mf, and second, which holds second.txt and
|
||||
// index.mf.
|
||||
for name, tc := range map[string]struct {
|
||||
paths []string
|
||||
listed []string
|
||||
}{
|
||||
"directory": {
|
||||
[]string{"tree"},
|
||||
[]string{testFileTxt, testSubdirManifest},
|
||||
},
|
||||
"two directories": {
|
||||
[]string{"tree", "second"},
|
||||
[]string{testFileTxt, testSubdirManifest, "second.txt"},
|
||||
},
|
||||
"files": {
|
||||
[]string{"tree/" + testFileTxt, "tree/" + defaultManifestName},
|
||||
[]string{testFileTxt},
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
tree := filepath.Join(root, "tree")
|
||||
second := filepath.Join(root, "second")
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
require.NoError(t, fs.MkdirAll(filepath.Join(tree, testSubdir), 0o750))
|
||||
require.NoError(t, fs.MkdirAll(second, 0o750))
|
||||
writeTestFile(t, fs, filepath.Join(tree, testFileTxt), "hello")
|
||||
writeTestFile(t, fs, filepath.Join(tree, defaultManifestName), "an ordinary file")
|
||||
writeTestFile(t, fs, filepath.Join(tree, testSubdirManifest), "an ordinary file")
|
||||
writeTestFile(t, fs, filepath.Join(second, "second.txt"), "second")
|
||||
writeTestFile(t, fs, filepath.Join(second, defaultManifestName), "an ordinary file")
|
||||
|
||||
paths := make([]string, 0, len(tc.paths))
|
||||
for _, path := range tc.paths {
|
||||
paths = append(paths, filepath.Join(root, path))
|
||||
}
|
||||
|
||||
output := filepath.Join(root, "other.mf")
|
||||
opts := testOpts(slices.Concat(
|
||||
[]string{testApp, cmdGenerate, "-q", "-o", output}, paths,
|
||||
), fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
|
||||
assert.ElementsMatch(t, tc.listed, manifestPaths(t, fs, output))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// writeTestTree writes file.txt and sub/nested.txt under dir.
|
||||
func writeTestTree(t *testing.T, fs afero.Fs, dir string) {
|
||||
t.Helper()
|
||||
|
||||
@@ -411,12 +411,17 @@ func (mfa *CLIApp) freshenScan(
|
||||
startScan := time.Now()
|
||||
showProgress := cmd.Bool("progress")
|
||||
|
||||
// Leave out the manifest and a temp file left by an interrupted run,
|
||||
// as gen does. A path that cannot be stat'd, normally because no file
|
||||
// is there, needs no leaving out.
|
||||
// Leave out the manifest, a temp file left by an interrupted run, and
|
||||
// index.mf at the top of the base directory, where fetch saves the
|
||||
// manifest, as gen does. A path that cannot be stat'd, normally because
|
||||
// no file is there, needs no leaving out.
|
||||
var excluded []fs.FileInfo
|
||||
|
||||
for _, p := range []string{manifestPath, manifestTempPath(manifestPath)} {
|
||||
for _, p := range []string{
|
||||
manifestPath,
|
||||
manifestTempPath(manifestPath),
|
||||
filepath.Join(absBase, defaultManifestName),
|
||||
} {
|
||||
info, err := mfa.Fs.Stat(p)
|
||||
if err == nil {
|
||||
excluded = append(excluded, info)
|
||||
|
||||
@@ -413,6 +413,34 @@ func TestFreshenLeavesLeftoverTempFileOutOfListing(t *testing.T) {
|
||||
manifestPaths(t, fs, manifestPath))
|
||||
}
|
||||
|
||||
// TestFreshenLeavesTopIndexMFOutOfListing freshens a manifest kept outside
|
||||
// the directory --base names, after index.mf and sub/index.mf are added
|
||||
// there: index.mf at the top is not listed, since fetch saves the manifest
|
||||
// there, while sub/index.mf is listed. The file is recognized by file
|
||||
// identity, which needs the real filesystem.
|
||||
func TestFreshenLeavesTopIndexMFOutOfListing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
root := t.TempDir()
|
||||
tree := filepath.Join(root, "tree")
|
||||
manifestPath := filepath.Join(root, "other.mf")
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
require.NoError(t, fs.MkdirAll(filepath.Join(tree, testSubdir), 0o750))
|
||||
writeTestFile(t, fs, filepath.Join(tree, testFileTxt), "hello")
|
||||
|
||||
opts := testOpts([]string{testApp, cmdGenerate, "-q", "-o", manifestPath, tree}, fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
|
||||
writeTestFile(t, fs, filepath.Join(tree, defaultManifestName), "an ordinary file")
|
||||
writeTestFile(t, fs, filepath.Join(tree, testSubdirManifest), "an ordinary file")
|
||||
|
||||
runFreshen(t, fs, tree, manifestPath)
|
||||
|
||||
assert.ElementsMatch(t, []string{testFileTxt, testSubdirManifest},
|
||||
manifestPaths(t, fs, manifestPath))
|
||||
}
|
||||
|
||||
// TestFreshenResolvesEntriesAgainstManifestDirectory adds sub/c.txt, then
|
||||
// runs freshen from the directory above sub, on the manifest in sub.
|
||||
// Without --base, the manifest then lists the files in sub, whether freshen
|
||||
|
||||
+20
-4
@@ -111,15 +111,19 @@ func (mfa *CLIApp) outputPath(cmd *cli.Command) (string, error) {
|
||||
return defaultManifestName, nil
|
||||
}
|
||||
|
||||
arg := cmd.Args().First()
|
||||
return mfa.topIndexMFPath(cmd.Args().First()), nil
|
||||
}
|
||||
|
||||
// topIndexMFPath returns index.mf in the directory path names, or beside
|
||||
// the file it names, which is the top of the tree gen lists for that path.
|
||||
func (mfa *CLIApp) topIndexMFPath(path string) string {
|
||||
// A path that does not exist is refused when it is enumerated.
|
||||
info, err := mfa.Fs.Stat(arg)
|
||||
info, err := mfa.Fs.Stat(path)
|
||||
if err == nil && !info.IsDir() {
|
||||
return filepath.Join(filepath.Dir(arg), defaultManifestName), nil
|
||||
return filepath.Join(filepath.Dir(path), defaultManifestName)
|
||||
}
|
||||
|
||||
return filepath.Join(arg, defaultManifestName), nil
|
||||
return filepath.Join(path, defaultManifestName)
|
||||
}
|
||||
|
||||
// buildScannerOptions constructs scanner options from the CLI flags and
|
||||
@@ -139,6 +143,18 @@ func (mfa *CLIApp) buildScannerOptions(
|
||||
ExcludePaths: []string{output, manifestTempPath(output)},
|
||||
}
|
||||
|
||||
// Nor does index.mf at the top of the tree, since fetch saves the
|
||||
// manifest there and refuses a manifest that lists it. With no path
|
||||
// given, gen lists the current directory.
|
||||
paths := cmd.Args().Slice()
|
||||
if len(paths) == 0 {
|
||||
paths = []string{"."}
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
opts.ExcludePaths = append(opts.ExcludePaths, mfa.topIndexMFPath(path))
|
||||
}
|
||||
|
||||
// Set seed for deterministic UUID if provided
|
||||
if seed := cmd.String("seed"); seed != "" {
|
||||
opts.Seed = seed
|
||||
|
||||
Reference in New Issue
Block a user