Merge lp:~niemeyer/pyjuju/go-charm-bits into lp:pyjuju/go

Proposed by Gustavo Niemeyer
Status: Merged
Approved by: William Reade
Approved revision: 13
Merged at revision: 12
Proposed branch: lp:~niemeyer/pyjuju/go-charm-bits
Merge into: lp:pyjuju/go
Diff against target: 326 lines (+80/-132)
7 files modified
charm/Makefile (+0/-1)
charm/bundle.go (+9/-0)
charm/dir.go (+49/-49)
charm/dir_test.go (+19/-2)
charm/rel_test.go (+1/-2)
charm/testrepo/dummy/hooks/install (+2/-0)
charm/walk.go (+0/-78)
To merge this branch: bzr merge lp:~niemeyer/pyjuju/go-charm-bits
Reviewer Review Type Date Requested Status
Roger Peppe (community) Approve
William Reade (community) Approve
Review via email: mp+76910@code.launchpad.net

Description of the change

Improvements and fixes in charm directory packing

- Ignore hidden files as the Python version.
- Use the new filepath.Walk interface.
- Bundle dirs as well, so that empty dirs are included.
- Pack unix file mode into the charm bundle.

The last change requires this Go CL to be merged,
so the branch can't be merged before it goes in:

 http://codereview.appspot.com/5124044/

To post a comment you must log in.
lp:~niemeyer/pyjuju/go-charm-bits updated
13. By Gustavo Niemeyer

Bundle directories into the zip as well, so that empty directories are
handled properly.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

The upstream CL went in already, so one will just need a tip branch to
test this for the moment.

Revision history for this message
William Reade (fwereade) wrote :

[0]

+// join builds a path rooted at the charm's expended directory

s/expended/expanded

Otherwise all looks very clean, +1

review: Approve
Revision history for this message
Roger Peppe (rogpeppe) wrote :

general +1

[1]
charm/bundle.go
 for _, header := range zipr.File {

Seems to me like the body of this loop would be better
in a separate function. Then you could just defer
zf.Close and things would be generally cleaner.

[2]
charm/dir.go
 parts = append([]string{dir.Path}, parts...)

appending to parts directly is bad behaviour because
it could interfere with any callers that call join
with a slice. i know you don't do that, but it sets
a dubious example.

[3]
charm/dir.go
 hidden := len(relpath) > 1 && relpath[0] == '.'

I hope this behaviour is well documented. I've provided
config files as part of a charm before, and it's only
luck that they didn't start with a ".".
If the object is to exclude source-code control files
(e.g. .hg and .bzr) perhaps it would be better to
name the exclusions specifically, rather than excluding
all files starting with ".".

[4]
charm/reltest.go
  got, err := filepath_Rel(test.root, test.path)

i thought Rel had now been accepted.

Revision history for this message
Roger Peppe (rogpeppe) :
review: Needs Fixing
Revision history for this message
Roger Peppe (rogpeppe) :
review: Approve
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

[1]

That's done. Thanks.

[2]

As we talked online, that's not the case.

[3]

It's not well documented to be honest, but it's easy to go from
more restrictive to more liberal than otherwise. In general depending
on . files at runtime is a bad idea, though, so let's way for someone
to complain so that we can see a real use case.

[4]

It was, but this work was were filepath.Rel was born, so it wasn't
around by the time this branch was pushed. I'll remove the internal
copy now.

Thanks for the review.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

William, that's done as well, thanks.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charm/Makefile'
2--- charm/Makefile 2011-09-24 15:21:23 +0000
3+++ charm/Makefile 2011-09-25 21:15:26 +0000
4@@ -10,7 +10,6 @@
5 dir.go\
6 charm.go\
7 meta.go\
8- walk.go\
9 rel.go\
10
11 GOFMT=gofmt
12
13=== modified file 'charm/bundle.go'
14--- charm/bundle.go 2011-09-24 15:21:23 +0000
15+++ charm/bundle.go 2011-09-25 21:15:26 +0000
16@@ -5,6 +5,7 @@
17 "io"
18 "os"
19 "path/filepath"
20+ "strings"
21 )
22
23 // ReadBundle returns a Bundle for the charm in path.
24@@ -129,6 +130,14 @@
25 continue
26 }
27 path := filepath.Join(dir, filepath.Clean(header.Name))
28+ if strings.HasSuffix(header.Name, "/") {
29+ err = os.MkdirAll(path, 0755)
30+ if err != nil {
31+ zf.Close()
32+ lasterr = err
33+ }
34+ continue
35+ }
36 base, _ := filepath.Split(path)
37 err = os.MkdirAll(base, 0755)
38 if err != nil {
39
40=== modified file 'charm/dir.go'
41--- charm/dir.go 2011-09-24 15:21:23 +0000
42+++ charm/dir.go 2011-09-25 21:15:26 +0000
43@@ -43,6 +43,13 @@
44 // Trick to ensure *Dir implements the Charm interface.
45 var _ Charm = (*Dir)(nil)
46
47+// join builds a path rooted at the charm's expended directory
48+// path and the extra path components provided.
49+func (dir *Dir) join(parts ...string) string {
50+ parts = append([]string{dir.Path}, parts...)
51+ return filepath.Join(parts...)
52+}
53+
54 // Meta returns the Meta representing the metadata.yaml file
55 // for the charm expanded in dir.
56 func (dir *Dir) Meta() *Meta {
57@@ -58,63 +65,56 @@
58 // BundleTo creates a charm file from the charm expanded in dir.
59 func (dir *Dir) BundleTo(w io.Writer) (err os.Error) {
60 zipw := zip.NewWriter(w)
61- defer func() {
62- zipw.Close()
63- handleZipError(&err)
64- }()
65- visitor := zipVisitor{zipw, dir.Path}
66- walk(dir.Path, &visitor)
67- return nil
68+ defer zipw.Close()
69+ zp := zipPacker{zipw, dir.Path}
70+ return filepath.Walk(dir.Path, zp.WalkFunc())
71 }
72
73-type zipVisitor struct {
74+type zipPacker struct {
75 *zip.Writer
76 root string
77 }
78
79-func (zipw *zipVisitor) VisitDir(path string, f *os.FileInfo) bool {
80- relpath, err := filepath_Rel(zipw.root, path)
81- zipw.Error(path, err)
82- return relpath != "build"
83+func (zp *zipPacker) WalkFunc() filepath.WalkFunc {
84+ return func(path string, fi *os.FileInfo, err os.Error) os.Error {
85+ return zp.Visit(path, fi, err)
86+ }
87 }
88
89-func (zipw *zipVisitor) VisitFile(path string, f *os.FileInfo) {
90- relpath, err := filepath_Rel(zipw.root, path)
91- zipw.Error(path, err)
92- w, err := zipw.Create(relpath)
93- zipw.Error(path, err)
94+func (zp *zipPacker) Visit(path string, fi *os.FileInfo, err os.Error) os.Error {
95+ if err != nil {
96+ return err
97+ }
98+ relpath, err := filepath_Rel(zp.root, path)
99+ if err != nil {
100+ return err
101+ }
102+ hidden := len(relpath) > 1 && relpath[0] == '.'
103+ if fi.IsDirectory() {
104+ if relpath == "build" {
105+ return filepath.SkipDir
106+ }
107+ if hidden {
108+ return filepath.SkipDir
109+ }
110+ relpath += "/"
111+ }
112+ if hidden {
113+ return nil
114+ }
115+ h := &zip.FileHeader{
116+ Name: relpath,
117+ Method: zip.Deflate,
118+ }
119+ h.SetMode(fi.Mode)
120+ w, err := zp.CreateHeader(h)
121+ if err != nil || fi.IsDirectory() {
122+ return err
123+ }
124 data, err := ioutil.ReadFile(path)
125- zipw.Error(path, err)
126+ if err != nil {
127+ return err
128+ }
129 _, err = w.Write(data)
130- zipw.Error(path, err)
131-}
132-
133-type zipError os.Error
134-
135-func (zipw *zipVisitor) Error(path string, err os.Error) {
136- if err != nil {
137- panic(zipError(err))
138- }
139-}
140-
141-func handleZipError(err *os.Error) {
142- if *err != nil {
143- return // Do not override a previous problem
144- }
145- panicv := recover()
146- if panicv == nil {
147- return
148- }
149- if e, ok := panicv.(zipError); ok {
150- *err = (os.Error)(e)
151- return
152- }
153- panic(panicv) // Something else
154-}
155-
156-// join builds a path rooted at the charm's expended directory
157-// path and the extra path components provided.
158-func (dir *Dir) join(parts ...string) string {
159- parts = append([]string{dir.Path}, parts...)
160- return filepath.Join(parts...)
161+ return err
162 }
163
164=== modified file 'charm/dir_test.go'
165--- charm/dir_test.go 2011-09-24 15:21:23 +0000
166+++ charm/dir_test.go 2011-09-25 21:15:26 +0000
167@@ -6,6 +6,7 @@
168 "launchpad.net/juju/go/charm"
169 "os"
170 "path/filepath"
171+ "syscall"
172 )
173
174 func repoDir(name string) (path string) {
175@@ -34,14 +35,20 @@
176 c.Assert(err, IsNil)
177 defer zipr.Close()
178
179- var metaf *zip.File
180+ var metaf, instf, emptyf *zip.File
181 for _, f := range zipr.File {
182 c.Logf("Bundled file: %s", f.Name)
183 switch f.Name {
184 case "metadata.yaml":
185 metaf = f
186+ case "hooks/install":
187+ instf = f
188+ case "empty/":
189+ emptyf = f
190 case "build/ignored":
191- c.Fatal("bundle includes build/*")
192+ c.Errorf("bundle includes build/*: %s", f.Name)
193+ case ".ignored", ".dir/ignored":
194+ c.Errorf("bundle includes .* entries: %s", f.Name)
195 }
196 }
197
198@@ -52,4 +59,14 @@
199 meta, err := charm.ReadMeta(reader)
200 c.Assert(err, IsNil)
201 c.Assert(meta.Name, Equals, "dummy")
202+
203+ c.Assert(instf, NotNil)
204+ mode, err := instf.Mode()
205+ c.Assert(err, IsNil)
206+ c.Assert(mode & 0777, Equals, uint32(0755))
207+
208+ c.Assert(emptyf, NotNil)
209+ mode, err = emptyf.Mode()
210+ c.Assert(err, IsNil)
211+ c.Assert(mode & syscall.S_IFMT, Equals, uint32(syscall.S_IFDIR))
212 }
213
214=== modified file 'charm/rel_test.go'
215--- charm/rel_test.go 2011-09-24 15:21:23 +0000
216+++ charm/rel_test.go 2011-09-25 21:15:26 +0000
217@@ -1,7 +1,6 @@
218 package charm
219
220 import (
221- "path/filepath"
222 "runtime"
223 "testing"
224 )
225@@ -64,7 +63,7 @@
226 tests = append(tests, winreltests...)
227 }
228 for _, test := range tests {
229- got, err := filepath.Rel(test.root, test.path)
230+ got, err := filepath_Rel(test.root, test.path)
231 if test.want == "err" {
232 if err == nil {
233 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
234
235=== added directory 'charm/testrepo/dummy/.dir'
236=== added file 'charm/testrepo/dummy/.dir/ignored'
237=== added directory 'charm/testrepo/dummy/hooks'
238=== added file 'charm/testrepo/dummy/hooks/install'
239--- charm/testrepo/dummy/hooks/install 1970-01-01 00:00:00 +0000
240+++ charm/testrepo/dummy/hooks/install 2011-09-25 21:15:26 +0000
241@@ -0,0 +1,2 @@
242+#!/bin/bash
243+echo "Done!"
244
245=== removed file 'charm/walk.go'
246--- charm/walk.go 2011-09-24 15:21:23 +0000
247+++ charm/walk.go 1970-01-01 00:00:00 +0000
248@@ -1,78 +0,0 @@
249-// Copyright 2009 The Go Authors. All rights reserved.
250-// Use of this source code is governed by a BSD-style
251-// license that can be found in the LICENSE file.
252-package charm
253-
254-import (
255- "os"
256- "path/filepath"
257- "sort"
258-)
259-
260-// Drop in replacement for filepath.Walk while issue 2237 is not solved:
261-//
262-// http://code.google.com/p/go/issues/detail?id=2237
263-
264-type walkErrorHandler interface {
265- Error(path string, err os.Error)
266-}
267-
268-// walk walks the file tree rooted at root, calling v.VisitDir or
269-// v.VisitFile for each directory or file in the tree, including root.
270-// If v.VisitDir returns false, Walk skips the directory's entries;
271-// otherwise it invokes itself for each directory entry in sorted order.
272-// If the visitor implements the walkErrorHandler interface, any errors
273-// found will be dispatched to it.
274-func walk(root string, v filepath.Visitor) {
275- f, err := os.Lstat(root)
276- if err != nil {
277- if eh, ok := v.(walkErrorHandler); ok {
278- eh.Error(root, err)
279- }
280- return // can't progress
281- }
282- walk_(root, f, v)
283-}
284-
285-func walk_(path string, f *os.FileInfo, v filepath.Visitor) {
286- if !f.IsDirectory() {
287- v.VisitFile(path, f)
288- return
289- }
290- if !v.VisitDir(path, f) {
291- return // skip directory entries
292- }
293- list, err := readDir(path)
294- if err != nil {
295- if eh, ok := v.(walkErrorHandler); ok {
296- eh.Error(path, err)
297- }
298- }
299- for _, e := range list {
300- walk_(filepath.Join(path, e.Name), e, v)
301- }
302-}
303-
304-func readDir(dirname string) ([]*os.FileInfo, os.Error) {
305- f, err := os.Open(dirname)
306- if err != nil {
307- return nil, err
308- }
309- list, err := f.Readdir(-1)
310- f.Close()
311- if err != nil {
312- return nil, err
313- }
314- fi := make(fileInfoList, len(list))
315- for i := range list {
316- fi[i] = &list[i]
317- }
318- sort.Sort(fi)
319- return fi, nil
320-}
321-
322-type fileInfoList []*os.FileInfo
323-
324-func (f fileInfoList) Len() int { return len(f) }
325-func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name }
326-func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

Subscribers

People subscribed via source and target branches