@nimba/git
v0.6.0
Published
Nimba's version-control surface: git plumbing, forge clients (Forgejo, local bare repos), and the GitHub bridge used for dependency resolution and releases.
Readme
@nimba/git
Nimba's version-control surface. Everything that talks to git or to a git host
lives here, so @nimba/core keeps the Salesforce domain and @nimba/sf keeps the
Salesforce client.
Three layers, deliberately separate:
GitBridge— the local machine. Clone, check out a branch, put files in a working tree, turn them into a commit, push it. No server concept at all.ForgeBridge— the hosting side. Repositories, forks, pull requests, merges.GitHubBridge— the read/release surface dependency resolution runs on: file contents at a ref, releases, annotated-tag package metadata, archives.
A caller that only needs "commit what I just retrieved" takes a GitBridge and
never learns what hosts the repository. A caller that needs a fork or a pull
request takes a ForgeBridge and never shells git.
Neither knows why the work is happening. Anything whose vocabulary is bounties, contributors or payouts belongs to the caller.
Forges
import { createGitBridge, createLocalForge, createForgejoForge } from '@nimba/git';
// Bare repositories on disk. No server. Real git: forks carry history, merges
// really conflict.
const forge = createLocalForge({ root: '/var/lib/nimba/git' });
// Forgejo (Gitea-compatible API). One admin token; repository owners are
// organizations created on demand.
const forge = createForgejoForge({
apiUrl: 'http://forgejo:3000', // reachable address — REST *and* git remotes
publicUrl: 'https://example.com/git', // browser links only
token: process.env.FORGEJO_TOKEN!,
user: 'nimba',
});Both satisfy ForgeBridge, so code written against the local forge runs
unchanged against Forgejo. That is the point: the whole chain works on a laptop
with no server.
The chain
const git = createGitBridge({ root: '/var/lib/nimba/git' });
const base = await forge.ensureRepo({ owner: 'acme', repo: 'core', defaultBranch: 'integration' });
const seed = await git.workspace(base, 'integration');
// …write files into seed.dir…
await seed.commitAll('seed: import from acme');
await seed.push();
const fork = await forge.fork(base, { owner: 'hunter-1', repo: 'core-wi-101' });
const work = await git.workspace(fork, 'work/wi-101', { from: 'origin/integration' });
// …write files into work.dir…
const commit = await work.commitAll('retrieve: 2 components'); // null when nothing changed
await work.push();
const pr = await forge.openPullRequest({
base,
baseBranch: 'integration',
head: fork,
headBranch: 'work/wi-101',
title: 'WI-101',
});
const merge = await forge.mergePullRequest(base, pr.number);
if (!merge.merged) console.log(merge.reason, merge.conflicts);Notes
- Workspaces are caches, not state. Every
workspace()resets the checkout to the remote, so a directory left behind by a crashed process cannot contribute a file to the next commit. Delete one at any time. - Line endings are pinned.
core.autocrlfis off on every invocation, because source written on one platform is deployed on another and must survive byte for byte. - Remotes carry credentials.
ForgeRepo.remoteis an internal value; log it and you have logged a token.forge.webUrl()is the safe one. - A conflict is a result, not an error.
mergePullRequestreturns{ merged: false, conflicts, reason }rather than throwing.
