git-drive 0.1.6 → 0.1.7

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.
Files changed (95) hide show
  1. package/.github/workflows/ci.yml +77 -0
  2. package/.planning/codebase/ARCHITECTURE.md +151 -0
  3. package/.planning/codebase/CONCERNS.md +191 -0
  4. package/.planning/codebase/CONVENTIONS.md +169 -0
  5. package/.planning/codebase/INTEGRATIONS.md +94 -0
  6. package/.planning/codebase/STACK.md +77 -0
  7. package/.planning/codebase/STRUCTURE.md +157 -0
  8. package/.planning/codebase/TESTING.md +156 -0
  9. package/Dockerfile.cli +30 -0
  10. package/Dockerfile.server +32 -0
  11. package/README.md +157 -0
  12. package/docker-compose.yml +48 -0
  13. package/package.json +20 -55
  14. package/packages/cli/Dockerfile +26 -0
  15. package/packages/cli/jest.config.js +26 -0
  16. package/packages/cli/package.json +65 -0
  17. package/packages/cli/src/__tests__/commands/companion.test.ts +152 -0
  18. package/packages/cli/src/__tests__/commands/init.test.ts +154 -0
  19. package/packages/cli/src/__tests__/commands/list.test.ts +122 -0
  20. package/packages/cli/src/__tests__/commands/push.test.ts +155 -0
  21. package/packages/cli/src/__tests__/commands/restore.test.ts +135 -0
  22. package/packages/cli/src/__tests__/commands/status.test.ts +199 -0
  23. package/packages/cli/src/__tests__/config.test.ts +198 -0
  24. package/packages/cli/src/__tests__/e2e.test.ts +125 -0
  25. package/packages/cli/src/__tests__/errors.test.ts +66 -0
  26. package/packages/cli/src/__tests__/git.test.ts +250 -0
  27. package/packages/cli/src/__tests__/server.test.ts +371 -0
  28. package/packages/cli/src/commands/archive.ts +39 -0
  29. package/packages/cli/src/commands/companion.ts +205 -0
  30. package/packages/cli/src/commands/init.ts +130 -0
  31. package/packages/cli/src/commands/link.ts +151 -0
  32. package/packages/cli/src/commands/list.ts +94 -0
  33. package/packages/cli/src/commands/push.ts +77 -0
  34. package/packages/cli/src/commands/restore.ts +36 -0
  35. package/packages/cli/src/commands/status.ts +127 -0
  36. package/packages/cli/src/config.ts +73 -0
  37. package/packages/cli/src/errors.ts +23 -0
  38. package/packages/cli/src/git.ts +60 -0
  39. package/packages/cli/src/index.ts +129 -0
  40. package/packages/cli/src/server.ts +700 -0
  41. package/packages/cli/tsconfig.json +13 -0
  42. package/packages/git-drive-docker/package.json +15 -0
  43. package/packages/server/package.json +44 -0
  44. package/packages/server/src/index.ts +569 -0
  45. package/packages/server/tsconfig.json +9 -0
  46. package/packages/ui/README.md +73 -0
  47. package/packages/ui/eslint.config.js +23 -0
  48. package/packages/ui/index.html +13 -0
  49. package/packages/ui/package.json +52 -0
  50. package/packages/ui/postcss.config.js +6 -0
  51. package/packages/ui/public/vite.svg +1 -0
  52. package/packages/ui/src/App.css +23 -0
  53. package/packages/ui/src/App.test.tsx +248 -0
  54. package/packages/ui/src/App.tsx +803 -0
  55. package/packages/ui/src/assets/react.svg +8 -0
  56. package/packages/ui/src/assets/vite.svg +3 -0
  57. package/packages/ui/src/index.css +37 -0
  58. package/packages/ui/src/main.tsx +14 -0
  59. package/packages/ui/src/test/setup.ts +1 -0
  60. package/packages/ui/tailwind.config.js +11 -0
  61. package/packages/ui/tsconfig.app.json +28 -0
  62. package/packages/ui/tsconfig.json +26 -0
  63. package/packages/ui/tsconfig.node.json +12 -0
  64. package/packages/ui/vite.config.ts +7 -0
  65. package/packages/ui/vitest.config.ts +20 -0
  66. package/pnpm-workspace.yaml +4 -0
  67. package/rewrite_app.js +731 -0
  68. package/tsconfig.json +14 -0
  69. package/dist/__tests__/commands/init.test.js +0 -123
  70. package/dist/__tests__/commands/list.test.js +0 -91
  71. package/dist/__tests__/commands/push.test.js +0 -128
  72. package/dist/__tests__/commands/restore.test.js +0 -99
  73. package/dist/__tests__/commands/status.test.js +0 -151
  74. package/dist/__tests__/config.test.js +0 -150
  75. package/dist/__tests__/e2e.test.js +0 -107
  76. package/dist/__tests__/errors.test.js +0 -56
  77. package/dist/__tests__/git.test.js +0 -184
  78. package/dist/__tests__/server.test.js +0 -310
  79. package/dist/commands/archive.js +0 -32
  80. package/dist/commands/init.js +0 -55
  81. package/dist/commands/link.js +0 -175
  82. package/dist/commands/list.js +0 -83
  83. package/dist/commands/push.js +0 -112
  84. package/dist/commands/restore.js +0 -30
  85. package/dist/commands/status.js +0 -116
  86. package/dist/config.js +0 -62
  87. package/dist/errors.js +0 -30
  88. package/dist/git.js +0 -67
  89. package/dist/index.js +0 -108
  90. package/dist/server.js +0 -535
  91. /package/{ui → packages/cli/ui}/assets/index-Br8xQbJz.js +0 -0
  92. /package/{ui → packages/cli/ui}/assets/index-Cc2q1t5k.js +0 -0
  93. /package/{ui → packages/cli/ui}/assets/index-DrL7ojPA.css +0 -0
  94. /package/{ui → packages/cli/ui}/index.html +0 -0
  95. /package/{ui → packages/cli/ui}/vite.svg +0 -0
@@ -0,0 +1,23 @@
1
+ export class GitDriveError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = "GitDriveError";
5
+ }
6
+ }
7
+
8
+ export function handleError(err: unknown): void {
9
+ if (err instanceof GitDriveError) {
10
+ console.error(`error: ${err.message}`);
11
+ } else if (err instanceof Error) {
12
+ const msg = err.message;
13
+ // execSync errors include stderr in the message
14
+ const stderrMatch = msg.match(/stderr:\s*([\s\S]*)/);
15
+ if (stderrMatch) {
16
+ console.error(`error: ${stderrMatch[1].trim()}`);
17
+ } else {
18
+ console.error(`error: ${msg}`);
19
+ }
20
+ } else {
21
+ console.error("An unexpected error occurred.");
22
+ }
23
+ }
@@ -0,0 +1,60 @@
1
+ import { execSync } from "child_process";
2
+ import { basename } from "path";
3
+ import { getDiskInfo } from "node-disk-info";
4
+
5
+ export function git(args: string, cwd?: string): string {
6
+ return execSync(`git ${args}`, {
7
+ cwd,
8
+ encoding: "utf-8",
9
+ stdio: ["pipe", "pipe", "pipe"],
10
+ }).trim();
11
+ }
12
+
13
+ export async function listDrives(): Promise<any[]> {
14
+ const drives = await getDiskInfo();
15
+ return drives.filter((d: any) => {
16
+ const mp = d.mounted;
17
+ if (!mp) return false;
18
+ if (mp === "/" || mp === "100%") return false;
19
+
20
+ // Exclude temporary and system paths on all platforms
21
+ if (mp.startsWith("/var/") || mp.startsWith("/private/var/") || mp.startsWith("/tmp") || mp.startsWith("/private/tmp")) return false;
22
+ if (mp.includes("TemporaryItems") || mp.includes("NSIRD_")) return false;
23
+ if (mp.startsWith("/System/") || mp.startsWith("/Library/")) return false;
24
+
25
+ if (process.platform === "darwin") {
26
+ return mp.startsWith("/Volumes/") && !mp.startsWith("/Volumes/Recovery");
27
+ }
28
+
29
+ if (mp.startsWith("/sys") || mp.startsWith("/proc") || mp.startsWith("/run") || mp.startsWith("/snap") || mp.startsWith("/boot")) return false;
30
+ if (d.filesystem === "tmpfs" || d.filesystem === "devtmpfs" || d.filesystem === "udev" || d.filesystem === "overlay") return false;
31
+
32
+ return true;
33
+ });
34
+ }
35
+
36
+ export function getRepoRoot(): string {
37
+ return git("rev-parse --show-toplevel");
38
+ }
39
+
40
+ export function getProjectName(): string {
41
+ const root = getRepoRoot();
42
+ return basename(root);
43
+ }
44
+
45
+ export function getRemoteUrl(remoteName: string): string | null {
46
+ try {
47
+ return git(`remote get-url ${remoteName}`);
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ export function isGitRepo(): boolean {
54
+ try {
55
+ git("rev-parse --is-inside-work-tree");
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process';
4
+ import { readFileSync } from 'fs';
5
+ import { push } from "./commands/push.js";
6
+ import { list } from "./commands/list.js";
7
+ import { status } from "./commands/status.js";
8
+ import { link } from "./commands/link.js";
9
+ import { init } from "./commands/init.js";
10
+ import { companion } from "./commands/companion.js";
11
+ import { handleError } from "./errors.js";
12
+ import { ensureServerRunning } from "./server.js";
13
+
14
+ // Get version from package.json
15
+ declare const __dirname: string;
16
+ const { version: VERSION } = JSON.parse(readFileSync(__dirname + '/../package.json', 'utf-8'));
17
+
18
+ const commands: Record<string, (args: string[]) => void | Promise<void>> = {
19
+ init,
20
+ push,
21
+ list,
22
+ status,
23
+ link,
24
+ companion,
25
+ server: startServer,
26
+ start: startServer,
27
+ ui: startServer,
28
+ };
29
+
30
+ // Commands that don't need the server running
31
+ const NO_SERVER_COMMANDS = ['server', 'start', 'ui', 'companion'];
32
+
33
+ function printUsage(): void {
34
+ console.log(`
35
+ git-drive - Turn any external drive into a git remote backup for your code
36
+
37
+ Usage:
38
+ git-drive <command> [options]
39
+
40
+ Commands:
41
+ init Initialize git-drive on an external drive
42
+ link Link current repo to a drive
43
+ push Push current repo to drive
44
+ list Show connected drives and their status
45
+ status Show detailed status of drives and repos
46
+ companion [path] Run git-drive from a drive (companion mode)
47
+ server, start, ui Start the git-drive web UI server
48
+
49
+ Options:
50
+ -v, -V, --version Show version number
51
+ -h, --help Show this help message
52
+
53
+ Examples:
54
+ git-drive init /Volumes/MyDrive Initialize git-drive on a drive
55
+ git-drive link Link current repo to a drive
56
+ git-drive push Push current repo to drive
57
+ git-drive list List connected drives
58
+ git-drive status Show detailed status
59
+ git-drive companion Run companion mode (interactive)
60
+ git-drive companion /Volumes/USB Run companion mode for specific drive
61
+ git-drive server Start the web UI at http://localhost:4483
62
+
63
+ Environment Variables:
64
+ GIT_DRIVE_PORT Port for the web server (default: 4483)
65
+ GIT_DRIVE_COMPANION_MODE Set to 'true' for companion mode
66
+ GIT_DRIVE_COMPANION_DRIVE Drive path in companion mode
67
+
68
+ Docker:
69
+ docker run -it --rm -v /Volumes:/Volumes -p 4483:4483 git-drive
70
+
71
+ Documentation:
72
+ https://github.com/josmanvis/git-drive
73
+ `);
74
+ }
75
+
76
+ function startServer(_args: string[]): void {
77
+ console.log('\n 🚀 Starting Git Drive server...\n');
78
+ console.log(' Web UI: http://localhost:4483\n');
79
+ console.log(' Press Ctrl+C to stop\n');
80
+
81
+ const serverPath = require.resolve('./server.js');
82
+ const child = spawn(process.execPath, [serverPath], {
83
+ stdio: 'inherit',
84
+ env: process.env
85
+ });
86
+
87
+ child.on('error', (err) => {
88
+ console.error('Failed to start server:', err.message);
89
+ process.exit(1);
90
+ });
91
+
92
+ child.on('exit', (code) => {
93
+ process.exit(code || 0);
94
+ });
95
+ }
96
+
97
+ const [command, ...args] = process.argv.slice(2);
98
+
99
+ (async () => {
100
+ try {
101
+ if (!command || command === "--help" || command === "-h") {
102
+ printUsage();
103
+ process.exit(0);
104
+ }
105
+
106
+ // Handle version flags
107
+ if (command === "--version" || command === "-v" || command === "-V" || command === "version") {
108
+ console.log(`git-drive v${VERSION}`);
109
+ process.exit(0);
110
+ }
111
+
112
+ const handler = commands[command];
113
+ if (!handler) {
114
+ console.error(`Unknown command: ${command}\n`);
115
+ printUsage();
116
+ process.exit(1);
117
+ }
118
+
119
+ // Ensure server is running for commands that need it
120
+ if (!NO_SERVER_COMMANDS.includes(command)) {
121
+ await ensureServerRunning();
122
+ }
123
+
124
+ await handler(args);
125
+ } catch (err) {
126
+ handleError(err);
127
+ process.exit(1);
128
+ }
129
+ })();