repo-cloak-cli 1.2.4 ā 1.3.2
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/release.yml +92 -92
- package/LICENSE +21 -21
- package/README.md +138 -118
- package/bin/repo-cloak.js +9 -9
- package/package.json +50 -50
- package/src/cli.js +86 -84
- package/src/commands/pull.js +582 -352
- package/src/commands/push.js +250 -235
- package/src/core/anonymizer.js +128 -128
- package/src/core/copier.js +139 -139
- package/src/core/crypto.js +128 -128
- package/src/core/git.js +61 -0
- package/src/core/mapper.js +235 -235
- package/src/core/scanner.js +137 -137
- package/src/index.js +8 -8
- package/src/ui/banner.js +70 -70
- package/src/ui/fileSelector.js +256 -256
- package/src/ui/prompts.js +165 -165
- package/tests/anonymizer.test.js +127 -127
- package/tests/copier.test.js +94 -94
- package/tests/crypto.test.js +106 -106
- package/tests/git.test.js +103 -0
- package/tests/mapper.test.js +166 -166
- package/tests/scanner.test.js +100 -100
- package/medium.md +0 -319
package/src/core/scanner.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Directory Scanner
|
|
3
|
-
* Scans and builds file tree structure
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readdirSync, statSync } from 'fs';
|
|
7
|
-
import { join, relative, sep, extname } from 'path';
|
|
8
|
-
|
|
9
|
-
// Binary file extensions to copy without modification
|
|
10
|
-
const BINARY_EXTENSIONS = new Set([
|
|
11
|
-
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.svg',
|
|
12
|
-
'.mp3', '.mp4', '.wav', '.avi', '.mov', '.mkv', '.webm',
|
|
13
|
-
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
14
|
-
'.zip', '.rar', '.7z', '.tar', '.gz', '.bz2',
|
|
15
|
-
'.exe', '.dll', '.so', '.dylib', '.bin',
|
|
16
|
-
'.ttf', '.otf', '.woff', '.woff2', '.eot',
|
|
17
|
-
'.sqlite', '.db', '.mdb'
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
// Directories to always ignore
|
|
21
|
-
const IGNORE_DIRS = new Set([
|
|
22
|
-
'node_modules',
|
|
23
|
-
'.git',
|
|
24
|
-
'.svn',
|
|
25
|
-
'.hg',
|
|
26
|
-
'.DS_Store',
|
|
27
|
-
'Thumbs.db',
|
|
28
|
-
'.idea',
|
|
29
|
-
'.vscode',
|
|
30
|
-
'__pycache__',
|
|
31
|
-
'.pytest_cache',
|
|
32
|
-
'dist',
|
|
33
|
-
'build',
|
|
34
|
-
'.next',
|
|
35
|
-
'.nuxt',
|
|
36
|
-
'coverage',
|
|
37
|
-
'.nyc_output',
|
|
38
|
-
'.repo-cloak-map.json',
|
|
39
|
-
'.env',
|
|
40
|
-
'.env.local'
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Check if a file is binary based on extension
|
|
45
|
-
*/
|
|
46
|
-
export function isBinaryFile(filePath) {
|
|
47
|
-
const ext = extname(filePath).toLowerCase();
|
|
48
|
-
return BINARY_EXTENSIONS.has(ext);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Check if a file/folder should be ignored
|
|
53
|
-
*/
|
|
54
|
-
export function shouldIgnore(name) {
|
|
55
|
-
return IGNORE_DIRS.has(name) || name.startsWith('.');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Recursively get all files in a directory
|
|
60
|
-
*/
|
|
61
|
-
export function getAllFiles(dir, basePath = dir, files = []) {
|
|
62
|
-
try {
|
|
63
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
64
|
-
|
|
65
|
-
for (const entry of entries) {
|
|
66
|
-
const fullPath = join(dir, entry.name);
|
|
67
|
-
|
|
68
|
-
if (shouldIgnore(entry.name)) continue;
|
|
69
|
-
|
|
70
|
-
if (entry.isDirectory()) {
|
|
71
|
-
getAllFiles(fullPath, basePath, files);
|
|
72
|
-
} else {
|
|
73
|
-
files.push({
|
|
74
|
-
absolutePath: fullPath,
|
|
75
|
-
relativePath: relative(basePath, fullPath),
|
|
76
|
-
name: entry.name,
|
|
77
|
-
isBinary: isBinaryFile(fullPath)
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
// Permission denied or other errors - skip
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return files;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get directory structure for display
|
|
90
|
-
*/
|
|
91
|
-
export function getDirectoryTree(dir, basePath = dir, depth = 0, maxDepth = 5) {
|
|
92
|
-
const tree = [];
|
|
93
|
-
|
|
94
|
-
if (depth > maxDepth) return tree;
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
98
|
-
|
|
99
|
-
// Sort: folders first, then files
|
|
100
|
-
entries.sort((a, b) => {
|
|
101
|
-
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
102
|
-
if (!a.isDirectory() && b.isDirectory()) return 1;
|
|
103
|
-
return a.name.localeCompare(b.name);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
for (const entry of entries) {
|
|
107
|
-
if (shouldIgnore(entry.name)) continue;
|
|
108
|
-
|
|
109
|
-
const fullPath = join(dir, entry.name);
|
|
110
|
-
const node = {
|
|
111
|
-
name: entry.name,
|
|
112
|
-
path: fullPath,
|
|
113
|
-
relativePath: relative(basePath, fullPath),
|
|
114
|
-
isDirectory: entry.isDirectory(),
|
|
115
|
-
depth
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
tree.push(node);
|
|
119
|
-
|
|
120
|
-
if (entry.isDirectory()) {
|
|
121
|
-
const children = getDirectoryTree(fullPath, basePath, depth + 1, maxDepth);
|
|
122
|
-
tree.push(...children);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
// Skip inaccessible directories
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return tree;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Count files in a directory (recursive)
|
|
134
|
-
*/
|
|
135
|
-
export function countFiles(dir) {
|
|
136
|
-
return getAllFiles(dir).length;
|
|
137
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Directory Scanner
|
|
3
|
+
* Scans and builds file tree structure
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readdirSync, statSync } from 'fs';
|
|
7
|
+
import { join, relative, sep, extname } from 'path';
|
|
8
|
+
|
|
9
|
+
// Binary file extensions to copy without modification
|
|
10
|
+
const BINARY_EXTENSIONS = new Set([
|
|
11
|
+
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.svg',
|
|
12
|
+
'.mp3', '.mp4', '.wav', '.avi', '.mov', '.mkv', '.webm',
|
|
13
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
14
|
+
'.zip', '.rar', '.7z', '.tar', '.gz', '.bz2',
|
|
15
|
+
'.exe', '.dll', '.so', '.dylib', '.bin',
|
|
16
|
+
'.ttf', '.otf', '.woff', '.woff2', '.eot',
|
|
17
|
+
'.sqlite', '.db', '.mdb'
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
// Directories to always ignore
|
|
21
|
+
const IGNORE_DIRS = new Set([
|
|
22
|
+
'node_modules',
|
|
23
|
+
'.git',
|
|
24
|
+
'.svn',
|
|
25
|
+
'.hg',
|
|
26
|
+
'.DS_Store',
|
|
27
|
+
'Thumbs.db',
|
|
28
|
+
'.idea',
|
|
29
|
+
'.vscode',
|
|
30
|
+
'__pycache__',
|
|
31
|
+
'.pytest_cache',
|
|
32
|
+
'dist',
|
|
33
|
+
'build',
|
|
34
|
+
'.next',
|
|
35
|
+
'.nuxt',
|
|
36
|
+
'coverage',
|
|
37
|
+
'.nyc_output',
|
|
38
|
+
'.repo-cloak-map.json',
|
|
39
|
+
'.env',
|
|
40
|
+
'.env.local'
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if a file is binary based on extension
|
|
45
|
+
*/
|
|
46
|
+
export function isBinaryFile(filePath) {
|
|
47
|
+
const ext = extname(filePath).toLowerCase();
|
|
48
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a file/folder should be ignored
|
|
53
|
+
*/
|
|
54
|
+
export function shouldIgnore(name) {
|
|
55
|
+
return IGNORE_DIRS.has(name) || name.startsWith('.');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Recursively get all files in a directory
|
|
60
|
+
*/
|
|
61
|
+
export function getAllFiles(dir, basePath = dir, files = []) {
|
|
62
|
+
try {
|
|
63
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
64
|
+
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const fullPath = join(dir, entry.name);
|
|
67
|
+
|
|
68
|
+
if (shouldIgnore(entry.name)) continue;
|
|
69
|
+
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
getAllFiles(fullPath, basePath, files);
|
|
72
|
+
} else {
|
|
73
|
+
files.push({
|
|
74
|
+
absolutePath: fullPath,
|
|
75
|
+
relativePath: relative(basePath, fullPath),
|
|
76
|
+
name: entry.name,
|
|
77
|
+
isBinary: isBinaryFile(fullPath)
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// Permission denied or other errors - skip
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return files;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get directory structure for display
|
|
90
|
+
*/
|
|
91
|
+
export function getDirectoryTree(dir, basePath = dir, depth = 0, maxDepth = 5) {
|
|
92
|
+
const tree = [];
|
|
93
|
+
|
|
94
|
+
if (depth > maxDepth) return tree;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
98
|
+
|
|
99
|
+
// Sort: folders first, then files
|
|
100
|
+
entries.sort((a, b) => {
|
|
101
|
+
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
102
|
+
if (!a.isDirectory() && b.isDirectory()) return 1;
|
|
103
|
+
return a.name.localeCompare(b.name);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
if (shouldIgnore(entry.name)) continue;
|
|
108
|
+
|
|
109
|
+
const fullPath = join(dir, entry.name);
|
|
110
|
+
const node = {
|
|
111
|
+
name: entry.name,
|
|
112
|
+
path: fullPath,
|
|
113
|
+
relativePath: relative(basePath, fullPath),
|
|
114
|
+
isDirectory: entry.isDirectory(),
|
|
115
|
+
depth
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
tree.push(node);
|
|
119
|
+
|
|
120
|
+
if (entry.isDirectory()) {
|
|
121
|
+
const children = getDirectoryTree(fullPath, basePath, depth + 1, maxDepth);
|
|
122
|
+
tree.push(...children);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// Skip inaccessible directories
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return tree;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Count files in a directory (recursive)
|
|
134
|
+
*/
|
|
135
|
+
export function countFiles(dir) {
|
|
136
|
+
return getAllFiles(dir).length;
|
|
137
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* repo-cloak - Main exports
|
|
3
|
-
* š Selectively extract and anonymize files from repositories
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { pull } from './commands/pull.js';
|
|
7
|
-
export { push } from './commands/push.js';
|
|
8
|
-
export { showBanner } from './ui/banner.js';
|
|
1
|
+
/**
|
|
2
|
+
* repo-cloak - Main exports
|
|
3
|
+
* š Selectively extract and anonymize files from repositories
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { pull } from './commands/pull.js';
|
|
7
|
+
export { push } from './commands/push.js';
|
|
8
|
+
export { showBanner } from './ui/banner.js';
|
package/src/ui/banner.js
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fancy ASCII Banner
|
|
3
|
-
* Shows a colorful intro when the CLI starts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import figlet from 'figlet';
|
|
8
|
-
|
|
9
|
-
export async function showBanner() {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
figlet.text('repo-cloak', {
|
|
12
|
-
font: 'Standard',
|
|
13
|
-
horizontalLayout: 'default',
|
|
14
|
-
verticalLayout: 'default'
|
|
15
|
-
}, (err, data) => {
|
|
16
|
-
if (err) {
|
|
17
|
-
console.log(chalk.magentaBright.bold('\nš repo-cloak\n'));
|
|
18
|
-
resolve();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Create gradient effect
|
|
23
|
-
const lines = data.split('\n');
|
|
24
|
-
const colors = [
|
|
25
|
-
chalk.hex('#FF6B6B'), // Coral
|
|
26
|
-
chalk.hex('#FF8E53'), // Orange
|
|
27
|
-
chalk.hex('#FEC89A'), // Peach
|
|
28
|
-
chalk.hex('#A8E6CF'), // Mint
|
|
29
|
-
chalk.hex('#88D8B0'), // Seafoam
|
|
30
|
-
chalk.hex('#7B68EE'), // Medium Slate Blue
|
|
31
|
-
chalk.hex('#9D4EDD'), // Purple
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
console.log('\n');
|
|
35
|
-
lines.forEach((line, index) => {
|
|
36
|
-
const color = colors[index % colors.length];
|
|
37
|
-
console.log(color(line));
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Tagline box
|
|
41
|
-
const tagline = 'š Selectively extract & anonymize repository files';
|
|
42
|
-
const version = 'v1.0.0';
|
|
43
|
-
|
|
44
|
-
console.log('');
|
|
45
|
-
console.log(chalk.dim('ā'.repeat(55)));
|
|
46
|
-
console.log(chalk.white.bold(` ${tagline}`));
|
|
47
|
-
console.log(chalk.dim(` Compatible with Windows, macOS, and Linux | ${version}`));
|
|
48
|
-
console.log(chalk.dim('ā'.repeat(55)));
|
|
49
|
-
console.log('');
|
|
50
|
-
|
|
51
|
-
resolve();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function showSuccess(message) {
|
|
57
|
-
console.log(chalk.green.bold(`\nā
${message}\n`));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function showError(message) {
|
|
61
|
-
console.log(chalk.red.bold(`\nā ${message}\n`));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function showWarning(message) {
|
|
65
|
-
console.log(chalk.yellow.bold(`\nā ļø ${message}\n`));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function showInfo(message) {
|
|
69
|
-
console.log(chalk.cyan(`\nā¹ļø ${message}\n`));
|
|
70
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Fancy ASCII Banner
|
|
3
|
+
* Shows a colorful intro when the CLI starts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import figlet from 'figlet';
|
|
8
|
+
|
|
9
|
+
export async function showBanner() {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
figlet.text('repo-cloak', {
|
|
12
|
+
font: 'Standard',
|
|
13
|
+
horizontalLayout: 'default',
|
|
14
|
+
verticalLayout: 'default'
|
|
15
|
+
}, (err, data) => {
|
|
16
|
+
if (err) {
|
|
17
|
+
console.log(chalk.magentaBright.bold('\nš repo-cloak\n'));
|
|
18
|
+
resolve();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Create gradient effect
|
|
23
|
+
const lines = data.split('\n');
|
|
24
|
+
const colors = [
|
|
25
|
+
chalk.hex('#FF6B6B'), // Coral
|
|
26
|
+
chalk.hex('#FF8E53'), // Orange
|
|
27
|
+
chalk.hex('#FEC89A'), // Peach
|
|
28
|
+
chalk.hex('#A8E6CF'), // Mint
|
|
29
|
+
chalk.hex('#88D8B0'), // Seafoam
|
|
30
|
+
chalk.hex('#7B68EE'), // Medium Slate Blue
|
|
31
|
+
chalk.hex('#9D4EDD'), // Purple
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
console.log('\n');
|
|
35
|
+
lines.forEach((line, index) => {
|
|
36
|
+
const color = colors[index % colors.length];
|
|
37
|
+
console.log(color(line));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Tagline box
|
|
41
|
+
const tagline = 'š Selectively extract & anonymize repository files';
|
|
42
|
+
const version = 'v1.0.0';
|
|
43
|
+
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log(chalk.dim('ā'.repeat(55)));
|
|
46
|
+
console.log(chalk.white.bold(` ${tagline}`));
|
|
47
|
+
console.log(chalk.dim(` Compatible with Windows, macOS, and Linux | ${version}`));
|
|
48
|
+
console.log(chalk.dim('ā'.repeat(55)));
|
|
49
|
+
console.log('');
|
|
50
|
+
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function showSuccess(message) {
|
|
57
|
+
console.log(chalk.green.bold(`\nā
${message}\n`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function showError(message) {
|
|
61
|
+
console.log(chalk.red.bold(`\nā ${message}\n`));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function showWarning(message) {
|
|
65
|
+
console.log(chalk.yellow.bold(`\nā ļø ${message}\n`));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function showInfo(message) {
|
|
69
|
+
console.log(chalk.cyan(`\nā¹ļø ${message}\n`));
|
|
70
|
+
}
|