licenseguard-cli 1.0.0 → 1.2.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 CHANGED
@@ -153,7 +153,7 @@ This configuration is used by the git hooks to display license information.
153
153
  LicenseGuard installs two informational git hooks:
154
154
 
155
155
  ### Post-Checkout Hook
156
- Displays a notification after `git clone` or `git checkout`:
156
+ Displays a notification after `git checkout` or branch switches:
157
157
  ```
158
158
  📜 This project uses MIT License by Your Name
159
159
  ```
@@ -169,6 +169,40 @@ Displays a reminder before each commit:
169
169
  - Hooks always exit with code 0 (success)
170
170
  - If `.licenseguardrc` is missing, hooks silently exit
171
171
 
172
+ ### Auto-Setup on Clone (npm projects)
173
+
174
+ Git hooks live in `.git/hooks/` which is **not tracked by git**. This means hooks are not copied when someone clones your repository.
175
+
176
+ For npm projects, you can use the `prepare` script to automatically set up hooks when developers run `npm install`:
177
+
178
+ **Add to your package.json:**
179
+ ```json
180
+ {
181
+ "devDependencies": {
182
+ "licenseguard-cli": "^1.1.0"
183
+ },
184
+ "scripts": {
185
+ "prepare": "licenseguard --setup || true"
186
+ }
187
+ }
188
+ ```
189
+
190
+ **What happens when someone clones your project:**
191
+ ```bash
192
+ git clone <your-repo> # Gets code + .licenseguardrc
193
+ npm install # AUTOMATICALLY:
194
+ # 📜 "This project uses MIT License by Your Name"
195
+ # ✓ Git hooks installed
196
+ git checkout feature # Notification appears (hooks active)
197
+ git commit # Reminder appears (hooks active)
198
+ ```
199
+
200
+ The `--setup` command:
201
+ - Reads `.licenseguardrc` and displays license notification
202
+ - Installs git hooks automatically
203
+ - Always exits 0 (never breaks `npm install`)
204
+ - Safe to run multiple times (idempotent)
205
+
172
206
  ### Existing Hooks
173
207
 
174
208
  If you already have git hooks, LicenseGuard will NOT overwrite them. Instead, it creates variants:
@@ -182,6 +216,7 @@ You can manually merge these with your existing hooks if desired.
182
216
  If your project is not a git repository:
183
217
  - Interactive mode (`--init`) will offer to run `git init`
184
218
  - Fast mode (`--init-fast`) will skip hooks and warn you
219
+ - `--setup` command will skip hooks with a warning
185
220
  - LICENSE file is always created regardless of git status
186
221
 
187
222
  ## FAQ
@@ -4,9 +4,10 @@ const { program } = require('commander')
4
4
  const { runList } = require('../lib/commands/list')
5
5
  const { runInit } = require('../lib/commands/init')
6
6
  const { runInitFast } = require('../lib/commands/init-fast')
7
+ const { setupCommand } = require('../lib/commands/setup')
7
8
 
8
9
  program
9
- .version('1.0.0')
10
+ .version('1.1.0')
10
11
  .description('License setup & compliance helper for developers')
11
12
 
12
13
  program
@@ -17,6 +18,10 @@ program
17
18
  .option('--year <year>', 'Copyright year (for --init-fast)')
18
19
  .option('--url <url>', 'Project URL (for --init-fast)')
19
20
  .option('--ls', 'List available license templates')
21
+ .option(
22
+ '--setup',
23
+ 'Setup license notification and install git hooks (for npm prepare script)'
24
+ )
20
25
  .parse(process.argv)
21
26
 
22
27
  const options = program.opts()
@@ -36,6 +41,11 @@ if (options.init) {
36
41
  console.error('Error:', err.message)
37
42
  process.exit(1)
38
43
  })
44
+ } else if (options.setup) {
45
+ setupCommand().catch((err) => {
46
+ // Even on error, don't exit 1 - npm prepare compatibility
47
+ console.error('Setup warning:', err.message)
48
+ })
39
49
  } else {
40
50
  program.help()
41
51
  }
@@ -0,0 +1,56 @@
1
+ const fs = require('fs')
2
+ const chalk = require('chalk')
3
+ const { isGitRepo, installHooks } = require('../utils/git-helpers')
4
+
5
+ /**
6
+ * Setup command - Install git hooks and show license notification
7
+ * Used in npm prepare script for auto-setup on clone
8
+ *
9
+ * This command is designed for use in package.json prepare scripts:
10
+ * {
11
+ * "scripts": {
12
+ * "prepare": "licenseguard --setup || true"
13
+ * }
14
+ * }
15
+ *
16
+ * @returns {Promise<void>}
17
+ */
18
+ async function setupCommand() {
19
+ try {
20
+ // Check for .licenseguardrc
21
+ if (!fs.existsSync('.licenseguardrc')) {
22
+ console.log(
23
+ chalk.yellow(
24
+ 'No .licenseguardrc found. Run \'licenseguard --init\' first.'
25
+ )
26
+ )
27
+ return // Exit 0 implicitly
28
+ }
29
+
30
+ // Read config
31
+ const configContent = fs.readFileSync('.licenseguardrc', 'utf-8')
32
+ const config = JSON.parse(configContent)
33
+
34
+ // Display license notification (MAIN PURPOSE)
35
+ // This is what developers see when they run npm install
36
+ console.log(
37
+ chalk.blue(
38
+ `📜 This project uses ${config.license.toUpperCase()} License by ${config.owner}`
39
+ )
40
+ )
41
+
42
+ // Install hooks if git repo
43
+ if (isGitRepo()) {
44
+ installHooks()
45
+ // Note: installHooks() already prints success/warning messages
46
+ } else {
47
+ console.log(chalk.yellow('âš ī¸ Skipping git hooks (not a git repo)'))
48
+ }
49
+ } catch (error) {
50
+ // Always exit 0 - never break npm install
51
+ // This is CRITICAL for npm prepare script compatibility
52
+ console.log(chalk.yellow(`âš ī¸ Setup warning: ${error.message}`))
53
+ }
54
+ }
55
+
56
+ module.exports = { setupCommand }
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const os = require('os')
6
+ const { execSync } = require('child_process')
7
+
8
+ /**
9
+ * Setup global git hooks for automatic license notifications
10
+ * Runs automatically after npm install -g licenseguard-cli
11
+ */
12
+
13
+ const homeDir = os.homedir()
14
+ const templateDir = path.join(homeDir, '.git-templates')
15
+ const hooksDir = path.join(templateDir, 'hooks')
16
+
17
+ // Self-contained hook scripts (only needs Node.js, not licenseguard)
18
+ const postCheckoutHook = `#!/usr/bin/env node
19
+ const fs = require('fs')
20
+ const path = require('path')
21
+
22
+ try {
23
+ const configPath = path.join(process.cwd(), '.licenseguardrc')
24
+ if (!fs.existsSync(configPath)) process.exit(0)
25
+
26
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
27
+ const blue = '\\x1b[34m'
28
+ const reset = '\\x1b[0m'
29
+
30
+ console.log(\`\${blue}📜 This project uses \${config.license.toUpperCase()} License by \${config.owner}\${reset}\`)
31
+ } catch (e) {
32
+ // Silent fail - never block git
33
+ }
34
+ process.exit(0)
35
+ `
36
+
37
+ const preCommitHook = `#!/usr/bin/env node
38
+ const fs = require('fs')
39
+ const path = require('path')
40
+
41
+ try {
42
+ const configPath = path.join(process.cwd(), '.licenseguardrc')
43
+ if (!fs.existsSync(configPath)) process.exit(0)
44
+
45
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
46
+ const blue = '\\x1b[34m'
47
+ const reset = '\\x1b[0m'
48
+
49
+ console.log(\`\${blue}â„šī¸ Reminder: This project is licensed under \${config.license.toUpperCase()}\${reset}\`)
50
+ } catch (e) {
51
+ // Silent fail - never block git
52
+ }
53
+ process.exit(0)
54
+ `
55
+
56
+ function setupGlobalHooks() {
57
+ try {
58
+ // Only setup global hooks when installed globally
59
+ // Check if we're in a global npm directory
60
+ const isGlobalInstall =
61
+ process.env.npm_config_global === 'true' ||
62
+ __dirname.includes('npm-global') ||
63
+ __dirname.includes('/usr/lib/node_modules') ||
64
+ __dirname.includes('/usr/local/lib/node_modules')
65
+
66
+ if (!isGlobalInstall) {
67
+ // Local install, skip global hooks setup
68
+ return
69
+ }
70
+
71
+ // Create template directories
72
+ if (!fs.existsSync(hooksDir)) {
73
+ fs.mkdirSync(hooksDir, { recursive: true })
74
+ }
75
+
76
+ // Write hook files
77
+ const hooks = [
78
+ { name: 'post-checkout', script: postCheckoutHook },
79
+ { name: 'pre-commit', script: preCommitHook },
80
+ ]
81
+
82
+ for (const hook of hooks) {
83
+ const hookPath = path.join(hooksDir, hook.name)
84
+
85
+ // Check for existing hooks
86
+ if (fs.existsSync(hookPath)) {
87
+ const existing = fs.readFileSync(hookPath, 'utf8')
88
+ if (!existing.includes('.licenseguardrc')) {
89
+ // Not our hook, create variant
90
+ const variantPath = path.join(hooksDir, `licenseguard-${hook.name}`)
91
+ fs.writeFileSync(variantPath, hook.script, 'utf8')
92
+ fs.chmodSync(variantPath, 0o755)
93
+ console.log(`âš ī¸ Existing ${hook.name} found, created licenseguard-${hook.name}`)
94
+ }
95
+ // Our hook already exists, skip
96
+ } else {
97
+ fs.writeFileSync(hookPath, hook.script, 'utf8')
98
+ fs.chmodSync(hookPath, 0o755)
99
+ }
100
+ }
101
+
102
+ // Configure git to use template directory
103
+ execSync(`git config --global init.templateDir "${templateDir}"`, {
104
+ stdio: 'pipe',
105
+ })
106
+
107
+ console.log('✓ LicenseGuard global hooks installed')
108
+ console.log(` Template directory: ${templateDir}`)
109
+ console.log(' Now every git clone/init will check for .licenseguardrc')
110
+ } catch (error) {
111
+ // Don't fail installation if hooks setup fails
112
+ console.log('âš ī¸ Could not setup global hooks:', error.message)
113
+ console.log(' LicenseGuard still works, run licenseguard --setup manually')
114
+ }
115
+ }
116
+
117
+ setupGlobalHooks()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "licenseguard-cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "License setup & compliance helper for developers",
5
5
  "bin": {
6
6
  "licenseguard": "./bin/licenseguard.js"
@@ -9,11 +9,7 @@
9
9
  "doc": "docs"
10
10
  },
11
11
  "scripts": {
12
- "test": "jest",
13
- "test:watch": "jest --watch",
14
- "test:coverage": "jest --coverage",
15
- "lint": "eslint .",
16
- "format": "prettier --write ."
12
+ "postinstall": "node lib/postinstall.js"
17
13
  },
18
14
  "keywords": [
19
15
  "license",
@@ -29,11 +25,5 @@
29
25
  "chalk": "^4.1.2",
30
26
  "commander": "^11.1.0",
31
27
  "inquirer": "^8.2.5"
32
- },
33
- "devDependencies": {
34
- "eslint": "^8.54.0",
35
- "jest": "^29.7.0",
36
- "mock-fs": "^5.2.0",
37
- "prettier": "^3.1.0"
38
28
  }
39
29
  }