@wipcomputer/wip-license-hook 1.0.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/LICENSE +52 -0
- package/README.md +200 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.js +170 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/detector.d.ts +12 -0
- package/dist/core/detector.js +104 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/ledger.d.ts +49 -0
- package/dist/core/ledger.js +72 -0
- package/dist/core/ledger.js.map +1 -0
- package/dist/core/reporter.d.ts +14 -0
- package/dist/core/reporter.js +227 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/scanner.d.ts +39 -0
- package/dist/core/scanner.js +325 -0
- package/dist/core/scanner.js.map +1 -0
- package/hooks/pre-pull.sh +55 -0
- package/hooks/pre-push.sh +51 -0
- package/package.json +36 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License ledger — read/write/compare LICENSE-LEDGER.json + snapshot archiving.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
const LEDGER_FILE = "LICENSE-LEDGER.json";
|
|
7
|
+
const SNAPSHOT_DIR = "ledger/snapshots";
|
|
8
|
+
export function ledgerPath(repoRoot) {
|
|
9
|
+
return join(repoRoot, LEDGER_FILE);
|
|
10
|
+
}
|
|
11
|
+
export function createEmptyLedger() {
|
|
12
|
+
return {
|
|
13
|
+
version: 1,
|
|
14
|
+
dependencies: [],
|
|
15
|
+
last_full_scan: null,
|
|
16
|
+
alerts: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function readLedger(repoRoot) {
|
|
20
|
+
const p = ledgerPath(repoRoot);
|
|
21
|
+
if (!existsSync(p))
|
|
22
|
+
return createEmptyLedger();
|
|
23
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
24
|
+
}
|
|
25
|
+
export function writeLedger(repoRoot, ledger) {
|
|
26
|
+
const p = ledgerPath(repoRoot);
|
|
27
|
+
writeFileSync(p, JSON.stringify(ledger, null, 2) + "\n", "utf-8");
|
|
28
|
+
}
|
|
29
|
+
export function findEntry(ledger, name) {
|
|
30
|
+
return ledger.dependencies.find((d) => d.name === name);
|
|
31
|
+
}
|
|
32
|
+
export function upsertEntry(ledger, entry) {
|
|
33
|
+
const idx = ledger.dependencies.findIndex((d) => d.name === entry.name);
|
|
34
|
+
if (idx >= 0) {
|
|
35
|
+
ledger.dependencies[idx] = entry;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
ledger.dependencies.push(entry);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Archive a LICENSE file snapshot for a dependency.
|
|
43
|
+
*/
|
|
44
|
+
export function archiveSnapshot(repoRoot, depName, licenseContent, date) {
|
|
45
|
+
const d = date ?? new Date().toISOString().slice(0, 10);
|
|
46
|
+
const dir = join(repoRoot, SNAPSHOT_DIR, depName);
|
|
47
|
+
mkdirSync(dir, { recursive: true });
|
|
48
|
+
const filename = `LICENSE-${d}.txt`;
|
|
49
|
+
const p = join(dir, filename);
|
|
50
|
+
writeFileSync(p, licenseContent, "utf-8");
|
|
51
|
+
return p;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Compare an entry's current license against its adoption license.
|
|
55
|
+
* Returns true if changed.
|
|
56
|
+
*/
|
|
57
|
+
export function hasLicenseChanged(entry) {
|
|
58
|
+
return entry.license_at_adoption !== entry.license_current;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Add an alert to the ledger.
|
|
62
|
+
*/
|
|
63
|
+
export function addAlert(ledger, dep, from, to) {
|
|
64
|
+
ledger.alerts.push({
|
|
65
|
+
dependency: dep,
|
|
66
|
+
from,
|
|
67
|
+
to,
|
|
68
|
+
detected: new Date().toISOString(),
|
|
69
|
+
message: `⚠️ License changed: ${dep} went from ${from} → ${to}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/core/ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter — generate reports, alerts, and static dashboard HTML.
|
|
3
|
+
*/
|
|
4
|
+
import { type Ledger } from "./ledger.js";
|
|
5
|
+
import type { ScanResult } from "./scanner.js";
|
|
6
|
+
export declare function formatScanReport(results: ScanResult[]): string;
|
|
7
|
+
export declare function formatGateOutput(safe: boolean, alerts: string[]): string;
|
|
8
|
+
export declare function formatLedgerReport(ledger: Ledger): string;
|
|
9
|
+
export declare function generateDashboardHtml(ledger: Ledger): string;
|
|
10
|
+
export declare function generateBadgeUrl(ledger: Ledger): string;
|
|
11
|
+
/**
|
|
12
|
+
* Write the dashboard HTML to disk.
|
|
13
|
+
*/
|
|
14
|
+
export declare function writeDashboard(repoRoot: string, ledger?: Ledger): string;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter — generate reports, alerts, and static dashboard HTML.
|
|
3
|
+
*/
|
|
4
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { readLedger } from "./ledger.js";
|
|
7
|
+
// ─── Console reports ───
|
|
8
|
+
export function formatScanReport(results) {
|
|
9
|
+
const lines = [
|
|
10
|
+
"",
|
|
11
|
+
"╔══════════════════════════════════════════════════╗",
|
|
12
|
+
"║ wip-license-hook — Scan Report ║",
|
|
13
|
+
"╚══════════════════════════════════════════════════╝",
|
|
14
|
+
"",
|
|
15
|
+
];
|
|
16
|
+
const changed = results.filter((r) => r.wasChanged);
|
|
17
|
+
const newDeps = results.filter((r) => r.isNew);
|
|
18
|
+
const clean = results.filter((r) => !r.wasChanged && !r.isNew);
|
|
19
|
+
if (changed.length > 0) {
|
|
20
|
+
lines.push("🚨 LICENSE CHANGES DETECTED:");
|
|
21
|
+
lines.push("─".repeat(50));
|
|
22
|
+
for (const r of changed) {
|
|
23
|
+
lines.push(` 🚫 ${r.name} (${r.type})`);
|
|
24
|
+
lines.push(` License changed → now: ${r.detectedLicense}`);
|
|
25
|
+
lines.push(` Source: ${r.source}`);
|
|
26
|
+
}
|
|
27
|
+
lines.push("");
|
|
28
|
+
}
|
|
29
|
+
if (newDeps.length > 0) {
|
|
30
|
+
lines.push(`📦 New dependencies found: ${newDeps.length}`);
|
|
31
|
+
for (const r of newDeps) {
|
|
32
|
+
lines.push(` ➕ ${r.name} (${r.type}) — ${r.detectedLicense}`);
|
|
33
|
+
}
|
|
34
|
+
lines.push("");
|
|
35
|
+
}
|
|
36
|
+
if (clean.length > 0) {
|
|
37
|
+
lines.push(`✅ Clean dependencies: ${clean.length}`);
|
|
38
|
+
for (const r of clean) {
|
|
39
|
+
lines.push(` ✓ ${r.name} — ${r.detectedLicense}`);
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
}
|
|
43
|
+
lines.push(`Total scanned: ${results.length}`);
|
|
44
|
+
lines.push("");
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
export function formatGateOutput(safe, alerts) {
|
|
48
|
+
const lines = [];
|
|
49
|
+
if (safe) {
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push("╔══════════════════════════════════════════════════╗");
|
|
52
|
+
lines.push("║ ✅ LICENSE CHECK PASSED — All licenses clean ║");
|
|
53
|
+
lines.push("╚══════════════════════════════════════════════════╝");
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push("╔══════════════════════════════════════════════════╗");
|
|
59
|
+
lines.push("║ 🚫 LICENSE CHECK FAILED — Changes detected! ║");
|
|
60
|
+
lines.push("╚══════════════════════════════════════════════════╝");
|
|
61
|
+
lines.push("");
|
|
62
|
+
for (const alert of alerts) {
|
|
63
|
+
lines.push(` ${alert}`);
|
|
64
|
+
}
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push(" Action required: Review license changes before proceeding.");
|
|
67
|
+
lines.push(" Run: wip-license-hook scan --verbose for details.");
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
return lines.join("\n");
|
|
71
|
+
}
|
|
72
|
+
export function formatLedgerReport(ledger) {
|
|
73
|
+
const lines = [
|
|
74
|
+
"",
|
|
75
|
+
"╔══════════════════════════════════════════════════╗",
|
|
76
|
+
"║ wip-license-hook — Ledger Status ║",
|
|
77
|
+
"╚══════════════════════════════════════════════════╝",
|
|
78
|
+
"",
|
|
79
|
+
`Last full scan: ${ledger.last_full_scan ?? "never"}`,
|
|
80
|
+
`Total dependencies: ${ledger.dependencies.length}`,
|
|
81
|
+
`Active alerts: ${ledger.alerts.length}`,
|
|
82
|
+
"",
|
|
83
|
+
];
|
|
84
|
+
const statusIcon = {
|
|
85
|
+
clean: "✅",
|
|
86
|
+
changed: "🚫",
|
|
87
|
+
removed: "❓",
|
|
88
|
+
unknown: "❔",
|
|
89
|
+
};
|
|
90
|
+
for (const dep of ledger.dependencies) {
|
|
91
|
+
const icon = statusIcon[dep.status] ?? "❔";
|
|
92
|
+
lines.push(` ${icon} ${dep.name} (${dep.type})`);
|
|
93
|
+
lines.push(` Adopted: ${dep.license_at_adoption} on ${dep.adopted_date}`);
|
|
94
|
+
lines.push(` Current: ${dep.license_current} (checked ${dep.last_checked})`);
|
|
95
|
+
lines.push(` Source: ${dep.source}`);
|
|
96
|
+
}
|
|
97
|
+
if (ledger.alerts.length > 0) {
|
|
98
|
+
lines.push("");
|
|
99
|
+
lines.push("⚠️ ALERTS:");
|
|
100
|
+
lines.push("─".repeat(50));
|
|
101
|
+
for (const a of ledger.alerts) {
|
|
102
|
+
lines.push(` ${a.message}`);
|
|
103
|
+
lines.push(` Detected: ${a.detected}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
lines.push("");
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
// ─── Dashboard HTML generation ───
|
|
110
|
+
export function generateDashboardHtml(ledger) {
|
|
111
|
+
const rows = ledger.dependencies
|
|
112
|
+
.map((d) => {
|
|
113
|
+
const statusClass = d.status === "clean" ? "clean" : d.status === "changed" ? "changed" : "unknown";
|
|
114
|
+
const statusEmoji = d.status === "clean" ? "✅" : d.status === "changed" ? "🚫" : "❔";
|
|
115
|
+
return `<tr class="${statusClass}">
|
|
116
|
+
<td>${statusEmoji} ${escHtml(d.name)}</td>
|
|
117
|
+
<td>${escHtml(d.type)}</td>
|
|
118
|
+
<td>${escHtml(d.license_at_adoption)}</td>
|
|
119
|
+
<td>${escHtml(d.license_current)}</td>
|
|
120
|
+
<td>${escHtml(d.adopted_date)}</td>
|
|
121
|
+
<td>${escHtml(d.last_checked)}</td>
|
|
122
|
+
<td>${escHtml(d.status)}</td>
|
|
123
|
+
</tr>`;
|
|
124
|
+
})
|
|
125
|
+
.join("\n");
|
|
126
|
+
const alertRows = ledger.alerts
|
|
127
|
+
.map((a) => `<tr>
|
|
128
|
+
<td>⚠️ ${escHtml(a.dependency)}</td>
|
|
129
|
+
<td>${escHtml(a.from)} → ${escHtml(a.to)}</td>
|
|
130
|
+
<td>${escHtml(a.detected)}</td>
|
|
131
|
+
<td>${escHtml(a.message)}</td>
|
|
132
|
+
</tr>`)
|
|
133
|
+
.join("\n");
|
|
134
|
+
return `<!DOCTYPE html>
|
|
135
|
+
<html lang="en">
|
|
136
|
+
<head>
|
|
137
|
+
<meta charset="UTF-8">
|
|
138
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
139
|
+
<title>License Compliance Dashboard — wip-license-hook</title>
|
|
140
|
+
<style>
|
|
141
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
142
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0d1117; color: #c9d1d9; padding: 2rem; }
|
|
143
|
+
h1 { color: #58a6ff; margin-bottom: 0.5rem; }
|
|
144
|
+
.subtitle { color: #8b949e; margin-bottom: 2rem; }
|
|
145
|
+
.stats { display: flex; gap: 2rem; margin-bottom: 2rem; }
|
|
146
|
+
.stat { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1rem 1.5rem; }
|
|
147
|
+
.stat-value { font-size: 2rem; font-weight: bold; }
|
|
148
|
+
.stat-label { color: #8b949e; font-size: 0.875rem; }
|
|
149
|
+
.stat-clean .stat-value { color: #3fb950; }
|
|
150
|
+
.stat-changed .stat-value { color: #f85149; }
|
|
151
|
+
.stat-total .stat-value { color: #58a6ff; }
|
|
152
|
+
table { width: 100%; border-collapse: collapse; background: #161b22; border: 1px solid #30363d; border-radius: 8px; overflow: hidden; margin-bottom: 2rem; }
|
|
153
|
+
th { background: #21262d; text-align: left; padding: 0.75rem 1rem; color: #8b949e; font-weight: 600; border-bottom: 1px solid #30363d; }
|
|
154
|
+
td { padding: 0.75rem 1rem; border-bottom: 1px solid #30363d; }
|
|
155
|
+
tr.changed td { background: #f8514922; }
|
|
156
|
+
tr.clean td { }
|
|
157
|
+
.footer { color: #8b949e; font-size: 0.8rem; margin-top: 2rem; }
|
|
158
|
+
h2 { color: #58a6ff; margin: 1.5rem 0 1rem; }
|
|
159
|
+
</style>
|
|
160
|
+
</head>
|
|
161
|
+
<body>
|
|
162
|
+
<h1>🔒 License Compliance Dashboard</h1>
|
|
163
|
+
<p class="subtitle">Generated by wip-license-hook — ${new Date().toISOString()}</p>
|
|
164
|
+
|
|
165
|
+
<div class="stats">
|
|
166
|
+
<div class="stat stat-total">
|
|
167
|
+
<div class="stat-value">${ledger.dependencies.length}</div>
|
|
168
|
+
<div class="stat-label">Total Dependencies</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="stat stat-clean">
|
|
171
|
+
<div class="stat-value">${ledger.dependencies.filter((d) => d.status === "clean").length}</div>
|
|
172
|
+
<div class="stat-label">Clean</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="stat stat-changed">
|
|
175
|
+
<div class="stat-value">${ledger.dependencies.filter((d) => d.status === "changed").length}</div>
|
|
176
|
+
<div class="stat-label">Changed</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<h2>Dependencies</h2>
|
|
181
|
+
<table>
|
|
182
|
+
<thead>
|
|
183
|
+
<tr><th>Name</th><th>Type</th><th>License (Adopted)</th><th>License (Current)</th><th>Adopted</th><th>Last Checked</th><th>Status</th></tr>
|
|
184
|
+
</thead>
|
|
185
|
+
<tbody>${rows}</tbody>
|
|
186
|
+
</table>
|
|
187
|
+
|
|
188
|
+
${ledger.alerts.length > 0
|
|
189
|
+
? `<h2>⚠️ Alerts</h2>
|
|
190
|
+
<table>
|
|
191
|
+
<thead><tr><th>Dependency</th><th>Change</th><th>Detected</th><th>Message</th></tr></thead>
|
|
192
|
+
<tbody>${alertRows}</tbody>
|
|
193
|
+
</table>`
|
|
194
|
+
: ""}
|
|
195
|
+
|
|
196
|
+
<div class="footer">
|
|
197
|
+
<p>Last full scan: ${ledger.last_full_scan ?? "never"}</p>
|
|
198
|
+
<p>Powered by <a href="https://github.com/wipcomputer/wip-license-hook" style="color: #58a6ff;">wip-license-hook</a></p>
|
|
199
|
+
</div>
|
|
200
|
+
</body>
|
|
201
|
+
</html>`;
|
|
202
|
+
}
|
|
203
|
+
function escHtml(s) {
|
|
204
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
205
|
+
}
|
|
206
|
+
// ─── Badge generation (shields.io style) ───
|
|
207
|
+
export function generateBadgeUrl(ledger) {
|
|
208
|
+
const changed = ledger.dependencies.filter((d) => d.status === "changed").length;
|
|
209
|
+
const total = ledger.dependencies.length;
|
|
210
|
+
const color = changed === 0 ? "brightgreen" : "red";
|
|
211
|
+
const label = "license%20compliance";
|
|
212
|
+
const message = changed === 0 ? `${total}%20clean` : `${changed}%20changed`;
|
|
213
|
+
return `https://img.shields.io/badge/${label}-${message}-${color}`;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Write the dashboard HTML to disk.
|
|
217
|
+
*/
|
|
218
|
+
export function writeDashboard(repoRoot, ledger) {
|
|
219
|
+
const l = ledger ?? readLedger(repoRoot);
|
|
220
|
+
const html = generateDashboardHtml(l);
|
|
221
|
+
const dir = join(repoRoot, "dashboard");
|
|
222
|
+
mkdirSync(dir, { recursive: true });
|
|
223
|
+
const outPath = join(dir, "index.html");
|
|
224
|
+
writeFileSync(outPath, html, "utf-8");
|
|
225
|
+
return outPath;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner — detects dependencies and their licenses across package managers.
|
|
3
|
+
* Supports: npm, pip, cargo, go modules. Works offline.
|
|
4
|
+
*/
|
|
5
|
+
import { type LicenseId } from "./detector.js";
|
|
6
|
+
import { type DependencyType } from "./ledger.js";
|
|
7
|
+
export interface ScanResult {
|
|
8
|
+
name: string;
|
|
9
|
+
source: string;
|
|
10
|
+
type: DependencyType;
|
|
11
|
+
detectedLicense: LicenseId;
|
|
12
|
+
licenseText?: string;
|
|
13
|
+
wasChanged: boolean;
|
|
14
|
+
isNew: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface ScanOptions {
|
|
17
|
+
repoRoot: string;
|
|
18
|
+
offline?: boolean;
|
|
19
|
+
verbose?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function findLicenseFile(dir: string): string | null;
|
|
22
|
+
export declare function readLicenseFromDir(dir: string): {
|
|
23
|
+
license: LicenseId;
|
|
24
|
+
text: string;
|
|
25
|
+
} | null;
|
|
26
|
+
export declare function scanAll(opts: ScanOptions): ScanResult[];
|
|
27
|
+
/**
|
|
28
|
+
* Run a full scan and update the ledger. Returns results with change detection.
|
|
29
|
+
*/
|
|
30
|
+
export declare function scanAndUpdate(opts: ScanOptions): ScanResult[];
|
|
31
|
+
/**
|
|
32
|
+
* Gate check — returns true if safe to proceed, false if blocked.
|
|
33
|
+
*/
|
|
34
|
+
export declare function gateCheck(repoRoot: string, offline: boolean): {
|
|
35
|
+
safe: boolean;
|
|
36
|
+
results: ScanResult[];
|
|
37
|
+
alerts: string[];
|
|
38
|
+
};
|
|
39
|
+
export {};
|