|
|
@@ -38,6 +38,205 @@ function getVersion() { return BUILD_VERSION; } |
|
|
|
// support runas lauched directly from shell. |
|
|
|
// pass in environment in hta to shellexecute. |
|
|
|
|
|
|
|
const https = require('https') |
|
|
|
const http = require('http'); |
|
|
|
const { Console } = require('console'); |
|
|
|
const { env } = require('process'); |
|
|
|
const RESTAPI = (function(){ |
|
|
|
|
|
|
|
// Singleton |
|
|
|
function RESTAPI(){} |
|
|
|
// RESTAPI.create = RESTAPI; // Returns the one singe instance which is the class itself |
|
|
|
// const httpsoptions = { |
|
|
|
// hostname: 'whatever.com', |
|
|
|
// port: 443, |
|
|
|
// path: '/todos', |
|
|
|
// method: 'POST', |
|
|
|
// headers: { |
|
|
|
// 'Content-Type': 'application/json', |
|
|
|
// 'Content-Length': data.length |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
RESTAPI.method = function(options, resolve, reject){ |
|
|
|
options.headers = options.headers || { 'Content-Type': 'application/json' } |
|
|
|
|
|
|
|
const data = options.payload ? new TextEncoder().encode( JSON.stringify(options.payload) ) : null |
|
|
|
options.headers = options.payload ? (options.headers || { |
|
|
|
'Content-Type': 'application/json', |
|
|
|
'Content-Length': data.length |
|
|
|
}) : (options.headers || {}) |
|
|
|
!options.headers.Authorization && usertokens[options.username] ? (()=>{ |
|
|
|
options.headers.Authorization = `token ${usertokens[options.username]}` |
|
|
|
delete options.username |
|
|
|
onauthenticated() |
|
|
|
})() : (()=>{ |
|
|
|
if(!usertokens[options.username] && !options.isAuthCall) { |
|
|
|
RESTAPI.authenticate( utils.assign( {isAuthCall : true}, options ) , onauthenticated, function(err){ |
|
|
|
// PB : TODO -- Retry without auth... |
|
|
|
console.error('Auth failed or not accessible') |
|
|
|
reject(err) |
|
|
|
}) |
|
|
|
} |
|
|
|
else onauthenticated() |
|
|
|
})() |
|
|
|
|
|
|
|
function onauthenticated(){ |
|
|
|
|
|
|
|
var acquirer = getHTTPorS(options) |
|
|
|
const req = acquirer.request(options, res => { |
|
|
|
if (res.statusCode < 200 || res.statusCode >= 300) { |
|
|
|
// new Error('statusCode=' + res.statusCode) |
|
|
|
return reject('statusCode = ' + res.statusCode); |
|
|
|
} |
|
|
|
var body = []; |
|
|
|
// res.setEncoding('utf8'); |
|
|
|
res.on('data', (chunk)=>{ body.push(chunk); }); |
|
|
|
res.on('end', ()=>{ |
|
|
|
try { |
|
|
|
if(res.headers['content-type'] && res.headers['content-type'].split(';').find( (i)=> i.trim() === 'application/json' )) body = JSON.parse(Buffer.concat(body).toString()); |
|
|
|
else body = Buffer.concat(body).toString(); |
|
|
|
} catch(e) { return reject(e); } |
|
|
|
resolve(body); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
req.on('error', error => { reject(error) }) |
|
|
|
if(options.payload) req.write(data) |
|
|
|
req.end() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var usertokens = {} |
|
|
|
|
|
|
|
RESTAPI.authenticate = function(options, resolve, reject){ |
|
|
|
options.headers = options.headers || { 'Content-Type': 'application/json' } |
|
|
|
|
|
|
|
if(!usertokens[options.username]) { |
|
|
|
// Authenticate and acquire token. |
|
|
|
// https://git.bbh/api/v1/users/<username>/tokens |
|
|
|
// curl -XPOST -H "Content-Type: application/json" -k -d '{"name":"demo"}' -u demo:demo123 http://git.bbh/api/v1/users/demo/tokens |
|
|
|
var _options = Object.assign({}, options) |
|
|
|
// _options.username = 'demo' |
|
|
|
// _options.password = 'demo123' |
|
|
|
_options.method = 'POST' |
|
|
|
_options.headers.Authorization = `Basic ${Buffer.from(`${_options.username}:${_options.password}`).toString('base64')}` |
|
|
|
_options.path = `/api/v1/users/${_options.username}/tokens` |
|
|
|
|
|
|
|
var postoptions = { name : _options.username } |
|
|
|
delete _options.username |
|
|
|
delete _options.password |
|
|
|
_options.payload = postoptions |
|
|
|
RESTAPI.post( _options, function(tokenresp){ |
|
|
|
// tokenresp = JSON.parse(tokenresp) |
|
|
|
usertokens[options.username] = tokenresp.sha1 |
|
|
|
resolve(tokenresp) |
|
|
|
}, |
|
|
|
function(err){ reject(err) } |
|
|
|
) |
|
|
|
} |
|
|
|
else resolve(tokenresp.sha1) |
|
|
|
} |
|
|
|
|
|
|
|
RESTAPI.put = RESTAPI.get = RESTAPI.post = RESTAPI.method |
|
|
|
|
|
|
|
return RESTAPI |
|
|
|
})(); |
|
|
|
|
|
|
|
var getHTTPorS = function(options){ return options.protocol.startsWith('https') ? https : http; } |
|
|
|
|
|
|
|
const GITEA = (function(){ |
|
|
|
|
|
|
|
function GITEA(){} |
|
|
|
GITEA.APIROOT = '/api/v1' |
|
|
|
GITEA.repository = { |
|
|
|
fork( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
// forkoptions = { owner : httpoptions.username, repo : {{reoptoFork}} } |
|
|
|
// giteaoptions = { |
|
|
|
// organization string |
|
|
|
// --- organization name, if forking into an organization |
|
|
|
// } |
|
|
|
// http://try.gitea.io/api/v1/repos/{owner}/{repo}/forks |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${cmdoptions.owner}/${cmdoptions.repo}/forks` |
|
|
|
httpoptions.method = 'POST' |
|
|
|
httpoptions.payload = giteaoptions; |
|
|
|
return RESTAPI.post(httpoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
, updateattributes( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${cmdoptions.owner}/${cmdoptions.repo}` |
|
|
|
httpoptions.method = 'PATCH' |
|
|
|
httpoptions.payload = giteaoptions; |
|
|
|
return RESTAPI.post(httpoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
, exists( httpoptions, repo, owner ) { |
|
|
|
// /users/{username} // Get a user |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${owner}/${repo}` |
|
|
|
httpoptions.method = 'GET' |
|
|
|
return new Promise( (resolve, reject) => { |
|
|
|
RESTAPI.get(httpoptions, function(o){ |
|
|
|
if(o.id) return resolve(true) |
|
|
|
return resolve(false) }, reject || function(){ return false } ) |
|
|
|
}) |
|
|
|
} |
|
|
|
, addcollaborator( httpoptions, repodef, owner, collaborator, permission ) { |
|
|
|
permission = permission || 'Read' |
|
|
|
httpoptions.payload = { permission } |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${owner}/${repodef.repo}/collaborators/${collaborator}` |
|
|
|
httpoptions.method = 'PUT' |
|
|
|
return new Promise( (resolve, reject) => { |
|
|
|
RESTAPI.put(httpoptions, function(o){ |
|
|
|
// |
|
|
|
if(o === "") return resolve(true) |
|
|
|
return resolve(false) }, reject || function(){ return false } ) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
GITEA.user = { |
|
|
|
getuser( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
// /users/{username} // Get a user |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/users/${httpoptions.username}` |
|
|
|
httpoptions.method = 'GET' |
|
|
|
return RESTAPI.get(httpoptions, giteaoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return GITEA |
|
|
|
})(); |
|
|
|
|
|
|
|
// Wrapper for Git shell operations. Some meta operations will map to a bunch of GIT commands. |
|
|
|
const GIT = (function(){ |
|
|
|
function GIT(){} |
|
|
|
|
|
|
|
Object.assign(GIT, { |
|
|
|
'switch user'(username){ |
|
|
|
var httpoptions = new URL(selectedinstance.reposerver); |
|
|
|
httpoptions.username = selectedinstance.username |
|
|
|
httpoptions.password = selectedinstance.password |
|
|
|
|
|
|
|
return GITEA.user.getuser(httpoptions).then(()=>{ |
|
|
|
return nodeShellExec('git', ['config', '--replace-all', 'user.name', username], |
|
|
|
{ |
|
|
|
inherit: true, shell: true, |
|
|
|
env: ENV |
|
|
|
, cwd: instanceroot + '/' + repo |
|
|
|
, runas: processedArgs.runas |
|
|
|
, title: `'git', ${['config', '--replace-all', 'user.name', username].join(' ')}` |
|
|
|
}) |
|
|
|
} |
|
|
|
) |
|
|
|
.catch(e => { |
|
|
|
console.error(e + 'Could not switch. Probably no such user.') |
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
return GIT |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PB : NOTE -- iife doesnt work if previous statement is not terminated by ; |
|
|
|
(function () { |
|
|
|
"use strict"; |
|
|
@@ -346,7 +545,7 @@ shell_verse.acquireElevationState().then((isElevated) => { |
|
|
|
} |
|
|
|
, 'addcollaborator' : { |
|
|
|
// Usage : |
|
|
|
// elxr addcollaborator developer |
|
|
|
// elxr addcollaborator instancename developer |
|
|
|
cmdFn : function(args){ |
|
|
|
|
|
|
|
var __each = function(args){ |
|
|
@@ -357,7 +556,7 @@ shell_verse.acquireElevationState().then((isElevated) => { |
|
|
|
var repodef = selectedinstance.reposindexed[repo]; |
|
|
|
if(!repodef) return |
|
|
|
|
|
|
|
var remotenames = selectedinstance.selectedremotes.concat( selectedinstance.permanentremotes ) |
|
|
|
var remotenames = (selectedinstance.selectedremotes || []).concat( selectedinstance.permanentremotes ) |
|
|
|
var remotes = selectedinstance.reposerverinstances[selectedinstance.reposerver].remotes |
|
|
|
// console.log('-----------------------------------------------------') |
|
|
|
// console.log(repo) |
|
|
@@ -406,7 +605,7 @@ shell_verse.acquireElevationState().then((isElevated) => { |
|
|
|
|
|
|
|
var promises = [] |
|
|
|
var repos = Object.keys(selectedinstance.reposindexed) |
|
|
|
repos.forEach(repo => { promises.push(__each({ repo, collaborator : args._[1]})) }) |
|
|
|
repos.forEach(repo => { promises.push(__each({ repo, collaborator : args._[2]})) }) // PB : TODO -- Move to toArgs. |
|
|
|
return Promise.all(promises).then(pr => { console.log('addcollaborator done') }) |
|
|
|
} |
|
|
|
} |
|
|
@@ -3919,6 +4118,8 @@ shell_verse.acquireElevationState().then((isElevated) => { |
|
|
|
var pendingpulls = []; |
|
|
|
def.repos.forEach((def) => { |
|
|
|
pendingpulls.push( |
|
|
|
// PB : TODO serializing to prevent out of memory. |
|
|
|
// Need to optimize as batches and streams... |
|
|
|
function(){ |
|
|
|
// if(def.repo === 'setup') return; |
|
|
|
// if(def.repo !== 'chess-client-lib') return; |
|
|
@@ -4740,203 +4941,6 @@ shell_verse.acquireElevationState().then((isElevated) => { |
|
|
|
localInstanceDetected : false |
|
|
|
} |
|
|
|
|
|
|
|
const https = require('https') |
|
|
|
const http = require('http'); |
|
|
|
const { Console } = require('console'); |
|
|
|
const { env } = require('process'); |
|
|
|
const RESTAPI = (function(){ |
|
|
|
|
|
|
|
// Singleton |
|
|
|
function RESTAPI(){} |
|
|
|
// RESTAPI.create = RESTAPI; // Returns the one singe instance which is the class itself |
|
|
|
// const httpsoptions = { |
|
|
|
// hostname: 'whatever.com', |
|
|
|
// port: 443, |
|
|
|
// path: '/todos', |
|
|
|
// method: 'POST', |
|
|
|
// headers: { |
|
|
|
// 'Content-Type': 'application/json', |
|
|
|
// 'Content-Length': data.length |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
RESTAPI.method = function(options, resolve, reject){ |
|
|
|
options.headers = options.headers || { 'Content-Type': 'application/json' } |
|
|
|
|
|
|
|
const data = options.payload ? new TextEncoder().encode( JSON.stringify(options.payload) ) : null |
|
|
|
options.headers = options.payload ? (options.headers || { |
|
|
|
'Content-Type': 'application/json', |
|
|
|
'Content-Length': data.length |
|
|
|
}) : (options.headers || {}) |
|
|
|
!options.headers.Authorization && usertokens[options.username] ? (()=>{ |
|
|
|
options.headers.Authorization = `token ${usertokens[options.username]}` |
|
|
|
delete options.username |
|
|
|
onauthenticated() |
|
|
|
})() : (()=>{ |
|
|
|
if(!usertokens[options.username] && !options.isAuthCall) { |
|
|
|
RESTAPI.authenticate( utils.assign( {isAuthCall : true}, options ) , onauthenticated, function(err){ |
|
|
|
// PB : TODO -- Retry without auth... |
|
|
|
console.error('Auth failed or not accessible') |
|
|
|
reject(err) |
|
|
|
}) |
|
|
|
} |
|
|
|
else onauthenticated() |
|
|
|
})() |
|
|
|
|
|
|
|
function onauthenticated(){ |
|
|
|
|
|
|
|
var acquirer = getHTTPorS(options) |
|
|
|
const req = acquirer.request(options, res => { |
|
|
|
if (res.statusCode < 200 || res.statusCode >= 300) { |
|
|
|
// new Error('statusCode=' + res.statusCode) |
|
|
|
return reject('statusCode = ' + res.statusCode); |
|
|
|
} |
|
|
|
var body = []; |
|
|
|
// res.setEncoding('utf8'); |
|
|
|
res.on('data', (chunk)=>{ body.push(chunk); }); |
|
|
|
res.on('end', ()=>{ |
|
|
|
try { |
|
|
|
if(res.headers['content-type'] && res.headers['content-type'].split(';').find( (i)=> i.trim() === 'application/json' )) body = JSON.parse(Buffer.concat(body).toString()); |
|
|
|
else body = Buffer.concat(body).toString(); |
|
|
|
} catch(e) { return reject(e); } |
|
|
|
resolve(body); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
req.on('error', error => { reject(error) }) |
|
|
|
if(options.payload) req.write(data) |
|
|
|
req.end() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var usertokens = {} |
|
|
|
|
|
|
|
RESTAPI.authenticate = function(options, resolve, reject){ |
|
|
|
options.headers = options.headers || { 'Content-Type': 'application/json' } |
|
|
|
|
|
|
|
if(!usertokens[options.username]) { |
|
|
|
// Authenticate and acquire token. |
|
|
|
// https://git.bbh/api/v1/users/<username>/tokens |
|
|
|
// curl -XPOST -H "Content-Type: application/json" -k -d '{"name":"demo"}' -u demo:demo123 http://git.bbh/api/v1/users/demo/tokens |
|
|
|
var _options = Object.assign({}, options) |
|
|
|
// _options.username = 'demo' |
|
|
|
// _options.password = 'demo123' |
|
|
|
_options.method = 'POST' |
|
|
|
_options.headers.Authorization = `Basic ${Buffer.from(`${_options.username}:${_options.password}`).toString('base64')}` |
|
|
|
_options.path = `/api/v1/users/${_options.username}/tokens` |
|
|
|
|
|
|
|
var postoptions = { name : _options.username } |
|
|
|
delete _options.username |
|
|
|
delete _options.password |
|
|
|
_options.payload = postoptions |
|
|
|
RESTAPI.post( _options, function(tokenresp){ |
|
|
|
// tokenresp = JSON.parse(tokenresp) |
|
|
|
usertokens[options.username] = tokenresp.sha1 |
|
|
|
resolve(tokenresp) |
|
|
|
}, |
|
|
|
function(err){ reject(err) } |
|
|
|
) |
|
|
|
} |
|
|
|
else resolve(tokenresp.sha1) |
|
|
|
} |
|
|
|
|
|
|
|
RESTAPI.put = RESTAPI.get = RESTAPI.post = RESTAPI.method |
|
|
|
|
|
|
|
return RESTAPI |
|
|
|
})(); |
|
|
|
|
|
|
|
var getHTTPorS = function(options){ return options.protocol.startsWith('https') ? https : http; } |
|
|
|
|
|
|
|
const GITEA = (function(){ |
|
|
|
|
|
|
|
function GITEA(){} |
|
|
|
GITEA.APIROOT = '/api/v1' |
|
|
|
GITEA.repository = { |
|
|
|
fork( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
// forkoptions = { owner : httpoptions.username, repo : {{reoptoFork}} } |
|
|
|
// giteaoptions = { |
|
|
|
// organization string |
|
|
|
// --- organization name, if forking into an organization |
|
|
|
// } |
|
|
|
// http://try.gitea.io/api/v1/repos/{owner}/{repo}/forks |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${cmdoptions.owner}/${cmdoptions.repo}/forks` |
|
|
|
httpoptions.method = 'POST' |
|
|
|
httpoptions.payload = giteaoptions; |
|
|
|
return RESTAPI.post(httpoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
, updateattributes( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${cmdoptions.owner}/${cmdoptions.repo}` |
|
|
|
httpoptions.method = 'PATCH' |
|
|
|
httpoptions.payload = giteaoptions; |
|
|
|
return RESTAPI.post(httpoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
, exists( httpoptions, repo, owner ) { |
|
|
|
// /users/{username} // Get a user |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${owner}/${repo}` |
|
|
|
httpoptions.method = 'GET' |
|
|
|
return new Promise( (resolve, reject) => { |
|
|
|
RESTAPI.get(httpoptions, function(o){ |
|
|
|
if(o.id) return resolve(true) |
|
|
|
return resolve(false) }, reject || function(){ return false } ) |
|
|
|
}) |
|
|
|
} |
|
|
|
, addcollaborator( httpoptions, repodef, owner, collaborator, permission ) { |
|
|
|
permission = permission || 'Read' |
|
|
|
httpoptions.payload = { permission } |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/repos/${owner}/${repodef.repo}/collaborators/${collaborator}` |
|
|
|
httpoptions.method = 'PUT' |
|
|
|
return new Promise( (resolve, reject) => { |
|
|
|
RESTAPI.put(httpoptions, function(o){ |
|
|
|
// |
|
|
|
if(o === "") return resolve(true) |
|
|
|
return resolve(false) }, reject || function(){ return false } ) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
GITEA.user = { |
|
|
|
getuser( httpoptions, cmdoptions, giteaoptions, resolve, reject ){ |
|
|
|
// /users/{username} // Get a user |
|
|
|
httpoptions.path = `${GITEA.APIROOT}/users/${httpoptions.username}` |
|
|
|
httpoptions.method = 'GET' |
|
|
|
return RESTAPI.get(httpoptions, giteaoptions, resolve || function(){}, reject || function(){} ) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return GITEA |
|
|
|
})(); |
|
|
|
|
|
|
|
// Wrapper for Git shell operations. Some meta operations will map to a bunch of GIT commands. |
|
|
|
const GIT = (function(){ |
|
|
|
function GIT(){} |
|
|
|
|
|
|
|
Object.assign(GIT, { |
|
|
|
'switch user'(username){ |
|
|
|
var httpoptions = new URL(selectedinstance.reposerver); |
|
|
|
httpoptions.username = selectedinstance.username |
|
|
|
httpoptions.password = selectedinstance.password |
|
|
|
|
|
|
|
return GITEA.user.getuser(httpoptions).then(()=>{ |
|
|
|
return nodeShellExec('git', ['config', '--replace-all', 'user.name', username], |
|
|
|
{ |
|
|
|
inherit: true, shell: true, |
|
|
|
env: ENV |
|
|
|
, cwd: instanceroot + '/' + repo |
|
|
|
, runas: processedArgs.runas |
|
|
|
, title: `'git', ${['config', '--replace-all', 'user.name', username].join(' ')}` |
|
|
|
}) |
|
|
|
} |
|
|
|
) |
|
|
|
.catch(e => { |
|
|
|
console.error(e + 'Could not switch. Probably no such user.') |
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
return GIT |
|
|
|
})(); |
|
|
|
|
|
|
|
function createInstanceData(target, source) { |
|
|
|
|
|
|
|
var sourceinstance = source || target; |