@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,198 @@
1
+ #!/bin/bash
2
+
3
+ # The release orchestrator for secret-santa-hat-build-tools.
4
+ #
5
+ # Ported from git_create_release_quiet (gcrq). Given a JS project on the
6
+ # development or hotfix branch with a NEXT_VERSION changelog section, it bumps
7
+ # the version, finalizes and re-seeds the changelog, tags and pushes a release
8
+ # branch, publishes a GitHub release, merges into the default branch, and
9
+ # leaves the working branch ready for the next cycle.
10
+ #
11
+ # Honours DRY_RUN (set by the --dry-run flag): when true, it performs every
12
+ # read-only check and computes the target version but makes no changes.
13
+
14
+ # Clean, intentional abort. Clears the ERR trap first so a controlled failure
15
+ # does not print the "failed unexpectedly" backstop message, then exits.
16
+ function _release_abort() {
17
+ trap - ERR 2>/dev/null || true
18
+
19
+ exit 1
20
+ }
21
+
22
+ function ssh_create_release() {
23
+ local version_bump="$1"
24
+
25
+ local PACKAGE_MANAGER CURRENT_VERSION CURRENT_BRANCH DEFAULT_BRANCH
26
+ PACKAGE_MANAGER=$(get_package_manager_for_project)
27
+ CURRENT_VERSION=$(get_package_json_version)
28
+ CURRENT_BRANCH=$(get_current_branch)
29
+ DEFAULT_BRANCH=$(get_default_branch)
30
+
31
+ # --- Guards -------------------------------------------------------------
32
+ if ! is_git_repo; then
33
+ echo "โŒ Error: not inside a git repository."
34
+
35
+ _release_abort
36
+ fi
37
+
38
+ if [ ! -f "package.json" ]; then
39
+ echo "โŒ Error: no package.json found in $(pwd)."
40
+
41
+ _release_abort
42
+ fi
43
+
44
+ if [ "$CURRENT_BRANCH" != "development" ] && [ "$CURRENT_BRANCH" != "hotfix" ]; then
45
+ echo "โŒ Error: releases can only be created from 'development' or 'hotfix'."
46
+ echo " Current branch: $CURRENT_BRANCH"
47
+
48
+ _release_abort
49
+ fi
50
+
51
+ if ! changelog_check_next_version; then
52
+ echo "โŒ Error: the top CHANGELOG.md entry is not '## [NEXT_VERSION] - [UNRELEASED]'."
53
+ echo " Update CHANGELOG.md before releasing."
54
+
55
+ _release_abort
56
+ fi
57
+
58
+ # --- Determine the target version --------------------------------------
59
+ local NEW_VERSION=""
60
+ local do_bump="true"
61
+
62
+ if [ -n "$version_bump" ]; then
63
+ NEW_VERSION=$(calculate_new_version "$CURRENT_VERSION" "$version_bump")
64
+ else
65
+ echo ""
66
+ printf " ๐Ÿ”ข Version selection required (current: %s)\n" "$CURRENT_VERSION"
67
+
68
+ local options=(
69
+ "patch - Bug fixes (${CURRENT_VERSION} -> $(calculate_new_version "$CURRENT_VERSION" "patch"))"
70
+ "minor - New features (${CURRENT_VERSION} -> $(calculate_new_version "$CURRENT_VERSION" "minor"))"
71
+ "major - Breaking changes (${CURRENT_VERSION} -> $(calculate_new_version "$CURRENT_VERSION" "major"))"
72
+ "hotfix - Critical fixes (${CURRENT_VERSION} -> $(calculate_new_version "$CURRENT_VERSION" "hotfix"))"
73
+ "custom - Enter a custom version"
74
+ "stay - Stay at current version (${CURRENT_VERSION})"
75
+ )
76
+
77
+ local selected
78
+ selected=$(interactive_menu_select "Choose the version for this release:" "${options[@]}")
79
+
80
+ if [ -z "$selected" ]; then
81
+ echo "โŒ Version selection cancelled. Aborting release."
82
+
83
+ _release_abort
84
+ fi
85
+
86
+ local action
87
+ action=$(echo "$selected" | cut -d' ' -f1)
88
+ printf " โœ… Selected: %s\n" "$selected"
89
+
90
+ case "$action" in
91
+ stay)
92
+ do_bump="false"
93
+ NEW_VERSION="$CURRENT_VERSION"
94
+ ;;
95
+ custom)
96
+ read -r -e -p "Enter custom version: " -i "$CURRENT_VERSION" NEW_VERSION
97
+ if [ -z "$NEW_VERSION" ]; then
98
+ echo "โŒ No version supplied. Aborting release."
99
+
100
+ _release_abort
101
+ fi
102
+ ;;
103
+ *)
104
+ NEW_VERSION=$(calculate_new_version "$CURRENT_VERSION" "$action")
105
+ ;;
106
+ esac
107
+ fi
108
+
109
+ # --- Dry run: report the plan and stop ---------------------------------
110
+ if [ "${DRY_RUN:-false}" = "true" ]; then
111
+ echo ""
112
+ echo "๐Ÿงช Dry run - no changes will be made."
113
+ echo " - Package manager: $PACKAGE_MANAGER"
114
+ echo " - Current branch: $CURRENT_BRANCH"
115
+ echo " - Default branch: $DEFAULT_BRANCH"
116
+ echo " - Current version: $CURRENT_VERSION"
117
+
118
+ if [ "$do_bump" = "true" ]; then
119
+ echo " - Target version: $NEW_VERSION"
120
+ else
121
+ echo " - Target version: $CURRENT_VERSION (staying)"
122
+ fi
123
+
124
+ echo ""
125
+ echo " Would then: bump versions, replace [NEXT_VERSION] placeholders, commit,"
126
+ echo " finalize the changelog, push, create release/$NEW_VERSION, tag v$NEW_VERSION,"
127
+ echo " publish a GitHub release, merge into $DEFAULT_BRANCH, and re-seed the changelog."
128
+ echo ""
129
+ echo " โœ… Dry run complete."
130
+
131
+ return 0
132
+ fi
133
+
134
+ # --- Apply the version bump --------------------------------------------
135
+ if [ "$do_bump" = "true" ]; then
136
+ set_json_version "package.json" "$NEW_VERSION"
137
+ echo " - Updated package.json."
138
+
139
+ if [ -f "public/manifest.json" ]; then
140
+ set_json_version "public/manifest.json" "$NEW_VERSION"
141
+ echo " - Updated public/manifest.json."
142
+ fi
143
+
144
+ replace_next_version_placeholders "$NEW_VERSION"
145
+
146
+ git add . >/dev/null 2>&1
147
+ git commit -m "Version $NEW_VERSION bump." >/dev/null 2>&1
148
+ echo " - Committed version bump."
149
+ fi
150
+
151
+ # Refresh version in case it changed.
152
+ CURRENT_VERSION=$(get_package_json_version)
153
+
154
+ # --- Finalize the changelog for this version ---------------------------
155
+ changelog_update_current_version "$CURRENT_VERSION"
156
+ echo " - Finalized changelog for v$CURRENT_VERSION."
157
+
158
+ # --- Push, branch, tag -------------------------------------------------
159
+ git push -q >/dev/null 2>&1
160
+
161
+ git checkout -b "release/$CURRENT_VERSION" >/dev/null 2>&1
162
+ git push -q --set-upstream origin "release/$CURRENT_VERSION" >/dev/null 2>&1
163
+ echo " - Release branch created."
164
+
165
+ git tag -a "v$CURRENT_VERSION" -m "Version $CURRENT_VERSION" >/dev/null 2>&1
166
+ git push -q -u origin "v$CURRENT_VERSION" >/dev/null 2>&1
167
+ echo " - Tagged v$CURRENT_VERSION."
168
+
169
+ # --- GitHub release ----------------------------------------------------
170
+ if ! github_create_release "$CURRENT_VERSION"; then
171
+ echo "โŒ Error: GitHub release creation failed. Aborting." >&2
172
+
173
+ _release_abort
174
+ fi
175
+
176
+ if ! github_release_add_compare_link "$CURRENT_VERSION"; then
177
+ echo "โš ๏ธ Warning: failed to add compare link to the release." >&2
178
+ fi
179
+
180
+ echo " - GitHub release created."
181
+
182
+ # --- Merge into the default branch -------------------------------------
183
+ git checkout "$DEFAULT_BRANCH" >/dev/null 2>&1
184
+ git merge "release/$CURRENT_VERSION" --no-ff -m "Merge release/$CURRENT_VERSION into $DEFAULT_BRANCH" >/dev/null 2>&1
185
+ git push -q origin "$DEFAULT_BRANCH" >/dev/null 2>&1
186
+ echo " - Merged release/$CURRENT_VERSION into $DEFAULT_BRANCH."
187
+
188
+ # --- Re-seed for the next cycle ----------------------------------------
189
+ # These changes are left uncommitted and carried back to the working branch
190
+ # by the checkout below, matching the gcrq end state.
191
+ changelog_add_next_version_template --quiet
192
+ update_service_worker_next_version_template
193
+
194
+ git checkout "$CURRENT_BRANCH" >/dev/null 2>&1
195
+
196
+ echo ""
197
+ echo " โœ… Release v$CURRENT_VERSION created."
198
+ }
@@ -0,0 +1,86 @@
1
+ #!/bin/bash
2
+
3
+ # Tool availability checker for secret-santa-hat-build-tools.
4
+ #
5
+ # The JS repos only need git and an authenticated GitHub CLI. JSON edits are
6
+ # done with node, which is always present in a Node project, so jq is not
7
+ # required.
8
+
9
+ if ! command -v get_platform >/dev/null 2>&1; then
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ # shellcheck disable=SC1091
12
+ source "$SCRIPT_DIR/platform-utils.sh"
13
+ fi
14
+
15
+ # Check a single tool and print its version.
16
+ function check_tool() {
17
+ local tool="$1"
18
+ local description="$2"
19
+
20
+ if command_exists "$tool"; then
21
+ local version
22
+ version=$("$tool" --version 2>/dev/null | head -1 || echo "available")
23
+ echo "โœ… $tool: $version"
24
+
25
+ return 0
26
+ fi
27
+
28
+ echo "โŒ $tool: not found - $description"
29
+
30
+ return 1
31
+ }
32
+
33
+ # Check that the GitHub CLI is authenticated.
34
+ function check_gh_auth() {
35
+ if ! command_exists "gh"; then
36
+ echo "โŒ gh: not installed - GitHub CLI (required for releases)"
37
+
38
+ return 1
39
+ fi
40
+
41
+ if gh auth status >/dev/null 2>&1; then
42
+ echo "โœ… gh: authenticated"
43
+
44
+ return 0
45
+ fi
46
+
47
+ echo "โš ๏ธ gh: not authenticated (run 'gh auth login')"
48
+
49
+ return 1
50
+ }
51
+
52
+ # Check all required tools. Pass "true" to suppress the per-tool output.
53
+ function check_all_tools() {
54
+ local quiet_mode="${1:-false}"
55
+ local failed=0
56
+
57
+ if ! check_tool "git" "Version control (required)"; then
58
+ failed=$((failed + 1))
59
+ fi
60
+
61
+ if ! check_tool "node" "Node.js runtime (required for JSON edits)"; then
62
+ failed=$((failed + 1))
63
+ fi
64
+
65
+ if ! check_gh_auth; then
66
+ failed=$((failed + 1))
67
+ fi
68
+
69
+ if [ "$quiet_mode" != "true" ]; then
70
+ echo ""
71
+ echo "๐Ÿ“‹ Platform: $(get_platform)"
72
+ fi
73
+
74
+ if [ "$failed" -gt 0 ]; then
75
+ return 1
76
+ fi
77
+
78
+ return 0
79
+ }
80
+
81
+ # Silent pass/fail check for use as a gate.
82
+ function quick_tool_check() {
83
+ check_all_tools "true" >/dev/null 2>&1
84
+
85
+ return $?
86
+ }
package/bin/release.sh ADDED
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+
3
+ # Secret Santa Hat release script.
4
+ #
5
+ # A JavaScript-tailored port of the `git_create_release_quiet` (gcrq) bash
6
+ # function. Handles version bumping, changelog management, git tagging, GitHub
7
+ # releases, and the post-release merge/reseed cycle for the Secret Santa Hat
8
+ # JS repositories.
9
+
10
+ set -e
11
+
12
+ trap 'printf "\nโŒ Release failed unexpectedly on line %s (exit code: %s).\n" "$LINENO" "$?" >&2' ERR
13
+
14
+ export LANG="${LANG:-en_US.UTF-8}"
15
+ export LC_ALL="${LC_ALL:-en_US.UTF-8}"
16
+ export TERM="${TERM:-xterm-256color}"
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+ USER_PROJECT_DIR="$(pwd)"
20
+
21
+ function show_help() {
22
+ cat << 'EOF'
23
+ NAME
24
+ ssh-release - Secret Santa Hat release tool
25
+
26
+ SYNOPSIS
27
+ ssh-release [OPTIONS] [VERSION_TYPE]
28
+
29
+ DESCRIPTION
30
+ Creates a release for a Secret Santa Hat JavaScript project: bumps the
31
+ version, finalizes the changelog, tags the release, publishes a GitHub
32
+ release, merges into the default branch, and re-seeds the next changelog
33
+ section. Ported from the gcrq (git_create_release_quiet) bash flow.
34
+
35
+ Releases can only be created from the 'development' or 'hotfix' branch, and
36
+ CHANGELOG.md must have a "## [NEXT_VERSION] - [UNRELEASED]" section at the
37
+ top.
38
+
39
+ OPTIONS
40
+ --check-tools Check that git and gh (authenticated) are available
41
+ --dry-run Walk through the release without pushing, tagging, or
42
+ publishing anything (still shows the computed version)
43
+ --help, -h Show this help message
44
+ --version, -v Show version information
45
+
46
+ VERSION_TYPE
47
+ patch Bug fixes (1.0.0 -> 1.0.1)
48
+ minor New features (1.0.0 -> 1.1.0)
49
+ major Breaking changes (1.0.0 -> 2.0.0)
50
+ hotfix Critical fixes (1.0.0 -> 1.0.0.1)
51
+
52
+ If no VERSION_TYPE is given, interactive mode prompts for the bump.
53
+
54
+ EXAMPLES
55
+ ssh-release # Interactive mode
56
+ ssh-release patch # Patch release
57
+ ssh-release --dry-run minor # Preview a minor release
58
+ ssh-release --check-tools # Verify required tools
59
+
60
+ REQUIREMENTS
61
+ - git Version control
62
+ - gh GitHub CLI, authenticated (gh auth login)
63
+ - node Used for package.json / manifest.json edits (always present in a
64
+ Node project)
65
+
66
+ FILES OPERATED ON
67
+ package.json Version source of truth
68
+ CHANGELOG.md Release notes, NEXT_VERSION section
69
+ public/manifest.json Version, if present
70
+ public/sw.js APP_VERSION reset to [NEXT_VERSION], if present
71
+ * Any file containing the [NEXT_VERSION] placeholder
72
+ EOF
73
+ }
74
+
75
+ # Parse arguments.
76
+ DRY_RUN=false
77
+ CHECK_TOOLS=false
78
+ VERSION_TYPE=""
79
+
80
+ while [[ $# -gt 0 ]]; do
81
+ case $1 in
82
+ --help|-h)
83
+ show_help
84
+ exit 0
85
+ ;;
86
+ --version|-v)
87
+ echo "ssh-release (secret-santa-hat-build-tools)"
88
+ exit 0
89
+ ;;
90
+ --check-tools)
91
+ CHECK_TOOLS=true
92
+ shift
93
+ ;;
94
+ --dry-run)
95
+ DRY_RUN=true
96
+ shift
97
+ ;;
98
+ --quiet)
99
+ # Accepted for gcrq parity; the flow is quiet by default.
100
+ shift
101
+ ;;
102
+ patch|minor|major|hotfix)
103
+ if [ -n "$VERSION_TYPE" ]; then
104
+ echo "โŒ Error: multiple version types specified."
105
+ exit 1
106
+ fi
107
+ VERSION_TYPE="$1"
108
+ shift
109
+ ;;
110
+ -*)
111
+ echo "โŒ Error: unknown option '$1'. See 'ssh-release --help'."
112
+ exit 1
113
+ ;;
114
+ *)
115
+ echo "โŒ Error: unexpected argument '$1'. See 'ssh-release --help'."
116
+ exit 1
117
+ ;;
118
+ esac
119
+ done
120
+
121
+ # Source configuration if present.
122
+ if [ -f "$SCRIPT_DIR/config/release.conf" ]; then
123
+ # shellcheck disable=SC1091
124
+ source "$SCRIPT_DIR/config/release.conf"
125
+ fi
126
+
127
+ # Source helper libraries.
128
+ # shellcheck disable=SC1091
129
+ source "$SCRIPT_DIR/lib/platform-utils.sh"
130
+ # shellcheck disable=SC1091
131
+ source "$SCRIPT_DIR/lib/tool-checker.sh"
132
+ # shellcheck disable=SC1091
133
+ source "$SCRIPT_DIR/lib/general-functions.sh"
134
+ # shellcheck disable=SC1091
135
+ source "$SCRIPT_DIR/lib/git-functions.sh"
136
+ # shellcheck disable=SC1091
137
+ source "$SCRIPT_DIR/lib/release-functions.sh"
138
+
139
+ # Operate in the user's project directory.
140
+ cd "$USER_PROJECT_DIR"
141
+
142
+ if [ "$CHECK_TOOLS" = true ]; then
143
+ echo "๐Ÿ”ง Checking required tools..."
144
+ echo ""
145
+
146
+ if check_all_tools; then
147
+ echo ""
148
+ echo "โœ… All required tools are available. Ready to run ssh-release."
149
+ exit 0
150
+ else
151
+ echo ""
152
+ echo "โŒ Some required tools are missing or not configured."
153
+ echo " โ€ข git: https://git-scm.com/downloads"
154
+ echo " โ€ข gh: https://cli.github.com/ (then run: gh auth login)"
155
+ exit 1
156
+ fi
157
+ fi
158
+
159
+ # Normal release workflow: verify tools first.
160
+ if ! quick_tool_check; then
161
+ echo "๐Ÿ”ง Checking required tools..."
162
+ echo ""
163
+ check_all_tools || true
164
+ echo ""
165
+ echo "โŒ Missing required tools. Install them before running ssh-release."
166
+ echo " Run 'ssh-release --check-tools' for details."
167
+ exit 1
168
+ fi
169
+
170
+ if [ -n "$VERSION_TYPE" ]; then
171
+ ssh_create_release "$VERSION_TYPE"
172
+ else
173
+ ssh_create_release
174
+ fi
package/bin/setup.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Entry point for `npx @dream-encode/secret-santa-hat-build-tools setup`.
5
+ * Delegates to the interactive project setup.
6
+ */
7
+
8
+ const path = require( 'path' )
9
+
10
+ const args = process.argv.slice( 2 )
11
+
12
+ if ( args[ 0 ] === 'setup' ) {
13
+ process.argv = [ process.argv[ 0 ], process.argv[ 1 ], ...args.slice( 1 ) ]
14
+ }
15
+
16
+ require( path.join( __dirname, '..', 'scripts', 'setup-project.js' ) )
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Node wrapper for ssh-release.
5
+ *
6
+ * Spawns the bundled bash release script so the tool works identically whether
7
+ * it is invoked through `yarn release`, `npm run release`, or `npx ssh-release`.
8
+ * Forcing UTF-8 locale here fixes the emoji display issues yarn causes on Windows.
9
+ */
10
+
11
+ const { spawn } = require( 'child_process' )
12
+ const path = require( 'path' )
13
+
14
+ const scriptDir = __dirname
15
+ const releaseScript = path.join( scriptDir, 'release.sh' )
16
+
17
+ const isYarn = process.env.npm_config_user_agent && process.env.npm_config_user_agent.includes( 'yarn' )
18
+ const isWindows = process.platform === 'win32'
19
+
20
+ if ( isYarn && isWindows ) {
21
+ console.log( 'โš ๏ธ Note: running via yarn on Windows may garble emoji output. If it looks wrong, use: npm run release' )
22
+ console.log( '' )
23
+ }
24
+
25
+ const child = spawn( 'bash', [ releaseScript, ...process.argv.slice( 2 ) ], {
26
+ stdio: 'inherit',
27
+ cwd: process.cwd(),
28
+ env: {
29
+ ...process.env,
30
+ LANG: process.env.LANG || 'en_US.UTF-8',
31
+ LC_ALL: process.env.LC_ALL || 'en_US.UTF-8',
32
+ PYTHONIOENCODING: 'utf-8',
33
+ TERM: process.env.TERM || 'xterm-256color',
34
+ SSH_RELEASE_VIA_YARN: isYarn ? '1' : '0',
35
+ },
36
+ } )
37
+
38
+ child.on( 'error', ( err ) => {
39
+ console.error( 'โŒ Failed to launch the release script.' )
40
+
41
+ if ( err.code === 'ENOENT' ) {
42
+ console.error( ' `bash` was not found on your PATH. Install Git Bash (Windows) or bash (macOS/Linux).' )
43
+ } else {
44
+ console.error( ` ${ err.message }` )
45
+ }
46
+
47
+ process.exit( 1 )
48
+ } )
49
+
50
+ child.on( 'exit', ( code ) => {
51
+ process.exit( code === null ? 1 : code )
52
+ } )
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@dream-encode/secret-santa-hat-build-tools",
3
+ "version": "0.1.0",
4
+ "description": "Release build tools for Secret Santa Hat JavaScript repositories. Provides the `ssh-release` command, a JS-tailored port of the gcrq (git_create_release_quiet) release flow.",
5
+ "license": "MIT",
6
+ "author": "David Baumwald <david@dream-encode.com>",
7
+ "type": "commonjs",
8
+ "engines": {
9
+ "node": ">=18"
10
+ },
11
+ "bin": {
12
+ "ssh-release": "bin/ssh-release.js",
13
+ "secret-santa-hat-build-tools": "bin/setup.js"
14
+ },
15
+ "files": [
16
+ "bin/",
17
+ "scripts/",
18
+ "README.md",
19
+ "LICENSE",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "scripts": {
23
+ "postinstall": "node scripts/postinstall.js"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/dream-encode/secret-santa-hat-build-tools.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/dream-encode/secret-santa-hat-build-tools/issues"
31
+ },
32
+ "homepage": "https://github.com/dream-encode/secret-santa-hat-build-tools#readme",
33
+ "keywords": [
34
+ "secret-santa-hat",
35
+ "release",
36
+ "build",
37
+ "changelog",
38
+ "versioning",
39
+ "git",
40
+ "github"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+
3
+ /*
4
+ * Postinstall for @dream-encode/secret-santa-hat-build-tools.
5
+ *
6
+ * After the package is installed into a project, notify that a "release"
7
+ * script can be wired in. It never edits package.json automatically here;
8
+ * wiring happens through the interactive `setup` command so installs stay
9
+ * side-effect free (and CI-safe).
10
+ */
11
+
12
+ const fs = require( 'fs' )
13
+ const path = require( 'path' )
14
+ const { analyzeCurrentSetup, findProjectRoot } = require( './setup-project' )
15
+
16
+ const PACKAGE_NAME = '@dream-encode/secret-santa-hat-build-tools'
17
+
18
+ const colors = {
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[34m',
22
+ reset: '\x1b[0m',
23
+ bold: '\x1b[1m',
24
+ dim: '\x1b[2m',
25
+ }
26
+
27
+ function log( message, color = 'reset' ) {
28
+ console.log( `${ colors[ color ] }${ message }${ colors.reset }` )
29
+ }
30
+
31
+ function isInteractive() {
32
+ return process.stdout.isTTY && process.stdin.isTTY && ! process.env.CI
33
+ }
34
+
35
+ function isSelfInstall() {
36
+ try {
37
+ const pkg = JSON.parse( fs.readFileSync( path.join( process.cwd(), 'package.json' ), 'utf8' ) )
38
+
39
+ return pkg.name === PACKAGE_NAME
40
+ } catch {
41
+ return false
42
+ }
43
+ }
44
+
45
+ function shouldSkip() {
46
+ return !! ( process.env.CI || process.env.NO_SETUP || isSelfInstall() )
47
+ }
48
+
49
+ function main() {
50
+ if ( shouldSkip() ) {
51
+ return
52
+ }
53
+
54
+ try {
55
+ const projectRoot = findProjectRoot()
56
+
57
+ if ( ! projectRoot ) {
58
+ return
59
+ }
60
+
61
+ const pkg = JSON.parse( fs.readFileSync( path.join( projectRoot, 'package.json' ), 'utf8' ) )
62
+ const analysis = analyzeCurrentSetup( pkg )
63
+
64
+ if ( ! analysis.needsSetup ) {
65
+ return
66
+ }
67
+
68
+ log( '\n' + '='.repeat( 60 ), 'blue' )
69
+ log( '๐ŸŽ… secret-santa-hat-build-tools installed', 'bold' )
70
+ log( '='.repeat( 60 ), 'blue' )
71
+
72
+ if ( analysis.hasReleaseScript ) {
73
+ log( `\nโš ๏ธ Existing release script detected: "${ analysis.currentReleaseScript }"`, 'yellow' )
74
+ log( ' (setup will back it up as "release-backup")', 'dim' )
75
+ }
76
+
77
+ log( '\n๐Ÿ’ก Wire in the release script with:', 'bold' )
78
+ log( ` npx ${ PACKAGE_NAME } setup # interactive`, 'blue' )
79
+ log( ` npx ${ PACKAGE_NAME } setup --force # no prompts`, 'blue' )
80
+ log( '\n Then: yarn release (runs your prerelease script, then the release flow)', 'dim' )
81
+ log( '='.repeat( 60 ) + '\n', 'blue' )
82
+
83
+ if ( ! isInteractive() ) {
84
+ log( `Run: npx ${ PACKAGE_NAME } setup`, 'yellow' )
85
+ }
86
+ } catch ( error ) {
87
+ if ( process.env.DEBUG ) {
88
+ console.error( 'secret-santa-hat-build-tools postinstall error:', error )
89
+ }
90
+ }
91
+ }
92
+
93
+ main()