repo-anon 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/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # repo-anon
2
+
3
+ A Node.js CLI tool to anonymize and de-anonymize files in a repository based on a `.phrases` configuration file. Perfect for preparing repositories for AI processing while keeping sensitive info, such as company or brand names, protected.
4
+
5
+ ## Features
6
+
7
+ - **Anonymize**: Replaces sensitive phrases with configured placeholders.
8
+ - **De-anonymize**: Restores original phrases from placeholders.
9
+ - **Recursive**: Traverses through all project directories (ignoring `node_modules`, `.git`, etc.).
10
+ - **CI/CD Ready**: Includes GitLab pipeline configuration for publishing to the GitLab package registry.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install -g @your_gitlab_namespace/repo-anon
16
+ ```
17
+
18
+ *(Note: Replace `@your_gitlab_namespace` with your actual GitLab namespace).*
19
+
20
+ ## Usage
21
+
22
+ 1. Create a `.phrases` file in the current working directory:
23
+
24
+ ```json
25
+ {
26
+ "CompanyA": "ANON_COMPANY_A",
27
+ "BrandX": "ANON_BRAND_X"
28
+ }
29
+ ```
30
+
31
+ 2. Run the anonymization command:
32
+
33
+ ```bash
34
+ repo-anon anonymize
35
+ ```
36
+
37
+ 3. Revert changes (if needed):
38
+
39
+ ```bash
40
+ repo-anon deanonymize
41
+ ```
42
+
43
+ ## Development
44
+
45
+ - **Tests**: Run unit tests using `npm test`.
46
+ - **Linting**: Lint the project using `npm run lint`.
47
+
48
+ ## CI/CD Deployment
49
+
50
+ The project includes a `.gitlab-ci.yml` configured to automatically publish new versions to the GitLab package registry when a tag is pushed.
51
+
52
+ ---
53
+ Built with ❤️.
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const { anonymize, deanonymize } = require('./../lib/processor');
5
+
6
+ program
7
+ .version('1.0.0')
8
+ .description('Repository Anonymizer CLI');
9
+
10
+ program
11
+ .command('anonymize')
12
+ .description('Anonymize project based on .phrases file')
13
+ .action(anonymize);
14
+
15
+ program
16
+ .command('deanonymize')
17
+ .description('De-anonymize project based on .phrases file')
18
+ .action(deanonymize);
19
+
20
+ program.parse(process.argv);
@@ -0,0 +1,11 @@
1
+ import globals from "globals";
2
+ import pluginJs from "@eslint/js";
3
+
4
+ export default [
5
+ {
6
+ languageOptions: {
7
+ globals: { ...globals.node, ...globals.jest }
8
+ }
9
+ },
10
+ pluginJs.configs.recommended,
11
+ ];
@@ -0,0 +1,65 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const PHRASES_FILE = '.phrases';
5
+
6
+ function loadPhrases() {
7
+ if (!fs.existsSync(PHRASES_FILE)) {
8
+ throw new Error('No .phrases file found.');
9
+ }
10
+ return JSON.parse(fs.readFileSync(PHRASES_FILE, 'utf8'));
11
+ }
12
+
13
+ function processContent(content, phrases, reverse = false) {
14
+ let changed = false;
15
+ let newContent = content;
16
+
17
+ for (const [original, placeholder] of Object.entries(phrases)) {
18
+ const search = reverse ? placeholder : original;
19
+ const replace = reverse ? original : placeholder;
20
+ if (newContent.includes(search)) {
21
+ newContent = newContent.split(search).join(replace);
22
+ changed = true;
23
+ }
24
+ }
25
+ return changed ? newContent : null;
26
+ }
27
+
28
+ async function walk(dir, callback) {
29
+ const files = fs.readdirSync(dir);
30
+ for (const file of files) {
31
+ if (['node_modules', '.git', '.phrases', 'package.json', 'package-lock.json', 'bin', 'tests', '.gitlab-ci.yml'].includes(file)) continue;
32
+ const filePath = path.join(dir, file);
33
+ if (fs.statSync(filePath).isDirectory()) {
34
+ await walk(filePath, callback);
35
+ } else {
36
+ callback(filePath);
37
+ }
38
+ }
39
+ }
40
+
41
+ async function anonymize() {
42
+ const phrases = loadPhrases();
43
+ walk(process.cwd(), (filePath) => {
44
+ const content = fs.readFileSync(filePath, 'utf8');
45
+ const newContent = processContent(content, phrases);
46
+ if (newContent) {
47
+ fs.writeFileSync(filePath, newContent, 'utf8');
48
+ console.log(`Updated: ${filePath}`);
49
+ }
50
+ });
51
+ }
52
+
53
+ async function deanonymize() {
54
+ const phrases = loadPhrases();
55
+ walk(process.cwd(), (filePath) => {
56
+ const content = fs.readFileSync(filePath, 'utf8');
57
+ const newContent = processContent(content, phrases, true);
58
+ if (newContent) {
59
+ fs.writeFileSync(filePath, newContent, 'utf8');
60
+ console.log(`Updated: ${filePath}`);
61
+ }
62
+ });
63
+ }
64
+
65
+ module.exports = { anonymize, deanonymize, processContent };
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "repo-anon",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool to anonymize/de-anonymize repositories.",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "repo-anon": "bin/repo-anon.js"
8
+ },
9
+ "dependencies": {
10
+ "commander": "^13.1.0"
11
+ },
12
+ "scripts": {
13
+ "test": "jest",
14
+ "lint": "eslint ."
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "type": "commonjs",
20
+ "devDependencies": {
21
+ "@eslint/js": "^10.0.1",
22
+ "eslint": "^10.0.2",
23
+ "globals": "^17.4.0",
24
+ "jest": "^30.2.0"
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ const { processContent } = require('../lib/processor');
2
+
3
+ describe('repo-anon processor', () => {
4
+ test('should anonymize content', () => {
5
+ const phrases = { "company": "anon" };
6
+ const content = "my company is here";
7
+ const expected = "my anon is here";
8
+ const result = processContent(content, phrases);
9
+ expect(result).toBe(expected);
10
+ });
11
+
12
+ test('should deanonymize content', () => {
13
+ const phrases = { "company": "anon" };
14
+ const content = "my anon is here";
15
+ const expected = "my company is here";
16
+ // reverse = true
17
+ const result = processContent(content, phrases, true);
18
+ expect(result).toBe(expected);
19
+ });
20
+
21
+ test('should return null if no change', () => {
22
+ const phrases = { "company": "anon" };
23
+ const content = "nothing here";
24
+ const result = processContent(content, phrases);
25
+ expect(result).toBeNull();
26
+ });
27
+ });