-
Notifications
You must be signed in to change notification settings - Fork 3
/
plugins.go
172 lines (153 loc) · 3.86 KB
/
plugins.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
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/particle-iot/particle-cli-wrapper/Godeps/_workspace/src/github.com/dickeyxxx/golock"
"github.com/particle-iot/particle-cli-wrapper/gode"
)
// Plugin represents a javascript plugin
type Plugin struct {
Name string `json:"name"`
Version string `json:"version"`
}
// ParsePlugin requires the plugin's node module
// to get the commands and metadata
func ParsePlugin(name string) (*Plugin, error) {
script := `
var plugin = {};
var pjson = require('` + name + `/package.json');
plugin.name = pjson.name;
plugin.version = pjson.version;
console.log(JSON.stringify(plugin))`
cmd := gode.RunScript(script)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("Error reading plugin: %s\n%s\n%s", name, err, string(output))
}
var plugin Plugin
json.Unmarshal([]byte(output), &plugin)
return &plugin, nil
}
// GetPlugins goes through all the node plugins and returns them in Go stucts
func GetPlugins() map[string]*Plugin {
plugins := FetchPluginCache()
for name, plugin := range plugins {
if plugin == nil || !pluginExists(name) {
delete(plugins, name)
}
}
return plugins
}
// PluginNames lists all the plugin names
func PluginNames() []string {
plugins := FetchPluginCache()
names := make([]string, 0, len(plugins))
for _, plugin := range plugins {
if plugin != nil && pluginExists(plugin.Name) {
names = append(names, plugin.Name)
}
}
return names
}
// PluginNamesNotSymlinked returns all the plugins that are not symlinked
func PluginNamesNotSymlinked() []string {
a := PluginNames()
b := make([]string, 0, len(a))
for _, plugin := range a {
if !isPluginSymlinked(plugin) {
b = append(b, plugin)
}
}
return b
}
func isPluginSymlinked(plugin string) bool {
path := filepath.Join(AppDir(), "node_modules", plugin)
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.Mode()&os.ModeSymlink != 0
}
// ensure all the Javascript plugin are installed
func SetupPlugins(pluginNames ...string) {
newPluginNames := difference(pluginNames, PluginNames())
if len(newPluginNames) == 0 {
return
}
Err("particle: Installing plugins...")
if err := installPlugins(newPluginNames...); err != nil {
// retry once
PrintError(gode.RemovePackages(newPluginNames...), true)
PrintError(gode.ClearCache(), true)
Err("\rparticle: Installing plugins (retrying)...")
ExitIfError(installPlugins(newPluginNames...), true)
}
Errln(" done")
}
func difference(a, b []string) []string {
res := make([]string, 0, len(a))
for _, aa := range a {
if !contains(b, aa) {
res = append(res, aa)
}
}
return res
}
func contains(arr []string, s string) bool {
for _, a := range arr {
if a == s {
return true
}
}
return false
}
func installPlugins(names ...string) error {
for _, name := range names {
lockPlugin(name)
}
defer func() {
for _, name := range names {
unlockPlugin(name)
}
}()
err := gode.InstallPackages(names...)
if err != nil {
return err
}
plugins := make([]*Plugin, 0, len(names))
for _, name := range names {
plugin, err := ParsePlugin(name)
if err != nil {
return err
}
plugins = append(plugins, plugin)
}
AddPluginsToCache(plugins...)
return nil
}
func pluginExists(plugin string) bool {
exists, _ := fileExists(pluginPath(plugin))
return exists
}
// directory location of plugin
func pluginPath(plugin string) string {
return filepath.Join(AppDir(), "node_modules", plugin)
}
// lock a plugin for reading
func readLockPlugin(name string) {
lockfile := updateLockPath + "." + name
if exists, _ := fileExists(lockfile); exists {
lockPlugin(name)
unlockPlugin(name)
}
}
// lock a plugin for writing
func lockPlugin(name string) {
LogIfError(golock.Lock(updateLockPath + "." + name))
}
// unlock a plugin
func unlockPlugin(name string) {
LogIfError(golock.Unlock(updateLockPath + "." + name))
}