Showing 3 changed files with 149 additions and 2 deletions
+15 -1
.doc/host-manager.md
@@ -68,7 +68,13 @@ hosts.madagascar.xdev.ro
68 68
 
69 69
 Configurațiile de deployment sunt în `deploy/jumper/`.
70 70
 
71
-Instanța de pe jumper este instalată în `/usr/local/xdev-host-manager` și publicată prin:
71
+Checkout-ul de dezvoltare este local:
72
+
73
+```text
74
+/Users/bogdan/Documents/Workspaces/Xdev/Madagascar/LocalAuthority
75
+```
76
+
77
+Instanța runtime de pe jumper este instalată în `/usr/local/xdev-host-manager` și publicată prin:
72 78
 
73 79
 ```text
74 80
 http://hosts.madagascar.xdev.ro/
@@ -80,6 +86,14 @@ Secretul TOTP nu este în repo. Pentru bootstrap, citește URI-ul root-only de p
80 86
 ssh jumper.madagascar.xdev.ro 'cat /etc/xdev/host-manager.totp-uri'
81 87
 ```
82 88
 
89
+Codul aplicației se modifică local și se publică pe jumper cu:
90
+
91
+```bash
92
+scripts/deploy_to_jumper.sh
93
+```
94
+
95
+Scriptul de deploy nu copiază implicit `config/`, deoarece acesta conține date operaționale editabile de aplicația live. Folosește `--include-config` doar când vrei explicit să înlocuiești registrul runtime.
96
+
83 97
 ## OTP
84 98
 
85 99
 `HOST_MANAGER_TOTP_SECRET` trebuie să fie un secret Base32 compatibil TOTP. Fără această variabilă, healthcheck-ul și pagina de login funcționează, dar login-ul, API-ul, download-urile și scrierile nu.
+24 -1
README.md
@@ -2,7 +2,13 @@
2 2
 
3 3
 Local authority for Madagascar hosts, DNS manifests, work orders, and host certificates.
4 4
 
5
-This project lives on jumper and is the local source for:
5
+The development checkout lives locally at:
6
+
7
+```text
8
+/Users/bogdan/Documents/Workspaces/Xdev/Madagascar/LocalAuthority
9
+```
10
+
11
+The runtime instance lives on jumper and remains the local source for operational registry data:
6 12
 
7 13
 - `config/hosts.yaml` - git-versioned host registry
8 14
 - `config/local-hosts.tsv` - DNS manifest exported for local resolvers
@@ -25,6 +31,23 @@ The product name is **Madagascar Local Authority**. The technical service, Unix
25 31
 
26 32
 The web UI is OTP-protected for all registry data, downloads, exports, and writes. Automation should consume this repository through git with dedicated read-only keys, not through unauthenticated HTTP.
27 33
 
34
+## Local development and deployment
35
+
36
+Work on application code locally, commit changes, then deploy to jumper:
37
+
38
+```bash
39
+cd /Users/bogdan/Documents/Workspaces/Xdev/Madagascar/LocalAuthority
40
+scripts/deploy_to_jumper.sh
41
+```
42
+
43
+The deploy script copies code, docs and deployment assets, restarts `host-manager`, and checks `/healthz`.
44
+
45
+`config/` is not deployed by default because `hosts.yaml`, `local-hosts.tsv`, and `work-orders.yaml` are operational data that may be changed on jumper by the application. Deploy config only when intentionally replacing runtime registry data:
46
+
47
+```bash
48
+scripts/deploy_to_jumper.sh --include-config
49
+```
50
+
28 51
 The default internal domain is `madagascar.xdev.ro`. Short aliases are derived automatically from FQDNs, so `autonas01.madagascar.xdev.ro` also publishes `autonas01` without declaring it separately.
29 52
 
30 53
 Name removals with operational impact go through a Work Order. A WO records intent first; the operational checklist must be completed before confirmation can update `hosts.yaml`, mark the WO as confirmed, and regenerate `local-hosts.tsv`. Resolver sync remains an explicit operator step.
+110 -0
scripts/deploy_to_jumper.sh
@@ -0,0 +1,110 @@
1
+#!/usr/bin/env bash
2
+# Deploy Madagascar Local Authority application code to jumper.
3
+
4
+set -euo pipefail
5
+
6
+TARGET_HOST="${TARGET_HOST:-jumper.madagascar.xdev.ro}"
7
+TARGET_DIR="${TARGET_DIR:-/usr/local/xdev-host-manager}"
8
+RESTART=1
9
+ALLOW_DIRTY=0
10
+DRY_RUN=0
11
+INCLUDE_CONFIG=0
12
+
13
+usage() {
14
+    cat <<EOF
15
+Usage: $0 [--target host] [--target-dir path] [--no-restart] [--allow-dirty] [--dry-run] [--include-config]
16
+
17
+Deploys application code/docs to jumper. Runtime registry data in config/ is not
18
+deployed unless --include-config is explicit.
19
+
20
+Environment:
21
+  TARGET_HOST   Default: jumper.madagascar.xdev.ro
22
+  TARGET_DIR    Default: /usr/local/xdev-host-manager
23
+EOF
24
+}
25
+
26
+while [[ $# -gt 0 ]]; do
27
+    case "$1" in
28
+        --target)
29
+            TARGET_HOST="$2"
30
+            shift 2
31
+            ;;
32
+        --target-dir)
33
+            TARGET_DIR="$2"
34
+            shift 2
35
+            ;;
36
+        --no-restart)
37
+            RESTART=0
38
+            shift
39
+            ;;
40
+        --allow-dirty)
41
+            ALLOW_DIRTY=1
42
+            shift
43
+            ;;
44
+        --dry-run)
45
+            DRY_RUN=1
46
+            shift
47
+            ;;
48
+        --include-config)
49
+            INCLUDE_CONFIG=1
50
+            shift
51
+            ;;
52
+        --help|-h)
53
+            usage
54
+            exit 0
55
+            ;;
56
+        *)
57
+            echo "Unknown option: $1" >&2
58
+            usage >&2
59
+            exit 2
60
+            ;;
61
+    esac
62
+done
63
+
64
+cd "$(dirname "$0")/.."
65
+
66
+if [[ "$ALLOW_DIRTY" -ne 1 ]] && [[ -n "$(git status --porcelain)" ]]; then
67
+    echo "Working tree is dirty; commit first or pass --allow-dirty." >&2
68
+    git status --short >&2
69
+    exit 1
70
+fi
71
+
72
+command -v rsync >/dev/null 2>&1 || {
73
+    echo "rsync is required for deployment." >&2
74
+    exit 1
75
+}
76
+ssh "$TARGET_HOST" "command -v rsync >/dev/null 2>&1" || {
77
+    echo "rsync is required on $TARGET_HOST." >&2
78
+    exit 1
79
+}
80
+
81
+perl -c scripts/host_manager.pl >/dev/null
82
+
83
+rsync_args=(-az --delete)
84
+if [[ "$DRY_RUN" -eq 1 ]]; then
85
+    rsync_args+=(--dry-run --itemize-changes)
86
+fi
87
+
88
+ssh "$TARGET_HOST" "mkdir -p '$TARGET_DIR/scripts' '$TARGET_DIR/deploy' '$TARGET_DIR/.doc'"
89
+rsync "${rsync_args[@]}" scripts/ "$TARGET_HOST:$TARGET_DIR/scripts/"
90
+rsync "${rsync_args[@]}" deploy/ "$TARGET_HOST:$TARGET_DIR/deploy/"
91
+rsync "${rsync_args[@]}" .doc/ "$TARGET_HOST:$TARGET_DIR/.doc/"
92
+rsync "${rsync_args[@]}" README.md "$TARGET_HOST:$TARGET_DIR/README.md"
93
+
94
+if [[ "$INCLUDE_CONFIG" -eq 1 ]]; then
95
+    ssh "$TARGET_HOST" "mkdir -p '$TARGET_DIR/config'"
96
+    rsync "${rsync_args[@]}" config/ "$TARGET_HOST:$TARGET_DIR/config/"
97
+fi
98
+
99
+if [[ "$DRY_RUN" -eq 1 ]]; then
100
+    exit 0
101
+fi
102
+
103
+ssh "$TARGET_HOST" "cd '$TARGET_DIR' && perl -c scripts/host_manager.pl >/dev/null"
104
+
105
+if [[ "$RESTART" -eq 1 ]]; then
106
+    ssh "$TARGET_HOST" "sudo -n systemctl restart host-manager && systemctl is-active host-manager >/dev/null"
107
+fi
108
+
109
+ssh "$TARGET_HOST" "curl -fsS http://127.0.0.1:8088/healthz >/dev/null"
110
+echo "Deployed to $TARGET_HOST:$TARGET_DIR"