@edcalderon/versioning 1.1.2 → 1.3.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 +83 -17
- package/dist/extensions/cleanup-repo/index.d.ts +49 -0
- package/dist/extensions/cleanup-repo/index.js +649 -0
- package/dist/extensions/lifecycle-hooks/index.d.ts +4 -0
- package/dist/extensions/{lifecycle-hooks.js → lifecycle-hooks/index.js} +1 -1
- package/dist/extensions/npm-publish/index.d.ts +4 -0
- package/dist/extensions/{npm-publish.js → npm-publish/index.js} +1 -1
- package/dist/extensions/reentry-status/config-manager.js +3 -1
- package/dist/extensions/reentry-status/constants.d.ts +1 -1
- package/dist/extensions/reentry-status/constants.js +1 -1
- package/dist/extensions/reentry-status/extension.d.ts +4 -0
- package/dist/extensions/{reentry-status-extension.js → reentry-status/extension.js} +165 -18
- package/dist/extensions/reentry-status/git-context.d.ts +27 -0
- package/dist/extensions/reentry-status/git-context.js +94 -0
- package/dist/extensions/reentry-status/index.d.ts +3 -0
- package/dist/extensions/reentry-status/index.js +6 -0
- package/dist/extensions/reentry-status/roadmap-renderer.d.ts +7 -0
- package/dist/extensions/reentry-status/roadmap-renderer.js +27 -10
- package/dist/extensions/sample-extension/index.d.ts +4 -0
- package/dist/extensions/{sample-extension.js → sample-extension/index.js} +1 -1
- package/dist/extensions/secrets-check/index.d.ts +16 -0
- package/dist/extensions/secrets-check/index.js +264 -0
- package/dist/extensions.js +27 -15
- package/dist/versioning.d.ts +1 -0
- package/package.json +2 -2
- package/dist/extensions/lifecycle-hooks.d.ts +0 -4
- package/dist/extensions/npm-publish.d.ts +0 -4
- package/dist/extensions/reentry-status-extension.d.ts +0 -4
- package/dist/extensions/sample-extension.d.ts +0 -4
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.checkContentForSecrets = checkContentForSecrets;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
// Definition of patterns to search for
|
|
41
|
+
// Each pattern is a regex
|
|
42
|
+
const DEFAULT_PATTERNS = [
|
|
43
|
+
// PEM / JSON private keys
|
|
44
|
+
new RegExp('-----BEGIN ' + 'PRIVATE KEY-----'),
|
|
45
|
+
new RegExp('-----BEGIN ' + 'RSA PRIVATE KEY-----'),
|
|
46
|
+
/"private_key":\s*"/,
|
|
47
|
+
/"private_key_id":\s*"/,
|
|
48
|
+
// Cloud / API tokens
|
|
49
|
+
/AIza[0-9A-Za-z\-_]{35}/,
|
|
50
|
+
/ghp_[0-9A-Za-z]{36}/,
|
|
51
|
+
/npm_[0-9A-Za-z]{36}/,
|
|
52
|
+
// Ethereum / EVM hex private keys
|
|
53
|
+
/PRIVATE_DEPLOYER=[0-9a-fA-F]{40,}/,
|
|
54
|
+
/DEPLOYER_PRIVATE_KEY=0x[0-9a-fA-F]{40,}/,
|
|
55
|
+
/PRIVATE_KEY=[0-9a-fA-F]{64}/,
|
|
56
|
+
/_KEY=0x[0-9a-fA-F]{64}/,
|
|
57
|
+
/cast wallet address 0x[0-9a-fA-F]{64}/,
|
|
58
|
+
// Seed phrases
|
|
59
|
+
/MNEMONIC=.{20,}/
|
|
60
|
+
];
|
|
61
|
+
// Allowlist patterns that are safe
|
|
62
|
+
const DEFAULT_ALLOWLIST = [
|
|
63
|
+
"POOL_MANAGER_ADDRESS=",
|
|
64
|
+
"HOOK_OWNER=",
|
|
65
|
+
"NEXT_PUBLIC_",
|
|
66
|
+
"ETHERSCAN_API_KEY=YOUR_",
|
|
67
|
+
"RPC_URL=",
|
|
68
|
+
"YOUR_DEPLOYER_PRIVATE_KEY",
|
|
69
|
+
"YOUR_LOCAL_PRIVATE_KEY",
|
|
70
|
+
"YOUR_TESTNET_PRIVATE_KEY",
|
|
71
|
+
"your_private_key_here"
|
|
72
|
+
];
|
|
73
|
+
function checkContentForSecrets(content, patterns, allowlist, filename) {
|
|
74
|
+
const results = [];
|
|
75
|
+
const lines = content.split('\n');
|
|
76
|
+
for (let i = 0; i < lines.length; i++) {
|
|
77
|
+
const line = lines[i];
|
|
78
|
+
for (const pattern of patterns) {
|
|
79
|
+
if (pattern.test(line)) {
|
|
80
|
+
// Check allowance
|
|
81
|
+
let isAllowed = false;
|
|
82
|
+
for (const allow of allowlist) {
|
|
83
|
+
if (line.includes(allow)) {
|
|
84
|
+
isAllowed = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!isAllowed) {
|
|
89
|
+
results.push({
|
|
90
|
+
file: filename,
|
|
91
|
+
line: i + 1,
|
|
92
|
+
content: line.trim(),
|
|
93
|
+
pattern: pattern.toString()
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
const extension = {
|
|
102
|
+
name: 'secrets-check',
|
|
103
|
+
description: 'Checks for hardcoded secrets and private keys in staged files',
|
|
104
|
+
version: '1.1.0',
|
|
105
|
+
register: async (program, config) => {
|
|
106
|
+
// Try to get config from extensionConfig first, fallback to top-level secrets for backcompat
|
|
107
|
+
const extensionConfig = config.extensionConfig?.['secrets-check'];
|
|
108
|
+
const secretsConfig = extensionConfig || config.secrets || {};
|
|
109
|
+
if (secretsConfig.enabled === false) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Merge patterns
|
|
113
|
+
const patterns = [...DEFAULT_PATTERNS];
|
|
114
|
+
if (secretsConfig.patterns) {
|
|
115
|
+
for (const p of secretsConfig.patterns) {
|
|
116
|
+
try {
|
|
117
|
+
patterns.push(new RegExp(p));
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
console.warn(`⚠️ Invalid regex pattern in config: ${p}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Merge allowlist
|
|
125
|
+
const allowlist = [...DEFAULT_ALLOWLIST];
|
|
126
|
+
if (secretsConfig.allowlist) {
|
|
127
|
+
allowlist.push(...secretsConfig.allowlist);
|
|
128
|
+
}
|
|
129
|
+
const secretsCmd = program
|
|
130
|
+
.command('check-secrets')
|
|
131
|
+
.description('Scan staged files for potential secrets')
|
|
132
|
+
.action(async () => {
|
|
133
|
+
await runSecretsCheck();
|
|
134
|
+
});
|
|
135
|
+
secretsCmd
|
|
136
|
+
.command('husky')
|
|
137
|
+
.description('Add secrets check to Husky pre-commit hook')
|
|
138
|
+
.option('--remove', 'Remove secrets check from Husky hook', false)
|
|
139
|
+
.action(async (options) => {
|
|
140
|
+
const rootDir = process.cwd();
|
|
141
|
+
const huskyDir = path.join(rootDir, '.husky');
|
|
142
|
+
const hookPath = path.join(huskyDir, 'pre-commit');
|
|
143
|
+
const MARKER_START = '# === secrets-check: start ===';
|
|
144
|
+
const MARKER_END = '# === secrets-check: end ===';
|
|
145
|
+
if (options.remove) {
|
|
146
|
+
if (!fs.existsSync(hookPath)) {
|
|
147
|
+
console.log('ℹ️ Hook pre-commit does not exist.');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const content = fs.readFileSync(hookPath, 'utf8');
|
|
151
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
152
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
153
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
154
|
+
console.log('ℹ️ No secrets-check block found in pre-commit.');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const before = content.slice(0, startIdx).trimEnd();
|
|
158
|
+
const after = content.slice(endIdx + MARKER_END.length).trimStart();
|
|
159
|
+
const updated = [before, '', after].join('\n').replace(/\n{3,}/g, '\n\n').trim() + '\n';
|
|
160
|
+
fs.writeFileSync(hookPath, updated, 'utf8');
|
|
161
|
+
console.log('✅ Removed secrets check from .husky/pre-commit');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Add cleanup block to hook
|
|
165
|
+
if (!fs.existsSync(huskyDir)) {
|
|
166
|
+
fs.mkdirSync(huskyDir, { recursive: true });
|
|
167
|
+
}
|
|
168
|
+
const block = [
|
|
169
|
+
'',
|
|
170
|
+
MARKER_START,
|
|
171
|
+
'echo "🔒 Running secrets check…"',
|
|
172
|
+
'npx versioning check-secrets 2>/dev/null',
|
|
173
|
+
MARKER_END,
|
|
174
|
+
''
|
|
175
|
+
].join('\n');
|
|
176
|
+
if (fs.existsSync(hookPath)) {
|
|
177
|
+
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
178
|
+
if (existing.includes(MARKER_START)) {
|
|
179
|
+
console.log('ℹ️ Secrets check is already integrated in .husky/pre-commit.');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const updated = existing.trimEnd() + '\n' + block;
|
|
183
|
+
fs.writeFileSync(hookPath, updated, 'utf8');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const content = [
|
|
187
|
+
'#!/bin/sh',
|
|
188
|
+
'. "$(dirname "$0")/_/husky.sh"',
|
|
189
|
+
'',
|
|
190
|
+
block
|
|
191
|
+
].join('\n');
|
|
192
|
+
fs.writeFileSync(hookPath, content, { mode: 0o755 });
|
|
193
|
+
}
|
|
194
|
+
console.log('✅ Secrets check integrated into .husky/pre-commit');
|
|
195
|
+
});
|
|
196
|
+
async function runSecretsCheck() {
|
|
197
|
+
try {
|
|
198
|
+
console.log("🔒 Scanning for secrets...");
|
|
199
|
+
let failed = false;
|
|
200
|
+
// Get list of staged files
|
|
201
|
+
let output = '';
|
|
202
|
+
try {
|
|
203
|
+
output = (0, child_process_1.execSync)('git diff --cached --name-only --diff-filter=ACMR', { encoding: 'utf-8' });
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
// If not in a git repo or no staged files, just return
|
|
207
|
+
console.log("⚠️ Not a git repository or git error.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const files = output.split('\n').filter(f => f.trim() !== '');
|
|
211
|
+
if (files.length === 0) {
|
|
212
|
+
console.log("✅ No staged files to check.");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
for (const relativePath of files) {
|
|
216
|
+
const filePath = path.resolve(process.cwd(), relativePath);
|
|
217
|
+
// Skip check if file doesn't exist (deleted)
|
|
218
|
+
if (!fs.existsSync(filePath)) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// Skip lock files
|
|
222
|
+
if (relativePath.includes('lock.yaml') || relativePath.includes('lock.json')) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
// Read file content
|
|
226
|
+
let content = '';
|
|
227
|
+
try {
|
|
228
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
console.warn(`⚠️ Could not read file ${relativePath}:`, e);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const findings = checkContentForSecrets(content, patterns, allowlist, relativePath);
|
|
235
|
+
if (findings.length > 0) {
|
|
236
|
+
failed = true;
|
|
237
|
+
for (const finding of findings) {
|
|
238
|
+
console.log(`❌ POTENTIAL SECRET FOUND in ${finding.file}:${finding.line}`);
|
|
239
|
+
console.log(` ${finding.content}`);
|
|
240
|
+
console.log(` (matched pattern: ${finding.pattern})`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (failed) {
|
|
245
|
+
console.log("----------------------------------------------------");
|
|
246
|
+
console.log("⛔ COMMIT REJECTED");
|
|
247
|
+
console.log("Sensitive data was found in the staged files.");
|
|
248
|
+
console.log("Please remove the secrets before committing.");
|
|
249
|
+
console.log("----------------------------------------------------");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
console.log("✅ No secrets found.");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
console.error('❌ Check failed:', error instanceof Error ? error.message : String(error));
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
exports.default = extension;
|
|
264
|
+
//# sourceMappingURL=index.js.map
|
package/dist/extensions.js
CHANGED
|
@@ -81,24 +81,36 @@ async function loadExtensions(program) {
|
|
|
81
81
|
if (await fs.pathExists(extensionsDir)) {
|
|
82
82
|
const extensionFiles = await fs.readdir(extensionsDir);
|
|
83
83
|
for (const file of extensionFiles) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// Register hooks if available
|
|
92
|
-
if (extension.hooks) {
|
|
93
|
-
context.hooks.push(extension.hooks);
|
|
94
|
-
}
|
|
95
|
-
console.log(`✅ Loaded extension: ${extension.name}@${extension.version}`);
|
|
96
|
-
}
|
|
84
|
+
let extensionPath = path.join(extensionsDir, file);
|
|
85
|
+
const stats = await fs.stat(extensionPath);
|
|
86
|
+
if (stats.isDirectory()) {
|
|
87
|
+
// Check for index.js in directory
|
|
88
|
+
const indexPath = path.join(extensionPath, 'index.js');
|
|
89
|
+
if (await fs.pathExists(indexPath)) {
|
|
90
|
+
extensionPath = indexPath;
|
|
97
91
|
}
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
else {
|
|
93
|
+
continue;
|
|
100
94
|
}
|
|
101
95
|
}
|
|
96
|
+
else if (!file.endsWith('.js')) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const extensionModule = require(extensionPath);
|
|
101
|
+
const extension = extensionModule.default || extensionModule;
|
|
102
|
+
if (extension && typeof extension.register === 'function') {
|
|
103
|
+
await extension.register(program, config);
|
|
104
|
+
// Register hooks if available
|
|
105
|
+
if (extension.hooks) {
|
|
106
|
+
context.hooks.push(extension.hooks);
|
|
107
|
+
}
|
|
108
|
+
console.log(`✅ Loaded extension: ${extension.name}@${extension.version}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.warn(`⚠️ Failed to load extension ${file}:`, error instanceof Error ? error.message : String(error));
|
|
113
|
+
}
|
|
102
114
|
}
|
|
103
115
|
}
|
|
104
116
|
// Note: External extensions from config.extensions are not yet implemented
|
package/dist/versioning.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edcalderon/versioning",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A comprehensive versioning and changelog management tool for monorepos",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -78,4 +78,4 @@
|
|
|
78
78
|
"url": "git+https://github.com/edcalderon/my-second-brain.git",
|
|
79
79
|
"directory": "packages/versioning"
|
|
80
80
|
}
|
|
81
|
-
}
|
|
81
|
+
}
|