-
Notifications
You must be signed in to change notification settings - Fork 0
/
dm2.go
206 lines (166 loc) · 4.7 KB
/
dm2.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
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/alecthomas/kong"
)
var buildTime = "dev"
var buildHash = "dev"
// CLI Main.
// Main command
var cli struct {
Init cliInit `cmd:"" help:"Initialize current directory as new root."`
Info cliInfo `cmd:"" help:"Show infos about this repository."`
Edit cliEdit `cmd:"" help:"Edit config files."`
List cliList `cmd:"" help:"List available remote files or directories."`
Push cliPullPush `cmd:"" help:"Push local state of dataset to remote repository."`
Pull cliPullPush `cmd:"" help:"Pull remote state of dataset to local."`
}
// CLI context.
// Context provided to Run(...) method of CLI commands
type cliContext struct {
rc *RcloneWrapper
remote string
datasets map[string]*Dataset
args []string
}
// Return a Dataset pointer for given dataset name
func (ctx *cliContext) getDataset(dsName string) (*Dataset, string) {
// Get dataset from map
ds := ctx.datasets[dsName]
if ds == nil {
ErrorLogger.Fatalf("Unknown dataset %s", dsName)
}
// Create temporary filter file because all CLI commands need it
filterPath := ds.WriteTmpFilterFile()
return ds, filterPath
}
// CLI sub command.
// Init.
type cliInit struct{}
func (r *cliInit) Run(ctx *cliContext) error {
// Note: Root dir has to be created manually by the user.
// Make new config
// Write default ignore
f, err := os.Create(filepath.Join(DMRoot, GlobalIgnoreFile))
checkError(err)
defer f.Close()
_, err = f.WriteString(GlobalIgnoreDefault)
checkError(err)
// Make new empty dataset file
os.OpenFile(DatasetFile, os.O_RDONLY|os.O_CREATE, 0666)
return nil
}
// CLI sub command.
// Push and pull datasets.
type cliPullPush struct {
Ds string `arg:"" help:"Dataset name."`
Confirm bool `help:"Add flag to confirm action."`
}
func (r *cliPullPush) Run(ctx *cliContext) error {
_, filterPath := ctx.getDataset(r.Ds)
// Dry run flag is opposite of confirm flag
ctx.rc.DryRun = !r.Confirm
switch ctx.args[0] {
case "pull":
ctx.rc.exec("sync", ctx.remote, "--filter-from", filterPath, ".")
case "push":
ctx.rc.exec("sync", ".", "--filter-from", filterPath, ctx.remote)
}
err := os.Remove(filterPath)
checkError(err)
return nil
}
// CLI sub command.
// List remote directories
type cliList struct {
Ds string `arg:"" optional:"" help:"Dataset to list (optional). If not provided, all available directories are listed."`
}
func (r *cliList) Run(ctx *cliContext) error {
if r.Ds == "" {
// List directories
ctx.rc.List()
} else {
// List files of requested dataset
_, filterPath := ctx.getDataset(r.Ds)
ctx.rc.exec("ls", ctx.remote, "--filter-from", filterPath)
err := os.Remove(filterPath)
checkError(err)
}
return nil
}
// CLI sub command.
// Show infos.
type cliInfo struct{}
func (r *cliInfo) Run(ctx *cliContext) error {
// General info
fmt.Println("Dataset Manager 2")
fmt.Println("-----------------")
fmt.Printf("Build time: %s \n", buildTime)
fmt.Printf("Git commit: %s \n", buildHash)
fmt.Println()
fmt.Println("Root directory: " + DMRoot)
fmt.Println("-> Config file: ", DMConfigFile)
fmt.Println("-> Dataset definitions: ", DatasetFile)
fmt.Println("-> Global ignore list: ", GlobalIgnoreFile)
fmt.Println()
// Dataset info
fmt.Println("Configured datasets:")
for _, d := range getDatasetNames(ctx.datasets) {
fmt.Println("* " + d)
}
return nil
}
// CLI sub command.
// Edit config files
type cliEdit struct {
File string `arg:"" default:"datasets" enum:"config,datasets,ignore"`
}
func (r *cliEdit) Run(ctx *cliContext) error {
// Selct which file to edit
var file string
switch r.File {
case "config":
file = DMConfigFile
case "ignore":
file = GlobalIgnoreFile
default:
file = DatasetFile
}
// Prepare vim for editing
cmd := exec.Command("vim", filepath.Join(DMRoot, file))
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
// Run it
err := cmd.Run()
checkError(err)
return nil
}
func main() {
// Parse arguments
ctx := kong.Parse(&cli)
// Init loggers
initLoggers(os.Stdout, LevelError)
// Check if root exists
_, err := os.Stat(DMRoot)
if os.IsNotExist(err) {
ErrorLogger.Fatalf("This directory does not contain the `%s` root directory. Please create it manually.", DMRoot)
}
// Setup rclone wrapper
rc := NewRcloneWrapper(filepath.Join(DMRoot, DMConfigFile), true)
// Load global ignores
ignores := loadGlobalIgnore(filepath.Join(DMRoot, GlobalIgnoreFile))
// Load datasets from file and prefix with rclone basedir
datasets := loadDatasetConfig(filepath.Join(DMRoot, DatasetFile), ignores)
// Run cli
err = ctx.Run(&cliContext{
rc: rc,
remote: rc.preparePath(""),
datasets: datasets,
args: ctx.Args,
})
ctx.FatalIfErrorf(err)
}