Sign or timestamp an unchanged tree's manifest in freshen (closes #190)
check / check (push) Waiting to run
check / check (push) Waiting to run
freshen skipped the write whenever no file was added, changed or removed, so --sign-key and --include-timestamps on an unchanged tree did nothing. It now skips only when the manifest would come out the same: nothing in the tree changed, the manifest is signed by the --sign-key key (unsigned without one), and it has a createdAt timestamp exactly when --include-timestamps asks for one. A file whose recorded mode is not what --include-permissions asks for already counts as changed. Deviation: the mfer package gains SecretKeyFingerprint and a loaded manifest's CreatedAt, which the comparison needs. Model: opus-5-5
This commit is contained in:
@@ -84,6 +84,23 @@ func manifestFiles(t *testing.T, fs afero.Fs, path string) []*mfer.MFFilePath {
|
||||
return manifest.Files()
|
||||
}
|
||||
|
||||
// manifestCreatedAt returns the createdAt timestamp of the manifest at
|
||||
// path, or nil if it has none.
|
||||
func manifestCreatedAt(t *testing.T, fs afero.Fs, path string) *mfer.Timestamp {
|
||||
t.Helper()
|
||||
|
||||
runMu.Lock()
|
||||
defer runMu.Unlock()
|
||||
|
||||
manifest, err := mfer.NewManifestFromFile(&mfer.ManifestFromFileOptions{
|
||||
Path: path,
|
||||
Fs: fs,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return manifest.CreatedAt()
|
||||
}
|
||||
|
||||
// TestFreshenUnchanged freshens a tree that has not changed since gen
|
||||
// made its manifest: the manifest must list the same entries as before.
|
||||
func TestFreshenUnchanged(t *testing.T) {
|
||||
@@ -141,6 +158,41 @@ func TestFreshenRecordsModeOnlyWhenAsked(t *testing.T) {
|
||||
recordedModes())
|
||||
}
|
||||
|
||||
// TestFreshenIncludeTimestampsOnUnchangedTree runs freshen
|
||||
// --include-timestamps on a tree that has not changed since gen made its
|
||||
// manifest, which has no createdAt timestamp. freshen must rewrite the
|
||||
// manifest with one. Run again with the flag it must leave the manifest as
|
||||
// it is; run without it, it must rewrite the manifest without one.
|
||||
func TestFreshenIncludeTimestampsOnUnchangedTree(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
root, manifestPath := setupFreshenDir(t, fs, map[string]string{testFileTxt: "hello"})
|
||||
|
||||
freshenWithTimestamps := func() {
|
||||
opts := testOpts([]string{
|
||||
testApp, cmdFreshen, "-q", "--include-timestamps",
|
||||
testFlagBase, root, manifestPath,
|
||||
}, fs)
|
||||
require.Equal(t, 0, runCLI(opts), "stderr: %s", testStderr(t, opts))
|
||||
}
|
||||
|
||||
freshenWithTimestamps()
|
||||
assert.NotNil(t, manifestCreatedAt(t, fs, manifestPath))
|
||||
|
||||
before, err := afero.ReadFile(fs, manifestPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
freshenWithTimestamps()
|
||||
|
||||
after, err := afero.ReadFile(fs, manifestPath)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, before, after, "manifest that has a createdAt was rewritten")
|
||||
|
||||
runFreshen(t, fs, root, manifestPath)
|
||||
assert.Nil(t, manifestCreatedAt(t, fs, manifestPath))
|
||||
}
|
||||
|
||||
// assertManifestLists asserts that the manifest at manifestPath lists
|
||||
// exactly the files in want, each with the size and SHA-256 hash of its
|
||||
// content in want and the mtime of the file of that name under root.
|
||||
|
||||
Reference in New Issue
Block a user