commit fe414d996ec90cda4b4c6d7365c0c7d0b068fbce
parent 77a3fc05b7c4cef9f548cdf4b2132710b8472374
Author: Remy Noulin (Spartatek) <remy.noulin@spartatek.se>
Date: Sun, 20 Nov 2016 21:28:13 +0100
add scp transport
add local config option thisrepo
add mode scp
add partial help
add ignoreStderr to exec function
src/gitoff.coffee | 324 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 285 insertions(+), 39 deletions(-)
Diffstat:
| M | src/gitoff.coffee | | | 324 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
1 file changed, 285 insertions(+), 39 deletions(-)
diff --git a/src/gitoff.coffee b/src/gitoff.coffee
@@ -1,10 +1,12 @@
#! /usr/bin/env coffee
# TODO
-# detect when not setup
-# default setup global
-# option to setup locally
-# create git off modes
+# add help
+# add rsync backend - change push and smudge
+# add pem config
+# handle wrong config like getLog
+# add multiple config like our git fat
+# add a command to copy stores
#
# parse params
# use logger
@@ -15,12 +17,26 @@
#
# COMMANDS
#
-# git off install
+# git off install [thisrepo]
# setup git config (default global)
+# thisrepo sets up config in current repo
+#
+# git off mode [thisrepo] [copy|scp]
+# set/show git off mode
+#
+# git off scp [thisrepo] [host]
+# setup scp config
+# host has format host:path
+# Example: localhost:/tmp/offStore
+#
+# git off scpuser [thisrepo] [username]
+# setup scp username config
#
# git off track
# setup gitattribute filters
# example: git off track '*.bin'
+# without parameter, list git off attributes
+# calls git off install
#
# git off clean
# internal filter
@@ -45,6 +61,12 @@
# git off defaults
# shows first time config
#
+# git off env
+# shows config
+#
+# git off help
+# shows git off help
+#
# config:
# gitoff mode rsync, http, copy
# store=/we
@@ -80,7 +102,8 @@
# offHelpers: git-off helpers
#
# core
-# globalConfig: handles global git config
+# transport: transport functions for git off store
+# gitConfig: handles global git config
# offCommands: command line functions
#
# main
@@ -121,6 +144,9 @@ externalHelpers =
'gitDiff': 'git diff-tree -r'
'gitCat': 'git cat-file -p'
'sha': 'git hash-object --no-filters'
+ 'listAttr': 'git check-attr -a'
+ 'ssh': 'ssh -C -o StrictHostKeyChecking=no -o ConnectTimeout=3'
+ 'scp': 'scp -C -o StrictHostKeyChecking=no -o ConnectTimeout=3'
# config built by offHelpers
# use offHelpers to access runtimeConfig
@@ -128,9 +154,13 @@ runtimeConfig =
'currentRepoRoot': ''
'objectPath': ''
'offStore': ''
+ 'offMode': ''
+ 'offScp': ''
+ 'offScpUser': ''
'log': ''
# default configuration for first time install
+# objectPath is git off cache in repo
offDEFAULTS =
'objectPath': '/.git/off/objects'
'mode': 'copy'
@@ -154,7 +184,8 @@ expandHome = (path) ->
offLog = (s) ->
if runtimeConfig.log == ''
# set runtimeConfig.log and get config in git
- r = syncexec externalHelpers.gitConfig + ' off.log'
+ # use syncexec to avoid circular calls
+ r = syncexec externalHelpers.gitConfig + ' off.log'
runtimeConfig.log = expandHome r.stdout.trim()
# error handling
if runtimeConfig.log == ''
@@ -176,13 +207,14 @@ offLogRepo = (s) ->
# paramsArray is an array of strings
# errors are logged in off.log and in console
# returns r (r.stdout, r.stderr)
-exec = (cmdName, paramsArray=[]) ->
- #console.log externalHelpers[cmdName] + ' ' + paramsArray.join(' ')
+exec = (cmdName, paramsArray=[], ignoreStderr=false) ->
+ console.log externalHelpers[cmdName] + ' ' + paramsArray.join(' ')
#offLog externalHelpers[cmdName] + ' ' + paramsArray.join(' ')
r = syncexec externalHelpers[cmdName] + ' ' + paramsArray.join(' ')
+ #console.log r.stdout
# offLog r
# offLog r.stdout
- if r.stderr != ''
+ if r.stderr != '' and ignoreStderr == false
console.error r.stderr.red.bold
offLog r.stderr.red.bold
process.exit(1)
@@ -211,9 +243,16 @@ copy = (src, dst) ->
return
# git-off helpers
-# gitRepoRoot: sets and returns runtimeConfig.currentRepoRoot
-# objectPath: sets and returns runtimeConfig.objectPath
-# getLog: sets and returns runtimeConfig.log
+# gitRepoRoot: sets and returns runtimeConfig.currentRepoRoot
+# objectPath: sets and returns runtimeConfig.objectPath
+# offStore: sets and returns runtimeConfig.offStore
+# offMode: sets and returns runtimeConfig.offMode
+# offScp: sets and returns runtimeConfig.offScp
+# offScpUser: sets and returns runtimeConfig.offScpUser
+# log: sets and returns runtimeConfig.log
+# getLog: sets and returns runtimeConfig.log with error checking
+# userAt: returns user@ or ''
+# setTransport: set send and receive functions for transport
offHelpers =
'gitRepoRoot': ->
if runtimeConfig.currentRepoRoot == ''
@@ -232,17 +271,69 @@ offHelpers =
runtimeConfig.offStore = expandHome r.stdout.trim()
runtimeConfig.offStore
- 'getLog': ->
+ 'offMode': ->
+ if runtimeConfig.offMode == ''
+ r = exec 'gitConfig', ['off.mode']
+ runtimeConfig.offMode = expandHome r.stdout.trim()
+ runtimeConfig.offMode
+
+ 'offScp': ->
+ if runtimeConfig.offScp == ''
+ r = exec 'gitConfig', ['off.scphost']
+ runtimeConfig.offScp = expandHome r.stdout.trim()
+ runtimeConfig.offScp
+
+ 'offScpUser': ->
+ if runtimeConfig.offScpUser == ''
+ r = exec 'gitConfig', ['off.scpuser']
+ runtimeConfig.offScpUser = expandHome r.stdout.trim()
+ runtimeConfig.offScpUser
+
+ 'log': ->
if runtimeConfig.log == ''
- # get config in git
+ # use syncexec to avoid circular calls
r = syncexec externalHelpers.gitConfig + ' off.log'
runtimeConfig.log = expandHome r.stdout.trim()
- # error handling
- if runtimeConfig.log == ''
- console.error 'Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold
- process.exit(1)
runtimeConfig.log
+ 'getLog': ->
+ offHelpers.log()
+ # error handling
+ if runtimeConfig.log == ''
+ console.error 'Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold
+ process.exit(1)
+ runtimeConfig.log
+
+ 'userAt': ->
+ if offHelpers.offScpUser() != ''
+ user = offHelpers.offScpUser() + '@'
+ else
+ user = ''
+ user
+
+ 'setTransport': ->
+ if offHelpers.offMode() == 'copy'
+ transport['send'] = (file) ->
+ copy offHelpers.objectPath() + '/' + file, offHelpers.offStore() + '/' + file
+ fs.chmodSync offHelpers.offStore() + '/' + file, '444'
+ return
+ transport['receive'] = (file) ->
+ readStream = fs.createReadStream(offHelpers.offStore() + '/' + file)
+ readStream.pipe(process.stdout)
+ return
+ else if offHelpers.offMode() == 'scp'
+ transport['send'] = (file) ->
+ user = offHelpers.userAt()
+ exec 'scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file]
+ return
+ transport['receive'] = (file) ->
+ user = offHelpers.userAt()
+ exec 'scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]
+ readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file)
+ readStream.pipe(process.stdout)
+ return
+ return
+
# list offHelpers, run 'git off'
#console.log Object.keys(offHelpers)
@@ -250,15 +341,34 @@ offHelpers =
# core
###
+transport =
+ 'send': (src) ->
+ # to be initialized by setTransport
+ return
+ 'receive': (src) ->
+ # send data to stdout
+ # to be initialized by setTransport
+ return
+
# handles global git config
-globalConfig =
+gitConfig =
'set': (key, value)->
exec 'gitConfigGlobal', [key, '"' + value + '"']
return
+ 'setLocal': (key, value) ->
+ exec('gitConfig', [key, '"' + value + '"'])
+ return
+
+ 'get': (key) ->
+ exec('gitConfig', [key]).stdout.trim()
+
# command line functions
# localSetup: setup current git
# install: setup git config (default global)
+# mode: set/show git off mode
+# scp: setup scp config
+# scpuser: setup scp username config
# track: setup gitattribute filters
# clean: replace files handled by git off with reference
# prepush: read stdin and calls push
@@ -267,6 +377,7 @@ globalConfig =
# clearAll: delete store, cache in current git and log
# clearCache: delete cache in current git
# defaults: show first time config (offDEFAULTS)
+# env: show config
offCommands =
'localSetup': ->
# setup current git
@@ -284,28 +395,82 @@ offCommands =
# process.exit(1)
return
- 'install': (setCfg = globalConfig.set) ->
+ 'install': (setCfg = gitConfig.set) ->
# setup git config
# create off.store
+
offCommands.localSetup()
- setCfg('filter.off.clean','git off clean %f')
- setCfg('filter.off.smudge','git off smudge %f')
- setCfg('off.mode', offDEFAULTS.mode)
- setCfg('off.store',offDEFAULTS.store)
- setCfg('off.log',offDEFAULTS.log)
- if offDEFAULTS.mode == 'copy'
- mkdirParents offDEFAULTS.store
+
+ # setup config only if not already set
+ if gitConfig.get('filter.off.clean') == '' or setCfg != gitConfig.set
+ setCfg('filter.off.clean','git off clean %f')
+ if gitConfig.get('filter.off.smudge') == '' or setCfg != gitConfig.set
+ setCfg('filter.off.smudge','git off smudge %f')
+ if offHelpers.log() == '' or setCfg != gitConfig.set
+ setCfg('off.log',offDEFAULTS.log)
+
+ if offHelpers.offMode() == '' or setCfg != gitConfig.set
+ setCfg('off.mode', offDEFAULTS.mode)
+ if offHelpers.offStore() == '' or setCfg != gitConfig.set
+ setCfg('off.store',offDEFAULTS.store)
+
+ # create off.store
+
+ if runtimeConfig.offMode == 'copy'
+ mkdirParents runtimeConfig.offStore
+
+ if runtimeConfig.offMode == 'scp'
+ user = offHelpers.userAt()
+ # scphost format is host:path
+ h_l = offHelpers.offScp().split(':')
+ # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
+ sshCmd = '"mkdir '+ h_l[1]
+ sshCmd += '"'
+ # ignore error from mkdir for already existing store
+ exec 'ssh', [user + h_l[0], sshCmd], true
+ return
+
+ 'mode': (setCfg) ->
+ len = process.argv.length
+ mode = process.argv[len-1]
+ if mode != 'mode' and mode != 'thisrepo'
+ setCfg('off.mode', mode)
+ else
+ console.log 'off.mode '.blue.bold + offHelpers.offMode()
+ return
+
+ 'scp': (setCfg) ->
+ len = process.argv.length
+ scpHost = process.argv[len-1]
+ if scpHost != 'scp' and scpHost != 'thisrepo'
+ setCfg('off.scphost', scpHost)
+ else
+ console.log 'off.scp '.blue.bold + offHelpers.offScp()
+ return
+
+ 'scpUser': (setCfg) ->
+ len = process.argv.length
+ scpUser = process.argv[len-1]
+ if scpUser != 'scpuser' and scpUser != 'thisrepo'
+ setCfg('off.scpuser', scpUser)
+ else
+ console.log 'off.scpuser '.blue.bold + offHelpers.offScpUser()
return
'track': ->
# setup gitattribute filters in current folder
+ # list current git off attributes when there is no parameter
if process.argv[3] != undefined
offCommands.install()
- #TODO git command to add stuff in attributes
- fs.writeFileSync(offHelpers.gitRepoRoot() + '/.gitattributes', process.argv[3] + ' filter=off -text')
+ fs.appendFileSync(offHelpers.gitRepoRoot() + '/.gitattributes', process.argv[3] + ' filter=off -text\n')
else
- #TODO
- console.log 'TODO: list current attributes'.red
+ # list current git off attributes
+
+ r = exec 'listAttr', ['`cd ' + offHelpers.gitRepoRoot() + '; git ls-files`']
+ # gff/b.bin: filter: off
+ for l in r.stdout.split('\n')
+ if l.indexOf(': filter: off') != -1
+ console.log l
return
'clean': ->
@@ -349,6 +514,8 @@ offCommands =
# read stdin and calls push
# stdin (data from git) is a line with remoteName url localRef localSha remoteRef remoteSha
+ offHelpers.setTransport()
+
rl = readline.createInterface(
input: process.stdin
output: process.stdout
@@ -414,8 +581,8 @@ offCommands =
# ### git-off v1 sha:be3e02b60effe3eab232d5590a6a2e2c2c2f443b size:119 b.bin
offFile = offRef.split(':')[1].split(' ')[0]
- copy offHelpers.objectPath() + '/' + offFile, offHelpers.offStore() + '/' + offFile
- fs.chmodSync offHelpers.offStore() + '/' + offFile, '444'
+
+ transport.send offFile
# test prevent push by issuing an error
#process.exit(1)
@@ -428,6 +595,8 @@ offCommands =
# load header
# for git off object, replace git off reference with data from cache or off.store
# for normal object, copy data from repo to stdout
+
+ offHelpers.setTransport()
offCommands.localSetup()
#not used file = process.argv[3]
@@ -450,10 +619,15 @@ offCommands =
if header.slice(0,offDEFAULTS.offSignature.length) == offDEFAULTS.offSignature
# for git off object, replace git off reference with data from cache or off.store
- # TODO detect if file is already in cache
offFile = header.split(':')[1]
- readStream = fs.createReadStream(offHelpers.offStore() + '/' + offFile)
- readStream.pipe(process.stdout)
+ # detect if file is already in cache
+ if fs.existsSync(offHelpers.objectPath() + '/' + offFile) == true
+ # copy from cache
+ readStream = fs.createReadStream(offHelpers.objectPath() + '/' + offFile)
+ readStream.pipe(process.stdout)
+ else
+ # copy from off.store
+ transport.receive offFile
else
# for normal object, copy data from repo to stdout
@@ -464,7 +638,17 @@ offCommands =
'clearAll': ->
# delete store, cache in current git and log
- rmAll offHelpers.offStore()
+ if offHelpers.offMode() == 'copy'
+ rmAll offHelpers.offStore()
+ if offHelpers.offMode() == 'scp'
+ user = offHelpers.userAt()
+ # scphost format is host:path
+ h_l = offHelpers.offScp().split(':')
+ # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
+ sshCmd = '"rm -rf '+ h_l[1]
+ sshCmd += '"'
+ # ignore error from rm
+ exec 'ssh', [user + h_l[0], sshCmd], true
rmAll offHelpers.objectPath()
rmAll offHelpers.getLog()
return
@@ -480,6 +664,14 @@ offCommands =
console.log k.blue.bold + ' ' + offDEFAULTS[k]
return
+ 'env': ->
+ console.log 'off.mode '.blue.bold + offHelpers.offMode()
+ console.log 'off.store '.blue.bold + offHelpers.offStore()
+ console.log 'off.scphost '.blue.bold + offHelpers.offScp()
+ console.log 'off.scpuser '.blue.bold + offHelpers.offScpUser()
+ console.log 'off.log '.blue.bold + offHelpers.getLog()
+ return
+
# list offCommands, run 'git off'
#console.log Object.keys(offCommands)
@@ -489,8 +681,25 @@ offCommands =
# parse CLI arguments
+thisrepo = (cmd) ->
+ if process.argv[3] != 'thisrepo'
+ cmd(gitConfig['set'])
+ else
+ # setup config in current repo
+ cmd(gitConfig['setLocal'])
+ return
+
if process.argv[2] == 'install'
- offCommands.install()
+ thisrepo offCommands['install']
+
+if process.argv[2] == 'mode'
+ thisrepo offCommands['mode']
+
+if process.argv[2] == 'scp'
+ thisrepo offCommands['scp']
+
+if process.argv[2] == 'scpuser'
+ thisrepo offCommands['scpUser']
if process.argv[2] == 'track'
offCommands.track()
@@ -512,3 +721,40 @@ if (process.argv[2] == 'clearCache') || (process.argv[2] == 'cc')
if process.argv[2] == 'defaults'
offCommands.defaults()
+
+if process.argv[2] == 'env'
+ offCommands.env()
+
+if process.argv[2] == 'help' or process.argv[2] == undefined
+ console.log 'git-off help\n'.green.bold
+ if process.argv[3] == undefined
+ console.log '# USAGE'.green.bold
+ console.log 'Setup:'
+ console.log "git off track '*.bin'"
+ console.log " git off now handles files with bin extensions".green
+ console.log 'git add .'
+ console.log ' use git normally'.green
+ console.log 'git commit'
+ console.log 'git push'
+ console.log 'git checkout master'
+ console.log ' checkout commit with bin files'.green
+ console.log '\n# Other\n'.green.bold
+ console.log 'git off install'
+ console.log ' setup git config (default global)'.green
+ console.log 'git off mode scp'
+ console.log ' store object in a remote sshhost, setup host and path with git off scp'.green
+ console.log 'git off scp localhost:/tmp/offStore'
+ console.log ' setup host and path'.green
+ console.log 'git off scpuser username'
+ console.log ' setup scp username config'.green
+ console.log 'git off cc'
+ console.log ' clear cache in current repo'.green
+ console.log 'git off ca'
+ console.log ' clear store and cache'.green
+ console.log 'git off env'
+ console.log ' shows current config'.green
+ console.log 'git off defaults'
+ console.log ' shows first time config'.green
+ else
+ console.log 'Command help TODO'
+ # TODO option: thisrepo for local config