mynodes / deploy.sh
Newer Older
158 lines | 5.679kb
Bogdan Timofte authored 2 weeks ago
1
#!/usr/bin/env bash
2
set -euo pipefail
3

            
4
# Deploy a Node-RED node folder to a remote host and restart Node-RED
5
# Usage: ./deploy.sh <node-folder-path> [remote_user@host|local]
6
# Example: ./deploy.sh adapters/smart-socket/homebus-adapter node-red@192.168.2.104
7
#
8
# Known deploy targets:
9
# - testing:    node-red@192.168.2.104
10
# - production: node-red@192.168.2.101
11
# - legacy:     pi@192.168.2.133
12

            
13
REMOTE_DEFAULT="node-red@192.168.2.104"
14
REMOTE_NODERED_DIR="~/.node-red"
15
RSYNC_OPTS=( -az --delete --exclude=node_modules --exclude=.git )
16

            
17
# Parse arguments and flags.
18
# Usage: ./deploy.sh <node-folder-path> [remote_user@host|local] [--restart]
19
RESTART=false
20
POS=()
21

            
22
while [[ ${#@} -gt 0 ]]; do
23
  case "$1" in
24
    -h|--help)
25
      cat <<EOF
26
Usage: $0 <node-folder-path> [remote_user@host|local] [--restart]
27

            
28
This will sync the folder at <node-folder-path> to the remote staging dir,
29
run npm install for that node inside ~/.node-red, and optionally restart Node-RED
30
when the --restart flag is provided.
31

            
32
Examples:
33
  $0 presence-detector                     # deploy without restarting
34
  $0 adapters/smart-socket/homebus-adapter --restart
35
  $0 adapters/smart-socket/homebus-adapter pi@host
36
  $0 adapters/contact-sensor/homekit-adapter local
37
  $0 adapters/water-leak-sensor/homebus-adapter --restart pi@host
38

            
39
Notes:
40
- SSH access must be available to the remote host (use keys for passwordless login).
41
- The remote user must have sudo rights to run: sudo systemctl restart nodered (only required with --restart)
42
- Known targets:
43
  testing    -> node-red@192.168.2.104
44
  production -> node-red@192.168.2.101
45
  legacy     -> pi@192.168.2.133
46
EOF
47
      exit 0
48
      ;;
49
    --restart)
50
      RESTART=true
51
      shift
52
      ;;
53
    --*)
54
      echo "Unknown option: $1" >&2
55
      exit 3
56
      ;;
57
    *)
58
      POS+=("$1")
59
      shift
60
      ;;
61
  esac
62
done
63

            
64
# Validate positional args
65
if [[ ${#POS[@]} -lt 1 ]]; then
66
  echo "Error: missing <node-folder-path>" >&2
67
  echo "Run '$0 --help' for usage." >&2
68
  exit 1
69
fi
70

            
71
NODE_PATH="${POS[0]}"
72
REMOTE_TARGET="${POS[1]:-$REMOTE_DEFAULT}"
73
LOCAL_DEPLOY=false
74
if [[ "$REMOTE_TARGET" == "local" ]]; then
75
  LOCAL_DEPLOY=true
76
fi
77
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
78

            
79
# Resolve local path
80
if [[ -d "$NODE_PATH" ]]; then
81
  LOCAL_PATH="$NODE_PATH"
82
elif [[ -d "$PWD/$NODE_PATH" ]]; then
83
  LOCAL_PATH="$PWD/$NODE_PATH"
84
elif [[ -d "$SCRIPT_DIR/$NODE_PATH" ]]; then
85
  LOCAL_PATH="$SCRIPT_DIR/$NODE_PATH"
86
else
87
  LOCAL_PATH="$PWD/$NODE_PATH"
88
fi
89
if [[ ! -d "$LOCAL_PATH" ]]; then
90
  echo "Error: local folder '$LOCAL_PATH' does not exist." >&2
91
  echo "Checked: '$NODE_PATH', '$PWD/$NODE_PATH', '$SCRIPT_DIR/$NODE_PATH'" >&2
92
  exit 2
93
fi
94

            
95
# Remote staging directory (copy here first, then npm install from it on the Pi)
96
# NOTE: staged path mirrors the relative adapter path, e.g. "~/incomming/adapters/smart-socket/homebus-adapter"
97
REMOTE_STAGING_DIR="~/incomming"
98

            
99
DEST_DIR="${REMOTE_STAGING_DIR%/}/$NODE_PATH"
100

            
101
if [[ "$LOCAL_DEPLOY" == "true" ]]; then
102
  LOCAL_STAGING_DIR="$HOME/incomming"
103
  LOCAL_DEST_DIR="${LOCAL_STAGING_DIR%/}/$NODE_PATH"
104
  LOCAL_NODERED_DIR="$HOME/.node-red"
105

            
106
  echo "Deploying node '$NODE_PATH' locally to ${LOCAL_DEST_DIR} (staging), then installing into ${LOCAL_NODERED_DIR}."
107

            
108
  mkdir -p "${LOCAL_STAGING_DIR%/}"
109
  mkdir -p "$(dirname "${LOCAL_DEST_DIR}")"
110
  rm -rf "${LOCAL_DEST_DIR}"
111
  mkdir -p "${LOCAL_DEST_DIR}"
112
  if command -v rsync >/dev/null 2>&1; then
113
    rsync "${RSYNC_OPTS[@]}" "$LOCAL_PATH/" "${LOCAL_DEST_DIR}/"
114
  else
115
    # Fallback when rsync is not available: copy with tar while excluding large/generated dirs.
116
    tar --exclude='.git' --exclude='node_modules' -C "$LOCAL_PATH" -cf - . | tar -C "${LOCAL_DEST_DIR}" -xf -
117
  fi
118
  ( cd "${LOCAL_NODERED_DIR%/}" && npm install --no-audit --no-fund "${LOCAL_STAGING_DIR%/}/$NODE_PATH" || true )
119

            
120
  if [[ "$RESTART" == "true" ]]; then
121
    echo "Restarting local Node-RED service (sudo may ask for a password)..."
122
    sudo systemctl restart nodered
123
  else
124
    echo "Skipping Node-RED restart (use --restart to enable)."
125
  fi
126
else
127
  echo "Deploying node '$NODE_PATH' to ${REMOTE_TARGET}:${DEST_DIR} (staging), then installing into ${REMOTE_NODERED_DIR}. Use --restart to restart Node-RED after install."
128
  if ! command -v rsync >/dev/null 2>&1; then
129
    echo "Error: rsync is required for remote deploy mode but is not installed locally." >&2
130
    echo "Use '$0 $NODE_PATH local' for local deploy, or install rsync." >&2
131
    exit 4
132
  fi
133

            
134
  # Ensure staging dir exists on remote
135
  ssh "$REMOTE_TARGET" "mkdir -p ${REMOTE_STAGING_DIR%/}"
136
  ssh "$REMOTE_TARGET" "mkdir -p ${DEST_DIR%/*}"
137

            
138
  # Sync node files to staging dir (omit node_modules and .git). Fallback to tar when remote rsync is missing.
139
  if ssh "$REMOTE_TARGET" "command -v rsync >/dev/null 2>&1"; then
140
    rsync "${RSYNC_OPTS[@]}" -e ssh "$LOCAL_PATH/" "$REMOTE_TARGET:$DEST_DIR/"
141
  else
142
    ssh "$REMOTE_TARGET" "rm -rf ${DEST_DIR} && mkdir -p ${DEST_DIR}"
143
    tar --exclude='.git' --exclude='node_modules' -C "$LOCAL_PATH" -cf - . | ssh "$REMOTE_TARGET" "tar -C ${DEST_DIR} -xf -"
144
  fi
145

            
146
  # Run npm install on the remote (from within ~/.node-red using staged path)
147
  ssh "$REMOTE_TARGET" "set -euo pipefail; cd ${REMOTE_NODERED_DIR%/} && npm install --no-audit --no-fund ${REMOTE_STAGING_DIR%/}/$NODE_PATH || true"
148

            
149
  # Conditionally restart Node-RED (only if --restart was passed)
150
  if [[ "$RESTART" == "true" ]]; then
151
    echo "Restarting Node-RED on ${REMOTE_TARGET} (sudo may ask for a password)..."
152
    ssh "$REMOTE_TARGET" "sudo systemctl restart nodered"
153
  else
154
    echo "Skipping Node-RED restart (use --restart to enable)."
155
  fi
156
fi
157

            
158
echo "✅ Deploy finished: $NODE_PATH -> $REMOTE_TARGET"