-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeb.go
161 lines (136 loc) · 4.15 KB
/
deb.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
// Copyright 2019 Dominik Zeromski <dzeromsk@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package debpack packs files to deb files.
//
// It is designed to be simple to use and deploy, not requiring any
// filesystem access to create deb files.
// It is influenced heavily in style and interface from the
// github.com/google/rpmpack package.
package debpack
import (
"bytes"
"io"
"sort"
"text/template"
"github.com/blakesmith/ar"
"github.com/pkg/errors"
)
// DEBMetaData contains meta info about the whole package.
type DEBMetaData struct {
Name string
Version string
Arch string
Maintainer string
Description string
InstalledSize uint
}
// DEBFile contains a particular file's entry and data.
type DEBFile struct {
Name string
Body []byte
Mode int64
Owner string
Group string
MTime int64
}
// DEB holds the state of a particular deb file. Please use NewDEB to instantiate it.
type DEB struct {
DEBMetaData
files map[string]DEBFile
}
// NewDEB creates and returns a new DEB struct.
func NewDEB(m DEBMetaData) (*DEB, error) {
return &DEB{
DEBMetaData: m,
files: make(map[string]DEBFile),
}, nil
}
// Write closes the deb and writes the whole deb to an io.Writer
func (d *DEB) Write(w io.Writer) error {
// Add all of the files, sorted alphabetically.
fnames := []string{}
for fn := range d.files {
fnames = append(fnames, fn)
}
sort.Strings(fnames)
// gen data.tar.gz
data := newArchive()
for _, fn := range fnames {
if err := data.write(d.files[fn]); err != nil {
return errors.Wrapf(err, "cannot add %q file to data archive", fn)
}
}
md5sums := data.md5sums()
d.InstalledSize = data.payloadSize / 1024
if err := data.Close(); err != nil {
return errors.Wrap(err, "failed to close data archive payload")
}
// gen control.tar.gz
control := newArchive()
var c bytes.Buffer
if err := controlTmpl.Execute(&c, d); err != nil {
return errors.Wrap(err, "failed execute control template")
}
if err := control.writeFile("control", c.Bytes(), 0644); err != nil {
return errors.Wrap(err, "cannot add control file to control archive")
}
if err := control.writeFile("md5sums", []byte(md5sums), 0644); err != nil {
return errors.Wrap(err, "cannot add md5sums file to control archive")
}
if err := control.Close(); err != nil {
return errors.Wrap(err, "failed to close control archive payload")
}
a := ar.NewWriter(w)
if err := a.WriteGlobalHeader(); err != nil {
return errors.Wrap(err, "failed write ar header")
}
if err := writeFile(a, "debian-binary", []byte("2.0\n"), 0644); err != nil {
return errors.Wrap(err, "cannot add debian-binary to deb")
}
if err := writeFile(a, "control.tar.gz", control.Bytes(), 0644); err != nil {
return errors.Wrap(err, "cannot add control.tar.gz to deb")
}
if err := writeFile(a, "data.tar.gz", data.Bytes(), 0644); err != nil {
return errors.Wrap(err, "cannot add data.tar.gz to deb")
}
return nil
}
// AddFile adds an DEBFile to an existing deb.
func (d *DEB) AddFile(f DEBFile) {
if f.Name == "/" { // deb does not allow the root dir to be included.
return
}
d.files[f.Name] = f
}
func writeFile(a *ar.Writer, name string, data []byte, mode int64) error {
var header = ar.Header{
Name: name,
Size: int64(len(data)),
Mode: mode,
}
if err := a.WriteHeader(&header); err != nil {
return errors.Wrap(err, "cannot write ar file header")
}
_, err := a.Write(data)
return err
}
var controlTmpl = template.Must(template.New("control").Parse(`
{{- /* Mandatory fields */ -}}
Package: {{.Name}}
Version: {{.Version}}
Architecture: {{.Arch}}
Installed-Size: {{.InstalledSize}}
Maintainer: {{.Maintainer}}
Description: {{.Description}}
`))