tangled
alpha
login
or
join now
atscan.net
/
plcbundle-go
1
fork
atom
[DEPRECATED] Go implementation of plcbundle
1
fork
atom
overview
issues
pulls
pipelines
cmd verify
tree.fail
4 months ago
99fde59a
9ada429c
+357
-61
2 changed files
expand all
collapse all
unified
split
cmd
plcbundle
commands
verify.go
main.go
+355
-59
cmd/plcbundle/commands/verify.go
···
3
3
import (
4
4
"context"
5
5
"fmt"
6
6
+
"os"
7
7
+
"time"
6
8
7
7
-
flag "github.com/spf13/pflag"
8
8
-
9
9
+
"github.com/spf13/cobra"
9
10
"tangled.org/atscan.net/plcbundle/bundle"
11
11
+
"tangled.org/atscan.net/plcbundle/cmd/plcbundle/ui"
10
12
)
11
13
12
12
-
// VerifyCommand handles the verify subcommand
13
13
-
func VerifyCommand(args []string) error {
14
14
-
fs := flag.NewFlagSet("verify", flag.ExitOnError)
15
15
-
bundleNum := fs.Int("bundle", 0, "specific bundle to verify (0 = verify chain)")
16
16
-
verbose := fs.Bool("v", false, "verbose output")
14
14
+
func NewVerifyCommand() *cobra.Command {
15
15
+
var (
16
16
+
bundleNum int
17
17
+
rangeStr string
18
18
+
chain bool
19
19
+
parallel bool
20
20
+
workers int
21
21
+
)
17
22
18
18
-
if err := fs.Parse(args); err != nil {
19
19
-
return err
20
20
-
}
23
23
+
cmd := &cobra.Command{
24
24
+
Use: "verify [flags]",
25
25
+
Short: "Verify bundle integrity and chain",
26
26
+
Long: `Verify bundle integrity and cryptographic chain
21
27
22
22
-
mgr, dir, err := getManager("")
23
23
-
if err != nil {
24
24
-
return err
25
25
-
}
26
26
-
defer mgr.Close()
28
28
+
Verifies bundle file hashes, content integrity, and chain linkage.
29
29
+
By default, verifies the entire chain. You can verify specific bundles
30
30
+
or ranges using flags.
27
31
28
28
-
fmt.Printf("Working in: %s\n", dir)
32
32
+
Verification checks:
33
33
+
• Compressed file hash matches index
34
34
+
• Content hash matches index
35
35
+
• File sizes match metadata
36
36
+
• Chain parent references are correct
37
37
+
• Chain hash calculations are valid`,
29
38
30
30
-
ctx := context.Background()
39
39
+
Example: ` # Verify entire chain
40
40
+
plcbundle verify
41
41
+
plcbundle verify --chain
42
42
+
43
43
+
# Verify specific bundle
44
44
+
plcbundle verify --bundle 42
31
45
32
32
-
if *bundleNum > 0 {
33
33
-
return verifySingleBundle(ctx, mgr, *bundleNum, *verbose)
46
46
+
# Verify range of bundles
47
47
+
plcbundle verify --range 1-100
48
48
+
49
49
+
# Verbose output
50
50
+
plcbundle verify --chain -v
51
51
+
52
52
+
# Parallel verification (faster for ranges)
53
53
+
plcbundle verify --range 1-1000 --parallel --workers 8`,
54
54
+
55
55
+
RunE: func(cmd *cobra.Command, args []string) error {
56
56
+
verbose, _ := cmd.Root().PersistentFlags().GetBool("verbose")
57
57
+
58
58
+
mgr, dir, err := getManagerFromCommand(cmd, "")
59
59
+
if err != nil {
60
60
+
return err
61
61
+
}
62
62
+
defer mgr.Close()
63
63
+
64
64
+
if !verbose {
65
65
+
fmt.Fprintf(os.Stderr, "Working in: %s\n\n", dir)
66
66
+
}
67
67
+
68
68
+
ctx := context.Background()
69
69
+
70
70
+
// Determine what to verify
71
71
+
if rangeStr != "" {
72
72
+
return verifyRange(ctx, mgr, rangeStr, verbose, parallel, workers)
73
73
+
}
74
74
+
75
75
+
if bundleNum > 0 {
76
76
+
return verifySingleBundle(ctx, mgr, bundleNum, verbose)
77
77
+
}
78
78
+
79
79
+
// Default: verify entire chain
80
80
+
return verifyChain(ctx, mgr, verbose)
81
81
+
},
34
82
}
35
83
36
36
-
return verifyChain(ctx, mgr, *verbose)
84
84
+
cmd.Flags().IntVarP(&bundleNum, "bundle", "b", 0, "Verify specific bundle number")
85
85
+
cmd.Flags().StringVarP(&rangeStr, "range", "r", "", "Verify bundle range (e.g., '1-100')")
86
86
+
cmd.Flags().BoolVarP(&chain, "chain", "c", false, "Verify entire chain (default)")
87
87
+
cmd.Flags().BoolVar(¶llel, "parallel", false, "Use parallel verification for ranges")
88
88
+
cmd.Flags().IntVar(&workers, "workers", 4, "Number of parallel workers")
89
89
+
90
90
+
return cmd
37
91
}
38
92
39
93
func verifySingleBundle(ctx context.Context, mgr *bundle.Manager, bundleNum int, verbose bool) error {
40
40
-
fmt.Printf("Verifying bundle %06d...\n", bundleNum)
94
94
+
fmt.Fprintf(os.Stderr, "Verifying bundle %06d...\n", bundleNum)
41
95
96
96
+
start := time.Now()
42
97
result, err := mgr.VerifyBundle(ctx, bundleNum)
98
98
+
elapsed := time.Since(start)
99
99
+
43
100
if err != nil {
44
101
return fmt.Errorf("verification failed: %w", err)
45
102
}
46
103
47
104
if result.Valid {
48
48
-
fmt.Printf("✓ Bundle %06d is valid\n", bundleNum)
105
105
+
fmt.Fprintf(os.Stderr, "✓ Bundle %06d is valid (%s)\n", bundleNum, elapsed.Round(time.Millisecond))
49
106
if verbose {
50
50
-
fmt.Printf(" File exists: %v\n", result.FileExists)
51
51
-
fmt.Printf(" Hash match: %v\n", result.HashMatch)
52
52
-
fmt.Printf(" Hash: %s...\n", result.LocalHash[:16])
107
107
+
fmt.Fprintf(os.Stderr, "\nDetails:\n")
108
108
+
fmt.Fprintf(os.Stderr, " File exists: %v\n", result.FileExists)
109
109
+
fmt.Fprintf(os.Stderr, " Hash match: %v\n", result.HashMatch)
110
110
+
fmt.Fprintf(os.Stderr, " Hash: %s...%s\n", result.LocalHash[:16], result.LocalHash[len(result.LocalHash)-16:])
111
111
+
fmt.Fprintf(os.Stderr, " Verification time: %s\n", elapsed)
53
112
}
54
113
return nil
55
114
}
56
115
57
57
-
fmt.Printf("✗ Bundle %06d is invalid\n", bundleNum)
116
116
+
fmt.Fprintf(os.Stderr, "✗ Bundle %06d is invalid (%s)\n", bundleNum, elapsed.Round(time.Millisecond))
58
117
if result.Error != nil {
59
59
-
fmt.Printf(" Error: %v\n", result.Error)
118
118
+
fmt.Fprintf(os.Stderr, " Error: %v\n", result.Error)
60
119
}
61
120
if !result.FileExists {
62
62
-
fmt.Printf(" File not found\n")
121
121
+
fmt.Fprintf(os.Stderr, " File not found\n")
63
122
}
64
123
if !result.HashMatch && result.FileExists {
65
65
-
fmt.Printf(" Expected hash: %s...\n", result.ExpectedHash[:16])
66
66
-
fmt.Printf(" Actual hash: %s...\n", result.LocalHash[:16])
124
124
+
fmt.Fprintf(os.Stderr, " Expected hash: %s...\n", result.ExpectedHash[:16])
125
125
+
fmt.Fprintf(os.Stderr, " Actual hash: %s...\n", result.LocalHash[:16])
67
126
}
68
127
return fmt.Errorf("bundle verification failed")
69
128
}
···
73
132
bundles := index.GetBundles()
74
133
75
134
if len(bundles) == 0 {
76
76
-
fmt.Println("No bundles to verify")
135
135
+
fmt.Fprintf(os.Stderr, "No bundles to verify\n")
77
136
return nil
78
137
}
79
138
80
80
-
fmt.Printf("Verifying chain of %d bundles...\n\n", len(bundles))
139
139
+
fmt.Fprintf(os.Stderr, "Verifying chain of %d bundles...\n\n", len(bundles))
140
140
+
141
141
+
start := time.Now()
142
142
+
143
143
+
var progress *ui.ProgressBar
144
144
+
if !verbose {
145
145
+
progress = ui.NewProgressBar(len(bundles))
146
146
+
}
81
147
82
148
verifiedCount := 0
83
149
errorCount := 0
84
84
-
lastPercent := -1
150
150
+
var firstError error
85
151
86
152
for i, meta := range bundles {
87
153
bundleNum := meta.BundleNumber
88
154
89
89
-
percent := (i * 100) / len(bundles)
90
90
-
if percent != lastPercent || verbose {
91
91
-
if verbose {
92
92
-
fmt.Printf(" [%3d%%] Verifying bundle %06d...", percent, bundleNum)
93
93
-
} else if percent%10 == 0 && percent != lastPercent {
94
94
-
fmt.Printf(" [%3d%%] Verified %d/%d bundles...\n", percent, i, len(bundles))
95
95
-
}
96
96
-
lastPercent = percent
155
155
+
if verbose {
156
156
+
fmt.Fprintf(os.Stderr, " Verifying bundle %06d... ", bundleNum)
97
157
}
98
158
99
159
result, err := mgr.VerifyBundle(ctx, bundleNum)
100
160
if err != nil {
101
161
if verbose {
102
102
-
fmt.Printf(" ERROR\n")
162
162
+
fmt.Fprintf(os.Stderr, "ERROR\n")
103
163
}
104
104
-
fmt.Printf("\n✗ Failed to verify bundle %06d: %v\n", bundleNum, err)
164
164
+
fmt.Fprintf(os.Stderr, "\n✗ Failed to verify bundle %06d: %v\n", bundleNum, err)
105
165
errorCount++
166
166
+
if firstError == nil {
167
167
+
firstError = err
168
168
+
}
106
169
continue
107
170
}
108
171
109
172
if !result.Valid {
110
173
if verbose {
111
111
-
fmt.Printf(" INVALID\n")
174
174
+
fmt.Fprintf(os.Stderr, "INVALID\n")
112
175
}
113
113
-
fmt.Printf("\n✗ Bundle %06d hash verification failed\n", bundleNum)
176
176
+
fmt.Fprintf(os.Stderr, "\n✗ Bundle %06d hash verification failed\n", bundleNum)
114
177
if result.Error != nil {
115
115
-
fmt.Printf(" Error: %v\n", result.Error)
178
178
+
fmt.Fprintf(os.Stderr, " Error: %v\n", result.Error)
179
179
+
if firstError == nil {
180
180
+
firstError = result.Error
181
181
+
}
116
182
}
117
183
errorCount++
118
184
continue
119
185
}
120
186
187
187
+
// Verify chain link
121
188
if i > 0 {
122
189
prevMeta := bundles[i-1]
123
190
if meta.Parent != prevMeta.Hash {
124
191
if verbose {
125
125
-
fmt.Printf(" CHAIN BROKEN\n")
192
192
+
fmt.Fprintf(os.Stderr, "CHAIN BROKEN\n")
126
193
}
127
127
-
fmt.Printf("\n✗ Chain broken at bundle %06d\n", bundleNum)
128
128
-
fmt.Printf(" Expected parent: %s...\n", prevMeta.Hash[:16])
129
129
-
fmt.Printf(" Actual parent: %s...\n", meta.Parent[:16])
194
194
+
fmt.Fprintf(os.Stderr, "\n✗ Chain broken at bundle %06d\n", bundleNum)
195
195
+
fmt.Fprintf(os.Stderr, " Expected parent: %s...\n", prevMeta.Hash[:16])
196
196
+
fmt.Fprintf(os.Stderr, " Actual parent: %s...\n", meta.Parent[:16])
130
197
errorCount++
198
198
+
if firstError == nil {
199
199
+
firstError = fmt.Errorf("chain broken at bundle %d", bundleNum)
200
200
+
}
131
201
continue
132
202
}
133
203
}
134
204
135
205
if verbose {
136
136
-
fmt.Printf(" ✓\n")
206
206
+
fmt.Fprintf(os.Stderr, "✓\n")
137
207
}
138
208
verifiedCount++
209
209
+
210
210
+
if progress != nil {
211
211
+
progress.Set(i + 1)
212
212
+
}
139
213
}
140
214
141
141
-
fmt.Println()
215
215
+
if progress != nil {
216
216
+
progress.Finish()
217
217
+
}
218
218
+
219
219
+
elapsed := time.Since(start)
220
220
+
221
221
+
fmt.Fprintf(os.Stderr, "\n")
142
222
if errorCount == 0 {
143
143
-
fmt.Printf("✓ Chain is valid (%d bundles verified)\n", verifiedCount)
144
144
-
fmt.Printf(" First bundle: %06d\n", bundles[0].BundleNumber)
145
145
-
fmt.Printf(" Last bundle: %06d\n", bundles[len(bundles)-1].BundleNumber)
146
146
-
fmt.Printf(" Chain head: %s...\n", bundles[len(bundles)-1].Hash[:16])
223
223
+
fmt.Fprintf(os.Stderr, "✓ Chain is valid (%d bundles verified)\n", verifiedCount)
224
224
+
fmt.Fprintf(os.Stderr, " First bundle: %06d\n", bundles[0].BundleNumber)
225
225
+
fmt.Fprintf(os.Stderr, " Last bundle: %06d\n", bundles[len(bundles)-1].BundleNumber)
226
226
+
fmt.Fprintf(os.Stderr, " Chain head: %s...%s\n",
227
227
+
bundles[len(bundles)-1].Hash[:16],
228
228
+
bundles[len(bundles)-1].Hash[len(bundles[len(bundles)-1].Hash)-16:])
229
229
+
230
230
+
// Timing information
231
231
+
fmt.Fprintf(os.Stderr, "\nPerformance:\n")
232
232
+
fmt.Fprintf(os.Stderr, " Time: %s\n", elapsed.Round(time.Millisecond))
233
233
+
if elapsed.Seconds() > 0 {
234
234
+
bundlesPerSec := float64(verifiedCount) / elapsed.Seconds()
235
235
+
fmt.Fprintf(os.Stderr, " Throughput: %.1f bundles/sec\n", bundlesPerSec)
236
236
+
237
237
+
// Calculate data throughput
238
238
+
index := mgr.GetIndex()
239
239
+
stats := index.GetStats()
240
240
+
if totalSize, ok := stats["total_size"].(int64); ok && totalSize > 0 {
241
241
+
mbPerSec := float64(totalSize) / elapsed.Seconds() / (1024 * 1024)
242
242
+
fmt.Fprintf(os.Stderr, " Data rate: %.1f MB/sec (compressed)\n", mbPerSec)
243
243
+
}
244
244
+
}
147
245
return nil
148
246
}
149
247
150
150
-
fmt.Printf("✗ Chain verification failed\n")
151
151
-
fmt.Printf(" Verified: %d/%d bundles\n", verifiedCount, len(bundles))
152
152
-
fmt.Printf(" Errors: %d\n", errorCount)
248
248
+
fmt.Fprintf(os.Stderr, "✗ Chain verification failed\n")
249
249
+
fmt.Fprintf(os.Stderr, " Verified: %d/%d bundles\n", verifiedCount, len(bundles))
250
250
+
fmt.Fprintf(os.Stderr, " Errors: %d\n", errorCount)
251
251
+
fmt.Fprintf(os.Stderr, " Time: %s\n", elapsed.Round(time.Millisecond))
153
252
return fmt.Errorf("chain verification failed")
154
253
}
254
254
+
255
255
+
func verifyRange(ctx context.Context, mgr *bundle.Manager, rangeStr string, verbose bool, parallel bool, workers int) error {
256
256
+
start, end, err := parseBundleRange(rangeStr)
257
257
+
if err != nil {
258
258
+
return err
259
259
+
}
260
260
+
261
261
+
fmt.Fprintf(os.Stderr, "Verifying bundles %06d - %06d", start, end)
262
262
+
if parallel {
263
263
+
fmt.Fprintf(os.Stderr, " (parallel, %d workers)", workers)
264
264
+
}
265
265
+
fmt.Fprintf(os.Stderr, "\n\n")
266
266
+
267
267
+
total := end - start + 1
268
268
+
overallStart := time.Now()
269
269
+
270
270
+
var verifyErr error
271
271
+
if parallel {
272
272
+
verifyErr = verifyRangeParallel(ctx, mgr, start, end, workers, verbose)
273
273
+
} else {
274
274
+
verifyErr = verifyRangeSequential(ctx, mgr, start, end, total, verbose)
275
275
+
}
276
276
+
277
277
+
elapsed := time.Since(overallStart)
278
278
+
279
279
+
// Add timing summary
280
280
+
fmt.Fprintf(os.Stderr, "\nPerformance:\n")
281
281
+
fmt.Fprintf(os.Stderr, " Time: %s\n", elapsed.Round(time.Millisecond))
282
282
+
if elapsed.Seconds() > 0 {
283
283
+
bundlesPerSec := float64(total) / elapsed.Seconds()
284
284
+
fmt.Fprintf(os.Stderr, " Throughput: %.1f bundles/sec\n", bundlesPerSec)
285
285
+
286
286
+
// Estimate average time per bundle
287
287
+
avgTime := elapsed / time.Duration(total)
288
288
+
fmt.Fprintf(os.Stderr, " Avg/bundle: %s\n", avgTime.Round(time.Millisecond))
289
289
+
}
290
290
+
291
291
+
return verifyErr
292
292
+
}
293
293
+
294
294
+
func verifyRangeSequential(ctx context.Context, mgr *bundle.Manager, start, end, total int, verbose bool) error {
295
295
+
var progress *ui.ProgressBar
296
296
+
if !verbose {
297
297
+
progress = ui.NewProgressBar(total)
298
298
+
}
299
299
+
300
300
+
verified := 0
301
301
+
failed := 0
302
302
+
failedBundles := []int{}
303
303
+
304
304
+
for bundleNum := start; bundleNum <= end; bundleNum++ {
305
305
+
result, err := mgr.VerifyBundle(ctx, bundleNum)
306
306
+
307
307
+
if verbose {
308
308
+
fmt.Fprintf(os.Stderr, "Bundle %06d: ", bundleNum)
309
309
+
}
310
310
+
311
311
+
if err != nil {
312
312
+
if verbose {
313
313
+
fmt.Fprintf(os.Stderr, "ERROR - %v\n", err)
314
314
+
}
315
315
+
failed++
316
316
+
failedBundles = append(failedBundles, bundleNum)
317
317
+
} else if !result.Valid {
318
318
+
if verbose {
319
319
+
fmt.Fprintf(os.Stderr, "INVALID - %v\n", result.Error)
320
320
+
}
321
321
+
failed++
322
322
+
failedBundles = append(failedBundles, bundleNum)
323
323
+
} else {
324
324
+
if verbose {
325
325
+
fmt.Fprintf(os.Stderr, "✓\n")
326
326
+
}
327
327
+
verified++
328
328
+
}
329
329
+
330
330
+
if progress != nil {
331
331
+
progress.Set(bundleNum - start + 1)
332
332
+
}
333
333
+
}
334
334
+
335
335
+
if progress != nil {
336
336
+
progress.Finish()
337
337
+
}
338
338
+
339
339
+
fmt.Fprintf(os.Stderr, "\n")
340
340
+
if failed == 0 {
341
341
+
fmt.Fprintf(os.Stderr, "✓ All %d bundles verified successfully\n", verified)
342
342
+
return nil
343
343
+
}
344
344
+
345
345
+
fmt.Fprintf(os.Stderr, "✗ Verification failed\n")
346
346
+
fmt.Fprintf(os.Stderr, " Verified: %d/%d\n", verified, total)
347
347
+
fmt.Fprintf(os.Stderr, " Failed: %d\n", failed)
348
348
+
349
349
+
if len(failedBundles) > 0 && len(failedBundles) <= 20 {
350
350
+
fmt.Fprintf(os.Stderr, "\nFailed bundles: ")
351
351
+
for i, num := range failedBundles {
352
352
+
if i > 0 {
353
353
+
fmt.Fprintf(os.Stderr, ", ")
354
354
+
}
355
355
+
fmt.Fprintf(os.Stderr, "%06d", num)
356
356
+
}
357
357
+
fmt.Fprintf(os.Stderr, "\n")
358
358
+
} else if len(failedBundles) > 20 {
359
359
+
fmt.Fprintf(os.Stderr, "\nFailed bundles: %06d, %06d, ... and %d more\n",
360
360
+
failedBundles[0], failedBundles[1], len(failedBundles)-2)
361
361
+
}
362
362
+
363
363
+
return fmt.Errorf("verification failed for %d bundles", failed)
364
364
+
}
365
365
+
366
366
+
func verifyRangeParallel(ctx context.Context, mgr *bundle.Manager, start, end, workers int, verbose bool) error {
367
367
+
type job struct {
368
368
+
bundleNum int
369
369
+
}
370
370
+
371
371
+
type result struct {
372
372
+
bundleNum int
373
373
+
valid bool
374
374
+
err error
375
375
+
}
376
376
+
377
377
+
total := end - start + 1
378
378
+
jobs := make(chan job, total)
379
379
+
results := make(chan result, total)
380
380
+
381
381
+
// Start workers
382
382
+
for w := 0; w < workers; w++ {
383
383
+
go func() {
384
384
+
for j := range jobs {
385
385
+
vr, err := mgr.VerifyBundle(ctx, j.bundleNum)
386
386
+
results <- result{
387
387
+
bundleNum: j.bundleNum,
388
388
+
valid: err == nil && vr.Valid,
389
389
+
err: err,
390
390
+
}
391
391
+
}
392
392
+
}()
393
393
+
}
394
394
+
395
395
+
// Send jobs
396
396
+
for bundleNum := start; bundleNum <= end; bundleNum++ {
397
397
+
jobs <- job{bundleNum: bundleNum}
398
398
+
}
399
399
+
close(jobs)
400
400
+
401
401
+
// Collect results with progress
402
402
+
progress := ui.NewProgressBar(total)
403
403
+
verified := 0
404
404
+
failed := 0
405
405
+
failedBundles := []int{}
406
406
+
407
407
+
for i := 0; i < total; i++ {
408
408
+
res := <-results
409
409
+
410
410
+
if res.valid {
411
411
+
verified++
412
412
+
} else {
413
413
+
failed++
414
414
+
failedBundles = append(failedBundles, res.bundleNum)
415
415
+
if verbose {
416
416
+
fmt.Fprintf(os.Stderr, "\nBundle %06d: FAILED - %v\n", res.bundleNum, res.err)
417
417
+
}
418
418
+
}
419
419
+
420
420
+
progress.Set(i + 1)
421
421
+
}
422
422
+
423
423
+
progress.Finish()
424
424
+
425
425
+
fmt.Fprintf(os.Stderr, "\n")
426
426
+
if failed == 0 {
427
427
+
fmt.Fprintf(os.Stderr, "✓ All %d bundles verified successfully\n", verified)
428
428
+
return nil
429
429
+
}
430
430
+
431
431
+
fmt.Fprintf(os.Stderr, "✗ Verification failed\n")
432
432
+
fmt.Fprintf(os.Stderr, " Verified: %d/%d\n", verified, total)
433
433
+
fmt.Fprintf(os.Stderr, " Failed: %d\n", failed)
434
434
+
435
435
+
if len(failedBundles) > 0 && len(failedBundles) <= 20 {
436
436
+
fmt.Fprintf(os.Stderr, "\nFailed bundles: ")
437
437
+
for i, num := range failedBundles {
438
438
+
if i > 0 {
439
439
+
fmt.Fprintf(os.Stderr, ", ")
440
440
+
}
441
441
+
fmt.Fprintf(os.Stderr, "%06d", num)
442
442
+
}
443
443
+
fmt.Fprintf(os.Stderr, "\n")
444
444
+
} else if len(failedBundles) > 20 {
445
445
+
fmt.Fprintf(os.Stderr, "\nFailed bundles: %06d, %06d, ... and %d more\n",
446
446
+
failedBundles[0], failedBundles[1], len(failedBundles)-2)
447
447
+
}
448
448
+
449
449
+
return fmt.Errorf("verification failed for %d bundles", failed)
450
450
+
}
+2
-2
cmd/plcbundle/main.go
···
55
55
// Status & info (root level)
56
56
cmd.AddCommand(commands.NewStatusCommand())
57
57
/*cmd.AddCommand(commands.NewLogCommand())
58
58
-
cmd.AddCommand(commands.NewGapsCommand())
58
58
+
cmd.AddCommand(commands.NewGapsCommand())*/
59
59
cmd.AddCommand(commands.NewVerifyCommand())
60
60
-
cmd.AddCommand(commands.NewDiffCommand())
60
60
+
/*cmd.AddCommand(commands.NewDiffCommand())
61
61
cmd.AddCommand(commands.NewStatsCommand())
62
62
cmd.AddCommand(commands.NewInspectCommand())
63
63