From 237f6eb99d94186f5d533a3efe5d5198c1d05fea Mon Sep 17 00:00:00 2001 From: Weii Wang Date: Wed, 10 Jul 2024 07:57:16 +0000 Subject: [PATCH] fix: include parent dirs when listing archive members Zip archives can omit files' parent directories, as they will be automatically created during extraction. When listing members from a zip archive, ensure that all parent directories are included. This guarantees that during charm cleanup, directories are not missed. --- charmarchive.go | 11 ++++++++++- charmarchive_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/charmarchive.go b/charmarchive.go index 97027b1..e8259b4 100644 --- a/charmarchive.go +++ b/charmarchive.go @@ -273,7 +273,16 @@ func (a *CharmArchive) ArchiveMembers() (set.Strings, error) { if err != nil { return set.NewStrings(), err } - manifest := set.NewStrings(paths...) + manifest := set.NewStrings() + // Zip archives can omit file's parent directories, + // as these are created automatically during extraction. + // Ensure that parent directories exist in the member list. + for _, path := range paths { + for path != "." && path != "/" { + manifest.Add(path) + path = filepath.Dir(path) + } + } // We always write out a revision file, even if there isn't one in the // archive; and we always strip ".", because that's sometimes not present. manifest.Add("revision") diff --git a/charmarchive_test.go b/charmarchive_test.go index 62c70a9..5a3e6b3 100644 --- a/charmarchive_test.go +++ b/charmarchive_test.go @@ -7,6 +7,7 @@ import ( "archive/zip" "bytes" "fmt" + "io" "io/ioutil" "os" "os/exec" @@ -221,6 +222,14 @@ func (s *CharmArchiveSuite) TestArchiveMembersSymlink(c *gc.C) { c.Assert(manifest, gc.DeepEquals, set.NewStrings(expected...)) } +func (s *CharmArchiveSuite) TestArchiveMembersOmitDirs(c *gc.C) { + srcPath := cloneDir(c, charmDirPath(c, "dummy")) + archive := archiveDirOnlyFiles(c, srcPath) + manifest, err := archive.ArchiveMembers() + c.Assert(err, gc.IsNil) + c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyArchiveMembers...)) +} + func (s *CharmArchiveSuite) TestExpandTo(c *gc.C) { archive, err := charm.ReadCharmArchive(s.archivePath) c.Assert(err, gc.IsNil) @@ -475,3 +484,38 @@ func archiveDir(c *gc.C, dirpath string) *charm.CharmArchive { c.Assert(err, gc.IsNil) return archive } + +func archiveDirOnlyFiles(c *gc.C, dirpath string) *charm.CharmArchive { + dir, err := charm.ReadCharmDir(dirpath) + c.Assert(err, gc.IsNil) + buf := new(bytes.Buffer) + err = dir.ArchiveTo(buf) + r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) + c.Assert(err, gc.IsNil) + buf = new(bytes.Buffer) + w := zip.NewWriter(buf) + for _, f := range r.File { + if f.FileInfo().IsDir() { + continue + } + rf, err := f.Open() + if err != nil { + c.Assert(err, gc.IsNil) + } + wf, err := w.Create(f.Name) + if err != nil { + c.Assert(err, gc.IsNil) + } + _, err = io.Copy(wf, rf) + if err != nil { + c.Assert(err, gc.IsNil) + } + } + err = w.Close() + if err != nil { + c.Assert(err, gc.IsNil) + } + archive, err := charm.ReadCharmArchiveBytes(buf.Bytes()) + c.Assert(err, gc.IsNil) + return archive +}