mirror of
https://github.com/aljazceru/gitpear.git
synced 2025-12-17 06:04:25 +01:00
Merge branch 'dzdidi:master' into master
This commit is contained in:
@@ -38,10 +38,11 @@ All data will be persisted in application directory (default `~/.gitpear`). To c
|
|||||||
|
|
||||||
* `git pear daemon <-s, --start | -k, --stop>` - start or stop daemon
|
* `git pear daemon <-s, --start | -k, --stop>` - start or stop daemon
|
||||||
* `git pear key` - print out public key. Share it with your peers so that they can do `git pull pear:<public key>/<repo name>`
|
* `git pear key` - print out public key. Share it with your peers so that they can do `git pull pear:<public key>/<repo name>`
|
||||||
* `git pear init [-s, --share] <path>` - It will create [bare repository](https://git-scm.com/docs/git-init#Documentation/git-init.txt---bare) of the same name in application directory (default ~/.gitpear/<repository name>). It will add [git remote](https://git-scm.com/docs/git-remote) in current repository with name `pear`. So just like in traditional flow doing `git push orign`, here we do `git push pear`. By default repository will not be shared. To enable sharing provide `-s` or call `gitpear share <path>` later
|
* `git pear init <path> [-s, --share [branch]]` - It will create [bare repository](https://git-scm.com/docs/git-init#Documentation/git-init.txt---bare) of the same name in application directory (default ~/.gitpear/<repository name>). It will add [git remote](https://git-scm.com/docs/git-remote) in current repository with name `pear`. So just like in traditional flow doing `git push orign`, here we do `git push pear`. By default repository will not be shared. To enable sharing provide `-s | --share [branch]` (default branch to share is current) or call `gitpear share <path>` later
|
||||||
* `git pear share <path>` - makes repository sharable
|
* `git pear share [-p, --path [path (default: ".")]> [-b, --branch [branch name (default: "_current_")] [-v, --visibility <private|public> (default: "public")]` - share repository, if branch is not specified, default branch will be shared
|
||||||
* `git pear unshare <path>` - stop sharing repository
|
* `git pear unshare <path>` - stop sharing repository
|
||||||
* `git pear list [-s, --shared]` - list all or (only shared) repositories
|
* `git pear list [-s, --shared]` - list all or (only shared) repositories
|
||||||
|
* `git pear list <url>` - list repositories of a peer
|
||||||
|
|
||||||
### ACL (for authenticated access to enable support of PUSH)
|
### ACL (for authenticated access to enable support of PUSH)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
rm -rf result
|
rm -rf result
|
||||||
nix build '.#'
|
nix build '.#' --extra-experimental-features nix-command --extra-experimental-features flakes
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
dream2nix.lib.makeFlakeOutputs {
|
dream2nix.lib.makeFlakeOutputs {
|
||||||
systems = [ "x86_64-darwin" "x86_64-linux" "aarch64-darwin" ];
|
systems = [ "x86_64-darwin" "x86_64-linux" "aarch64-darwin" "aarch64-linux" ];
|
||||||
config.projectRoot = ./.;
|
config.projectRoot = ./.;
|
||||||
source = ./.;
|
source = ./.;
|
||||||
projects = ./projects.toml;
|
projects = ./projects.toml;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ROLES = {
|
|||||||
}
|
}
|
||||||
const DEFAULT_ACL = {
|
const DEFAULT_ACL = {
|
||||||
visibility: 'public', // public|private
|
visibility: 'public', // public|private
|
||||||
protectedBranches: ['master'],
|
protectedBranches: ['master', 'main'],
|
||||||
ACL: {}
|
ACL: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
src/cli.js
35
src/cli.js
@@ -22,7 +22,7 @@ program
|
|||||||
.command('init')
|
.command('init')
|
||||||
.description('initialize a gitpear repo')
|
.description('initialize a gitpear repo')
|
||||||
.addArgument(new commander.Argument('[p]', 'path to the repo').default('.'))
|
.addArgument(new commander.Argument('[p]', 'path to the repo').default('.'))
|
||||||
.option('-s, --share', 'share the repo, default false')
|
.option('-s, --share [branch]', 'share the repo as public, default false, default branch is current', '')
|
||||||
.action(async (p, options) => {
|
.action(async (p, options) => {
|
||||||
const fullPath = path.resolve(p)
|
const fullPath = path.resolve(p)
|
||||||
if (!fs.existsSync(path.join(fullPath, '.git'))) {
|
if (!fs.existsSync(path.join(fullPath, '.git'))) {
|
||||||
@@ -51,21 +51,27 @@ program
|
|||||||
console.log(`Added git remote for "${name}" as "pear"`)
|
console.log(`Added git remote for "${name}" as "pear"`)
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
|
|
||||||
|
let branchToShare = await git.getCurrentBranch()
|
||||||
|
if (options.share && options.share !== true) {
|
||||||
|
branchToShare = options.share
|
||||||
|
}
|
||||||
|
|
||||||
if (options.share) {
|
if (options.share) {
|
||||||
try { home.shareAppFolder(name) } catch (e) { }
|
try { home.shareAppFolder(name) } catch (e) { }
|
||||||
try { acl.setACL(name) } catch (e) { }
|
try { acl.setACL(name) } catch (e) { }
|
||||||
try { await git.push() } catch (e) { }
|
try { await git.push(branchToShare) } catch (e) { }
|
||||||
console.log(`Shared "${name}" project`)
|
console.log(`Shared "${name}" project, ${branchToShare} branch`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('share')
|
.command('share')
|
||||||
.description('share a gitpear repo')
|
.description('share a gitpear repo')
|
||||||
.addArgument(new commander.Argument('[p]', 'path to the repo').default('.'))
|
.option('-b, --branch [b]', 'branch to share, default is current branch', '')
|
||||||
.addArgument(new commander.Argument('[v]', 'visibility of the repo').default('public'))
|
.option('-v, --visibility [v]', 'visibility of the repo', 'public')
|
||||||
.action(async (p, v, options) => {
|
.option('-p, --path [p]', 'path to the repo', '.')
|
||||||
const fullPath = path.resolve(p)
|
.action(async (options) => {
|
||||||
|
const fullPath = path.resolve(options.path)
|
||||||
if (!fs.existsSync(path.join(fullPath, '.git'))) {
|
if (!fs.existsSync(path.join(fullPath, '.git'))) {
|
||||||
console.error('Not a git repo')
|
console.error('Not a git repo')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@@ -77,10 +83,12 @@ program
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentBranch = await git.getCurrentBranch()
|
||||||
|
const branchToShare = options.branch || currentBranch
|
||||||
try { home.shareAppFolder(name) } catch (e) { }
|
try { home.shareAppFolder(name) } catch (e) { }
|
||||||
try { acl.setACL(name, { visibility: v }) } catch (e) { }
|
try { acl.setACL(name, { visibility: options.visibility }) } catch (e) { }
|
||||||
try { await git.push() } catch (e) { }
|
try { await git.push(branchToShare) } catch (e) { }
|
||||||
console.log(`Shared "${name}" project, as ${v} repo`)
|
console.log(`Shared "${name}" project, ${branchToShare} branch, as ${options.visibility} repo`)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -224,10 +232,13 @@ program
|
|||||||
program
|
program
|
||||||
.command('list')
|
.command('list')
|
||||||
.description('list all gitpear repos')
|
.description('list all gitpear repos')
|
||||||
|
.addArgument(new commander.Argument('[u]', 'url to remote pear').default(''))
|
||||||
.option('-s, --shared', 'list only shared repos')
|
.option('-s, --shared', 'list only shared repos')
|
||||||
.action((p, options) => {
|
.action((u, options) => {
|
||||||
|
if (u) return require('./list-remote')(u)
|
||||||
|
|
||||||
const k = home.readPk()
|
const k = home.readPk()
|
||||||
const s = options.opts().shared
|
const s = options.shared
|
||||||
home.list(s).forEach(n => console.log(n, ...(s ? ['\t', `pear://${k}/${n}`] : [])))
|
home.list(s).forEach(n => console.log(n, ...(s ? ['\t', `pear://${k}/${n}`] : [])))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,11 @@ const targetKey = matches[1]
|
|||||||
const repoName = matches[2]
|
const repoName = matches[2]
|
||||||
|
|
||||||
const store = new Corestore(RAM)
|
const store = new Corestore(RAM)
|
||||||
const swarm = new Hyperswarm({ keyPair: home.getKeyPair() })
|
const swarmOpts = {}
|
||||||
|
if (process.env.GIT_PEAR_AUTH && process.env.GIT_PEAR_AUTH !== 'native') {
|
||||||
|
swarmOpts.keyPair = home.getKeyPair()
|
||||||
|
}
|
||||||
|
const swarm = new Hyperswarm(swarmOpts)
|
||||||
|
|
||||||
if (!home.isDaemonRunning()) {
|
if (!home.isDaemonRunning()) {
|
||||||
console.error('Please start git pear daemon')
|
console.error('Please start git pear daemon')
|
||||||
|
|||||||
31
src/git.js
31
src/git.js
@@ -20,6 +20,25 @@ async function getCommit () {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getCurrentBranch () {
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
const process = spawn('git', ['rev-parse', '--abbrev-ref', 'HEAD'])
|
||||||
|
let outBuffer = Buffer.from('')
|
||||||
|
process.stdout.on('data', data => {
|
||||||
|
outBuffer = Buffer.concat([outBuffer, data])
|
||||||
|
})
|
||||||
|
|
||||||
|
let errBuffer = Buffer.from('')
|
||||||
|
process.stderr.on('err', data => {
|
||||||
|
errBuffer = Buffer.concat([errBuffer, data])
|
||||||
|
})
|
||||||
|
|
||||||
|
process.on('close', code => {
|
||||||
|
return code === 0 ? resolve(outBuffer.toString().replace('\n', '')) : reject(errBuffer)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function lsPromise (url) {
|
async function lsPromise (url) {
|
||||||
const ls = spawn('git', ['ls-remote', url])
|
const ls = spawn('git', ['ls-remote', url])
|
||||||
const res = {}
|
const res = {}
|
||||||
@@ -201,4 +220,14 @@ async function unpackStream (packStream) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { lsPromise, uploadPack, unpackFile, unpackStream, createBareRepo, addRemote, push, getCommit }
|
module.exports = {
|
||||||
|
lsPromise,
|
||||||
|
uploadPack,
|
||||||
|
unpackFile,
|
||||||
|
unpackStream,
|
||||||
|
createBareRepo,
|
||||||
|
addRemote,
|
||||||
|
push,
|
||||||
|
getCommit,
|
||||||
|
getCurrentBranch,
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const fs = require('fs')
|
|||||||
const APP_HOME = process.env.GIT_PEAR || `${homedir}/.gitpear`
|
const APP_HOME = process.env.GIT_PEAR || `${homedir}/.gitpear`
|
||||||
|
|
||||||
function createAppFolder (name) {
|
function createAppFolder (name) {
|
||||||
fs.mkdirSync(`${APP_HOME}/${name}/code`, { recursive: true })
|
fs.mkdirSync(`${APP_HOME}/${name}`, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
function shareAppFolder (name) {
|
function shareAppFolder (name) {
|
||||||
@@ -24,7 +24,7 @@ function unshareAppFolder (name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isInitialized (name) {
|
function isInitialized (name) {
|
||||||
return fs.existsSync(`${APP_HOME}/${name}/code/HEAD`)
|
return fs.existsSync(`${APP_HOME}/${name}/HEAD`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isShared (name) {
|
function isShared (name) {
|
||||||
@@ -39,7 +39,7 @@ function list (sharedOnly) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCodePath (name) {
|
function getCodePath (name) {
|
||||||
return `${APP_HOME}/${name}/code`
|
return `${APP_HOME}/${name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPk () {
|
function readPk () {
|
||||||
|
|||||||
53
src/list-remote.js
Normal file
53
src/list-remote.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
const ProtomuxRPC = require('protomux-rpc')
|
||||||
|
|
||||||
|
const Hyperswarm = require('hyperswarm')
|
||||||
|
const crypto = require('hypercore-crypto')
|
||||||
|
|
||||||
|
const home = require('./home')
|
||||||
|
const auth = require('./auth')
|
||||||
|
|
||||||
|
module.exports = async function listRemote (url) {
|
||||||
|
const matches = url.match(/pear:\/\/([a-f0-9]{64})/)
|
||||||
|
|
||||||
|
if (!matches || matches.length < 2) {
|
||||||
|
console.error('Invalid URL')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetKey = matches[1]
|
||||||
|
console.log('Connecting to:', targetKey)
|
||||||
|
|
||||||
|
const swarmOpts = {}
|
||||||
|
if (process.env.GIT_PEAR_AUTH && process.env.GIT_PEAR_AUTH !== 'native') {
|
||||||
|
swarmOpts.keyPair = home.getKeyPair()
|
||||||
|
}
|
||||||
|
const swarm = new Hyperswarm(swarmOpts)
|
||||||
|
|
||||||
|
swarm.join(crypto.discoveryKey(Buffer.from(targetKey, 'hex')), { server: false })
|
||||||
|
|
||||||
|
swarm.on('connection', async (socket) => {
|
||||||
|
const rpc = new ProtomuxRPC(socket)
|
||||||
|
|
||||||
|
let payload = { body: { url, method: 'get-repos' } }
|
||||||
|
if (process.env.GIT_PEAR_AUTH && process.env.GIT_PEAR_AUTH !== 'native') {
|
||||||
|
payload.header = await auth.getToken(payload.body)
|
||||||
|
console.debug('Retreiving data using authenticated access')
|
||||||
|
} else {
|
||||||
|
console.debug('Retreiving data using un-authenticated access')
|
||||||
|
}
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
const reposRes = await rpc.request('get-repos', Buffer.from(JSON.stringify(payload)))
|
||||||
|
const repositories = JSON.parse(reposRes.toString())
|
||||||
|
if (!repositories) {
|
||||||
|
console.error('Failed to retrieve repositories')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Repositories:', '\t', 'HEAD')
|
||||||
|
for (const repo in repositories) {
|
||||||
|
console.log(repo, '\t', repositories[repo])
|
||||||
|
}
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -15,14 +15,15 @@ module.exports = async function setState (store, drives = {}) {
|
|||||||
await drives[repo].ready()
|
await drives[repo].ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
const ls = await git.lsPromise(home.getCodePath(repo))
|
const homePath = home.getCodePath(repo)
|
||||||
|
const ls = await git.lsPromise(homePath)
|
||||||
|
|
||||||
repositories[repo] = {}
|
repositories[repo] = {}
|
||||||
for (const ref in ls) {
|
for (const ref in ls) {
|
||||||
repositories[repo][ref] = ls[ref]
|
repositories[repo][ref] = ls[ref]
|
||||||
announcedRefs[ls[ref]] = repo
|
announcedRefs[ls[ref]] = repo
|
||||||
|
|
||||||
const localPackStream = git.uploadPack(home.getCodePath(repo), ls[ref])
|
const localPackStream = git.uploadPack(homePath, ls[ref])
|
||||||
const driveStream = drives[repo].createWriteStream(`/packs/${ls[ref]}.pack`)
|
const driveStream = drives[repo].createWriteStream(`/packs/${ls[ref]}.pack`)
|
||||||
localPackStream.on('ready', () => localPackStream.stdout.pipe(driveStream))
|
localPackStream.on('ready', () => localPackStream.stdout.pipe(driveStream))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user