6
6
package cmd
7
7
8
8
import (
9
+ "encoding/json"
9
10
"fmt"
10
11
"io/ioutil"
11
12
"os"
12
13
"path"
13
14
"path/filepath"
15
+ "strings"
14
16
"time"
15
17
16
18
"code.gitea.io/gitea/models"
17
19
"code.gitea.io/gitea/modules/log"
18
20
"code.gitea.io/gitea/modules/setting"
19
21
20
- "github.com/unknwon/cae/zip"
22
+ "gitea.com/macaron/session"
23
+ archiver "github.com/mholt/archiver/v3"
21
24
"github.com/unknwon/com"
22
25
"github.com/urfave/cli"
23
26
)
24
27
28
+ func addFile (w archiver.Writer , filePath string , absPath string , verbose bool ) error {
29
+ if verbose {
30
+ log .Info ("Adding file %s\n " , filePath )
31
+ }
32
+ file , err := os .Open (absPath )
33
+ if err != nil {
34
+ return err
35
+ }
36
+ defer file .Close ()
37
+ fileInfo , err := file .Stat ()
38
+ if err != nil {
39
+ return err
40
+ }
41
+
42
+ return w .Write (archiver.File {
43
+ FileInfo : archiver.FileInfo {
44
+ FileInfo : fileInfo ,
45
+ CustomName : filePath ,
46
+ },
47
+ ReadCloser : file ,
48
+ })
49
+ }
50
+
51
+ func addRecursive (w archiver.Writer , dirPath string , absPath string , verbose bool ) error {
52
+ if verbose {
53
+ log .Info ("Adding dir %s\n " , dirPath )
54
+ }
55
+ dir , err := os .Open (absPath )
56
+ if err != nil {
57
+ return fmt .Errorf ("Could not open directory %s: %s" , absPath , err )
58
+ }
59
+ files , err := dir .Readdir (0 )
60
+ if err != nil {
61
+ return fmt .Errorf ("Unable to list files in %s: %s" , absPath , err )
62
+ }
63
+
64
+ if err := addFile (w , dirPath , absPath , false ); err != nil {
65
+ return err
66
+ }
67
+
68
+ for _ , fileInfo := range files {
69
+ if fileInfo .IsDir () {
70
+ err = addRecursive (w , filepath .Join (dirPath , fileInfo .Name ()), filepath .Join (absPath , fileInfo .Name ()), verbose )
71
+ } else {
72
+ err = addFile (w , filepath .Join (dirPath , fileInfo .Name ()), filepath .Join (absPath , fileInfo .Name ()), verbose )
73
+ }
74
+ if err != nil {
75
+ return err
76
+ }
77
+ }
78
+ return nil
79
+ }
80
+
81
+ func isSubdir (upper string , lower string ) (bool , error ) {
82
+ if relPath , err := filepath .Rel (upper , lower ); err != nil {
83
+ return false , err
84
+ } else if relPath == "." || ! strings .HasPrefix (relPath , "." ) {
85
+ return true , nil
86
+ }
87
+ return false , nil
88
+ }
89
+
90
+ type outputType struct {
91
+ Enum []string
92
+ Default string
93
+ selected string
94
+ }
95
+
96
+ func (o outputType ) Join () string {
97
+ return strings .Join (o .Enum , ", " )
98
+ }
99
+
100
+ func (o * outputType ) Set (value string ) error {
101
+ for _ , enum := range o .Enum {
102
+ if enum == value {
103
+ o .selected = value
104
+ return nil
105
+ }
106
+ }
107
+
108
+ return fmt .Errorf ("allowed values are %s" , o .Join ())
109
+ }
110
+
111
+ func (o outputType ) String () string {
112
+ if o .selected == "" {
113
+ return o .Default
114
+ }
115
+ return o .selected
116
+ }
117
+
118
+ var outputTypeEnum = & outputType {
119
+ Enum : []string {"zip" , "tar" , "tar.gz" , "tar.xz" , "tar.bz2" },
120
+ Default : "zip" ,
121
+ }
122
+
25
123
// CmdDump represents the available dump sub-command.
26
124
var CmdDump = cli.Command {
27
125
Name : "dump" ,
@@ -33,7 +131,7 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
33
131
cli.StringFlag {
34
132
Name : "file, f" ,
35
133
Value : fmt .Sprintf ("gitea-dump-%d.zip" , time .Now ().Unix ()),
36
- Usage : "Name of the dump file which will be created." ,
134
+ Usage : "Name of the dump file which will be created. Supply '-' for stdout. See type for available types. " ,
37
135
},
38
136
cli.BoolFlag {
39
137
Name : "verbose, V" ,
@@ -56,6 +154,11 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
56
154
Name : "skip-log, L" ,
57
155
Usage : "Skip the log dumping" ,
58
156
},
157
+ cli.GenericFlag {
158
+ Name : "type" ,
159
+ Value : outputTypeEnum ,
160
+ Usage : fmt .Sprintf ("Dump output format: %s" , outputTypeEnum .Join ()),
161
+ },
59
162
},
60
163
}
61
164
@@ -65,79 +168,113 @@ func fatal(format string, args ...interface{}) {
65
168
}
66
169
67
170
func runDump (ctx * cli.Context ) error {
171
+ var file * os.File
172
+ fileName := ctx .String ("file" )
173
+ if fileName == "-" {
174
+ file = os .Stdout
175
+ err := log .DelLogger ("console" )
176
+ if err != nil {
177
+ fatal ("Deleting default logger failed. Can not write to stdout: %v" , err )
178
+ }
179
+ }
68
180
setting .NewContext ()
181
+ // make sure we are logging to the console no matter what the configuration tells us do to
182
+ if _ , err := setting .Cfg .Section ("log" ).NewKey ("MODE" , "console" ); err != nil {
183
+ fatal ("Setting logging mode to console failed: %v" , err )
184
+ }
185
+ if _ , err := setting .Cfg .Section ("log.console" ).NewKey ("STDERR" , "true" ); err != nil {
186
+ fatal ("Setting console logger to stderr failed: %v" , err )
187
+ }
69
188
setting .NewServices () // cannot access session settings otherwise
70
189
71
190
err := models .SetEngine ()
72
191
if err != nil {
73
192
return err
74
193
}
75
194
76
- tmpDir := ctx .String ("tempdir" )
77
- if _ , err := os .Stat (tmpDir ); os .IsNotExist (err ) {
78
- fatal ("Path does not exist: %s" , tmpDir )
79
- }
80
- tmpWorkDir , err := ioutil .TempDir (tmpDir , "gitea-dump-" )
81
- if err != nil {
82
- fatal ("Failed to create tmp work directory: %v" , err )
195
+ if file == nil {
196
+ file , err = os .Create (fileName )
197
+ if err != nil {
198
+ fatal ("Unable to open %s: %v" , fileName , err )
199
+ }
83
200
}
84
- log . Info ( "Creating tmp work dir: %s" , tmpWorkDir )
201
+ defer file . Close ( )
85
202
86
- // work-around #1103
87
- if os .Getenv ("TMPDIR" ) == "" {
88
- os .Setenv ("TMPDIR" , tmpWorkDir )
203
+ verbose := ctx .Bool ("verbose" )
204
+ outType := ctx .String ("type" )
205
+ var iface interface {}
206
+ if fileName == "-" {
207
+ iface , err = archiver .ByExtension (fmt .Sprintf (".%s" , outType ))
208
+ } else {
209
+ iface , err = archiver .ByExtension (fileName )
89
210
}
90
-
91
- dbDump := path .Join (tmpWorkDir , "gitea-db.sql" )
92
-
93
- fileName := ctx .String ("file" )
94
- log .Info ("Packing dump files..." )
95
- z , err := zip .Create (fileName )
96
211
if err != nil {
97
- fatal ("Failed to create %s : %v" , fileName , err )
212
+ fatal ("Unable to get archiver for extension : %v" , err )
98
213
}
99
214
100
- zip .Verbose = ctx .Bool ("verbose" )
215
+ w , _ := iface .(archiver.Writer )
216
+ if err := w .Create (file ); err != nil {
217
+ fatal ("Creating archiver.Writer failed: %v" , err )
218
+ }
219
+ defer w .Close ()
101
220
102
221
if ctx .IsSet ("skip-repository" ) && ctx .Bool ("skip-repository" ) {
103
222
log .Info ("Skip dumping local repositories" )
104
223
} else {
105
- log .Info ("Dumping local repositories...%s" , setting .RepoRootPath )
106
- reposDump := path .Join (tmpWorkDir , "gitea-repo.zip" )
107
- if err := zip .PackTo (setting .RepoRootPath , reposDump , true ); err != nil {
108
- fatal ("Failed to dump local repositories: %v" , err )
224
+ log .Info ("Dumping local repositories... %s" , setting .RepoRootPath )
225
+ if err := addRecursive (w , "repos" , setting .RepoRootPath , verbose ); err != nil {
226
+ fatal ("Failed to include repositories: %v" , err )
109
227
}
110
- if err := z .AddFile ("gitea-repo.zip" , reposDump ); err != nil {
111
- fatal ("Failed to include gitea-repo.zip: %v" , err )
228
+
229
+ if _ , err := os .Stat (setting .LFS .ContentPath ); ! os .IsNotExist (err ) {
230
+ log .Info ("Dumping lfs... %s" , setting .LFS .ContentPath )
231
+ if err := addRecursive (w , "lfs" , setting .LFS .ContentPath , verbose ); err != nil {
232
+ fatal ("Failed to include lfs: %v" , err )
233
+ }
112
234
}
113
235
}
114
236
237
+ tmpDir := ctx .String ("tempdir" )
238
+ if _ , err := os .Stat (tmpDir ); os .IsNotExist (err ) {
239
+ fatal ("Path does not exist: %s" , tmpDir )
240
+ }
241
+
242
+ dbDump , err := ioutil .TempFile (tmpDir , "gitea-db.sql" )
243
+ if err != nil {
244
+ fatal ("Failed to create tmp file: %v" , err )
245
+ }
246
+ defer os .Remove (dbDump .Name ())
247
+
115
248
targetDBType := ctx .String ("database" )
116
249
if len (targetDBType ) > 0 && targetDBType != setting .Database .Type {
117
250
log .Info ("Dumping database %s => %s..." , setting .Database .Type , targetDBType )
118
251
} else {
119
252
log .Info ("Dumping database..." )
120
253
}
121
254
122
- if err := models .DumpDatabase (dbDump , targetDBType ); err != nil {
255
+ if err := models .DumpDatabase (dbDump . Name () , targetDBType ); err != nil {
123
256
fatal ("Failed to dump database: %v" , err )
124
257
}
125
258
126
- if err := z . AddFile ( "gitea-db.sql" , dbDump ); err != nil {
259
+ if err := addFile ( w , "gitea-db.sql" , dbDump . Name (), verbose ); err != nil {
127
260
fatal ("Failed to include gitea-db.sql: %v" , err )
128
261
}
129
262
130
263
if len (setting .CustomConf ) > 0 {
131
264
log .Info ("Adding custom configuration file from %s" , setting .CustomConf )
132
- if err := z . AddFile ( "app.ini" , setting .CustomConf ); err != nil {
265
+ if err := addFile ( w , "app.ini" , setting .CustomConf , verbose ); err != nil {
133
266
fatal ("Failed to include specified app.ini: %v" , err )
134
267
}
135
268
}
136
269
137
270
customDir , err := os .Stat (setting .CustomPath )
138
271
if err == nil && customDir .IsDir () {
139
- if err := z .AddDir ("custom" , setting .CustomPath ); err != nil {
140
- fatal ("Failed to include custom: %v" , err )
272
+ if is , _ := isSubdir (setting .AppDataPath , setting .CustomPath ); ! is {
273
+ if err := addRecursive (w , "custom" , setting .CustomPath , verbose ); err != nil {
274
+ fatal ("Failed to include custom: %v" , err )
275
+ }
276
+ } else {
277
+ log .Info ("Custom dir %s is inside data dir %s, skipped" , setting .CustomPath , setting .AppDataPath )
141
278
}
142
279
} else {
143
280
log .Info ("Custom dir %s doesn't exist, skipped" , setting .CustomPath )
@@ -146,11 +283,19 @@ func runDump(ctx *cli.Context) error {
146
283
if com .IsExist (setting .AppDataPath ) {
147
284
log .Info ("Packing data directory...%s" , setting .AppDataPath )
148
285
149
- var sessionAbsPath string
150
- if setting .SessionConfig .Provider == "file" {
151
- sessionAbsPath = setting .SessionConfig .ProviderConfig
286
+ var excludes []string
287
+ if setting .Cfg .Section ("session" ).Key ("PROVIDER" ).Value () == "file" {
288
+ var opts session.Options
289
+ if err = json .Unmarshal ([]byte (setting .SessionConfig .ProviderConfig ), & opts ); err != nil {
290
+ return err
291
+ }
292
+ excludes = append (excludes , opts .ProviderConfig )
152
293
}
153
- if err := zipAddDirectoryExclude (z , "data" , setting .AppDataPath , sessionAbsPath ); err != nil {
294
+
295
+ excludes = append (excludes , setting .RepoRootPath )
296
+ excludes = append (excludes , setting .LFS .ContentPath )
297
+ excludes = append (excludes , setting .LogRootPath )
298
+ if err := addRecursiveExclude (w , "data" , setting .AppDataPath , excludes , verbose ); err != nil {
154
299
fatal ("Failed to include data directory: %v" , err )
155
300
}
156
301
}
@@ -161,32 +306,42 @@ func runDump(ctx *cli.Context) error {
161
306
if ctx .IsSet ("skip-log" ) && ctx .Bool ("skip-log" ) {
162
307
log .Info ("Skip dumping log files" )
163
308
} else if com .IsExist (setting .LogRootPath ) {
164
- if err := z . AddDir ( "log" , setting .LogRootPath ); err != nil {
309
+ if err := addRecursive ( w , "log" , setting .LogRootPath , verbose ); err != nil {
165
310
fatal ("Failed to include log: %v" , err )
166
311
}
167
312
}
168
313
169
- if err = z .Close (); err != nil {
170
- _ = os .Remove (fileName )
171
- fatal ("Failed to save %s: %v" , fileName , err )
172
- }
314
+ if fileName != "-" {
315
+ if err = w .Close (); err != nil {
316
+ _ = os .Remove (fileName )
317
+ fatal ("Failed to save %s: %v" , fileName , err )
318
+ }
173
319
174
- if err := os .Chmod (fileName , 0600 ); err != nil {
175
- log .Info ("Can't change file access permissions mask to 0600: %v" , err )
320
+ if err := os .Chmod (fileName , 0600 ); err != nil {
321
+ log .Info ("Can't change file access permissions mask to 0600: %v" , err )
322
+ }
176
323
}
177
324
178
- log . Info ( "Removing tmp work dir: %s" , tmpWorkDir )
179
-
180
- if err := os . RemoveAll ( tmpWorkDir ); err != nil {
181
- fatal ( "Failed to remove %s: %v" , tmpWorkDir , err )
325
+ if fileName != "-" {
326
+ log . Info ( "Finish dumping in file %s" , fileName )
327
+ } else {
328
+ log . Info ( "Finish dumping to stdout" )
182
329
}
183
- log .Info ("Finish dumping in file %s" , fileName )
184
330
185
331
return nil
186
332
}
187
333
188
- // zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
189
- func zipAddDirectoryExclude (zip * zip.ZipArchive , zipPath , absPath string , excludeAbsPath string ) error {
334
+ func contains (slice []string , s string ) bool {
335
+ for _ , v := range slice {
336
+ if v == s {
337
+ return true
338
+ }
339
+ }
340
+ return false
341
+ }
342
+
343
+ // addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
344
+ func addRecursiveExclude (w archiver.Writer , insidePath , absPath string , excludeAbsPath []string , verbose bool ) error {
190
345
absPath , err := filepath .Abs (absPath )
191
346
if err != nil {
192
347
return err
@@ -197,24 +352,24 @@ func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, exclud
197
352
}
198
353
defer dir .Close ()
199
354
200
- zip .AddEmptyDir (zipPath )
201
-
202
355
files , err := dir .Readdir (0 )
203
356
if err != nil {
204
357
return err
205
358
}
206
359
for _ , file := range files {
207
360
currentAbsPath := path .Join (absPath , file .Name ())
208
- currentZipPath := path .Join (zipPath , file .Name ())
361
+ currentInsidePath := path .Join (insidePath , file .Name ())
209
362
if file .IsDir () {
210
- if currentAbsPath != excludeAbsPath {
211
- if err = zipAddDirectoryExclude (zip , currentZipPath , currentAbsPath , excludeAbsPath ); err != nil {
363
+ if ! contains (excludeAbsPath , currentAbsPath ) {
364
+ if err := addFile (w , currentInsidePath , currentAbsPath , false ); err != nil {
365
+ return err
366
+ }
367
+ if err = addRecursiveExclude (w , currentInsidePath , currentAbsPath , excludeAbsPath , verbose ); err != nil {
212
368
return err
213
369
}
214
370
}
215
-
216
371
} else {
217
- if err = zip . AddFile ( currentZipPath , currentAbsPath ); err != nil {
372
+ if err = addFile ( w , currentInsidePath , currentAbsPath , verbose ); err != nil {
218
373
return err
219
374
}
220
375
}
0 commit comments