diff --git a/Readme.md b/Readme.md index 4b4fc19..aabfd97 100644 --- a/Readme.md +++ b/Readme.md @@ -32,27 +32,48 @@ npm nix See `./result` - for binaries build by nix. To make the available add to path by running `PATH="${PATH:+${PATH}:}${PWD}/result/bin/"` -## +## Running All data will be persisted in application directory (default `~/.gitpear`). To change it. Provide environment variable `GIT_PEAR` * `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:/` - * `git pear init [-s, --share] ` - 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/). 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 ` later - * `git pear share ` - makes repository sharable - * `git pear unshare ` - stop sharing repository - * `git pear list [-s, --shared]` - list all or (only shared) repositories -## Usage example (NO PUSH) +### ACL (for authenticated access to enable support of PUSH) -Please not this is only remote helper and its intention is only to enable direct `clone|fetch|pull` of repository hosted on private computer. +Support of `push` capabilities only enabled for authenticated users. Currently supported authentication is based on [NIP98](https://github.com/nostr-protocol/nips/blob/master/98.md). +To start daemon with authenticated support provile environment varibales `GIT_PEAR_AUTH` with value `nip98` and `GIT_PEAR_AUTH_NSEC` with value of your [NIP19 nsec](https://github.com/nostr-protocol/nips/blob/master/19.md). +For example: +``` +GIT_PEAR_AUTH=nip98 GIT_PEAR_AUTH_NSEC=nsec.... git pear daemon -s +``` -Collaboration is possible however with the following flow between Alice and Bob in a pure peer-to-peer manner of git. +To manage access to repository use one or combination of the following commands, if `path` is not provide the command will be executed in the current directory. For `userId` use [NIP19 npub](https://github.com/nostr-protocol/nips/blob/master/19.md). + +* `git pear acl [command] ` - ACL managegement +* `git pear acl list [userId] ` - list repository visitbility and user's role (or roles of all users if userId is not provided) +* `git pear acl add ` - add user as a "role" to repository, available roles are `viewer`, `contributor`, `admin`. Roles exaplained: + * `viewer` - can read all branches; + * `contributor` - can edit all branches except protected (default master) + * `admin` - can edit protected branches +* `git pear acl remove ` - revoke use access to repository + + +### Branch protection rules +It is possible to setup basic branch protection rules (master is proteted by default). +* `git pear branch`, same as `git pear branch list .` - list protection rules +* `git pear branch add ` - mark branch as protected (defatul repo path is ".") +* `git pear branch remove ` - unmark branch as protected + +# Examples of usage + +## Un authenticated usage example (no push) + +Collaboration is possible with the following flow between Alice and Bob in a pure peer-to-peer manner of git. 1. Both Alice and Bob have gitpear installed and Alice wants Bob to help her with repo Repo 2. Alice steps are: @@ -95,4 +116,40 @@ git fetch origin git pull ``` -## Usage example (PUSH) +## Authenticated usage example (push) + +Collaboration is possible with the following flow between Carol and David in a pure peer-to-peer manner of git. + +### Carol steps (as a server of code) +1. Start daemon +* `GIT_PEAR_AUTH_NSEC= GIT_PEAR_AUTH='nip98' git pear daemon -s` +2. Go to repository +* `cd repo` +3. Initialize git pear repository +* `git pear init .` +4. Share repository wit hviben visibility () - (default is `public`) +* `git pear share . ` +5. Add Daviv as a `contirbutor`. +6. List David's npub as a contributor +* `git pear acl add :contributor` +7. Retreive repo url and share it with Dave +* `git pear list -s` + +### Dave side (a collaborator for code) +1. Start daemon. This will be needed later for push. Not that no auth or sec are provided which means that push to this place will not be supportedd. +* `git pear daemon -s` +2. Clone repository. Authorization data and type are necesary for server (Carol) to grant corresponding access persmissions +* `GIT_PEAR_AUTH_NSEC= GIT_PEAR_AUTH='nip98' git clone pear:///` +3. Do the necessary change in separate branch +* `git checkout -b feat/david` +* // do change +* `git add .` +* `git commit -s -m 'made by David'` +4. Push branch to origin +* `GIT_PEAR_AUTH_NSEC= GIT_PEAR_AUTH='nip98' git push origin feat/david` + +### Carol steps +1. For Carol the changes will arrive as branch `feat/david` into her `pear` +* `git fetch pear` +2. From there she can do +* `git diff pear/feat/david` or `git pull pear feat/david` ... merge to master and push to `pear` diff --git a/src/cli.js b/src/cli.js index 3756e19..4a5361b 100755 --- a/src/cli.js +++ b/src/cli.js @@ -38,17 +38,23 @@ program process.exit(1) } - home.createAppFolder(name) - console.log(`Added project "${name}" to gitpear`) - await git.createBareRepo(name) - console.log(`Created bare repo for "${name}"`) - await git.addRemote(name) - console.log(`Added git remote for "${name}" as "pear"`) + try { + home.createAppFolder(name) + console.log(`Added project "${name}" to gitpear`) + } catch (e) { } + try { + await git.createBareRepo(name) + console.log(`Created bare repo for "${name}"`) + } catch (e) { } + try { + await git.addRemote(name) + console.log(`Added git remote for "${name}" as "pear"`) + } catch (e) { } if (options.share) { - home.shareAppFolder(name) - acl.setACL(name) - await git.push() + try { home.shareAppFolder(name) } catch (e) { } + try { acl.setACL(name) } catch (e) { } + try { await git.push() } catch (e) { } console.log(`Shared "${name}" project`) } }) @@ -71,13 +77,55 @@ program process.exit(1) } - home.shareAppFolder(name) - acl.setACL(name, { visibility: v }) - await git.push() + try { home.shareAppFolder(name) } catch (e) { } + try { acl.setACL(name, { visibility: v }) } catch (e) { } + try { await git.push() } catch (e) { } console.log(`Shared "${name}" project, as ${v} repo`) return }) +program + .command('branch') + .description('branch protection rules') + .addArgument(new commander.Argument('[a]', 'actiont to perform').choices(['add', 'remove', 'list']).default('list')) + .addArgument(new commander.Argument('[b]', 'branch name').default('')) + .addArgument(new commander.Argument('[p]', 'path to the repo').default('.')) + .action(async (a, b, p, options) => { + const fullPath = path.resolve(p) + if (!fs.existsSync(path.join(fullPath, '.git'))) { + console.error('Not a git repo') + process.exit(1) + } + + const name = fullPath.split(path.sep).pop() + if (!home.isInitialized(name)) { + console.error(`${name} is not initialized`) + process.exit(1) + } + + if (a === 'list' && !b) { logBranches(name) } + + if (a === 'add') { + acl.addProtectedBranch(name, b) + logBranches(name) + } + + if (a === 'remove') { + acl.removeProtectedBranch(name, b) + logBranches(name) + } + + function logBranches(name) { + const repoACL = acl.getACL(name) + console.log('Visibility:', '\t', repoACL.visibility) + console.log('Branch:') + for (const branch of repoACL.protectedBranches) { console.log(branch) } + } + + return + }) + + program .command('acl') .description('set acl of a gitpear repo') @@ -85,6 +133,8 @@ program .addArgument(new commander.Argument('[u]', 'user to add/remove/list').default('')) .addArgument(new commander.Argument('[p]', 'path to the repo').default('.')) .action(async (a, u, p, options) => { + + // TODO: add branch protection logic const fullPath = path.resolve(p) if (!fs.existsSync(path.join(fullPath, '.git'))) { console.error('Not a git repo') @@ -99,7 +149,7 @@ program const repoACL = acl.getACL(name) if (a === 'list' && !u) { - console.log('Visibility:', '\t', repoACL.visibility) + console.log('Repo Visibility:', '\t', repoACL.visibility) console.log('User:', '\t', 'Role:') for (const user in repoACL.ACL) { console.log(user, '\t', repoACL.ACL[user]) @@ -108,8 +158,8 @@ program } if (a === 'list') { - console.log('Visibility:', '\t', repoACL.visibility) - console.log('User:', u, '\t', 'Role:', repoACL.ACL[u]) + console.log('Repo Visibility:', '\t', repoACL.visibility) + console.log('User:', u, '\t', repoACL.ACL[u]) return }