-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathget_installer_info.go
153 lines (126 loc) Β· 4.25 KB
/
get_installer_info.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
package installer
import (
"io"
"path/filepath"
"time"
"github.com/itchio/savior"
"github.com/go-errors/errors"
"github.com/itchio/butler/archive"
"github.com/itchio/butler/configurator"
"github.com/itchio/wharf/eos"
"github.com/itchio/wharf/state"
)
func GetInstallerInfo(consumer *state.Consumer, file eos.File) (*InstallerInfo, error) {
stat, err := file.Stat()
if err != nil {
return nil, errors.Wrap(err, 0)
}
target := stat.Name()
ext := filepath.Ext(target)
name := filepath.Base(target)
consumer.Infof("β For source (%s)", name)
if typ, ok := installerForExt[ext]; ok {
if typ == InstallerTypeArchive {
// let code flow, probe it as archive
} else {
consumer.Infof("β Using file extension registry (%s)", typ)
return &InstallerInfo{
Type: typ,
}, nil
}
}
// configurator is what we do first because it's generally fast:
// it shouldn't read *much* of the remote file, and with httpfile
// caching, it's even faster. whereas 7-zip might read a *bunch*
// of an .exe file before it gives up
consumer.Infof(" Probing with configurator...")
beforeConfiguratorProbe := time.Now()
candidate, err := configurator.Sniff(file, target, stat.Size())
if err != nil {
return nil, errors.Wrap(err, 0)
}
consumer.Debugf(" (took %s)", time.Since(beforeConfiguratorProbe))
var typePerConfigurator = InstallerTypeUnknown
if candidate != nil {
consumer.Infof(" Candidate: %s", candidate.String())
typePerConfigurator = getInstallerTypeForCandidate(consumer, candidate)
} else {
consumer.Infof(" No results from configurator")
}
if typePerConfigurator == InstallerTypeUnknown || typePerConfigurator == InstallerTypeNaked || typePerConfigurator == InstallerTypeArchive {
// some archive types are better sniffed by 7-zip and/or butler's own
// decompression engines, so if configurator returns naked, we try
// to open as an archive.
beforeArchiveProbe := time.Now()
consumer.Infof(" Probing as archive...")
// seek to start first because configurator may have seeked itself
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return nil, errors.Wrap(err, 0)
}
archiveInfo, err := archive.Probe(&archive.TryOpenParams{
File: file,
Consumer: consumer,
})
consumer.Debugf(" (took %s)", time.Since(beforeArchiveProbe))
if err == nil {
consumer.Infof("β Source is a supported archive format (%s)", archiveInfo.Format)
if archiveInfo.Features.ResumeSupport == savior.ResumeSupportNone {
// TODO: force downloading to disk first for those
consumer.Warnf(" ...but this format has no/poor resume support, interruptions will waste network/CPU time")
}
return &InstallerInfo{
Type: InstallerTypeArchive,
ArchiveInfo: archiveInfo,
}, nil
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return nil, errors.Wrap(err, 0)
}
}
consumer.Infof("β Using configurator results")
return &InstallerInfo{
Type: typePerConfigurator,
}, nil
}
func getInstallerTypeForCandidate(consumer *state.Consumer, candidate *configurator.Candidate) InstallerType {
switch candidate.Flavor {
case configurator.FlavorNativeWindows:
if candidate.WindowsInfo != nil && candidate.WindowsInfo.InstallerType != "" {
typ := (InstallerType)(candidate.WindowsInfo.InstallerType)
consumer.Infof(" β Windows installer of type %s", typ)
return typ
}
consumer.Infof(" β Native windows executable, but not an installer")
return InstallerTypeNaked
case configurator.FlavorNativeMacos:
consumer.Infof(" β Native macOS executable")
return InstallerTypeNaked
case configurator.FlavorNativeLinux:
consumer.Infof(" β Native linux executable")
return InstallerTypeNaked
case configurator.FlavorScript:
consumer.Infof(" β Script")
if candidate.ScriptInfo != nil && candidate.ScriptInfo.Interpreter != "" {
consumer.Infof(" with interpreter %s", candidate.ScriptInfo.Interpreter)
}
return InstallerTypeNaked
case configurator.FlavorScriptWindows:
consumer.Infof(" β Windows script")
return InstallerTypeNaked
}
return InstallerTypeUnknown
}
func IsWindowsInstaller(typ InstallerType) bool {
switch typ {
case InstallerTypeMSI:
return true
case InstallerTypeNsis:
return true
case InstallerTypeInno:
return true
default:
return false
}
}