git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

commit 44f21712387b9a4d37dc05c4aca5796d08af9556
parent e5108dc86261ca9c7902d7740c04c3648a4cb39a
Author: Remy Noulin (Spartatek) <remy.noulin@spartatek.se>
Date:   Tue, 22 Nov 2016 22:19:06 +0100

Fix ssh config, add integrity check

README.md         |  13 +++++
package.json      |   2 +-
src/git-off       | 143 ++++++++++++++++++++++++----------------------------
src/gitoff.coffee | 146 +++++++++++++++++++++++++-----------------------------
4 files changed, 148 insertions(+), 156 deletions(-)

Diffstat:
MREADME.md | 13+++++++++++++
Mpackage.json | 2+-
Msrc/git-off | 143+++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/gitoff.coffee | 146+++++++++++++++++++++++++++++++++++++------------------------------------------
4 files changed, 148 insertions(+), 156 deletions(-)

diff --git a/README.md b/README.md @@ -10,6 +10,14 @@ By default the files are stored in the home directory. npm install -g git-off ``` +Then + +``` +git off +``` + +for help. + [https://www.npmjs.com/package/git-off](https://www.npmjs.com/package/git-off) # USAGE @@ -60,6 +68,11 @@ git off install [thisrepo] git off mode [thisrepo] [copy|scp] set/show git off mode +git off integrity [thisrepo] [enable|disable] + set/show git off integrity. + when enabled, the SHA of the file received from the store is + checked again the SHA of the original file + git off scp [thisrepo] [host] setup scp config host has format host:path diff --git a/package.json b/package.json @@ -1,6 +1,6 @@ { "name": "git-off", - "version": "0.0.2", + "version": "0.0.3", "description": "large file handler for git", "bin": "./src/git-off", "scripts": { diff --git a/src/git-off b/src/git-off @@ -1,65 +1,5 @@ #! /usr/bin/env node /* - * USAGE - * - * COMMANDS - * - * 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 - * - * git off smudge - * internal filter - * - * git off pre-push - * internal filter - * - * git off smudge - * internal filter - * - * git off clearAll - * git off ca - * delete store, cache and log - * - * git off clearCache - * git off cc - * deletes cache in current git - * - * 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 - */ - -/* * CODE * * modules @@ -149,6 +89,7 @@ runtimeConfig = { 'objectPath': '', 'offStore': '', 'offMode': '', + 'offIntegrity': '', 'offScp': '', 'offScpUser': '', 'log': '' @@ -157,6 +98,7 @@ runtimeConfig = { offDEFAULTS = { 'objectPath': '/.git/off/objects', 'mode': 'copy', + 'integrity': 'disable', 'store': '~/.git-off/offStore', 'log': '~/.git-off/log', '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 "$@"', @@ -284,6 +226,14 @@ offHelpers = { } return runtimeConfig.offMode; }, + 'offIntegrity': function() { + var r; + if (runtimeConfig.offIntegrity === '') { + r = gitConfig.get('off.integrity'); + runtimeConfig.offIntegrity = expandHome(r); + } + return runtimeConfig.offIntegrity; + }, 'offScp': function() { if (runtimeConfig.offScp === '') { runtimeConfig.offScp = gitConfig.get('off.scphost'); @@ -327,9 +277,13 @@ offHelpers = { var h_l, host, port, portAndPath_l, storePath, user; user = offHelpers.userAt(); h_l = offHelpers.offScp().split(':'); - portAndPath_l = h_l[1].split('/'); - port = parseInt(portAndPath_l[0]); - if (port === NaN) { + if (h_l[1] === void 0) { + port = NaN; + } else { + portAndPath_l = h_l[1].split('/'); + port = parseInt(portAndPath_l[0]); + } + if (isNaN(port)) { host = h_l[0]; storePath = h_l[1]; } else { @@ -343,7 +297,7 @@ offHelpers = { h_l = offHelpers.getSSHConfig(); sshCmd = '"mkdir -p ' + h_l[1] + '/' + path; sshCmd += '"'; - if (h_l[2] === NaN) { + if (isNaN(h_l[2])) { exec('ssh', [h_l[0], sshCmd], true); } else { exec('ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true); @@ -354,12 +308,27 @@ offHelpers = { h_l = offHelpers.getSSHConfig(); sshCmd = '"rm -rf ' + h_l[1] + '/' + path; sshCmd += '"'; - if (h_l[2] === NaN) { + if (isNaN(h_l[2])) { exec('ssh', [h_l[0], sshCmd], true); } else { exec('ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true); } }, + 'checkIntegrity': function(path) { + var path_l, r, receivedSha, result; + result = true; + if (offHelpers.offIntegrity() === 'disable') { + return true; + } + r = exec('sha', [path]); + receivedSha = r.stdout.split(' ')[0].trim(); + path_l = path.split('/'); + if (path_l[path_l.length - 1] !== receivedSha) { + console.log('git-off: The file ' + path + ' differs from the one that was pushed.'); + result = false; + } + return result; + }, 'setTransport': function() { if (offHelpers.offMode() === 'copy') { transport['send'] = function(file) { @@ -373,37 +342,39 @@ offHelpers = { }; transport['receive'] = function(file) { var readStream; - readStream = fs.createReadStream(offHelpers.offStore() + '/' + file); - readStream.pipe(process.stdout); + if (offHelpers.checkIntegrity(offHelpers.offStore() + '/' + file)) { + readStream = fs.createReadStream(offHelpers.offStore() + '/' + file); + readStream.pipe(process.stdout); + } }; } else if (offHelpers.offMode() === 'scp') { transport['send'] = function(file) { - var f_l, h_l, user; - user = offHelpers.userAt(); + var f_l, h_l; f_l = file.split('/'); offHelpers.mkdirStore(f_l[0] + '/' + f_l[1]); h_l = offHelpers.getSSHConfig(); - if (h_l[2] === NaN) { - exec('scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file]); + if (isNaN(h_l[2])) { + exec('scp', [offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]); } else { exec('scp', ['-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]); } }; transport['receive'] = function(file) { - var f_l, h_l, readStream, user; + var f_l, h_l, readStream; f_l = file.split('/'); if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) { mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]); } - user = offHelpers.userAt(); h_l = offHelpers.getSSHConfig(); - if (h_l[2] === NaN) { - exec('scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]); + if (isNaN(h_l[2])) { + exec('scp', [h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]); } else { exec('scp', ['-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]); } - readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file); - readStream.pipe(process.stdout); + if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/' + file)) { + readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file); + readStream.pipe(process.stdout); + } }; } }, @@ -461,6 +432,9 @@ offCommands = { if (offHelpers.offMode() === '' || setCfg !== gitConfig.set) { setCfg('off.mode', offDEFAULTS.mode); } + if (offHelpers.offIntegrity() === '' || setCfg !== gitConfig.set) { + setCfg('off.integrity', offDEFAULTS.integrity); + } if (offHelpers.offStore() === '' || setCfg !== gitConfig.set) { setCfg('off.store', offDEFAULTS.store); } @@ -491,6 +465,16 @@ offCommands = { console.log('off.scp '.blue.bold + offHelpers.offScp()); } }, + 'integrity': function(setCfg) { + var integrity, len; + len = process.argv.length; + integrity = process.argv[len - 1]; + if (integrity !== 'integrity' && integrity !== 'thisrepo') { + setCfg('off.integrity', integrity); + } else { + console.log('off.integrity '.blue.bold + offHelpers.offIntegrity()); + } + }, 'scpUser': function(setCfg) { var len, scpUser; len = process.argv.length; @@ -639,6 +623,7 @@ offCommands = { }, 'env': function() { console.log('off.mode '.blue.bold + offHelpers.offMode()); + console.log('off.integrity '.blue.bold + offHelpers.offIntegrity()); console.log('off.store '.blue.bold + offHelpers.offStore()); console.log('off.scphost '.blue.bold + offHelpers.offScp()); console.log('off.scpuser '.blue.bold + offHelpers.offScpUser()); @@ -671,6 +656,10 @@ if (process.argv[2] === 'scp') { thisrepo(offCommands['scp']); } +if (process.argv[2] === 'integrity') { + thisrepo(offCommands['integrity']); +} + if (process.argv[2] === 'scpuser') { thisrepo(offCommands['scpUser']); } diff --git a/src/gitoff.coffee b/src/gitoff.coffee @@ -7,70 +7,14 @@ # add pem config # handle wrong config like getLog # add a command to copy stores +# check parameters from CLI # # parse params # use logger # - -### -# USAGE -# -# COMMANDS -# -# 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 -# -# git off smudge -# internal filter -# -# git off pre-push -# internal filter -# -# git off smudge -# internal filter -# -# git off clearAll -# git off ca -# delete store, cache and log -# -# git off clearCache -# git off cc -# deletes cache in current git -# -# 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 -### +# How to add a new config +# How to add a new transport +# How to add a new command ### # CODE @@ -155,6 +99,7 @@ runtimeConfig = 'objectPath': '' 'offStore': '' 'offMode': '' + 'offIntegrity': '' 'offScp': '' 'offScpUser': '' 'log': '' @@ -164,6 +109,7 @@ runtimeConfig = offDEFAULTS = 'objectPath': '/.git/off/objects' 'mode': 'copy' + 'integrity': 'disable' 'store': '~/.git-off/offStore' 'log': '~/.git-off/log' '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 "$@"' @@ -270,6 +216,7 @@ copy = (src, dst) -> # objectPath: sets and returns runtimeConfig.objectPath # offStore: sets and returns runtimeConfig.offStore # offMode: sets and returns runtimeConfig.offMode +# offIntegrity sets and returns runtimeConfig.offIntegrity # offScp: sets and returns runtimeConfig.offScp # offScpUser: sets and returns runtimeConfig.offScpUser # log: sets and returns runtimeConfig.log @@ -278,6 +225,7 @@ copy = (src, dst) -> # getSSHConfig extracts host, port and path from off.sshhost # mkdirStore mkdir in remote store # rmAllStore rm in remote store +# checkIntegrity check integrity of files coming from the store # setTransport: set send and receive functions for transport # getOffFilePath: creates path for offFile offHelpers = @@ -304,6 +252,12 @@ offHelpers = runtimeConfig.offMode = expandHome r runtimeConfig.offMode + 'offIntegrity': -> + if runtimeConfig.offIntegrity == '' + r = gitConfig.get 'off.integrity' + runtimeConfig.offIntegrity = expandHome r + runtimeConfig.offIntegrity + 'offScp': -> if runtimeConfig.offScp == '' runtimeConfig.offScp = gitConfig.get 'off.scphost' @@ -343,9 +297,12 @@ offHelpers = user = offHelpers.userAt() h_l = offHelpers.offScp().split(':') - portAndPath_l = h_l[1].split('/') - port = parseInt portAndPath_l[0] - if port == NaN + if h_l[1] == undefined + port = NaN + else + portAndPath_l = h_l[1].split('/') + port = parseInt portAndPath_l[0] + if isNaN port # use default port host = h_l[0] storePath = h_l[1] @@ -365,7 +322,7 @@ offHelpers = sshCmd += '"' # ignore error from mkdir for already existing store - if h_l[2] == NaN + if isNaN h_l[2] exec 'ssh', [h_l[0], sshCmd], true else exec 'ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true @@ -380,12 +337,33 @@ offHelpers = sshCmd = '"rm -rf '+ h_l[1] + '/' + path sshCmd += '"' # ignore error from rm - if h_l[2] == NaN + if isNaN h_l[2] exec 'ssh', [h_l[0], sshCmd], true else exec 'ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true return + 'checkIntegrity': (path) -> + # check integrity of files coming from the store + + result = true + + if offHelpers.offIntegrity() == 'disable' + return true + + r = exec 'sha', [path] + # trim because git hash-object adds \n + receivedSha = r.stdout.split(' ')[0].trim() + + path_l = path.split('/') + + if path_l[path_l.length-1] != receivedSha + # the file received is different from the one that was sent. + console.log 'git-off: The file ' + path + ' differs from the one that was pushed.' + result = false + + result + 'setTransport': -> # set send and receive functions for transport # copy, scp @@ -401,22 +379,21 @@ offHelpers = fs.chmodSync offHelpers.offStore() + '/' + file, '444' return transport['receive'] = (file) -> - readStream = fs.createReadStream(offHelpers.offStore() + '/' + file) - readStream.pipe(process.stdout) + if offHelpers.checkIntegrity offHelpers.offStore() + '/' + file + readStream = fs.createReadStream(offHelpers.offStore() + '/' + file) + readStream.pipe(process.stdout) return else if offHelpers.offMode() == 'scp' transport['send'] = (file) -> - user = offHelpers.userAt() # create file directories in store f_l = file.split '/' offHelpers.mkdirStore f_l[0] + '/' + f_l[1] - #TODO remove user h_l = offHelpers.getSSHConfig() - if h_l[2] == NaN - exec 'scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file] + if isNaN h_l[2] + exec 'scp', [offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file] else exec 'scp', ['-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file] return @@ -426,17 +403,15 @@ offHelpers = if fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) == false mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1] - user = offHelpers.userAt() - - #TODO remove user h_l = offHelpers.getSSHConfig() - if h_l[2] == NaN - exec 'scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file] + if isNaN h_l[2] + exec 'scp', [h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file] else exec 'scp', ['-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file] - readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) - readStream.pipe(process.stdout) + if offHelpers.checkIntegrity offHelpers.objectPath() + '/' + file + readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file) + readStream.pipe(process.stdout) return return 'getOffFilePath': (offFile) -> @@ -516,6 +491,8 @@ offCommands = if offHelpers.offMode() == '' or setCfg != gitConfig.set setCfg('off.mode', offDEFAULTS.mode) + if offHelpers.offIntegrity() == '' or setCfg != gitConfig.set + setCfg('off.integrity', offDEFAULTS.integrity) if offHelpers.offStore() == '' or setCfg != gitConfig.set setCfg('off.store',offDEFAULTS.store) @@ -546,6 +523,15 @@ offCommands = console.log 'off.scp '.blue.bold + offHelpers.offScp() return + 'integrity': (setCfg) -> + len = process.argv.length + integrity = process.argv[len-1] + if integrity != 'integrity' and integrity != 'thisrepo' + setCfg('off.integrity', integrity) + else + console.log 'off.integrity '.blue.bold + offHelpers.offIntegrity() + return + 'scpUser': (setCfg) -> len = process.argv.length scpUser = process.argv[len-1] @@ -764,6 +750,7 @@ offCommands = 'env': -> console.log 'off.mode '.blue.bold + offHelpers.offMode() + console.log 'off.integrity '.blue.bold + offHelpers.offIntegrity() console.log 'off.store '.blue.bold + offHelpers.offStore() console.log 'off.scphost '.blue.bold + offHelpers.offScp() console.log 'off.scpuser '.blue.bold + offHelpers.offScpUser() @@ -796,6 +783,9 @@ if process.argv[2] == 'mode' if process.argv[2] == 'scp' thisrepo offCommands['scp'] +if process.argv[2] == 'integrity' + thisrepo offCommands['integrity'] + if process.argv[2] == 'scpuser' thisrepo offCommands['scpUser']