git-drive 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +77 -0
- package/.planning/codebase/ARCHITECTURE.md +151 -0
- package/.planning/codebase/CONCERNS.md +191 -0
- package/.planning/codebase/CONVENTIONS.md +169 -0
- package/.planning/codebase/INTEGRATIONS.md +94 -0
- package/.planning/codebase/STACK.md +77 -0
- package/.planning/codebase/STRUCTURE.md +157 -0
- package/.planning/codebase/TESTING.md +156 -0
- package/Dockerfile.cli +30 -0
- package/Dockerfile.server +32 -0
- package/README.md +95 -0
- package/docker-compose.yml +48 -0
- package/package.json +25 -0
- package/packages/cli/Dockerfile +26 -0
- package/packages/cli/package.json +57 -0
- package/packages/cli/src/commands/archive.ts +39 -0
- package/packages/cli/src/commands/init.ts +34 -0
- package/packages/cli/src/commands/link.ts +115 -0
- package/packages/cli/src/commands/list.ts +94 -0
- package/packages/cli/src/commands/push.ts +64 -0
- package/packages/cli/src/commands/restore.ts +36 -0
- package/packages/cli/src/commands/status.ts +127 -0
- package/packages/cli/src/config.ts +73 -0
- package/packages/cli/src/errors.ts +23 -0
- package/packages/cli/src/git.ts +55 -0
- package/packages/cli/src/index.ts +97 -0
- package/packages/cli/src/server.ts +514 -0
- package/packages/cli/tsconfig.json +13 -0
- package/packages/cli/ui/assets/index-Cc2q1t5k.js +17 -0
- package/packages/cli/ui/assets/index-DrL7ojPA.css +1 -0
- package/packages/cli/ui/index.html +14 -0
- package/packages/cli/ui/vite.svg +1 -0
- package/packages/git-drive-docker/package.json +15 -0
- package/packages/server/package.json +44 -0
- package/packages/server/src/index.ts +569 -0
- package/packages/server/tsconfig.json +9 -0
- package/packages/ui/README.md +73 -0
- package/packages/ui/eslint.config.js +23 -0
- package/packages/ui/index.html +13 -0
- package/packages/ui/package.json +42 -0
- package/packages/ui/postcss.config.js +6 -0
- package/packages/ui/public/vite.svg +1 -0
- package/packages/ui/src/App.css +23 -0
- package/packages/ui/src/App.tsx +726 -0
- package/packages/ui/src/assets/react.svg +8 -0
- package/packages/ui/src/assets/vite.svg +3 -0
- package/packages/ui/src/index.css +37 -0
- package/packages/ui/src/main.tsx +14 -0
- package/packages/ui/tailwind.config.js +11 -0
- package/packages/ui/tsconfig.app.json +28 -0
- package/packages/ui/tsconfig.json +26 -0
- package/packages/ui/tsconfig.node.json +12 -0
- package/packages/ui/vite.config.ts +7 -0
- package/pnpm-workspace.yaml +4 -0
- package/rewrite_app.js +731 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout repository
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '20'
|
|
21
|
+
|
|
22
|
+
- name: Install pnpm
|
|
23
|
+
uses: pnpm/action-setup@v2
|
|
24
|
+
with:
|
|
25
|
+
version: 9
|
|
26
|
+
|
|
27
|
+
- name: Get pnpm store directory
|
|
28
|
+
shell: bash
|
|
29
|
+
run: |
|
|
30
|
+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
|
31
|
+
id: pnpm-cache
|
|
32
|
+
|
|
33
|
+
- name: Setup pnpm cache
|
|
34
|
+
uses: actions/cache@v4
|
|
35
|
+
with:
|
|
36
|
+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
37
|
+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
38
|
+
restore-keys: |
|
|
39
|
+
${{ runner.os }}-pnpm-store-
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: pnpm install --frozen-lockfile
|
|
43
|
+
|
|
44
|
+
- name: Build
|
|
45
|
+
run: pnpm build
|
|
46
|
+
|
|
47
|
+
publish-cli:
|
|
48
|
+
needs: build
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- name: Checkout repository
|
|
54
|
+
uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Setup Node.js
|
|
57
|
+
uses: actions/setup-node@v4
|
|
58
|
+
with:
|
|
59
|
+
node-version: '20'
|
|
60
|
+
registry-url: 'https://registry.npmjs.org'
|
|
61
|
+
|
|
62
|
+
- name: Install pnpm
|
|
63
|
+
uses: pnpm/action-setup@v2
|
|
64
|
+
with:
|
|
65
|
+
version: 9
|
|
66
|
+
|
|
67
|
+
- name: Install dependencies
|
|
68
|
+
run: pnpm install --frozen-lockfile
|
|
69
|
+
|
|
70
|
+
- name: Build CLI
|
|
71
|
+
run: pnpm build:cli
|
|
72
|
+
|
|
73
|
+
- name: Publish to npm
|
|
74
|
+
working-directory: packages/cli
|
|
75
|
+
run: npm publish --access public
|
|
76
|
+
env:
|
|
77
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-01-22
|
|
4
|
+
|
|
5
|
+
## Pattern Overview
|
|
6
|
+
|
|
7
|
+
**Overall:** Command-line application with modular command pattern and layered abstractions
|
|
8
|
+
|
|
9
|
+
**Key Characteristics:**
|
|
10
|
+
- CLI entry point dispatching to isolated command handlers
|
|
11
|
+
- Separation of concerns: command logic, git operations, configuration management, error handling
|
|
12
|
+
- Synchronous execution using Node.js child_process for git operations
|
|
13
|
+
- Configuration-driven behavior with persistent state in user home directory
|
|
14
|
+
- Minimal external dependencies (Node.js stdlib only)
|
|
15
|
+
|
|
16
|
+
## Layers
|
|
17
|
+
|
|
18
|
+
**CLI Layer (Command Dispatch):**
|
|
19
|
+
- Purpose: Parse command arguments, route to appropriate command handler, manage process lifecycle
|
|
20
|
+
- Location: `src/index.ts`
|
|
21
|
+
- Contains: Main entry point, command registry, usage documentation, error handling wrapper
|
|
22
|
+
- Depends on: All command modules, error handling
|
|
23
|
+
- Used by: Node.js process execution
|
|
24
|
+
|
|
25
|
+
**Command Layer:**
|
|
26
|
+
- Purpose: Implement high-level workflows for each feature (init, push, archive, restore, list, status)
|
|
27
|
+
- Location: `src/commands/*.ts` (init.ts, push.ts, archive.ts, restore.ts, list.ts, status.ts)
|
|
28
|
+
- Contains: Command-specific business logic, parameter validation, orchestration of lower layers
|
|
29
|
+
- Depends on: Config layer, Git layer, Error types
|
|
30
|
+
- Used by: CLI dispatch layer
|
|
31
|
+
|
|
32
|
+
**Configuration Layer:**
|
|
33
|
+
- Purpose: Load, save, and validate application configuration (drive path); assert drive connectivity
|
|
34
|
+
- Location: `src/config.ts`
|
|
35
|
+
- Contains: Config file I/O, config validation, drive path helpers
|
|
36
|
+
- Depends on: Node.js fs, path, os modules; Error types
|
|
37
|
+
- Used by: Most command handlers; config validation before operations
|
|
38
|
+
|
|
39
|
+
**Git Abstraction Layer:**
|
|
40
|
+
- Purpose: Encapsulate git command execution and repository queries with consistent error handling
|
|
41
|
+
- Location: `src/git.ts`
|
|
42
|
+
- Contains: git() execution wrapper, repo root queries, project name resolution, remote URL inspection
|
|
43
|
+
- Depends on: Node.js child_process, path modules
|
|
44
|
+
- Used by: All command handlers that interact with git
|
|
45
|
+
|
|
46
|
+
**Error Handling Layer:**
|
|
47
|
+
- Purpose: Define custom error types and provide unified error output formatting
|
|
48
|
+
- Location: `src/errors.ts`
|
|
49
|
+
- Contains: GitDriveError exception class, handleError() formatting function
|
|
50
|
+
- Depends on: None
|
|
51
|
+
- Used by: All layers for consistent error propagation and output
|
|
52
|
+
|
|
53
|
+
## Data Flow
|
|
54
|
+
|
|
55
|
+
**Push Workflow:**
|
|
56
|
+
|
|
57
|
+
1. CLI receives `git drive push` command
|
|
58
|
+
2. Index.ts routes to push() handler in `src/commands/push.ts`
|
|
59
|
+
3. push() validates git repository status (isGitRepo)
|
|
60
|
+
4. Loads configuration from ~/.config/git-drive/config.json
|
|
61
|
+
5. Asserts drive is mounted at configured drivePath
|
|
62
|
+
6. Queries project name from git repository root
|
|
63
|
+
7. Creates or updates bare git repository at {drive}/git-drive/{project-name}.git
|
|
64
|
+
8. Adds "drive" remote to local repository pointing to bare repo
|
|
65
|
+
9. Pushes all branches and tags to drive remote
|
|
66
|
+
10. Outputs success message
|
|
67
|
+
|
|
68
|
+
**Archive Workflow:**
|
|
69
|
+
|
|
70
|
+
1. CLI receives `git drive archive [--force]` command
|
|
71
|
+
2. archive() in `src/commands/archive.ts` validates git repository
|
|
72
|
+
3. Checks for uncommitted changes (unless --force flag provided)
|
|
73
|
+
4. Calls push() internally to backup all content
|
|
74
|
+
5. Changes working directory to parent
|
|
75
|
+
6. Removes entire local repository directory
|
|
76
|
+
7. Outputs archive confirmation with restore instructions
|
|
77
|
+
|
|
78
|
+
**Restore Workflow:**
|
|
79
|
+
|
|
80
|
+
1. CLI receives `git drive restore <project-name> [target-dir]`
|
|
81
|
+
2. restore() in `src/commands/restore.ts` validates configuration
|
|
82
|
+
3. Asserts drive is mounted
|
|
83
|
+
4. Checks that bare repository exists on drive at {drive}/git-drive/{project-name}.git
|
|
84
|
+
5. Clones bare repository to target directory
|
|
85
|
+
6. Renames "origin" remote to "drive" for consistency with push workflow
|
|
86
|
+
7. Outputs success message with restored path
|
|
87
|
+
|
|
88
|
+
**Status Query Workflow:**
|
|
89
|
+
|
|
90
|
+
1. CLI receives `git drive status`
|
|
91
|
+
2. status() in `src/commands/status.ts` loads configuration (non-fatal if missing)
|
|
92
|
+
3. Checks drive connectivity by filesystem existence check
|
|
93
|
+
4. If connected: lists all projects in {drive}/git-drive/ by enumerating .git directories
|
|
94
|
+
5. If in a git repository: reports project name and drive remote status
|
|
95
|
+
6. Outputs multi-line status report
|
|
96
|
+
|
|
97
|
+
**State Management:**
|
|
98
|
+
|
|
99
|
+
- Configuration state: Persistent JSON file at ~/.config/git-drive/config.json (drivePath property only)
|
|
100
|
+
- Repository state: Stored in git bare repositories on external drive; local repositories maintain standard git state
|
|
101
|
+
- No in-memory state caching; all reads are fresh from filesystem/git
|
|
102
|
+
|
|
103
|
+
## Key Abstractions
|
|
104
|
+
|
|
105
|
+
**Git Command Wrapper (git()):**
|
|
106
|
+
- Purpose: Provide safe, consistent git command execution with error propagation
|
|
107
|
+
- Examples: `src/git.ts` functions like git(), getRepoRoot(), getProjectName(), getRemoteUrl()
|
|
108
|
+
- Pattern: execSync with explicit error handling; stdio piping to capture output; optional cwd parameter for working directory context
|
|
109
|
+
|
|
110
|
+
**GitDriveError Exception:**
|
|
111
|
+
- Purpose: Distinguish application-level errors (user/config issues) from system errors
|
|
112
|
+
- Examples: "No drive configured", "Working tree has uncommitted changes", "Path not found"
|
|
113
|
+
- Pattern: Custom Error subclass with name property; caught and formatted in main error handler
|
|
114
|
+
|
|
115
|
+
**Configuration Contract:**
|
|
116
|
+
- Purpose: Validate drive path existence and accessibility before operations
|
|
117
|
+
- Examples: requireConfig() (throws if missing), assertDriveMounted() (checks filesystem)
|
|
118
|
+
- Pattern: Fail-fast validation at command start; errors reported immediately to user
|
|
119
|
+
|
|
120
|
+
**Command Handler Signature:**
|
|
121
|
+
- Purpose: Provide consistent interface for CLI dispatch
|
|
122
|
+
- Pattern: (args: string[]) => void; throws GitDriveError on validation failures; logs results to console
|
|
123
|
+
|
|
124
|
+
## Entry Points
|
|
125
|
+
|
|
126
|
+
**CLI Entry Point (Node.js executable):**
|
|
127
|
+
- Location: `src/index.ts` (shebang: #!/usr/bin/env node)
|
|
128
|
+
- Triggers: Installation via npm/git-drive binary; direct node execution
|
|
129
|
+
- Responsibilities: Command parsing, handler dispatch, error handling, process exit code management
|
|
130
|
+
|
|
131
|
+
**Command Handlers (6 total):**
|
|
132
|
+
- init: Initialize drive path; called once per setup
|
|
133
|
+
- push: Back up current repository; called before archive or periodically
|
|
134
|
+
- archive: Backup and remove local copy; called when archiving projects
|
|
135
|
+
- restore: Retrieve archived project; called to bring archived projects back online
|
|
136
|
+
- list: Display all archived projects; informational query
|
|
137
|
+
- status: Display current drive and repository state; diagnostic command
|
|
138
|
+
|
|
139
|
+
## Error Handling
|
|
140
|
+
|
|
141
|
+
**Strategy:** Fail-fast with clear user messaging; distinguish application errors from system errors
|
|
142
|
+
|
|
143
|
+
**Patterns:**
|
|
144
|
+
- GitDriveError for expected failures (missing config, wrong path, uncommitted changes)
|
|
145
|
+
- Error type checking in main handler: if GitDriveError show message directly, else parse stderr from execSync
|
|
146
|
+
- Process exits with code 1 on any error; code 0 on success
|
|
147
|
+
- All error messages prefixed with "error: " for consistency
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
*Architecture analysis: 2026-01-22*
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Codebase Concerns
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-01-22
|
|
4
|
+
|
|
5
|
+
## Tech Debt
|
|
6
|
+
|
|
7
|
+
**Command Injection Vulnerability via execSync:**
|
|
8
|
+
- Issue: Git arguments are passed directly to `execSync` without sanitization. Paths containing special characters could allow injection.
|
|
9
|
+
- Files: `src/git.ts` (lines 5-9), `src/commands/push.ts` (lines 21, 28, 31, 35-36), `src/commands/archive.ts` (line 16), `src/commands/restore.ts` (line 30, 33)
|
|
10
|
+
- Impact: Malicious project names or file paths could execute arbitrary shell commands
|
|
11
|
+
- Fix approach: Use array form of `execSync` or use `spawn` instead of shell interpolation. Alternatively, escape shell arguments using a library like `shell-escape`.
|
|
12
|
+
|
|
13
|
+
**Unguarded Process State Mutation in archive:**
|
|
14
|
+
- Issue: `process.chdir("..")` modifies global process state without cleanup or error handling. If removal fails, the process stays in parent directory.
|
|
15
|
+
- Files: `src/commands/archive.ts` (lines 34-35)
|
|
16
|
+
- Impact: If `rmSync` throws after `process.chdir`, the error handler exits from wrong directory, potentially confusing users. Subsequent operations in same process would operate from wrong directory.
|
|
17
|
+
- Fix approach: Wrap in try-finally to restore original cwd. Consider using relative paths or path.join instead of process.chdir.
|
|
18
|
+
|
|
19
|
+
**Error Information Leakage in Git Operations:**
|
|
20
|
+
- Issue: Git command failures expose full stderr output which might contain sensitive repository or system information.
|
|
21
|
+
- Files: `src/errors.ts` (lines 14-16), `src/git.ts` (lines 4-9)
|
|
22
|
+
- Impact: Sensitive paths, auth tokens, or system details could leak to stdout/stderr
|
|
23
|
+
- Fix approach: Sanitize git error messages, only show relevant error lines, hide sensitive paths.
|
|
24
|
+
|
|
25
|
+
## Known Bugs
|
|
26
|
+
|
|
27
|
+
**Race Condition in Push Remote Handling:**
|
|
28
|
+
- Symptoms: If two processes call `push` simultaneously, both might check for existing remote URL and create duplicate remotes
|
|
29
|
+
- Files: `src/commands/push.ts` (lines 26-31)
|
|
30
|
+
- Trigger: Run `git drive push` from two separate terminal windows on same repo simultaneously
|
|
31
|
+
- Workaround: None. User must manually clean up remotes with `git remote rm drive`
|
|
32
|
+
|
|
33
|
+
**Archive Command Fails Silently on Partial Deletion:**
|
|
34
|
+
- Symptoms: If `rmSync` fails due to file locks (on Windows) or permissions, the output still says "Archived" but local copy remains
|
|
35
|
+
- Files: `src/commands/archive.ts` (lines 34-37)
|
|
36
|
+
- Trigger: On Windows with antivirus scanning files, or with open file handles
|
|
37
|
+
- Workaround: Manually delete directory after error, or use `--force` flag which ignores uncommitted changes but won't prevent rmSync errors
|
|
38
|
+
|
|
39
|
+
**Restore Creates Clone with Wrong Remote:**
|
|
40
|
+
- Symptoms: Cloning a bare repo creates `origin` remote, then it's renamed to `drive`, but if clone fails midway and user retries, state is inconsistent
|
|
41
|
+
- Files: `src/commands/restore.ts` (lines 30, 33)
|
|
42
|
+
- Trigger: Clone fails (network error, disk full) and user retries
|
|
43
|
+
- Workaround: Manually delete target directory and retry
|
|
44
|
+
|
|
45
|
+
## Security Considerations
|
|
46
|
+
|
|
47
|
+
**Config File Permissions Not Set:**
|
|
48
|
+
- Risk: `~/.config/git-drive/config.json` is created world-readable if default umask is permissive. Contains drive path which could be sensitive.
|
|
49
|
+
- Files: `src/config.ts` (lines 20-21)
|
|
50
|
+
- Current mitigation: None
|
|
51
|
+
- Recommendations: Call `chmod 600` on config file after creation. Use `mode` option in `mkdirSync` to set 0o700 permissions.
|
|
52
|
+
|
|
53
|
+
**No Validation of Path Traversal in Restore:**
|
|
54
|
+
- Risk: User can specify `../../../etc/passwd` as target-dir. While Git clone itself prevents escaping, relative paths could be confusing.
|
|
55
|
+
- Files: `src/commands/restore.ts` (lines 9, 25)
|
|
56
|
+
- Current mitigation: Uses `resolve()` to canonicalize paths
|
|
57
|
+
- Recommendations: Validate target path is within expected directory or is absolute
|
|
58
|
+
|
|
59
|
+
**Insufficient Argument Validation:**
|
|
60
|
+
- Risk: Commands don't validate argument length or type, leading to confusing errors
|
|
61
|
+
- Files: `src/commands/restore.ts` (line 8), `src/index.ts` (line 32)
|
|
62
|
+
- Current mitigation: Basic checks in some commands
|
|
63
|
+
- Recommendations: Add argument count/type validation before processing
|
|
64
|
+
|
|
65
|
+
## Performance Bottlenecks
|
|
66
|
+
|
|
67
|
+
**List Command Iterates All Entries Twice:**
|
|
68
|
+
- Problem: `readdirSync()` called, then filtered, then iterated with `statSync()` for each. No caching or batching.
|
|
69
|
+
- Files: `src/commands/list.ts` (lines 17-33)
|
|
70
|
+
- Cause: Synchronous I/O in loop, no parallelization
|
|
71
|
+
- Improvement path: Use `Dirent.isFile()` option in readdir to avoid extra stat calls. Not a bottleneck for typical drive sizes (<1000 projects) but poor pattern.
|
|
72
|
+
|
|
73
|
+
**Git Operations Not Buffered:**
|
|
74
|
+
- Problem: Each git command is a separate `execSync` call. `push --all` and `push --tags` execute separately instead of combined.
|
|
75
|
+
- Files: `src/commands/push.ts` (lines 35-36)
|
|
76
|
+
- Cause: Two separate git invocations where one could be chained
|
|
77
|
+
- Improvement path: Combine into single command: `git("push drive --all --tags")`
|
|
78
|
+
|
|
79
|
+
## Fragile Areas
|
|
80
|
+
|
|
81
|
+
**Archive Command - Destructive Without Confirmation:**
|
|
82
|
+
- Files: `src/commands/archive.ts`
|
|
83
|
+
- Why fragile: Deletes entire local directory with only a weak check for uncommitted changes. `--force` flag disables even that.
|
|
84
|
+
- Safe modification: Add confirmation prompt before deletion (even with --force). Consider adding `--dry-run` to preview what would be deleted. Add atomic transaction guarantee: push succeeds before any deletion.
|
|
85
|
+
- Test coverage: No tests present. This is the most dangerous command.
|
|
86
|
+
|
|
87
|
+
**Git Remote Initialization Logic:**
|
|
88
|
+
- Files: `src/commands/push.ts` (lines 19-32)
|
|
89
|
+
- Why fragile: Multiple state branches (remote doesn't exist, exists with same URL, exists with different URL). Easy to miss edge case.
|
|
90
|
+
- Safe modification: Add tests for all three branches. Consider replacing with `git remote set-url --add` if it exists, then prune duplicates.
|
|
91
|
+
- Test coverage: No tests present.
|
|
92
|
+
|
|
93
|
+
**Config Persistence:**
|
|
94
|
+
- Files: `src/config.ts` (lines 13-21)
|
|
95
|
+
- Why fragile: No atomic writes, no backup of old config. If process crashes during JSON write, config corrupts.
|
|
96
|
+
- Safe modification: Write to temp file first, then rename. Add version field to config format.
|
|
97
|
+
- Test coverage: No tests present.
|
|
98
|
+
|
|
99
|
+
**Drive Mount Assumption:**
|
|
100
|
+
- Files: `src/config.ts` (line 33), multiple commands
|
|
101
|
+
- Why fragile: Assumes if path exists, drive is mounted. On filesystems with autounmount, path could exist but be stale mount point.
|
|
102
|
+
- Safe modification: Check if path is actually accessible (try to read), not just exists
|
|
103
|
+
- Test coverage: No tests present.
|
|
104
|
+
|
|
105
|
+
## Scaling Limits
|
|
106
|
+
|
|
107
|
+
**Bare Repository Storage:**
|
|
108
|
+
- Current capacity: Works fine for ~1000s of projects on typical external drive
|
|
109
|
+
- Limit: No size limits enforced. Bare repos can grow large; no compression or GC recommended to users
|
|
110
|
+
- Scaling path: Document repository GC recommendations. Add `git drive gc` command to run `git gc --aggressive` on all bare repos.
|
|
111
|
+
|
|
112
|
+
**Synchronous I/O Operations:**
|
|
113
|
+
- Current capacity: All operations block. On drive with 1000+ projects, list/status commands visibly slow
|
|
114
|
+
- Limit: ~100ms per large operation on typical USB drive, scales linearly with project count
|
|
115
|
+
- Scaling path: Switch to async operations for I/O-heavy commands. Parallelize directory operations.
|
|
116
|
+
|
|
117
|
+
## Dependencies at Risk
|
|
118
|
+
|
|
119
|
+
**No Production Dependencies:**
|
|
120
|
+
- Risk: Codebase has only dev dependencies (TypeScript, @types/node). Good security posture but limits future extensibility.
|
|
121
|
+
- Impact: Any new feature requiring external library (e.g., progress bars, colors) requires new dep
|
|
122
|
+
- Migration plan: Carefully evaluate any production dependency for security and maintenance status
|
|
123
|
+
|
|
124
|
+
**execSync from child_process:**
|
|
125
|
+
- Risk: Node's `execSync` doesn't have a maxBuffer safeguard - large git command output (massive commit messages, etc.) could hang or crash process
|
|
126
|
+
- Impact: User loses ability to abort hanging process gracefully
|
|
127
|
+
- Migration plan: Switch to spawn() with streaming output, or add timeout parameter to execSync
|
|
128
|
+
|
|
129
|
+
## Missing Critical Features
|
|
130
|
+
|
|
131
|
+
**No Progress Indication:**
|
|
132
|
+
- Problem: Large git push operations give no feedback. User doesn't know if hung or still running.
|
|
133
|
+
- Blocks: Difficult to debug stalled operations
|
|
134
|
+
|
|
135
|
+
**No Test Suite:**
|
|
136
|
+
- Problem: Zero test coverage. No unit, integration, or e2e tests.
|
|
137
|
+
- Blocks: Refactoring, verification of security fixes, confidence in changes
|
|
138
|
+
|
|
139
|
+
**No Dry Run Mode:**
|
|
140
|
+
- Problem: Destructive operations (archive, push to wrong remote) have no preview
|
|
141
|
+
- Blocks: Users cannot safely test commands
|
|
142
|
+
|
|
143
|
+
**No Concurrency Control:**
|
|
144
|
+
- Problem: Multiple processes can push/archive simultaneously, causing corruption
|
|
145
|
+
- Blocks: Safe concurrent usage
|
|
146
|
+
|
|
147
|
+
**No Recovery from Partial Operations:**
|
|
148
|
+
- Problem: If push succeeds but archive deletion fails, no way to resume or rollback
|
|
149
|
+
- Blocks: Reliable archive operations
|
|
150
|
+
|
|
151
|
+
## Test Coverage Gaps
|
|
152
|
+
|
|
153
|
+
**Archive Command - No Tests:**
|
|
154
|
+
- What's not tested: Successful archive flow, archive with uncommitted changes detection, archive with --force flag, rmSync error scenarios, edge case where push succeeds but rm fails
|
|
155
|
+
- Files: `src/commands/archive.ts`
|
|
156
|
+
- Risk: Most destructive operation has zero test coverage. Easy to introduce regressions.
|
|
157
|
+
- Priority: High
|
|
158
|
+
|
|
159
|
+
**Push Command - No Tests:**
|
|
160
|
+
- What's not tested: Creating bare repo, adding new remote, updating existing remote, concurrent pushes, large repository push
|
|
161
|
+
- Files: `src/commands/push.ts`
|
|
162
|
+
- Risk: Core functionality untested. Remote state bugs undetected.
|
|
163
|
+
- Priority: High
|
|
164
|
+
|
|
165
|
+
**Restore Command - No Tests:**
|
|
166
|
+
- What's not tested: Successful restore, nonexistent project error, directory exists error, clone failure scenarios
|
|
167
|
+
- Files: `src/commands/restore.ts`
|
|
168
|
+
- Risk: Data recovery untested. Silent failures possible.
|
|
169
|
+
- Priority: High
|
|
170
|
+
|
|
171
|
+
**Config System - No Tests:**
|
|
172
|
+
- What's not tested: Config loading/saving, corrupted JSON recovery, missing config, permission errors, drive mount detection
|
|
173
|
+
- Files: `src/config.ts`
|
|
174
|
+
- Risk: Configuration system untested. State corruption undetected.
|
|
175
|
+
- Priority: High
|
|
176
|
+
|
|
177
|
+
**Error Handling - No Tests:**
|
|
178
|
+
- What's not tested: GitDriveError formatting, execSync error parsing, unknown error handling, edge case error messages
|
|
179
|
+
- Files: `src/errors.ts`
|
|
180
|
+
- Risk: Error messages could be unhelpful or expose sensitive info, untested.
|
|
181
|
+
- Priority: Medium
|
|
182
|
+
|
|
183
|
+
**Index/CLI - No Tests:**
|
|
184
|
+
- What's not tested: Command routing, unknown command handling, help text, missing command handler errors
|
|
185
|
+
- Files: `src/index.ts`
|
|
186
|
+
- Risk: CLI argument parsing untested. Invalid input handling unverified.
|
|
187
|
+
- Priority: Medium
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
*Concerns audit: 2026-01-22*
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Coding Conventions
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-01-22
|
|
4
|
+
|
|
5
|
+
## Naming Patterns
|
|
6
|
+
|
|
7
|
+
**Files:**
|
|
8
|
+
- Kebab-case for command files: `init.ts`, `push.ts`, `archive.ts`, `restore.ts`, `list.ts`, `status.ts`
|
|
9
|
+
- Lowercase for utility modules: `config.ts`, `errors.ts`, `git.ts`, `index.ts`
|
|
10
|
+
- Commands are organized in `src/commands/` directory
|
|
11
|
+
- Extension: `.ts` for all TypeScript files
|
|
12
|
+
|
|
13
|
+
**Functions:**
|
|
14
|
+
- camelCase for all function names: `init()`, `push()`, `archive()`, `restore()`, `list()`, `status()`
|
|
15
|
+
- Command functions export with pattern: `export function <commandName>(args: string[]): void`
|
|
16
|
+
- Utility functions use camelCase: `loadConfig()`, `saveConfig()`, `getRepoRoot()`, `getProjectName()`, `assertDriveMounted()`
|
|
17
|
+
- Prefix conventions: `is*` for boolean checks (`isGitRepo()`)
|
|
18
|
+
- Prefix conventions: `get*` for retrieval functions (`getRepoRoot()`, `getProjectName()`, `getRemoteUrl()`)
|
|
19
|
+
- Prefix conventions: `require*` for mandatory getters with error throwing (`requireConfig()`)
|
|
20
|
+
- Prefix conventions: `assert*` for validation functions that throw on failure (`assertDriveMounted()`)
|
|
21
|
+
|
|
22
|
+
**Variables:**
|
|
23
|
+
- camelCase for all variables and parameters: `rawPath`, `drivePath`, `repoRoot`, `projectName`, `storePath`, `bareRepoPath`
|
|
24
|
+
- Constants: UPPER_SNAKE_CASE for configuration paths: `CONFIG_DIR`, `CONFIG_FILE`
|
|
25
|
+
- Private/internal prefixes: None used; all module-level constants are uppercase
|
|
26
|
+
|
|
27
|
+
**Types:**
|
|
28
|
+
- PascalCase for interfaces: `Config`
|
|
29
|
+
- Record types for object lookups: `Record<string, (args: string[]) => void>` for command handlers
|
|
30
|
+
- Error classes: PascalCase: `GitDriveError`
|
|
31
|
+
|
|
32
|
+
## Code Style
|
|
33
|
+
|
|
34
|
+
**Formatting:**
|
|
35
|
+
- Target: ES2022
|
|
36
|
+
- Module system: Node16 with `.js` extensions in imports
|
|
37
|
+
- Strict TypeScript enabled with `strict: true`
|
|
38
|
+
- 2-space indentation (inferred from package and compiled output)
|
|
39
|
+
- Semicolons used consistently at statement ends
|
|
40
|
+
|
|
41
|
+
**Linting:**
|
|
42
|
+
- No formal linting configuration detected (.eslintrc not present)
|
|
43
|
+
- No Prettier configuration detected (.prettierrc not present)
|
|
44
|
+
- TypeScript compiler with strict mode enforces type safety
|
|
45
|
+
|
|
46
|
+
**Type Safety:**
|
|
47
|
+
- All function parameters explicitly typed: `args: string[]`, `cwd?: string`, `remoteName: string`
|
|
48
|
+
- Return types explicitly declared: `void`, `string`, `Config`, `boolean`, `Config | null`
|
|
49
|
+
- Optional parameters marked: `cwd?: string`, `targetDir = args[1] || projectName`
|
|
50
|
+
- Union types used for nullable returns: `string | null`
|
|
51
|
+
|
|
52
|
+
## Import Organization
|
|
53
|
+
|
|
54
|
+
**Order:**
|
|
55
|
+
1. Node.js built-in modules (`fs`, `path`, `os`, `child_process`)
|
|
56
|
+
2. Relative imports from project modules (`./config.js`, `./errors.js`, `./commands/...`)
|
|
57
|
+
3. No third-party dependencies imported
|
|
58
|
+
|
|
59
|
+
**Path Aliases:**
|
|
60
|
+
- No path aliases configured; all imports use relative paths
|
|
61
|
+
- Imports include `.js` extension: `import { handleError } from "./errors.js"`
|
|
62
|
+
- Example: `import { init } from "./commands/init.js"`
|
|
63
|
+
|
|
64
|
+
**Export Pattern:**
|
|
65
|
+
- Named exports used consistently: `export function`, `export class`, `export interface`
|
|
66
|
+
- No default exports
|
|
67
|
+
- Single responsibility per file maintained
|
|
68
|
+
|
|
69
|
+
## Error Handling
|
|
70
|
+
|
|
71
|
+
**Patterns:**
|
|
72
|
+
- Custom error class `GitDriveError` extends native `Error` with consistent naming
|
|
73
|
+
- Error instantiation: `throw new GitDriveError("message")`
|
|
74
|
+
- Error handling at top level in `src/index.ts` try-catch block
|
|
75
|
+
- Type-safe error checking: `if (err instanceof GitDriveError)`, `if (err instanceof Error)`
|
|
76
|
+
- Fallback for unknown errors: `else { console.error("An unexpected error occurred.") }`
|
|
77
|
+
- stderr extraction from `execSync` errors: regex match on error message
|
|
78
|
+
- Examples from `src/errors.ts`:
|
|
79
|
+
```typescript
|
|
80
|
+
export function handleError(err: unknown): void {
|
|
81
|
+
if (err instanceof GitDriveError) {
|
|
82
|
+
console.error(`error: ${err.message}`);
|
|
83
|
+
} else if (err instanceof Error) {
|
|
84
|
+
const msg = err.message;
|
|
85
|
+
const stderrMatch = msg.match(/stderr:\s*([\s\S]*)/);
|
|
86
|
+
if (stderrMatch) {
|
|
87
|
+
console.error(`error: ${stderrMatch[1].trim()}`);
|
|
88
|
+
} else {
|
|
89
|
+
console.error(`error: ${msg}`);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
console.error("An unexpected error occurred.");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Logging
|
|
98
|
+
|
|
99
|
+
**Framework:** console (built-in)
|
|
100
|
+
|
|
101
|
+
**Patterns:**
|
|
102
|
+
- `console.log()` for normal output: user messages, status updates
|
|
103
|
+
- `console.error()` for error output: error handling
|
|
104
|
+
- Consistent prefix: `error: ` for error messages
|
|
105
|
+
- Informational output with context: `console.log(\`Drive: ${config.drivePath} (connected)\`)`
|
|
106
|
+
- String interpolation used for variables: `` `Drive configured: ${storePath}` ``
|
|
107
|
+
- Status updates: `console.log(\`Pushed ${projectName} to drive.\`)`
|
|
108
|
+
- Diagnostic output: `console.log(\`Created bare repo: ${bareRepoPath}\`)`
|
|
109
|
+
|
|
110
|
+
## Comments
|
|
111
|
+
|
|
112
|
+
**When to Comment:**
|
|
113
|
+
- Used for explaining non-obvious logic or git operations
|
|
114
|
+
- Precedes multi-step operations to clarify intent
|
|
115
|
+
- Examples from codebase:
|
|
116
|
+
- `// Create bare repo if it doesn't exist`
|
|
117
|
+
- `// Add or update remote`
|
|
118
|
+
- `// Push all branches and tags`
|
|
119
|
+
- `// Check for uncommitted changes`
|
|
120
|
+
- `// Push first`
|
|
121
|
+
- `// Remove local copy`
|
|
122
|
+
- `// execSync errors include stderr in the message`
|
|
123
|
+
- `// Rename origin to drive so the remote stays consistent`
|
|
124
|
+
|
|
125
|
+
**JSDoc/TSDoc:**
|
|
126
|
+
- Not used; minimal documentation approach
|
|
127
|
+
- Function purposes clear from names and context
|
|
128
|
+
|
|
129
|
+
## Function Design
|
|
130
|
+
|
|
131
|
+
**Size:**
|
|
132
|
+
- Typically 5-40 lines per function
|
|
133
|
+
- Largest file is `src/index.ts` at 51 lines (entry point with usage function)
|
|
134
|
+
- Command implementations: 30-40 lines each
|
|
135
|
+
- Utility functions: 2-10 lines each
|
|
136
|
+
|
|
137
|
+
**Parameters:**
|
|
138
|
+
- Prefer explicit parameters over implicit state: `function git(args: string, cwd?: string)`
|
|
139
|
+
- Command functions always accept `args: string[]` array from CLI arguments
|
|
140
|
+
- Optional parameters use defaults: `const targetDir = args[1] || projectName`
|
|
141
|
+
|
|
142
|
+
**Return Values:**
|
|
143
|
+
- Command handlers return `void` and communicate via console.log/error
|
|
144
|
+
- Utility functions return specific types: `string`, `boolean`, `Config`
|
|
145
|
+
- Nullable returns use union types: `string | null`
|
|
146
|
+
- No implicit undefined returns used
|
|
147
|
+
|
|
148
|
+
## Module Design
|
|
149
|
+
|
|
150
|
+
**Exports:**
|
|
151
|
+
- Single export per command file: one `export function <command>`
|
|
152
|
+
- Multiple exports in utility modules: `export function loadConfig()`, `export function saveConfig()`, etc.
|
|
153
|
+
- Interface exports for type sharing: `export interface Config`
|
|
154
|
+
- Error class export: `export class GitDriveError`
|
|
155
|
+
|
|
156
|
+
**Barrel Files:**
|
|
157
|
+
- Not used; imports reference specific files directly
|
|
158
|
+
- Example: `import { init } from "./commands/init.js"` not `import { init } from "./commands/"`
|
|
159
|
+
|
|
160
|
+
**Module Responsibilities:**
|
|
161
|
+
- `src/config.ts`: Configuration file I/O and drive path utilities
|
|
162
|
+
- `src/git.ts`: Git command execution and git repo inspection
|
|
163
|
+
- `src/errors.ts`: Error class definition and centralized error handling
|
|
164
|
+
- `src/commands/*.ts`: Individual command implementations
|
|
165
|
+
- `src/index.ts`: CLI entry point and command router
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
*Convention analysis: 2026-01-22*
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# External Integrations
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-01-22
|
|
4
|
+
|
|
5
|
+
## APIs & External Services
|
|
6
|
+
|
|
7
|
+
**None Detected**
|
|
8
|
+
|
|
9
|
+
No third-party APIs or external services are used in this codebase.
|
|
10
|
+
|
|
11
|
+
## Data Storage
|
|
12
|
+
|
|
13
|
+
**Databases:**
|
|
14
|
+
- Not used - No database integration
|
|
15
|
+
|
|
16
|
+
**File Storage:**
|
|
17
|
+
- Local filesystem only
|
|
18
|
+
- Configuration: `~/.config/git-drive/config.json` (user home directory)
|
|
19
|
+
- Git repositories: External USB drive or mounted filesystem
|
|
20
|
+
- Client: Built-in Node.js `fs` module (promises-based)
|
|
21
|
+
|
|
22
|
+
**Caching:**
|
|
23
|
+
- Not used
|
|
24
|
+
|
|
25
|
+
## Authentication & Identity
|
|
26
|
+
|
|
27
|
+
**Auth Provider:**
|
|
28
|
+
- Custom - No authentication provider integrated
|
|
29
|
+
- Approach: File-based configuration with implicit trust of drive mount
|
|
30
|
+
|
|
31
|
+
## Monitoring & Observability
|
|
32
|
+
|
|
33
|
+
**Error Tracking:**
|
|
34
|
+
- Not used
|
|
35
|
+
|
|
36
|
+
**Logs:**
|
|
37
|
+
- Console output only
|
|
38
|
+
- Error handling: `console.error()` for errors, `console.log()` for standard output
|
|
39
|
+
- No persistent logging
|
|
40
|
+
|
|
41
|
+
## CI/CD & Deployment
|
|
42
|
+
|
|
43
|
+
**Hosting:**
|
|
44
|
+
- npm registry only (published as package)
|
|
45
|
+
- Local development installation
|
|
46
|
+
|
|
47
|
+
**CI Pipeline:**
|
|
48
|
+
- Not configured - No CI configuration detected
|
|
49
|
+
|
|
50
|
+
## Environment Configuration
|
|
51
|
+
|
|
52
|
+
**Required Configuration:**
|
|
53
|
+
- External drive path (configured once via `git drive init <path>`)
|
|
54
|
+
- No environment variables used
|
|
55
|
+
- No `.env` files required
|
|
56
|
+
|
|
57
|
+
**Configuration Storage:**
|
|
58
|
+
- User configuration: `~/.config/git-drive/config.json`
|
|
59
|
+
- Format: JSON
|
|
60
|
+
- Structure:
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"drivePath": "/Volumes/ExternalDrive"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## System Integration
|
|
68
|
+
|
|
69
|
+
**Git Integration:**
|
|
70
|
+
- Uses `git` command-line tool (via `execSync`)
|
|
71
|
+
- Communicates with Git CLI for repository operations:
|
|
72
|
+
- `git rev-parse` - Get repository root
|
|
73
|
+
- `git init --bare` - Create bare repositories
|
|
74
|
+
- `git remote` - Manage git remotes
|
|
75
|
+
- `git push` - Push commits to bare repository
|
|
76
|
+
- `git clone` - Clone from bare repository
|
|
77
|
+
|
|
78
|
+
**File System Integration:**
|
|
79
|
+
- Direct file system access for:
|
|
80
|
+
- Drive detection and mounting verification
|
|
81
|
+
- Repository storage organization
|
|
82
|
+
- Project metadata (modification times)
|
|
83
|
+
|
|
84
|
+
## Webhooks & Callbacks
|
|
85
|
+
|
|
86
|
+
**Incoming:**
|
|
87
|
+
- None
|
|
88
|
+
|
|
89
|
+
**Outgoing:**
|
|
90
|
+
- None
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
*Integration audit: 2026-01-22*
|