-
Notifications
You must be signed in to change notification settings - Fork 54
/
apply2.go
160 lines (131 loc) · 3.85 KB
/
apply2.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
package apply2
import (
"encoding/gob"
"os"
"path"
"github.com/dchest/safefile"
"github.com/itchio/butler/comm"
"github.com/itchio/butler/mansion"
"github.com/itchio/savior/seeksource"
"github.com/itchio/wharf/eos"
"github.com/itchio/wharf/pools/fspool"
"github.com/itchio/wharf/pwr/bowl"
"github.com/itchio/wharf/pwr/patcher"
"github.com/itchio/wharf/state"
"github.com/pkg/errors"
)
var args = struct {
patch *string
dir *string
old *string
}{}
func Register(ctx *mansion.Context) {
cmd := ctx.App.Command("apply2", "(Advanced) Use a patch to resumably patch a directory to a new version")
args.patch = cmd.Arg("patch", "Patch file (.pwr), previously generated with the `diff` command.").Required().String()
args.old = cmd.Arg("old", "Directory with old files").Required().String()
args.dir = cmd.Flag("dir", "Directory for patched files and checkpoints").Required().String()
ctx.Register(cmd, do)
}
func do(ctx *mansion.Context) {
ctx.Must(Do(&Params{
Patch: *args.patch,
Old: *args.old,
Dir: *args.dir,
}))
}
type Params struct {
Patch string
Old string
Dir string
}
func Do(params *Params) error {
patch := params.Patch
old := params.Old
dir := params.Dir
consumer := &state.Consumer{
OnMessage: func(level string, message string) {
comm.Logf("[%s] %s", level, message)
},
}
patchReader, err := eos.Open(patch)
if err != nil {
return errors.Wrap(err, "opening patch")
}
patchSource := seeksource.FromFile(patchReader)
_, err = patchSource.Resume(nil)
if err != nil {
return errors.Wrap(err, "creating patch source")
}
p, err := patcher.New(patchSource, consumer)
if err != nil {
return errors.Wrap(err, "creating patcher")
}
// comm.StartProgressWithTotalBytes(patchSource.Size())
var checkpoint *patcher.Checkpoint
checkpointPath := path.Join(dir, "checkpoint.bwl")
checkpointFile, err := os.Open(checkpointPath)
if err != nil {
if !os.IsNotExist(err) {
return errors.Wrap(err, "opening checkpoint")
}
} else {
defer checkpointFile.Close()
checkpoint = &patcher.Checkpoint{}
dec := gob.NewDecoder(checkpointFile)
err := dec.Decode(checkpoint)
if err != nil {
return errors.Wrap(err, "decoding checkpoint")
}
// yay, we have a checkpoint!
}
p.SetSaveConsumer(&patcherSaveConsumer{
shouldSave: func() bool {
// TODO: patcher checkpoints are big. how often do we actually wanna do this?
return true
},
save: func(c *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
checkpointFile, err := safefile.Create(checkpointPath, 0644)
if err != nil {
return patcher.AfterSaveStop, errors.Wrap(err, "creating checkpoint file")
}
defer checkpointFile.Close()
enc := gob.NewEncoder(checkpointFile)
err = enc.Encode(c)
if err != nil {
return patcher.AfterSaveStop, errors.Wrap(err, "encoding checkpoint")
}
err = checkpointFile.Commit()
if err != nil {
return patcher.AfterSaveStop, errors.Wrap(err, "committing checkpoint file")
}
return patcher.AfterSaveContinue, nil
},
})
targetPool := fspool.New(p.GetTargetContainer(), old)
bowl, err := bowl.NewFreshBowl(&bowl.FreshBowlParams{
SourceContainer: p.GetSourceContainer(),
TargetContainer: p.GetTargetContainer(),
TargetPool: targetPool,
OutputFolder: dir,
})
if err != nil {
return errors.Wrap(err, "creating fresh bowl")
}
err = p.Resume(checkpoint, targetPool, bowl)
if err != nil {
return errors.Wrap(err, "patching")
}
comm.EndProgress()
return nil
}
type patcherSaveConsumer struct {
shouldSave func() bool
save func(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error)
}
var _ patcher.SaveConsumer = (*patcherSaveConsumer)(nil)
func (psc *patcherSaveConsumer) ShouldSave() bool {
return psc.shouldSave()
}
func (psc *patcherSaveConsumer) Save(checkpoint *patcher.Checkpoint) (patcher.AfterSaveAction, error) {
return psc.save(checkpoint)
}