@dream-encode/secret-santa-hat-build-tools 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.
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Project setup for @dream-encode/secret-santa-hat-build-tools.
5
+ *
6
+ * Wires a "release": "ssh-release" script into the consuming project's
7
+ * package.json. Any existing custom release script is backed up as
8
+ * "release-backup". An existing "prerelease" script is never touched, so the
9
+ * per-repo prerelease step keeps running before release.
10
+ */
11
+
12
+ const fs = require( 'fs' )
13
+ const path = require( 'path' )
14
+ const readline = require( 'readline' )
15
+
16
+ const PACKAGE_NAME = '@dream-encode/secret-santa-hat-build-tools'
17
+ const RELEASE_VALUE = 'ssh-release'
18
+
19
+ const VALID_RELEASE_SCRIPTS = [
20
+ 'ssh-release',
21
+ 'npx ssh-release',
22
+ `npx ${ PACKAGE_NAME }`,
23
+ ]
24
+
25
+ const colors = {
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ blue: '\x1b[34m',
29
+ red: '\x1b[31m',
30
+ reset: '\x1b[0m',
31
+ bold: '\x1b[1m',
32
+ dim: '\x1b[2m',
33
+ }
34
+
35
+ function log( message, color = 'reset' ) {
36
+ console.log( `${ colors[ color ] }${ message }${ colors.reset }` )
37
+ }
38
+
39
+ function isInteractive() {
40
+ return process.stdout.isTTY && process.stdin.isTTY && ! process.env.CI
41
+ }
42
+
43
+ function findProjectRoot() {
44
+ let currentDir = process.cwd()
45
+
46
+ while ( currentDir !== path.dirname( currentDir ) ) {
47
+ if ( fs.existsSync( path.join( currentDir, 'package.json' ) ) ) {
48
+ return currentDir
49
+ }
50
+
51
+ currentDir = path.dirname( currentDir )
52
+ }
53
+
54
+ return null
55
+ }
56
+
57
+ function readPackageJson( projectRoot ) {
58
+ try {
59
+ return JSON.parse( fs.readFileSync( path.join( projectRoot, 'package.json' ), 'utf8' ) )
60
+ } catch ( error ) {
61
+ log( `āŒ Error reading package.json: ${ error.message }`, 'red' )
62
+
63
+ return null
64
+ }
65
+ }
66
+
67
+ function writePackageJson( projectRoot, packageData ) {
68
+ try {
69
+ fs.writeFileSync(
70
+ path.join( projectRoot, 'package.json' ),
71
+ JSON.stringify( packageData, null, 2 ) + '\n',
72
+ 'utf8'
73
+ )
74
+
75
+ return true
76
+ } catch ( error ) {
77
+ log( `āŒ Error writing package.json: ${ error.message }`, 'red' )
78
+
79
+ return false
80
+ }
81
+ }
82
+
83
+ function analyzeCurrentSetup( pkg ) {
84
+ const releaseScript = pkg.scripts && pkg.scripts.release
85
+ const isReleaseConfigured = !! ( releaseScript && VALID_RELEASE_SCRIPTS.includes( releaseScript.trim() ) )
86
+
87
+ return {
88
+ hasReleaseScript: !! releaseScript,
89
+ currentReleaseScript: releaseScript,
90
+ isReleaseConfigured,
91
+ hasPrerelease: !! ( pkg.scripts && pkg.scripts.prerelease ),
92
+ needsSetup: ! isReleaseConfigured,
93
+ }
94
+ }
95
+
96
+ function promptUser( question ) {
97
+ return new Promise( ( resolve ) => {
98
+ const rl = readline.createInterface( { input: process.stdin, output: process.stdout } )
99
+
100
+ rl.question( question, ( answer ) => {
101
+ rl.close()
102
+ resolve( answer.toLowerCase().trim() )
103
+ } )
104
+ } )
105
+ }
106
+
107
+ async function setupReleaseScript( projectRoot, pkg, analysis, force = false ) {
108
+ log( '\nšŸ”§ secret-santa-hat-build-tools setup', 'bold' )
109
+ log( '=====================================', 'blue' )
110
+
111
+ if ( analysis.isReleaseConfigured ) {
112
+ log( 'āœ… Release script already configured: "release": "ssh-release"', 'green' )
113
+
114
+ return true
115
+ }
116
+
117
+ log( '\nšŸ“‹ Proposed changes:', 'bold' )
118
+
119
+ if ( analysis.hasReleaseScript ) {
120
+ log( ` • Back up existing release script: "${ analysis.currentReleaseScript }" -> "release-backup"`, 'yellow' )
121
+ }
122
+
123
+ log( ` • Set "release": "${ RELEASE_VALUE }"`, 'blue' )
124
+
125
+ if ( analysis.hasPrerelease ) {
126
+ log( ' • Leave your existing "prerelease" script untouched (it runs before release)', 'dim' )
127
+ } else {
128
+ log( ' • Tip: add a "prerelease" script (tests/lint/build) - it runs automatically before release', 'dim' )
129
+ }
130
+
131
+ if ( ! force && isInteractive() ) {
132
+ log( '\nā“ Proceed with setup?', 'bold' )
133
+ const answer = await promptUser( ' Type "yes" to continue, anything else to skip: ' )
134
+
135
+ if ( answer !== 'yes' && answer !== 'y' ) {
136
+ log( '\nā­ļø Setup skipped. Run it later with:', 'yellow' )
137
+ log( ` npx ${ PACKAGE_NAME } setup`, 'blue' )
138
+
139
+ return false
140
+ }
141
+ }
142
+
143
+ if ( ! pkg.scripts ) {
144
+ pkg.scripts = {}
145
+ }
146
+
147
+ if ( analysis.hasReleaseScript && ! analysis.isReleaseConfigured ) {
148
+ pkg.scripts[ 'release-backup' ] = analysis.currentReleaseScript
149
+ log( ' āœ… Backed up existing release script to "release-backup"', 'green' )
150
+ }
151
+
152
+ pkg.scripts.release = RELEASE_VALUE
153
+
154
+ if ( writePackageJson( projectRoot, pkg ) ) {
155
+ log( ' āœ… package.json updated.', 'green' )
156
+ log( '\nšŸŽ‰ Setup complete. You can now run:', 'bold' )
157
+ log( ' yarn release # or: npm run release', 'blue' )
158
+ log( ' ssh-release --help # all options', 'blue' )
159
+
160
+ return true
161
+ }
162
+
163
+ return false
164
+ }
165
+
166
+ async function main() {
167
+ const args = process.argv.slice( 2 )
168
+ const force = args.includes( '--force' ) || args.includes( '-f' )
169
+ const quiet = args.includes( '--quiet' ) || args.includes( '-q' )
170
+
171
+ if ( ! quiet ) {
172
+ log( 'šŸš€ secret-santa-hat-build-tools project setup\n', 'bold' )
173
+ }
174
+
175
+ const projectRoot = findProjectRoot()
176
+
177
+ if ( ! projectRoot ) {
178
+ log( 'āŒ No package.json found in this or any parent directory.', 'red' )
179
+ process.exit( 1 )
180
+ }
181
+
182
+ const pkg = readPackageJson( projectRoot )
183
+
184
+ if ( ! pkg ) {
185
+ process.exit( 1 )
186
+ }
187
+
188
+ const analysis = analyzeCurrentSetup( pkg )
189
+
190
+ if ( ! analysis.needsSetup && ! force ) {
191
+ if ( ! quiet ) {
192
+ log( 'āœ… Project already configured correctly.', 'green' )
193
+ }
194
+
195
+ process.exit( 0 )
196
+ }
197
+
198
+ const success = await setupReleaseScript( projectRoot, pkg, analysis, force )
199
+ process.exit( success ? 0 : 1 )
200
+ }
201
+
202
+ process.on( 'uncaughtException', ( error ) => {
203
+ log( `āŒ Unexpected error: ${ error.message }`, 'red' )
204
+ process.exit( 1 )
205
+ } )
206
+
207
+ if ( require.main === module ) {
208
+ main().catch( ( error ) => {
209
+ log( `āŒ Setup failed: ${ error.message }`, 'red' )
210
+ process.exit( 1 )
211
+ } )
212
+ }
213
+
214
+ module.exports = { setupReleaseScript, analyzeCurrentSetup, findProjectRoot }