Skip to content

Commit 164ad26

Browse files
committed
feat: initial commit
Establishes git repo for plumbing. Used to be part of git-cms.
1 parent 7d9daf7 commit 164ad26

10 files changed

Lines changed: 559 additions & 0 deletions

File tree

.editorconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
indent_style = space
8+
indent_size = 4
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = false
12+
insert_final_newline = false

.gitignore

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Dependency directories
2+
node_modules/
3+
jspm_packages/
4+
bun.lockb
5+
node_modules.bun/
6+
7+
# Deno
8+
.deno/
9+
deno.lock
10+
11+
# Build outputs
12+
dist/
13+
build/
14+
.next/
15+
.nuxt/
16+
.output/
17+
.cache/
18+
out/
19+
20+
# Logs
21+
logs
22+
*.log
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
lerna-debug.log*
27+
.pnpm-debug.log*
28+
29+
# Diagnostic reports (https://nodejs.org/api/report.html)
30+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
31+
32+
# Runtime data
33+
pids
34+
*.pid
35+
*.seed
36+
*.pid.lock
37+
38+
# Directory for instrumented libs generated by jscoverage/JSCover
39+
lib-cov
40+
41+
# Coverage directory used by tools like istanbul
42+
coverage
43+
*.lcov
44+
45+
# nyc documentation
46+
.nyc_output
47+
48+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
49+
.grunt
50+
51+
# Bower dependency directory (https://bower.io/)
52+
bower_components
53+
54+
# node-waf configuration
55+
.lock-wscript
56+
57+
# Compiled binary addons (https://nodejs.org/api/addons.html)
58+
build/Release
59+
60+
# Dependency directories
61+
jspm_packages/
62+
63+
# TypeScript v1 declaration files
64+
typings/
65+
66+
# TypeScript cache
67+
*.tsbuildinfo
68+
69+
# Optional npm cache directory
70+
.npm
71+
72+
# Optional eslint cache
73+
.eslintcache
74+
75+
# Optional stylelint cache
76+
.stylelintcache
77+
78+
# Optional REPL history
79+
.node_repl_history
80+
81+
# Output of 'npm pack'
82+
*.tgz
83+
84+
# Yarn Integrity file
85+
.yarn-integrity
86+
87+
# dotenv environment variable files
88+
.env
89+
.env.development.local
90+
.env.test.local
91+
.env.production.local
92+
.env.local
93+
94+
# parcel-bundler cache (https://parceljs.org/)
95+
.cache
96+
.parcel-cache
97+
98+
# Next.js build output
99+
.next
100+
out
101+
102+
# Nuxt.js build / generate output
103+
.nuxt
104+
dist
105+
106+
# Gatsby files
107+
.cache/
108+
public/
109+
110+
# vue-cli dist
111+
dist/
112+
113+
# Serverless directories
114+
.serverless/
115+
116+
# FuseBox cache
117+
.fusebox/
118+
119+
# DynamoDB Local files
120+
.dynamodb/
121+
122+
# Tern JS port file
123+
.tern-port
124+
125+
# Stores VS Code state
126+
.vscode/*
127+
!.vscode/settings.json
128+
!.vscode/tasks.json
129+
!.vscode/launch.json
130+
!.vscode/extensions.json
131+
!.vscode/code-actions.json
132+
133+
# IntelliJ IDEA
134+
.idea/
135+
136+
.crush
137+
.obsidian
138+
139+
# macOS
140+
.DS_Store
141+
.AppleDouble
142+
.LSOverride
143+
Icon
144+
145+
146+
# Thumbnails
147+
._*
148+
149+
# Files that might appear in the root of a volume
150+
.DocumentRevisions-V100
151+
.fseventsd
152+
.Spotlight-V100
153+
.TemporaryItems
154+
.Trashes
155+
.VolumeIcon.icns
156+
.com.apple.timemachine.donotpresent
157+
158+
# Directories potentially created by macOS
159+
.AppleDB
160+
.AppleDesktop
161+
Network Trash Folder
162+
Temporary Items
163+
.apdisk
164+
165+
# Other Editors
166+
*.swp
167+
*.swo
168+
*~

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# @git-stunts/plumbing
2+
3+
A robust, class-based wrapper for Git binary execution. Designed for "Git Stunts" applications that bypass the porcelain and interact directly with the object database.
4+
5+
## Features
6+
7+
- **Class-based API**: Encapsulates `cwd` and state.
8+
- **Zero Dependencies**: Uses Node.js standard library.
9+
- **Plumbing First**: Optimized for `commit-tree`, `hash-object`, and `update-ref`.
10+
- **Telemetry**: Error messages include `stdout` and `stderr` for easier debugging.
11+
12+
## Installation
13+
14+
```bash
15+
npm install @git-stunts/plumbing
16+
```
17+
18+
## Usage
19+
20+
```javascript
21+
import GitPlumbing from '@git-stunts/plumbing';
22+
23+
const git = new GitPlumbing({ cwd: './my-repo' });
24+
25+
// Create a blob
26+
const blobOid = git.execute({
27+
args: ['hash-object', '-w', '--stdin'],
28+
input: 'Hello world'
29+
});
30+
31+
// Create a commit pointing to the empty tree
32+
const commitSha = git.execute({
33+
args: ['commit-tree', git.emptyTree, '-m', 'Stunt #1'],
34+
});
35+
36+
// Update a ref
37+
git.updateRef({
38+
ref: 'refs/_blog/stunt',
39+
newSha: commitSha
40+
});
41+
```
42+
43+
## API
44+
45+
### `new GitPlumbing({ cwd })`
46+
Creates a new instance tied to a specific directory.
47+
48+
### `execute({ args, input })`
49+
Executes a git command. Throws if the command fails.
50+
51+
### `executeWithStatus({ args })`
52+
Executes a git command and returns `{ stdout, status }`, allowing you to handle non-zero exit codes (like `git diff`) without throwing.
53+
54+
### `emptyTree`
55+
Property returning the well-known SHA-1 of the empty tree.

ShellRunner.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { execFile } from 'node:child_process';
2+
3+
/**
4+
* ShellRunner provides a standard CommandRunner implementation using child_process.execFile.
5+
*/
6+
export default class ShellRunner {
7+
/**
8+
* Executes a command.
9+
* @param {Object} options
10+
* @param {string} options.command
11+
* @param {string[]} options.args
12+
* @param {string} [options.cwd]
13+
* @param {string|Buffer} [options.input]
14+
* @returns {Promise<{stdout: string, stderr: string, code: number}>}
15+
*/
16+
static async run({ command, args, cwd, input }) {
17+
return new Promise((resolve) => {
18+
const child = execFile(command, args, { cwd, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
19+
resolve({
20+
stdout: stdout || '',
21+
stderr: stderr || '',
22+
code: error ? error.code : 0
23+
});
24+
});
25+
26+
if (input && child.stdin) {
27+
child.stdin.write(input);
28+
child.stdin.end();
29+
}
30+
});
31+
}
32+
}

contract.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { z } from 'zod';
2+
3+
/**
4+
* Zod schema for the result returned by a CommandRunner.
5+
*/
6+
export const RunnerResultSchema = z.object({
7+
stdout: z.string(),
8+
stderr: z.string(),
9+
code: z.number().optional().default(0),
10+
});
11+
12+
/**
13+
* Zod schema for CommandRunner options.
14+
*/
15+
export const RunnerOptionsSchema = z.object({
16+
command: z.string(),
17+
args: z.array(z.string()),
18+
cwd: z.string().optional(),
19+
input: z.union([z.string(), z.instanceof(Buffer)]).optional(),
20+
});
21+
22+
/**
23+
* @typedef {z.infer<typeof RunnerResultSchema>} RunnerResult
24+
* @typedef {z.infer<typeof RunnerOptionsSchema>} RunnerOptions
25+
*/

eslint.config.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import js from "@eslint/js";
2+
3+
export default [
4+
js.configs.recommended,
5+
{
6+
languageOptions: {
7+
ecmaVersion: 2022,
8+
sourceType: "module",
9+
globals: {
10+
process: "readonly",
11+
Buffer: "readonly",
12+
console: "readonly",
13+
setTimeout: "readonly",
14+
clearTimeout: "readonly"
15+
}
16+
},
17+
rules: {
18+
// Logic & Complexity
19+
"complexity": ["error", 10],
20+
"max-depth": ["error", 3],
21+
"max-lines-per-function": ["error", 50],
22+
"max-params": ["error", 3],
23+
"max-nested-callbacks": ["error", 3],
24+
25+
// Strictness
26+
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
27+
"no-console": "error",
28+
"eqeqeq": ["error", "always"],
29+
"curly": ["error", "all"],
30+
"no-eval": "error",
31+
"no-implied-eval": "error",
32+
"no-new-wrappers": "error",
33+
"no-caller": "error",
34+
"no-undef-init": "error",
35+
"no-var": "error",
36+
"prefer-const": "error",
37+
"prefer-template": "error",
38+
"yoda": ["error", "never"],
39+
"no-return-await": "off", // We want explicit returns in some stunts
40+
"consistent-return": "error",
41+
"no-shadow": "error",
42+
"no-use-before-define": ["error", { "functions": false }],
43+
44+
// Style (that affects logic)
45+
"no-lonely-if": "error",
46+
"no-unneeded-ternary": "error",
47+
"one-var": ["error", "never"]
48+
}
49+
}
50+
];

0 commit comments

Comments
 (0)