-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpackager.go
218 lines (180 loc) · 5.99 KB
/
packager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package ian
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/penguinpowernz/go-ian/util/file"
"github.com/penguinpowernz/go-ian/util/str"
"github.com/penguinpowernz/go-ian/util/tell"
"github.com/penguinpowernz/md5walk"
)
// Debug is the default debug mode for the build options when they
// are not explicity specified with the BuildWithOpts() call
var Debug = false
// DefaultPackager returns a preconfigured packager
// using the default packaging steps/strategies
func DefaultPackager() (p Packager) {
return Packager{
StageFiles,
CleanRoot,
CalculateMD5Sums,
CalculateSize,
PrintPackageTree,
DpkgDebBuild,
}
}
// BuildRequest is like a context object for packager strategies
// to make us of and share knowledge
type BuildRequest struct {
pkg *Pkg
tmp string
debpath string
Debug bool
}
// CleanUp is run at the end of the package build to clean up
// any leftover resources
func (br *BuildRequest) CleanUp() {
_ = os.RemoveAll(br.tmp)
}
// PackagerStrategy is a function that represents a strategy or
// stage in the packaging process
type PackagerStrategy func(br *BuildRequest) error
// Packager is a collection of packaging steps/strategies that
// can be used together to build a package
type Packager []PackagerStrategy
type BuildOpts struct {
Outpath string
Debug bool
}
// Build will create a debian package from the given control file and directory. It does this by
// using rsync to copy the repo to a temp dir, excluded unwanted files and moving any files in the root
// of the package to a /usr/share/doc folder. Then it calculates the package size, file checksums and
// calls dpkg-deb to build the package. The path to the package and an error (if any) is returned.
func (pkgr Packager) Build(p *Pkg) (string, error) {
return pkgr.BuildWithOpts(p, BuildOpts{Debug: Debug})
}
// BuildWithOpts does the same as build but with specifc options
func (pkgr Packager) BuildWithOpts(p *Pkg, opts BuildOpts) (string, error) {
br := &BuildRequest{pkg: p, debpath: opts.Outpath, Debug: opts.Debug}
for i, fn := range pkgr {
err := fn(br)
if err != nil {
return "", fmt.Errorf("at step %d: %s", i+1, err)
}
}
br.CleanUp()
return br.debpath, nil
}
var PrintPackageTree = func(br *BuildRequest) error {
if !Debug {
return nil
}
os.Stderr.WriteString("\nResultant Package Tree\n")
os.Stderr.WriteString("-------------------------------------------------\n")
for _, fn := range file.Glob(br.tmp, "**") {
os.Stderr.WriteString(strings.Replace(fn, br.tmp+"/", "", -1) + "\n")
}
os.Stderr.WriteString("-------------------------------------------------\n\n")
return nil
}
// DpkgDebBuild is a packaging step that builds the package using dpkg-deb
var DpkgDebBuild = func(br *BuildRequest) error {
if br.debpath == "" {
br.debpath = br.pkg.Dir("pkg")
}
if err := os.MkdirAll(br.debpath, 0755); err != nil {
return fmt.Errorf("failed to make package dir at %s: %s", br.debpath, err)
}
// ensure correct perms on ctrl dir
if err := os.Chmod(br.pkg.dir, 0755); err != nil {
return fmt.Errorf("failed to set the proper perms on the control dir")
}
// ensure correct perms on ctrl files
for _, fpath := range br.pkg.CtrlFiles() {
if err := os.Chmod(fpath, 0755); err != nil {
return fmt.Errorf("failed to set the proper perms on the control file %s", fpath)
}
}
br.debpath = filepath.Join(br.debpath, br.pkg.ctrl.Filename())
cmd := exec.Command("/usr/bin/fakeroot", "dpkg-deb", "-b", "-Zgzip", br.tmp, br.debpath)
if br.Debug {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stderr
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to build package %s from %s: %s", br.debpath, br.tmp, err)
}
return nil
}
// CalculateSize of a directory using du, excluding any given paths
var CalculateSize = func(br *BuildRequest) error {
b, err := file.DirSize(br.tmp, br.pkg.Excludes())
if err != nil {
return fmt.Errorf("failed to calculate package size: %s", err)
}
br.pkg.ctrl.Size = strconv.Itoa(b / 1024)
br.pkg.ctrl.WriteFile((&Pkg{dir: br.tmp}).CtrlFile())
br.pkg.ctrl.WriteFile(br.pkg.CtrlFile())
return nil
}
// CalculateMD5Sums is a packaging step that calculates the file sums
var CalculateMD5Sums = func(br *BuildRequest) error {
outfile := (&Pkg{dir: br.tmp}).CtrlDir("md5sums")
sums, err := md5walk.Walk(br.tmp)
if err != nil {
return fmt.Errorf("failed to generate md5sums: %s", err)
}
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
return fmt.Errorf("failed to write md5sums: %s", err)
}
_, err = sums.Write(f)
return err
}
// StageFiles is a packaging step that stages the package files to a
// temporary directory to work from
var StageFiles = func(br *BuildRequest) error {
var err error
br.tmp, err = ioutil.TempDir("/tmp", "go-ian")
if err != nil {
return fmt.Errorf("couldn't make tmp dir: %s", err)
}
args := []string{"-rav"}
for _, s := range br.pkg.Excludes() {
if s == "" {
continue
}
args = append(args, fmt.Sprintf("--exclude=%s", s))
}
args = append(args, br.pkg.Dir()+"/", br.tmp)
cmd := exec.Command("/usr/bin/rsync", args...)
if br.Debug {
os.Stderr.WriteString("\nStaging files to " + br.tmp + "\n")
os.Stderr.WriteString("-------------------------------------------------\n")
tell.Debugf("running: %s", str.CommandString(cmd))
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stderr
}
err = cmd.Run()
if br.Debug {
os.Stderr.WriteString("-------------------------------------------------\n\n")
}
return err
}
// CleanRoot is a packaging step to clean the root folder of the
// package so that the target root file system is not polluted
var CleanRoot = func(br *BuildRequest) error {
list, err := file.ListFilesIn(br.tmp)
if err != nil {
return fmt.Errorf("failed to find root files: %s", err)
}
docpath := filepath.Join(br.tmp, "usr", "share", "doc", br.pkg.ctrl.Name)
if err := os.MkdirAll(docpath, 0755); err != nil {
return fmt.Errorf("failed to create the doc path %s: %s", docpath, err)
}
return file.MoveFiles(list, docpath)
}