💾 Archived View for gmi.noulin.net › gitRepositories › git-off › file › src › gitoff.coffee.gmi captured on 2023-07-10 at 16:00:37. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
gitoff.coffee (54141B)
1 #! /usr/bin/env coffee 2 3 # TODO 4 # use the path function 5 # handle wrong config like getLog 6 # check parameters from CLI 7 # 8 # parse params 9 # use logger 10 # 11 # How to add a new config 12 # add variable in runtimeConfig 13 # add default settings in offDEFAULTS 14 # add a helper in offHelpers 15 # add setting of default in offCommands.install 16 # add a command for setting value in offCommands 17 # print value in offCommands.env 18 # add command in CLI parser 19 # update help 20 # 21 # How to add a new transport 22 # add transport functions in offHelpers.setTransport 23 # add transport in offHelpers.mkdirStore and offHelpers.rmAllStore 24 # add transport in offCommands.clearAll 25 # update git off mode help 26 # if needed, add store creation in offCommands.install 27 # 28 # How to add a new command 29 # add command in offCommands 30 # add command in CLI parser 31 # update help 32 33 ### 34 # CODE 35 # 36 # modules 37 # defaults 38 # helpers 39 # core 40 # main 41 # 42 # 43 # modules 44 # all dependencies 45 # 46 # defaults 47 # objInfo fields: for indexing cat-diff response in push command 48 # externalHelpers: shell commands to be used with the exec function 49 # runtimeConfig: config built by offHelpers 50 # offDEFAULTS: default configuration for first time install 51 # 52 # helpers 53 # expandHome: expands ~/ 54 # gitConfig: handles global git config 55 # offLog: appends log to git config off.log 56 # offLogRepo: log for commands run in git repo 57 # exec: runs an externalHelpers with parameters 58 # mkdirParents: recursive mkdir 59 # rmAll: delete recursively files and directories 60 # copy: copies files 61 # offHelpers: git-off helpers 62 # 63 # core 64 # transport: transport functions for git off store 65 # offCommands: command line functions 66 # 67 # main 68 # parse CLI arguments 69 ### 70 71 72 ### 73 # modules 74 ### 75 76 require('colors') 77 fs = require('fs') 78 path = require('path') 79 fssync = require('fs-sync') 80 syncexec = require('sync-exec') 81 mkdirp = require('mkdirp') 82 rimraf = require('rimraf') 83 readline = require('readline') 84 StringDecoder = require('string_decoder').StringDecoder 85 86 ### 87 # defaults 88 ### 89 90 # objInfo fields for indexing cat-diff response in push command 91 oiPREVIOUSPERMISSIONS = 0 92 oiPERMISSIONS = 1 93 oiPREVIOUSOID = 2 94 oiOID = 3 95 oiNAME = 4 96 97 # shell commands to be used with the exec function 98 # example: exec 'gitRepoRoot' 99 externalHelpers = 100 'gitConfigGlobal': 'git config --global' 101 'gitConfig': 'git config' 102 'gitRepoRoot': 'git rev-parse --show-toplevel' 103 'gitList': 'git rev-list' 104 'gitListEmptyRemote': 'git rev-list --max-parents=0' 105 'gitDiff': 'git show --raw --format="" --no-abbrev' 106 'gitCat': 'git cat-file -p' 107 'sha': 'git hash-object --no-filters' 108 'listAttr': 'git check-attr -a' 109 'ssh': 'ssh' 110 'scp': 'scp' 111 'curl': 'curl' 112 'mv': 'mv' 113 'rsync': 'rsync' 114 115 # config built by offHelpers 116 # use offHelpers to access runtimeConfig 117 runtimeConfig = 118 'currentRepoRoot': '' 119 'objectPath': '' 120 'offStore': '' 121 'offHttp': '' 122 'offMode': '' 123 'offIntegrity': '' 124 'offScp': '' 125 'offScpUser': '' 126 'offPem': '' 127 'offSshOptions': '' 128 'offScpOptions': '' 129 'offRsyncOptions': '' 130 'offCurlOptions': '' 131 'log': '' 132 'offConfigAlways': '' 133 's3Region': '' 134 's3Bucket': '' 135 'transform': '' 136 'transformTo': '' 137 'transformFrom': '' 138 139 # default configuration for first time install 140 # objectPath is git off cache in repo 141 offDEFAULTS = 142 'objectPath': '/.git/off/objects' 143 'mode': 'copy' 144 'integrity': 'disable' 145 'pem': 'offNoValue' 146 'scpHost': 'offNoValue' 147 'scpUser': '' 148 'sshOptions': '-C -o StrictHostKeyChecking=no -o ConnectTimeout=3' 149 'scpOptions': '-C -o StrictHostKeyChecking=no -o ConnectTimeout=3 -p' 150 'rsyncOptions': '-az -e \'ssh -i _i\'' 151 'store': '~/.git-off/offStore' 152 'http': 'offNoValue' 153 'curlOptions': '-o' 154 'log': '~/.git-off/log' 155 'configAlways': 'offNoValue' 156 's3Region': 'offNoValue' 157 's3Bucket': 'offNoValue' 158 'transform': 'disable' 159 'transformTo': 'pbzip2 -9 -c _1 > _2' 160 'transformFrom': 'pbzip2 -d -c _1 > _2' 161 'prePush': '#!/bin/sh\ncommand -v git-off >/dev/null 2>&1 || { echo >&2 "\\nThis repository is configured for Git off but \'git-off\' was not found on your path. If you no longer wish to use git off, remove this hook by deleting .git/hooks/pre-push.\\n"; exit 2; }\ngit off pre-push "$@"' 162 'offSignature': '### git-off v1 sha:' 163 'shaLength': 40 164 165 ### 166 # helpers 167 ### 168 169 # expands ~/ 170 expandHome = (p) -> 171 if p.slice(0,2) == '~/' 172 p = process.env.HOME + '/' + p.slice(2) 173 p 174 175 # handles global git config 176 gitConfig = 177 'set': (key, value)-> 178 exec 'gitConfig', ['--global', key, '"' + value + '"'] 179 return 180 181 'setLocal': (key, value) -> 182 exec('gitConfig', [key, '"' + value + '"']) 183 return 184 185 'setThisRepo': (key, value) -> 186 exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key, '"' + value + '"']) 187 return 188 189 'get': (key) -> 190 # use configAlways setting or 191 # return value from 192 # GIT_OFF_CONFIG if found 193 # repo config if found 194 # global config 195 if offHelpers.offConfigAlways() == 'GIT_OFF_CONFIG' 196 r = '' 197 if process.env.GIT_OFF_CONFIG != undefined and fs.existsSync(process.env.GIT_OFF_CONFIG) == true 198 r = exec('gitConfig', ['--file ' + process.env.GIT_OFF_CONFIG, key]).stdout.trim() 199 return r 200 if offHelpers.offConfigAlways() == 'repo' 201 r = '' 202 if fs.existsSync(offHelpers.gitRepoRoot(true) + '/.git-off') == true 203 r = exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key]).stdout.trim() 204 return r 205 if offHelpers.offConfigAlways() == 'global' 206 return exec('gitConfig', [key]).stdout.trim() 207 if process.env.GIT_OFF_CONFIG != undefined and fs.existsSync(process.env.GIT_OFF_CONFIG) == true 208 r = exec('gitConfig', ['--file ' + process.env.GIT_OFF_CONFIG, key]).stdout.trim() 209 if r != '' 210 return r 211 if fs.existsSync(offHelpers.gitRepoRoot(true) + '/.git-off') == true 212 r = exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key]).stdout.trim() 213 if r != '' 214 return r 215 exec('gitConfig', [key]).stdout.trim() 216 217 218 'getSyncexec': (key) -> 219 # always global git config 220 syncexec externalHelpers.gitConfig + ' ' + key 221 222 # recursive mkdir 223 mkdirParents = (p) -> 224 p = expandHome p 225 mkdirp.sync(p, (err) -> 226 console.error err 227 return) 228 return 229 230 # delete recursively files and directories 231 rmAll = (p) -> 232 p = expandHome p 233 rimraf.sync p 234 return 235 236 # appends log to git config off.log 237 offLog = (s) -> 238 if runtimeConfig.log == '' 239 # set runtimeConfig.log and get config in git 240 # use syncexec to avoid circular calls 241 r = gitConfig.getSyncexec 'off.log' 242 runtimeConfig.log = expandHome r.stdout.trim() 243 # error handling 244 if runtimeConfig.log == '' 245 console.error 'Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold 246 process.exit(1) 247 # append log (s) 248 if fs.existsSync(path.dirname(runtimeConfig.log)) == false 249 mkdirParents path.dirname(runtimeConfig.log) 250 fs.appendFileSync(runtimeConfig.log, s + '\n') 251 return 252 253 # log for commands run in git repo 254 # adds current repo path to string s in log 255 offLogRepo = (s) -> 256 # depends on offHelpers 257 offLog 'REPO path: ' + offHelpers.gitRepoRoot() + ' ' + s 258 return 259 260 # runs an externalHelpers with parameters 261 # cmdName is string 262 # paramsArray is an array of strings 263 # errors are logged in off.log and in console 264 # returns r (r.stdout, r.stderr) 265 exec = (cmdName, paramsArray=[], ignoreStderr=false) -> 266 #console.log externalHelpers[cmdName] + ' ' + paramsArray.join(' ') 267 #offLog externalHelpers[cmdName] + ' ' + paramsArray.join(' ') 268 r = syncexec externalHelpers[cmdName] + ' ' + paramsArray.join(' ') 269 #console.log r.stdout 270 # offLog r 271 # offLog r.stdout 272 if r.stderr != '' and ignoreStderr == false 273 console.error r.stderr.red.bold 274 offLog r.stderr.red.bold 275 process.exit(1) 276 r 277 278 # copies files 279 # file modes are not kept 280 copy = (src, dst) -> 281 fssync.copy(src, dst) 282 return 283 284 # List all files in a directory recursively in a synchronous fashion 285 walkSync = (dir, filelist = []) -> 286 files = fs.readdirSync(dir) 287 files.forEach (file) -> 288 if fs.statSync(dir + '/' + file).isDirectory() 289 filelist = walkSync(dir + '/' + file, filelist) 290 else 291 filelist.push dir+ '/' + file 292 return 293 filelist 294 295 # git-off helpers 296 # gitRepoRoot: sets and returns runtimeConfig.currentRepoRoot 297 # objectPath: sets and returns runtimeConfig.objectPath 298 # offStore: sets and returns runtimeConfig.offStore 299 # offMode: sets and returns runtimeConfig.offMode 300 # offIntegrity sets and returns runtimeConfig.offIntegrity 301 # offScp: sets and returns runtimeConfig.offScp 302 # offScpUser: sets and returns runtimeConfig.offScpUser 303 # log: sets and returns runtimeConfig.log 304 # getLog: sets and returns runtimeConfig.log with error checking 305 # userAt: returns user@ or '' 306 # getSSHConfig extracts host, port and path from off.sshhost 307 # mkdirStore mkdir in remote store 308 # rmAllStore rm in remote store 309 # checkIntegrity check integrity of files coming from the store 310 # setTransport: set send and receive functions for transport 311 # getOffFilePath: creates path for offFile 312 offHelpers = 313 'gitRepoRoot': (ignoreStderr=false) -> 314 if runtimeConfig.currentRepoRoot == '' 315 r = exec 'gitRepoRoot', [], ignoreStderr 316 runtimeConfig.currentRepoRoot = r.stdout.trim() 317 runtimeConfig.currentRepoRoot 318 319 'objectPath': -> 320 if runtimeConfig.objectPath == '' 321 runtimeConfig.objectPath = this.gitRepoRoot() + offDEFAULTS.objectPath 322 runtimeConfig.objectPath 323 324 'offStore': -> 325 if runtimeConfig.offStore == '' 326 r = gitConfig.get 'off.store' 327 runtimeConfig.offStore = expandHome r 328 runtimeConfig.offStore 329 330 'offHttp': -> 331 if runtimeConfig.offHttp == '' 332 runtimeConfig.offHttp = gitConfig.get 'off.http' 333 runtimeConfig.offHttp 334 335 'offCurlOptions': -> 336 if runtimeConfig.offCurlOptions == '' 337 runtimeConfig.offCurlOptions = gitConfig.get 'off.curloptions' 338 runtimeConfig.offCurlOptions 339 340 'offMode': -> 341 if runtimeConfig.offMode == '' 342 runtimeConfig.offMode = gitConfig.get 'off.mode' 343 runtimeConfig.offMode 344 345 'offIntegrity': -> 346 if runtimeConfig.offIntegrity == '' 347 runtimeConfig.offIntegrity = gitConfig.get 'off.integrity' 348 runtimeConfig.offIntegrity 349 350 'offPem': -> 351 if runtimeConfig.offPem == '' 352 r = gitConfig.get 'off.pem' 353 runtimeConfig.offPem = expandHome r 354 runtimeConfig.offPem 355 356 'offSshOptions': -> 357 if runtimeConfig.offSshOptions == '' 358 runtimeConfig.offSshOptions = gitConfig.get 'off.sshoptions' 359 runtimeConfig.offSshOptions 360 361 'offScpOptions': -> 362 if runtimeConfig.offScpOptions == '' 363 runtimeConfig.offScpOptions = gitConfig.get 'off.scpoptions' 364 runtimeConfig.offScpOptions 365 366 'offRsyncOptions': -> 367 if runtimeConfig.offRsyncOptions == '' 368 runtimeConfig.offRsyncOptions = gitConfig.get 'off.rsyncoptions' 369 runtimeConfig.offRsyncOptions 370 371 'offScp': -> 372 if runtimeConfig.offScp == '' 373 runtimeConfig.offScp = gitConfig.get 'off.scphost' 374 runtimeConfig.offScp 375 376 'offScpUser': -> 377 if runtimeConfig.offScpUser == '' 378 runtimeConfig.offScpUser = gitConfig.get 'off.scpuser' 379 runtimeConfig.offScpUser 380 381 'log': -> 382 if runtimeConfig.log == '' 383 # use syncexec to avoid circular calls 384 r = gitConfig.getSyncexec 'off.log' 385 runtimeConfig.log = expandHome r.stdout.trim() 386 runtimeConfig.log 387 388 'getLog': -> 389 offHelpers.log() 390 # error handling 391 if runtimeConfig.log == '' 392 console.error 'Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold 393 process.exit(1) 394 runtimeConfig.log 395 396 'offConfigAlways': -> 397 if runtimeConfig.offConfigAlways == '' 398 r = gitConfig.getSyncexec 'off.configAlways' 399 runtimeConfig.offConfigAlways = r.stdout.trim() 400 runtimeConfig.offConfigAlways 401 402 's3Region': -> 403 if runtimeConfig.s3Region == '' 404 runtimeConfig.s3Region = gitConfig.get 'off.s3region' 405 runtimeConfig.s3Region 406 407 's3Bucket': -> 408 if runtimeConfig.s3Bucket == '' 409 runtimeConfig.s3Bucket = gitConfig.get 'off.s3bucket' 410 runtimeConfig.s3Bucket 411 412 'transform': -> 413 if runtimeConfig.transform == '' 414 runtimeConfig.transform = gitConfig.get 'off.transform' 415 runtimeConfig.transform 416 417 'transformTo': -> 418 if runtimeConfig.transformTo == '' 419 runtimeConfig.transformTo = gitConfig.get 'off.transformTo' 420 runtimeConfig.transformTo 421 422 'transformFrom': -> 423 if runtimeConfig.transformFrom == '' 424 runtimeConfig.transformFrom = gitConfig.get 'off.transformFrom' 425 runtimeConfig.transformFrom 426 427 'userAt': -> 428 if offHelpers.offScpUser() != '' 429 user = offHelpers.offScpUser() + '@' 430 else 431 user = '' 432 user 433 434 'getSSHConfig': -> 435 # extract host, port and path from off.sshhost 436 437 user = offHelpers.userAt() 438 439 h_l = offHelpers.offScp().split(':') 440 if h_l[1] == undefined 441 port = NaN 442 else 443 portAndPath_l = h_l[1].split('/') 444 port = parseInt portAndPath_l[0] 445 if isNaN port 446 # use default port 447 host = h_l[0] 448 storePath = h_l[1] 449 else 450 host = h_l[0] 451 storePath = '/' + portAndPath_l.slice(1).join('/') 452 [user + host, storePath, port] 453 454 455 'mkdirStore': (p) -> 456 # mkdir in remote store 457 458 # TODO handle multiple transports 459 h_l = offHelpers.getSSHConfig() 460 # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"' 461 sshCmd = '"mkdir -p '+ h_l[1] + '/' + p 462 sshCmd += '"' 463 464 # setup ssh/scp private key 465 if offHelpers.offPem() == '' or offHelpers.offPem() == 'offNoValue' 466 pem = '' 467 else 468 pem = '-i ' + offHelpers.offPem() 469 470 # ignore error from mkdir for already existing store 471 if isNaN h_l[2] 472 exec 'ssh', [offHelpers.offSshOptions(), pem, h_l[0], sshCmd], true 473 else 474 exec 'ssh', [offHelpers.offSshOptions(), pem, '-p ' + h_l[2], h_l[0], sshCmd], true 475 return 476 477 'rmAllStore': (p) -> 478 # rm in remote store 479 480 # setup ssh/scp private key 481 if offHelpers.offPem() == '' or offHelpers.offPem() == 'offNoValue' 482 pem = '' 483 else 484 pem = '-i ' + offHelpers.offPem() 485 486 # scphost format is host:path 487 h_l = offHelpers.getSSHConfig() 488 # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"' 489 sshCmd = '"rm -rf '+ h_l[1] + '/' + p 490 sshCmd += '"' 491 # ignore error from rm 492 if isNaN h_l[2] 493 exec 'ssh', [offHelpers.offSshOptions(), pem, h_l[0], sshCmd], true 494 else 495 exec 'ssh', [offHelpers.offSshOptions(), pem, '-p ' + h_l[2], h_l[0], sshCmd], true 496 return 497 498 'copyTo': -> 499 # list file in cache 500 # copy to store 501 502 # list file in cache 503 504 tfiles = walkSync(offHelpers.objectPath()) 505 files = [] 506 for f in tfiles 507 files.push f.replace(offHelpers.objectPath() + '/' , '') 508 509 # copy to store 510 511 for f in files 512 transport.send f 513 return 514 515 'checkIntegrity': (p) -> 516 # check integrity of files coming from the store 517 518 result = true 519 520 if offHelpers.offIntegrity() == 'disable' 521 return true 522 523 r = exec 'sha', [p] 524 # trim because git hash-object adds \n 525 receivedSha = r.stdout.split(' ')[0].trim() 526 527 if path.basename(p) != receivedSha 528 # the file received is different from the one that was sent. 529 console.log 'git-off: The file ' + p + ' differs from the one that was pushed.' 530 result = false 531 532 result 533 534 'setTransport': (mode = 'config') -> 535 # set send and receive functions for transport 536 # copy, scp 537 538 # use mode from config or from parameter 539 if mode == 'config' 540 mode = offHelpers.offMode() 541 542 # copy mode 543 if mode == 'copy' 544 transport['send'] = (file) -> 545 # create file directories in store 546 f_l = file.split '/' 547 if fs.existsSync(offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1]) == false 548 mkdirParents offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1] 549 550 copy offHelpers.objectPath() + '/' + file, offHelpers.offStore() + '/' + file 551 fs.chmodSync offHelpers.offStore() + '/' + file, '444' 552 return 553 transport['receive'] = (file) -> 554 # transfer and transform 555 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 556 # create file directories in objectPath 557 f_l = file.split '/' 558 if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false 559 mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] 560 561 copy offHelpers.offStore() + '/' + file, offHelpers.objectPath() + '/' + file 562 transport['transformFrom'](file) 563 if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file 564 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file) 565 readStream.pipe(process.stdout) 566 else 567 if offHelpers.checkIntegrity offHelpers.offStore() + '/' + file 568 readStream = fs.createReadStream(offHelpers.offStore() + '/' + file) 569 readStream.pipe(process.stdout) 570 return 571 572 # rsync mode 573 else if mode == 'rsync' 574 transport['send'] = (file) -> 575 576 # create file directories in store 577 f_l = file.split '/' 578 offHelpers.mkdirStore f_l[0] + '/' + f_l[1] 579 580 # set pem or not? check rsyncoptions 581 options = offHelpers.offRsyncOptions() 582 if options.indexOf('_i') != -1 583 # there is an identity setting in options 584 # setup ssh/scp private key 585 if offHelpers.offPem() != '' and offHelpers.offPem() != 'offNoValue' 586 options = options.replace '_i', offHelpers.offPem() 587 588 exec 'rsync', [options, offHelpers.objectPath() + '/' + file, offHelpers.offScp() + '/' + file] 589 return 590 transport['receive'] = (file) -> 591 # create file directories in cache 592 f_l = file.split '/' 593 if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false 594 mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] 595 596 # set pem or not? check rsyncoptions 597 options = offHelpers.offRsyncOptions() 598 if options.indexOf('_i') != -1 599 # there is an identity setting in options 600 # setup ssh/scp private key 601 if offHelpers.offPem() != '' and offHelpers.offPem() != 'offNoValue' 602 options = options.replace '_i', offHelpers.offPem() 603 604 exec 'rsync', [options, offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file] 605 606 # transform 607 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 608 transport['transformFrom'](file) 609 if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file 610 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file) 611 readStream.pipe(process.stdout) 612 else 613 if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file 614 readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) 615 readStream.pipe(process.stdout) 616 return 617 618 # scp mode 619 else if mode == 'scp' 620 transport['send'] = (file) -> 621 622 # create file directories in store 623 f_l = file.split '/' 624 offHelpers.mkdirStore f_l[0] + '/' + f_l[1] 625 626 # setup ssh/scp private key 627 if offHelpers.offPem() == '' or offHelpers.offPem() == 'offNoValue' 628 pem = '' 629 else 630 pem = '-i ' + offHelpers.offPem() 631 632 h_l = offHelpers.getSSHConfig() 633 if isNaN h_l[2] 634 exec 'scp', [offHelpers.offScpOptions(), pem, offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file] 635 else 636 exec 'scp', [offHelpers.offScpOptions(), pem, '-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file] 637 return 638 transport['receive'] = (file) -> 639 # create file directories in cache 640 f_l = file.split '/' 641 if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false 642 mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] 643 644 # setup ssh/scp private key 645 if offHelpers.offPem() == '' or offHelpers.offPem() == 'offNoValue' 646 pem = '' 647 else 648 pem = '-i ' + offHelpers.offPem() 649 650 h_l = offHelpers.getSSHConfig() 651 if isNaN h_l[2] 652 exec 'scp', [offHelpers.offScpOptions(), pem, h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file] 653 else 654 exec 'scp', [offHelpers.offScpOptions(), pem, '-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file] 655 656 # transform 657 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 658 transport['transformFrom'](file) 659 if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file 660 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file) 661 readStream.pipe(process.stdout) 662 else 663 if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file 664 readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) 665 readStream.pipe(process.stdout) 666 return 667 668 # http mode 669 else if mode == 'http' 670 transport['send'] = (file) -> 671 offLogRepo('Http mode is read-only: ' + file + ' is stored in cache only') 672 return 673 transport['receive'] = (file) -> 674 # create file directories in cache 675 f_l = file.split '/' 676 if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false 677 mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] 678 679 exec 'curl', [offHelpers.offCurlOptions(), offHelpers.objectPath() + '/' + file, offHelpers.offHttp() + '/' + file] 680 681 # transform 682 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 683 transport['transformFrom'](file) 684 if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file 685 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file) 686 readStream.pipe(process.stdout) 687 else 688 if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file 689 readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) 690 readStream.pipe(process.stdout) 691 return 692 693 # s3 mode 694 else if mode == 's3' 695 transport['send'] = (file) -> 696 AWS = require 'aws-sdk' 697 s3 = new AWS.S3({region: offHelpers.s3Region(), signatureVersion: 'v4'}) 698 699 # upload to s3 700 upParams = {Bucket: offHelpers.s3Bucket(), Key: file, Body: ''} 701 fileStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) 702 upParams.Body = fileStream 703 s3.upload(upParams, (err, data) -> 704 # TODO log eventual error 705 return 706 ) 707 708 return 709 transport['receive'] = (file) -> 710 AWS = require 'aws-sdk' 711 s3 = new AWS.S3({region: offHelpers.s3Region(), signatureVersion: 'v4'}) 712 713 # create file directories in cache 714 f_l = file.split '/' 715 if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false 716 mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] 717 718 # download from s3 719 dlParams = {Bucket: offHelpers.s3Bucket(), Key: file} 720 fileStream = fs.createWriteStream(offHelpers.objectPath() + '/' + file) 721 fileStream.on('finish', -> 722 # transfer is finished, the complete file is in the cache 723 # transform 724 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 725 transport['transformFrom'](file) 726 if offHelpers.checkIntegrity offHelpers.objectPath() + '/../tmp/' + file 727 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file) 728 readStream.pipe(process.stdout) 729 else 730 if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file 731 readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) 732 readStream.pipe(process.stdout) 733 return 734 ) 735 s3.getObject(dlParams, (err, data) -> 736 # TODO log eventual error 737 return 738 ).on('httpData', (chunk) -> 739 fileStream.write chunk 740 return 741 ).on('httpDone', -> 742 fileStream.end() 743 return 744 ) 745 return 746 return 747 748 'getOffFilePath': (offFile) -> 749 [offFile.slice(0,2) + '/' + offFile.slice(2,4) + '/' + offFile, offFile.slice(0,2) + '/' + offFile.slice(2,4)] 750 751 # list offHelpers, run 'git off' 752 #console.log Object.keys(offHelpers) 753 754 ### 755 # core 756 ### 757 758 transport = 759 'send': (src) -> 760 # to be initialized by setTransport 761 return 762 'receive': (src) -> 763 # send data to stdout 764 # to be initialized by setTransport 765 return 766 'transformFrom': (file) -> 767 # transform file in objectPath to objectPath/../tmp/file 768 # create directories in tmp 769 offFile = path.basename file 770 offFilePath = offHelpers.getOffFilePath(offFile) 771 if fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) == false 772 # create the file directory 773 mkdirParents offHelpers.objectPath() + '/../tmp/' + offFilePath[1] 774 775 cmd = offHelpers.transformFrom() 776 cmd = cmd.replace('_1', offHelpers.objectPath() + '/' + file).replace('_2', offHelpers.objectPath() + '/../tmp/' + file) 777 r = syncexec cmd 778 return 779 780 781 # command line functions 782 # localSetup: setup current git 783 # install: setup git config (default global) 784 # mode: set/show git off mode 785 # scp: setup scp config 786 # scpuser: setup scp username config 787 # track: setup gitattribute filters 788 # clean: replace files handled by git off with reference 789 # prepush: read stdin and calls push 790 # push: copy objects to off.store 791 # smudge: copy objects from off.store for files handled by git off 792 # clearAll: delete store, cache in current git and log 793 # clearCache: delete cache in current git 794 # clearStore: delete store 795 # defaults: show first time config (offDEFAULTS) 796 # env: show config 797 offCommands = 798 'localSetup': -> 799 # setup current git 800 # create off directories in current .git 801 # install pre-push hooks 802 mkdirParents offHelpers.objectPath() 803 804 hook = offHelpers.gitRepoRoot() + '/.git/hooks/pre-push' 805 if fs.existsSync(hook) == false 806 fs.writeFileSync hook, offDEFAULTS.prePush 807 fs.chmodSync hook, '755' 808 # error disabled because localSetup is run many times (called from clean and smudge) 809 # else 810 # console.error hook.red.bold + ' already exists. Skipping pre-push hook setup.'.red.bold 811 # process.exit(1) 812 return 813 814 'install': (setCfg = gitConfig.set) -> 815 # setup git config 816 # create off.store 817 818 offCommands.localSetup() 819 820 # setup config only if not already set 821 if gitConfig.get('filter.off.clean') == '' or setCfg != gitConfig.set 822 if setCfg == gitConfig.setThisRepo 823 gitConfig.setLocal('filter.off.clean','git off clean %f') 824 else 825 setCfg('filter.off.clean','git off clean %f') 826 if gitConfig.get('filter.off.smudge') == '' or setCfg != gitConfig.set 827 if setCfg == gitConfig.setThisRepo 828 gitConfig.setLocal('filter.off.smudge','git off smudge %f') 829 else 830 setCfg('filter.off.smudge','git off smudge %f') 831 if offHelpers.log() == '' or setCfg != gitConfig.set 832 # log always in global config 833 gitConfig.set('off.log',offDEFAULTS.log) 834 835 if offHelpers.offMode() == '' or setCfg != gitConfig.set 836 setCfg('off.mode', offDEFAULTS.mode) 837 if offHelpers.offIntegrity() == '' or setCfg != gitConfig.set 838 setCfg('off.integrity', offDEFAULTS.integrity) 839 if offHelpers.offPem() == '' or setCfg != gitConfig.set 840 setCfg('off.pem', offDEFAULTS.pem) 841 if offHelpers.offScp() == '' or setCfg != gitConfig.set 842 setCfg('off.scphost', offDEFAULTS.scpHost) 843 if offHelpers.offScpUser() == '' or setCfg != gitConfig.set 844 setCfg('off.scpuser', offDEFAULTS.scpUser) 845 if offHelpers.offSshOptions() == '' or setCfg != gitConfig.set 846 setCfg('off.sshoptions', offDEFAULTS.sshOptions) 847 if offHelpers.offScpOptions() == '' or setCfg != gitConfig.set 848 setCfg('off.scpoptions', offDEFAULTS.scpOptions) 849 if offHelpers.offRsyncOptions() == '' or setCfg != gitConfig.set 850 setCfg('off.rsyncoptions', offDEFAULTS.rsyncOptions) 851 if offHelpers.offStore() == '' or setCfg != gitConfig.set 852 setCfg('off.store', offDEFAULTS.store) 853 if offHelpers.offHttp() == '' or setCfg != gitConfig.set 854 setCfg('off.http', offDEFAULTS.http) 855 if offHelpers.offCurlOptions() == '' or setCfg != gitConfig.set 856 setCfg('off.curloptions', offDEFAULTS.curlOptions) 857 if offHelpers.offConfigAlways() == '' or setCfg != gitConfig.set 858 setCfg('off.configAlways', offDEFAULTS.configAlways) 859 if offHelpers.s3Region() == '' or setCfg != gitConfig.set 860 setCfg('off.s3Region', offDEFAULTS.s3Region) 861 if offHelpers.s3Bucket() == '' or setCfg != gitConfig.set 862 setCfg('off.s3Bucket', offDEFAULTS.s3Bucket) 863 if offHelpers.transform() == '' or setCfg != gitConfig.set 864 setCfg('off.transform', offDEFAULTS.transform) 865 if offHelpers.transformTo() == '' or setCfg != gitConfig.set 866 setCfg('off.transformTo', offDEFAULTS.transformTo) 867 if offHelpers.transformFrom() == '' or setCfg != gitConfig.set 868 setCfg('off.transformFrom', offDEFAULTS.transformFrom) 869 870 # create off.store 871 872 if runtimeConfig.offMode == 'copy' 873 mkdirParents runtimeConfig.offStore 874 875 if runtimeConfig.offMode == 'scp' or runtimeConfig.offMode == 'rsync' 876 offHelpers.mkdirStore '' 877 return 878 879 'mode': (setCfg) -> 880 len = process.argv.length 881 mode = process.argv[len-1] 882 if mode != 'mode' and mode != 'thisrepo' 883 setCfg('off.mode', mode) 884 else 885 console.log 'off.mode '.blue.bold + offHelpers.offMode() 886 return 887 888 'store': (setCfg) -> 889 len = process.argv.length 890 store = process.argv[len-1] 891 if store != 'store' and store != 'thisrepo' 892 setCfg('off.store', store) 893 else 894 console.log 'off.store '.blue.bold + offHelpers.offStore() 895 return 896 897 'scp': (setCfg) -> 898 len = process.argv.length 899 scpHost = process.argv[len-1] 900 if scpHost != 'scp' and scpHost != 'thisrepo' 901 setCfg('off.scphost', scpHost) 902 else 903 console.log 'off.scp '.blue.bold + offHelpers.offScp() 904 return 905 906 'http': (setCfg) -> 907 len = process.argv.length 908 http = process.argv[len-1] 909 if http != 'http' and http != 'thisrepo' 910 setCfg('off.http', http) 911 else 912 console.log 'off.http '.blue.bold + offHelpers.offHttp() 913 return 914 915 'curl': (setCfg) -> 916 len = process.argv.length 917 curl = process.argv[len-1] 918 if curl != 'curl' and curl != 'thisrepo' 919 setCfg('off.curloptions', curl) 920 else 921 console.log 'off.curloptions '.blue.bold + offHelpers.offCurlOptions() 922 return 923 924 'integrity': (setCfg) -> 925 len = process.argv.length 926 integrity = process.argv[len-1] 927 if integrity != 'integrity' and integrity != 'thisrepo' 928 setCfg('off.integrity', integrity) 929 else 930 console.log 'off.integrity '.blue.bold + offHelpers.offIntegrity() 931 return 932 933 'pem': (setCfg) -> 934 len = process.argv.length 935 pem = process.argv[len-1] 936 if pem != 'pem' and pem != 'thisrepo' 937 setCfg('off.pem', pem) 938 else 939 console.log 'off.pem '.blue.bold + offHelpers.offPem() 940 return 941 942 'sshoptions': (setCfg) -> 943 len = process.argv.length 944 sshoptions = process.argv[len-1] 945 if sshoptions != 'sshoptions' and sshoptions != 'thisrepo' 946 setCfg('off.sshoptions', sshoptions) 947 else 948 console.log 'off.sshoptions '.blue.bold + offHelpers.offSshOptions() 949 return 950 951 'scpoptions': (setCfg) -> 952 len = process.argv.length 953 scpoptions = process.argv[len-1] 954 if scpoptions != 'scpoptions' and scpoptions != 'thisrepo' 955 setCfg('off.scpoptions', scpoptions) 956 else 957 console.log 'off.scpoptions '.blue.bold + offHelpers.offScpOptions() 958 return 959 960 'rsyncoptions': (setCfg) -> 961 len = process.argv.length 962 rsyncoptions = process.argv[len-1] 963 if rsyncoptions != 'rsyncoptions' and rsyncoptions != 'thisrepo' 964 setCfg('off.rsyncoptions', rsyncoptions) 965 else 966 console.log 'off.rsyncoptions '.blue.bold + offHelpers.offRsyncOptions() 967 return 968 969 'scpUser': (setCfg) -> 970 len = process.argv.length 971 scpUser = process.argv[len-1] 972 if scpUser != 'scpuser' and scpUser != 'thisrepo' 973 setCfg('off.scpuser', scpUser) 974 else 975 console.log 'off.scpuser '.blue.bold + offHelpers.offScpUser() 976 return 977 978 'track': -> 979 # setup gitattribute filters in current folder 980 # list current git off attributes when there is no parameter 981 if process.argv[3] != undefined 982 offCommands.install() 983 fs.appendFileSync(offHelpers.gitRepoRoot() + '/.gitattributes', process.argv[3] + ' filter=off -text\n') 984 else 985 # list current git off attributes 986 987 r = exec 'listAttr', ['`cd ' + offHelpers.gitRepoRoot() + '; git ls-files`'] 988 # gff/b.bin: filter: off 989 for l in r.stdout.split('\n') 990 if l.indexOf(': filter: off') != -1 991 console.log l 992 return 993 994 'configAlways': -> 995 len = process.argv.length 996 configAlways = process.argv[len-1] 997 if configAlways != 'configAlways' 998 gitConfig.set 'off.configAlways', configAlways 999 else 1000 console.log 'off.configAlways '.blue.bold + offHelpers.offConfigAlways() 1001 return 1002 1003 's3region': (setCfg) -> 1004 len = process.argv.length 1005 region = process.argv[len-1] 1006 if region != 's3region' and region != 'thisrepo' 1007 setCfg('off.s3region', region) 1008 else 1009 console.log 'off.s3region '.blue.bold + offHelpers.s3Region() 1010 return 1011 1012 's3bucket': (setCfg) -> 1013 len = process.argv.length 1014 bucket = process.argv[len-1] 1015 if bucket != 's3bucket' and bucket != 'thisrepo' 1016 setCfg('off.s3bucket', bucket) 1017 else 1018 console.log 'off.s3bucket '.blue.bold + offHelpers.s3Bucket() 1019 return 1020 1021 'transform': (setCfg) -> 1022 len = process.argv.length 1023 setting = process.argv[len-1] 1024 if setting != 'transform' and setting != 'thisrepo' 1025 setCfg('off.transform', setting) 1026 else 1027 console.log 'off.transform '.blue.bold + offHelpers.transform() 1028 return 1029 1030 'transformTo': (setCfg) -> 1031 len = process.argv.length 1032 setting = process.argv[len-1] 1033 if setting != 'transformTo' and setting != 'thisrepo' 1034 setCfg('off.transformTo', setting) 1035 else 1036 console.log 'off.transformTo '.blue.bold + offHelpers.transformTo() 1037 return 1038 1039 'transformFrom': (setCfg) -> 1040 len = process.argv.length 1041 setting = process.argv[len-1] 1042 if setting != 'transformFrom' and setting != 'thisrepo' 1043 setCfg('off.transformFrom', setting) 1044 else 1045 console.log 'off.transformFrom '.blue.bold + offHelpers.transformFrom() 1046 return 1047 1048 'clean': -> 1049 # replace files handled by git off with reference 1050 # stdin is data from the working directory file 1051 # 1052 # create local setup in case the repo is freshly cloned 1053 # create file information (size, sha = git off filename) 1054 # copy stdin to git off cache in current git 1055 # transform input file 1056 # print git off ref to stdout for git 1057 1058 # create local setup in case the repo is freshly cloned 1059 offCommands.localSetup() 1060 1061 # create file information (size, sha) 1062 file = process.argv[3] 1063 size = fs.statSync(file).size 1064 quotedFile = '"'+file+'"' 1065 r = exec 'sha', [quotedFile] 1066 # trim because git hash-object adds \n 1067 offFile = r.stdout.split(' ')[0].trim() 1068 offFilePath = offHelpers.getOffFilePath(offFile) 1069 if fs.existsSync(offHelpers.objectPath() + '/' + offFilePath[1]) == false 1070 # create the file directory 1071 mkdirParents offHelpers.objectPath() + '/' + offFilePath[1] 1072 offFilePath = offFilePath[0] 1073 1074 # copy stdin to git off cache in current git 1075 # clean runs during git add and git commit, do work only once 1076 if fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) == false 1077 # stream stdin to sha in git off cache 1078 writeStream = fs.createWriteStream(offHelpers.objectPath() + '/' + offFilePath) 1079 pipe = process.stdin.pipe(writeStream) 1080 pipe.on('finish', -> 1081 # all objects in cache are read-only 1082 fs.chmodSync offHelpers.objectPath() + '/' + offFilePath, '444' 1083 1084 # transform input file 1085 if offHelpers.transform() == 'enable' and offHelpers.transformTo() != '' 1086 if fs.existsSync offHelpers.objectPath() + '/../tmp' == false 1087 mkdirParents offHelpers.objectPath() + '/../tmp' 1088 1089 # create directories in tmp 1090 offFilePath = offHelpers.getOffFilePath(offFile) 1091 if fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) == false 1092 # create the file directory 1093 mkdirParents offHelpers.objectPath() + '/../tmp/' + offFilePath[1] 1094 offFilePath = offFilePath[0] 1095 1096 # original file is moved to tmp 1097 # transformed file is stored in objectPath under the same name 1098 exec 'mv', [offHelpers.objectPath() + '/' + offFilePath, offHelpers.objectPath() + '/../tmp/' + offFilePath] 1099 cmd = offHelpers.transformTo() 1100 cmd = cmd.replace('_1', offHelpers.objectPath() + '/../tmp/' + offFilePath).replace('_2', offHelpers.objectPath() + '/' + offFilePath) 1101 r = syncexec cmd 1102 return 1103 ) 1104 else 1105 # file already exists, discard data 1106 writeStream = fs.createWriteStream('/dev/null') 1107 process.stdin.pipe(writeStream) 1108 1109 # print git off ref to stdout for git 1110 # ### git-off v1 sha:be3e02b60effe3eab232d5590a6a2e2c2c2f443b size:119 b.bin 1111 console.log offDEFAULTS.offSignature + offFile + ' size:' + size 1112 return 1113 1114 'prepush': -> 1115 # read stdin and calls push 1116 # stdin (data from git) is a line with remoteName url localRef localSha remoteRef remoteSha 1117 1118 offHelpers.setTransport() 1119 1120 rl = readline.createInterface( 1121 input: process.stdin 1122 output: process.stdout 1123 terminal: false) 1124 rl.on 'line', (line) -> 1125 offCommands.push(line) 1126 return 1127 return 1128 1129 'push': (line) -> 1130 # copy objects to off.store 1131 # 1132 # set remoteName url localRef localSha remoteRef remoteSha 1133 # list missing commits in remote 1134 # list changed objects in missing commits 1135 # check header with git cat 1136 # copy git off objects to off.store 1137 1138 len = process.argv.length 1139 1140 # set remoteName url localRef localSha remoteRef remoteSha 1141 remoteName = process.argv[len-2] 1142 url = process.argv[len-1] 1143 1144 line_l = line.split(' ') 1145 localRef = line_l[0] 1146 localSha = line_l[1] 1147 remoteRef = line_l[2] 1148 remoteSha = line_l[3] 1149 1150 # list missing commits in remote 1151 r = 0 1152 # handle empty cloned repo 1153 if remoteSha == '0000000000000000000000000000000000000000' 1154 r = exec 'gitListEmptyRemote', [localSha] 1155 else 1156 r = exec 'gitList', [localSha, '^' + remoteSha] 1157 commitListToPush = r.stdout.split('\n') 1158 # remove last empty line 1159 commitListToPush = commitListToPush.slice(0, commitListToPush.length-1) 1160 1161 1162 # list changed objects in missing commits 1163 for sha in commitListToPush 1164 # list changes for commit sha 1165 r = exec 'gitDiff', [sha] 1166 # :000000 100644 0000000000000000000000000000000000000000 23636079ef005f53e81066aaf092521d8f2346df A dsd 1167 # TODO filter according to .gitattributes, check files in filter only 1168 objInfoList = r.stdout.split('\n') 1169 # remove first line (commit) and last empty line 1170 objInfoList = objInfoList.slice(1, objInfoList.length-1) 1171 1172 for oi in objInfoList 1173 objInfo = oi.split(' ') 1174 1175 # dont consider deleted files 1176 if objInfo[oiNAME].slice(0,1) != 'D' 1177 1178 # check header with git cat 1179 1180 r = exec 'gitCat', [objInfo[oiOID]] 1181 offRef = r.stdout.split('\n')[0] 1182 1183 if offRef.indexOf(offDEFAULTS.offSignature) != -1 1184 1185 # copy git off objects to off.store 1186 # found an off reference 1187 # ### git-off v1 sha:be3e02b60effe3eab232d5590a6a2e2c2c2f443b size:119 b.bin 1188 1189 offFile = offRef.split(':')[1].split(' ')[0] 1190 offFilePath = offHelpers.getOffFilePath(offFile)[0] 1191 1192 transport.send offFilePath 1193 1194 # test prevent push by issuing an error 1195 #process.exit(1) 1196 return 1197 1198 'smudge': -> 1199 # copy objects from off.store for files handled by git off 1200 # stdin is the data from the git repo 1201 # 1202 # load header 1203 # for git off object, replace git off reference with data from cache or off.store 1204 # for normal object, copy data from repo to stdout 1205 1206 offHelpers.setTransport() 1207 offCommands.localSetup() 1208 #not used file = process.argv[3] 1209 1210 status = 'header' 1211 process.stdin.on('readable', -> 1212 if status == 'header' 1213 # change status before streaming to stdout 1214 status = 'stream' 1215 1216 # load header 1217 1218 data = process.stdin.read(offDEFAULTS.offSignature.length + offDEFAULTS.shaLength) 1219 decoder = new StringDecoder('utf8') 1220 if data == null 1221 # error no data 1222 offLogRepo 'smudge error: cant get data from stdin.' 1223 process.exit(1) 1224 header = decoder.write(data) 1225 1226 if header.slice(0,offDEFAULTS.offSignature.length) == offDEFAULTS.offSignature 1227 # for git off object, replace git off reference with data from cache or off.store 1228 1229 offFile = header.split(':')[1] 1230 offFilePath = offHelpers.getOffFilePath(offFile)[0] 1231 # detect if file is already in cache 1232 if fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) == true 1233 # copy from cache 1234 # transform file 1235 if offHelpers.transform() == 'enable' and offHelpers.transformFrom() != '' 1236 if fs.existsSync offHelpers.objectPath() + '/../tmp' == false 1237 mkdirParents offHelpers.objectPath() + '/../tmp' 1238 1239 # create directories in tmp 1240 offFilePath = offHelpers.getOffFilePath(offFile) 1241 if fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) == false 1242 # create the file directory 1243 mkdirParents offHelpers.objectPath() + '/../tmp/' + offFilePath[1] 1244 offFilePath = offFilePath[0] 1245 1246 cmd = offHelpers.transformFrom() 1247 cmd = cmd.replace('_1', offHelpers.objectPath() + '/' + offFilePath).replace('_2', offHelpers.objectPath() + '/../tmp/' + offFilePath) 1248 r = syncexec cmd 1249 readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + offFilePath) 1250 else 1251 readStream = fs.createReadStream(offHelpers.objectPath() + '/' + offFilePath) 1252 readStream.pipe(process.stdout) 1253 else 1254 # copy from off.store 1255 transport.receive offFilePath 1256 else 1257 # for normal object, copy data from repo to stdout 1258 1259 process.stdout.write(data) 1260 process.stdin.pipe(process.stdout) 1261 return) 1262 return 1263 1264 'copyTo': -> 1265 # copy cache to store for process.argv[3] mode 1266 if process.argv[3] == undefined 1267 console.log 'Choose a mode where to copy the cache'.red.bold 1268 return 1269 1270 offHelpers.setTransport(process.argv[3]) 1271 offHelpers.copyTo() 1272 return 1273 1274 'pushTo': -> 1275 if offHelpers.offMode() != undefined 1276 offHelpers.setTransport offHelpers.offMode() 1277 offHelpers.copyTo() 1278 return 1279 1280 'clearAll': -> 1281 # delete store, cache in current git and log 1282 offCommands.clearStore() 1283 offCommands.clearCache() 1284 rmAll offHelpers.getLog() 1285 return 1286 1287 'clearCache': -> 1288 # delete cache in current git 1289 rmAll offHelpers.gitRepoRoot()+'/.git/off' 1290 return 1291 1292 'clearStore': -> 1293 # delete store 1294 if offHelpers.offMode() == 'copy' 1295 rmAll offHelpers.offStore() 1296 if offHelpers.offMode() == 'scp' or offHelpers.offMode() == 'rsync' 1297 offHelpers.rmAllStore '' 1298 return 1299 1300 'clearTmp': -> 1301 # delete cache in current git 1302 rmAll offHelpers.gitRepoRoot()+'/.git/off/tmp' 1303 return 1304 1305 'defaults': -> 1306 # show first time config (offDEFAULTS) 1307 for k in Object.keys(offDEFAULTS) 1308 console.log k.blue.bold + ' ' + offDEFAULTS[k] 1309 return 1310 1311 'env': -> 1312 console.log 'off.mode '.blue.bold + offHelpers.offMode() 1313 console.log 'off.integrity '.blue.bold + offHelpers.offIntegrity() 1314 console.log 'off.pem '.blue.bold + offHelpers.offPem() 1315 console.log 'off.sshoptions '.blue.bold + offHelpers.offSshOptions() 1316 console.log 'off.scpoptions '.blue.bold + offHelpers.offScpOptions() 1317 console.log 'off.rsyncoptions '.blue.bold + offHelpers.offRsyncOptions() 1318 console.log 'off.store '.blue.bold + offHelpers.offStore() 1319 console.log 'off.http '.blue.bold + offHelpers.offHttp() 1320 console.log 'off.curloptions '.blue.bold + offHelpers.offCurlOptions() 1321 console.log 'off.scphost '.blue.bold + offHelpers.offScp() 1322 console.log 'off.scpuser '.blue.bold + offHelpers.offScpUser() 1323 console.log 'off.log '.blue.bold + offHelpers.getLog() 1324 console.log 'off.configAlways '.blue.bold + offHelpers.offConfigAlways() 1325 console.log 'off.s3region '.blue.bold + offHelpers.s3Region() 1326 console.log 'off.s3bucket '.blue.bold + offHelpers.s3Bucket() 1327 console.log 'off.transform '.blue.bold + offHelpers.transform() 1328 console.log 'off.transformTo '.blue.bold + offHelpers.transformTo() 1329 console.log 'off.transformFrom '.blue.bold + offHelpers.transformFrom() 1330 return 1331 1332 'help': -> 1333 console.log 'git-off help\n'.green.bold 1334 if process.argv[3] == undefined 1335 console.log '# Quick Start'.green.bold 1336 console.log 'Setup:' 1337 console.log "git off track '*.bin'" 1338 console.log 'git add .' 1339 console.log 'git commit' 1340 console.log 'git push' 1341 console.log 'git checkout master' 1342 console.log '\n# Other'.green.bold 1343 console.log 'git off install' 1344 console.log 'git off mode scp' 1345 console.log 'git off scp localhost:/tmp/offStore' 1346 console.log 'git off scpuser username' 1347 console.log 'git off cc' 1348 console.log 'git off ca' 1349 console.log 'git off env' 1350 console.log 'git off defaults\n' 1351 1352 for c in Object.keys(COMMAND_MAP) 1353 showCommandHelp(c) 1354 1355 console.log '\nGo to https://noulin.net/git-off/file/README.md.html for more information'.green.bold 1356 else 1357 if COMMAND_MAP[process.argv[3]] == undefined 1358 console.log COMMAND_MAP.help.h.green 1359 else 1360 showCommandHelp(process.argv[3]) 1361 return 1362 1363 # list offCommands, run 'git off' 1364 #console.log Object.keys(offCommands) 1365 1366 ### 1367 # main 1368 ### 1369 1370 # parse CLI arguments 1371 1372 thisrepo = (cmd) -> 1373 if process.argv[3] != 'thisrepo' 1374 cmd(gitConfig['set']) 1375 else 1376 # setup config in current repo 1377 cmd(gitConfig['setThisRepo']) 1378 return 1379 1380 showCommandHelp = (cmd) -> 1381 l_l = COMMAND_MAP[cmd].h.split('\n') 1382 console.log l_l[0] 1383 l_l = l_l.slice(1) 1384 console.log l_l.join('\n').green 1385 return 1386 1387 COMMAND_MAP = 1388 'install': 1389 f: -> 1390 thisrepo offCommands['install'] 1391 return 1392 h: 'git off install [thisrepo]\n setup git config (default global)\n thisrepo sets up config in current repo' 1393 1394 'mode': 1395 f: -> 1396 thisrepo offCommands['mode'] 1397 return 1398 h: 'git off mode [thisrepo] [copy|rsync|scp|http|s3]\n set/show git off mode' 1399 1400 'store': 1401 f: -> 1402 thisrepo offCommands['store'] 1403 return 1404 h: 'git off store [thisrepo] [path]\n set/show git off store path for copy mode' 1405 1406 'scp': 1407 f: -> 1408 thisrepo offCommands['scp'] 1409 return 1410 h: 'git off scp [thisrepo] [host]\n setup scp config\n host has format host:path, user@host:path, user@host:port/path\n Example: localhost:/tmp/offStore' 1411 1412 'http': 1413 f: -> 1414 thisrepo offCommands['http'] 1415 return 1416 h: 'git off http [thisrepo] [host]\n setup http config\n host has format http://host/path\n Example: http://localhost/offStore' 1417 1418 'curl': 1419 f: -> 1420 thisrepo offCommands['curl'] 1421 return 1422 h: 'git off curl [thisrepo] [options]\n setup curl config' 1423 1424 'integrity': 1425 f: -> 1426 thisrepo offCommands['integrity'] 1427 return 1428 h: 'git off integrity [thisrepo] [enable|disable]\n set/show git off integrity.\n when enabled, the SHA of the file received from the store is\n checked again the SHA of the original file' 1429 1430 'pem': 1431 f: -> 1432 thisrepo offCommands['pem'] 1433 return 1434 h: "git off pem [thisrepo] [pathToPrivateKey]\n set/show git off pem.\n off.pem is the private key for ssh, rsync and scp\n set 'offNoValue' to set an empty value (useful when there are multiple configs)" 1435 1436 'sshoptions': 1437 f: -> 1438 thisrepo offCommands['sshoptions'] 1439 return 1440 h: 'git off sshoptions [thisrepo] [options]\n set/show git off sshoptions' 1441 1442 'scpoptions': 1443 f: -> 1444 thisrepo offCommands['scpoptions'] 1445 return 1446 h: 'git off scpoptions [thisrepo] [options]\n set/show git off scpoptions' 1447 1448 'rsyncoptions': 1449 f: -> 1450 thisrepo offCommands['rsyncoptions'] 1451 return 1452 h: 'git off rsyncoptions [thisrepo] [options]\n set/show git off rsyncoptions' 1453 1454 'scpuser': 1455 f: -> 1456 thisrepo offCommands['scpUser'] 1457 return 1458 h: 'git off scpuser [thisrepo] [username]\n setup scp username config' 1459 1460 'track': 1461 f: -> 1462 offCommands.track() 1463 return 1464 h: "git off track\n setup gitattribute filters\n example: git off track '*.bin'\n without parameter, list git off attributes\n calls git off install" 1465 1466 'configAlways': 1467 f: -> 1468 offCommands.configAlways() 1469 return 1470 h: "git off configAlways [''|GIT_OFF_CONFIG|repo|global]\n '' disable configAlways\n GIT_OFF_CONFIG load all configurations from $GIT_OFF_CONFIG\n repo load all configurations from current git repo\n global load all configurations from global git config\n set 'offNoValue' to set an empty value" 1471 1472 's3region': 1473 f: -> 1474 thisrepo offCommands['s3region'] 1475 return 1476 h: 'git off s3region [thisrepo] [region]\n setup amazon s3 region for the bucket' 1477 1478 's3bucket': 1479 f: -> 1480 thisrepo offCommands['s3bucket'] 1481 return 1482 h: 'git off s3bucket [thisrepo] [bucket]\n setup amazon s3 bucket' 1483 1484 'transform': 1485 f: -> 1486 thisrepo offCommands['transform'] 1487 return 1488 h: 'git off transform [thisrepo] [enable|disable]\n enable transform in clean and smudge filters' 1489 1490 'transformTo': 1491 f: -> 1492 thisrepo offCommands['transformTo'] 1493 return 1494 h: "git off transformTo [thisrepo] ['cmd _1 _2']\n setup transform command for clear filter\n When the command is empty the regular transport is performed" 1495 1496 'transformFrom': 1497 f: -> 1498 thisrepo offCommands['transformFrom'] 1499 return 1500 h: "git off transformFrom [thisrepo] ['cmd _1 _2']\n setup transform command for smudge filter\n When the command is empty the regular transport is performed" 1501 1502 'clean': 1503 f: -> 1504 offCommands.clean() 1505 return 1506 h: 'git off clean\n internal filter\n dont use directly' 1507 1508 'pre-push': 1509 f: -> 1510 offCommands.prepush() 1511 return 1512 h: 'git off pre-push\n internal filter\n dont use directly' 1513 1514 'smudge': 1515 f: -> 1516 offCommands.smudge() 1517 return 1518 h: 'git off smudge\n internal filter\n dont use directly' 1519 1520 'copyTo': 1521 f: -> 1522 offCommands.copyTo() 1523 return 1524 h: 'git off copyTo [copy|rsync|scp|s3]\n copy cache to store for specified mode' 1525 1526 'push': 1527 f: -> 1528 offCommands.pushTo() 1529 return 1530 h: 'git off push\n copy cache to store for selected mode' 1531 1532 'clearAll': 1533 f: -> 1534 offCommands.clearAll() 1535 return 1536 h: 'git off clearAll\n delete store, cache and log' 1537 1538 'ca': 1539 f: -> 1540 offCommands.clearAll() 1541 return 1542 h: 'git off ca\n delete store, cache and log' 1543 1544 'clearCache': 1545 f: -> 1546 offCommands.clearCache() 1547 return 1548 h: 'git off clearCache\n delete cache in current git' 1549 1550 'cc': 1551 f: -> 1552 offCommands.clearCache() 1553 return 1554 h: 'git off cc\n delete cache in current git' 1555 1556 'clearStore': 1557 f: -> 1558 offCommands.clearStore() 1559 return 1560 h: 'git off clearStore\n delete store' 1561 1562 'cs': 1563 f: -> 1564 offCommands.clearStore() 1565 return 1566 h: 'git off ct\n delete store' 1567 1568 'clearTmp': 1569 f: -> 1570 offCommands.clearTmp() 1571 return 1572 h: 'git off clearTmp\n delete tmp in git off cache\n Useful when transform is enabled' 1573 1574 'ct': 1575 f: -> 1576 offCommands.clearTmp() 1577 return 1578 h: 'git off cs\n delete tmp in git off cache\n Useful when transform is enabled' 1579 1580 'defaults': 1581 f: -> 1582 offCommands.defaults() 1583 return 1584 h: 'git off defaults\n shows first time config' 1585 1586 'env': 1587 f: -> 1588 offCommands.env() 1589 return 1590 h: 'git off env\n shows config' 1591 1592 'help': 1593 f: -> 1594 offCommands.help() 1595 return 1596 h: 'git off help [cmd]\n git off help. Run git off help command to get help for a specific command.' 1597 1598 if process.argv[2] == undefined 1599 COMMAND_MAP.help.f() 1600 else 1601 if COMMAND_MAP[process.argv[2]] == undefined 1602 COMMAND_MAP['help'].f() 1603 else 1604 COMMAND_MAP[process.argv[2]].f() 1605 1606 ### 1607 # exports for unit tests 1608 # export everything 1609 ### 1610 module.exports = 1611 runtimeConfig: runtimeConfig 1612 offDEFAULTS: offDEFAULTS 1613 expandHome: expandHome 1614 gitConfig: gitConfig 1615 offLog: offLog 1616 offLogRepo: offLogRepo 1617 exec: exec 1618 mkdirParents: mkdirParents 1619 rmAll: rmAll 1620 copy: copy 1621 walkSync: walkSync 1622 offHelpers: offHelpers 1623 transport: transport 1624 offCommands: offCommands 1625 thisrepo: thisrepo 1626 showCommandHelp: showCommandHelp 1627 COMMAND_MAP: COMMAND_MAP