11# ============================================================================
22
3+ # Generate .shipnode/ directory with smart hook templates
4+ generate_shipnode_hooks () {
5+ # Create .shipnode directory
6+ mkdir -p .shipnode
7+
8+ # Detect ORM from package.json
9+ local orm_info=" "
10+ local orm_name=" "
11+ local migrate_cmd=" "
12+ local generate_cmd=" "
13+
14+ if [ -f " package.json" ]; then
15+ orm_info=$( detect_orm)
16+ IFS=' |' read -r orm_name migrate_cmd generate_cmd <<< " $orm_info"
17+ else
18+ orm_name=" none"
19+ fi
20+
21+ # Generate pre-deploy.sh
22+ cat > .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
23+ #!/bin/bash
24+ # ShipNode Pre-Deploy Hook
25+ # Runs BEFORE the app is activated (before PM2 reload)
26+ # Exit non-zero to abort deployment
27+ #
28+ # Available environment variables:
29+ # RELEASE_PATH - Path to the new release being deployed
30+ # REMOTE_PATH - Base deployment directory
31+ # PM2_APP_NAME - PM2 process name (backend only)
32+ # BACKEND_PORT - Application port (backend only)
33+ # SHARED_ENV_PATH - Path to the server's shared .env file
34+
35+ set -e # Exit on error
36+
37+ # Source the server's shared .env to use same variables as the app
38+ if [ -f "$SHARED_ENV_PATH" ]; then
39+ set -a
40+ source "$SHARED_ENV_PATH"
41+ set +a
42+ fi
43+
44+ cd "$RELEASE_PATH"
45+
46+ echo "Running pre-deploy hook for release: $RELEASE_PATH"
47+
48+ # ── ORM Database Migrations ──────────────────────────────────────
49+
50+ PREDEPLOY_EOF
51+
52+ # Add ORM-specific commands (uncommented if detected)
53+ if [ " $orm_name " = " Prisma" ]; then
54+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
55+ # Prisma (detected) - using package manager auto-detection
56+ if [ -f "pnpm-lock.yaml" ]; then
57+ pnpm prisma generate
58+ pnpm prisma migrate deploy
59+ elif [ -f "yarn.lock" ]; then
60+ yarn prisma generate
61+ yarn prisma migrate deploy
62+ else
63+ npx prisma generate
64+ npx prisma migrate deploy
65+ fi
66+
67+ PREDEPLOY_EOF
68+ else
69+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
70+ # Prisma (generate client + run migrations)
71+ # if [ -f "pnpm-lock.yaml" ]; then
72+ # pnpm prisma generate && pnpm prisma migrate deploy
73+ # elif [ -f "yarn.lock" ]; then
74+ # yarn prisma generate && yarn prisma migrate deploy
75+ # else
76+ # npx prisma generate && npx prisma migrate deploy
77+ # fi
78+
79+ PREDEPLOY_EOF
80+ fi
81+
82+ if [ " $orm_name " = " Drizzle" ]; then
83+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
84+ # Drizzle (detected)
85+ npx drizzle-kit migrate
86+
87+ PREDEPLOY_EOF
88+ else
89+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
90+ # Drizzle (run migrations)
91+ # npx drizzle-kit migrate
92+
93+ PREDEPLOY_EOF
94+ fi
95+
96+ if [ " $orm_name " = " TypeORM" ]; then
97+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
98+ # TypeORM (detected)
99+ npx typeorm migration:run
100+
101+ PREDEPLOY_EOF
102+ else
103+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
104+ # TypeORM (run migrations)
105+ # npx typeorm migration:run
106+
107+ PREDEPLOY_EOF
108+ fi
109+
110+ if [ " $orm_name " = " Sequelize" ]; then
111+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
112+ # Sequelize (detected)
113+ npx sequelize-cli db:migrate
114+
115+ PREDEPLOY_EOF
116+ else
117+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
118+ # Sequelize (run migrations)
119+ # npx sequelize-cli db:migrate
120+
121+ PREDEPLOY_EOF
122+ fi
123+
124+ if [ " $orm_name " = " Knex" ]; then
125+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
126+ # Knex (detected)
127+ npx knex migrate:latest
128+
129+ PREDEPLOY_EOF
130+ else
131+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
132+ # Knex (run migrations)
133+ # npx knex migrate:latest
134+
135+ PREDEPLOY_EOF
136+ fi
137+
138+ if [ " $orm_name " = " AdonisJS Lucid" ]; then
139+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
140+ # AdonisJS Lucid (detected)
141+ node ace migration:run --force
142+
143+ PREDEPLOY_EOF
144+ else
145+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
146+ # AdonisJS Lucid (run migrations)
147+ # node ace migration:run --force
148+
149+ PREDEPLOY_EOF
150+ fi
151+
152+ # Close pre-deploy.sh
153+ cat >> .shipnode/pre-deploy.sh << 'PREDEPLOY_EOF '
154+ # ─────────────────────────────────────────────────────────────────
155+
156+ echo "Pre-deploy hook completed successfully"
157+ exit 0
158+ PREDEPLOY_EOF
159+
160+ # Generate post-deploy.sh
161+ cat > .shipnode/post-deploy.sh << 'POSTDEPLOY_EOF '
162+ #!/bin/bash
163+ # ShipNode Post-Deploy Hook
164+ # Runs AFTER deployment completes. Failure won't rollback.
165+ #
166+ # Available environment variables:
167+ # RELEASE_PATH - Path to the current release (symlinked as 'current')
168+ # REMOTE_PATH - Base deployment directory
169+ # PM2_APP_NAME - PM2 process name (backend only)
170+ # BACKEND_PORT - Application port (backend only)
171+ # SHARED_ENV_PATH - Path to the server's shared .env file
172+
173+ set -e # Exit on error (but failure won't rollback deployment)
174+
175+ # Source the server's shared .env to use same variables as the app
176+ if [ -f "$SHARED_ENV_PATH" ]; then
177+ set -a
178+ source "$SHARED_ENV_PATH"
179+ set +a
180+ fi
181+
182+ cd "$RELEASE_PATH"
183+
184+ echo "Running post-deploy hook for release: $RELEASE_PATH"
185+
186+ # ── Examples ─────────────────────────────────────────────────────
187+
188+ # Seed database: npx prisma db seed
189+ # Clear cache: npm run cache:clear
190+ # Notify Slack: curl -X POST https://hooks.slack.com/... -d '{"text":"Deployed!"}'
191+ # Cleanup old logs: find /var/log/myapp -name "*.log" -mtime +7 -delete
192+ # Warm cache: curl -sf http://localhost:${BACKEND_PORT}/api/warmup
193+
194+ # ─────────────────────────────────────────────────────────────────
195+
196+ echo "Post-deploy hook completed successfully"
197+ exit 0
198+ POSTDEPLOY_EOF
199+
200+ # Make hooks executable
201+ chmod +x .shipnode/pre-deploy.sh .shipnode/post-deploy.sh
202+
203+ if [ " $orm_name " != " none" ]; then
204+ success " Generated .shipnode/ hooks with $orm_name commands"
205+ else
206+ success " Generated .shipnode/ hooks (no ORM detected)"
207+ fi
208+ }
209+
3210# Legacy non-interactive init (backward compatibility)
4211cmd_init_legacy () {
5212 if [ -f " shipnode.conf" ]; then
@@ -33,6 +240,9 @@ BACKEND_PORT=3000
33240# Frontend-specific (optional)
34241DOMAIN=myapp.com
35242
243+ # Environment file (optional)
244+ # ENV_FILE=.env
245+
36246# Zero-downtime deployment (optional)
37247ZERO_DOWNTIME=true
38248KEEP_RELEASES=5
@@ -369,6 +579,13 @@ DOMAIN=$domain
369579EOF
370580 fi
371581
582+ # Environment file (optional)
583+ cat >> shipnode.conf << EOF
584+
585+ # Environment file
586+ # ENV_FILE=.env
587+ EOF
588+
372589 # Zero-downtime settings
373590 cat >> shipnode.conf << EOF
374591
391608
392609 success " Created shipnode.conf"
393610
611+ # Generate .shipnode/ hooks
612+ echo " "
613+ generate_shipnode_hooks
614+
394615 # Users.yml wizard
395616 echo " "
396617 if prompt_yes_no " Add deployment users now?" ; then
@@ -818,6 +1039,13 @@ DOMAIN=$domain
8181039EOF
8191040 fi
8201041
1042+ # Environment file (optional)
1043+ cat >> shipnode.conf << EOF
1044+
1045+ # Environment file
1046+ # ENV_FILE=.env
1047+ EOF
1048+
8211049 # Zero-downtime settings
8221050 cat >> shipnode.conf << EOF
8231051
@@ -839,15 +1067,19 @@ EOF
8391067 fi
8401068
8411069 success " Created shipnode.conf"
842-
843- # 9. Users.yml wizard
1070+
1071+ # 9. Generate .shipnode/ hooks
1072+ echo " "
1073+ generate_shipnode_hooks
1074+
1075+ # 10. Users.yml wizard
8441076 echo " "
8451077 if prompt_yes_no " Add deployment users now?" ; then
8461078 init_users_yaml
8471079 else
8481080 info " You can add users later with: shipnode user sync"
8491081 fi
850-
1082+
8511083 echo " "
8521084 success " Initialization complete!"
8531085 info " Next steps:"
0 commit comments