muse-bridge-deploy.sh bash
199 lines 8.3 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 18 hours ago
1 #!/usr/bin/env bash
2 # muse-bridge-deploy.sh
3 #
4 # Complete MuseHub → external Git deployment pipeline.
5 #
6 # Usage:
7 # ./scripts/muse-bridge-deploy.sh "mirror: brief description of what changed"
8 #
9 # What this does (in order):
10 # 1. Security audit — npm audit fix (blocks the bridge if high vulns remain)
11 # 2. Commit audit fix — if audit changed package-lock.json, commits it to Muse
12 # 3. Bridge export — muse bridge git-export → isolated .muse/mirror checkout
13 # 4. GitHub PR — opens a PR from muse-mirror to main (skips if one already exists)
14 #
15 # Requirements:
16 # - muse CLI in PATH (from ~/.local/share/muse/venv/bin)
17 # - gh CLI in PATH and authenticated (gh auth status)
18 # - git remote "origin" points to your GitHub/GitLab repo
19 # - MUSE_BRIDGE_GIT_BRANCH env var (default: muse-mirror)
20 # - MUSE_BRIDGE_BASE_BRANCH env var (default: main)
21 # - MUSE_BRIDGE_MIRROR_DIR env var (default: .muse/mirror)
22
23 set -euo pipefail
24
25 # ── Config (override via env) ──────────────────────────────────────────────────
26 MIRROR_BRANCH="${MUSE_BRIDGE_GIT_BRANCH:-muse-mirror}"
27 BASE_BRANCH="${MUSE_BRIDGE_BASE_BRANCH:-main}"
28 PR_TITLE="${1:-mirror: deploy from MuseHub $(date '+%Y-%m-%d')}"
29 REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
30 MIRROR_DIR="${MUSE_BRIDGE_MIRROR_DIR:-.muse/mirror}"
31
32 # ── Colors ─────────────────────────────────────────────────────────────────────
33 RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; NC='\033[0m'
34 info() { echo -e "${BLUE}[bridge]${NC} $*"; }
35 success() { echo -e "${GREEN}[bridge]${NC} $*"; }
36 warn() { echo -e "${YELLOW}[bridge]${NC} $*"; }
37 fail() { echo -e "${RED}[bridge] ERROR:${NC} $*" >&2; exit 1; }
38
39 cd "$REPO_ROOT"
40
41 if [[ "$MIRROR_DIR" = /* ]]; then
42 MIRROR_DIR_ABS="$MIRROR_DIR"
43 else
44 MIRROR_DIR_ABS="${REPO_ROOT}/${MIRROR_DIR}"
45 fi
46
47 if [[ "$MIRROR_DIR_ABS" == "$REPO_ROOT" ]]; then
48 fail "MUSE_BRIDGE_MIRROR_DIR must not be the repository root. Refusing unsafe --git-dir . workflow."
49 fi
50
51 cleanup_sentinels() {
52 [[ -n "${ENV_SENTINEL:-}" && -f "$ENV_SENTINEL" ]] && rm -f "$ENV_SENTINEL"
53 [[ -n "${CONFIG_SENTINEL:-}" && -f "$CONFIG_SENTINEL" ]] && rm -f "$CONFIG_SENTINEL"
54 [[ -n "${DATA_SENTINEL:-}" && -f "$DATA_SENTINEL" ]] && rm -f "$DATA_SENTINEL"
55 [[ "${CREATED_DATA_DIR:-0}" == "1" && -d "${REPO_ROOT}/data" ]] && rmdir "${REPO_ROOT}/data" 2>/dev/null || true
56 }
57 trap cleanup_sentinels EXIT
58
59 ensure_mirror_checkout() {
60 local remote_url
61 remote_url=$(git config --get remote.origin.url 2>/dev/null || true)
62 [[ -n "$remote_url" ]] || fail "Git remote 'origin' is not configured."
63
64 if [[ -e "$MIRROR_DIR_ABS" && ! -d "${MIRROR_DIR_ABS}/.git" ]]; then
65 fail "Mirror path exists but is not a git repository: ${MIRROR_DIR_ABS}"
66 fi
67
68 if [[ ! -d "${MIRROR_DIR_ABS}/.git" ]]; then
69 info "Provisioning isolated mirror checkout at ${MIRROR_DIR}..."
70 mkdir -p "$(dirname "$MIRROR_DIR_ABS")"
71 git clone --single-branch --branch "$MIRROR_BRANCH" "$remote_url" "$MIRROR_DIR_ABS"
72 fi
73
74 git -C "$MIRROR_DIR_ABS" remote set-url origin "$remote_url"
75 git -C "$MIRROR_DIR_ABS" fetch origin "$MIRROR_BRANCH"
76 git -C "$MIRROR_DIR_ABS" checkout -B "$MIRROR_BRANCH" "origin/${MIRROR_BRANCH}"
77 git -C "$MIRROR_DIR_ABS" reset --hard "origin/${MIRROR_BRANCH}"
78 }
79
80 prepare_sentinels() {
81 ENV_SENTINEL="${REPO_ROOT}/.env.bridge-sentinel.$$"
82 CONFIG_SENTINEL="${REPO_ROOT}/config/bridge-sentinel-local.$$.yaml"
83 CREATED_DATA_DIR=0
84
85 if [[ ! -d "${REPO_ROOT}/data" ]]; then
86 mkdir -p "${REPO_ROOT}/data"
87 CREATED_DATA_DIR=1
88 fi
89 DATA_SENTINEL="${REPO_ROOT}/data/bridge-sentinel.$$.db"
90
91 printf 'bridge sentinel: no secrets\n' > "$ENV_SENTINEL"
92 printf 'bridge sentinel: no secrets\n' > "$CONFIG_SENTINEL"
93 printf 'bridge sentinel: no secrets\n' > "$DATA_SENTINEL"
94 }
95
96 verify_sentinels() {
97 [[ -f "$ENV_SENTINEL" ]] || fail "Bridge safety sentinel disappeared: ${ENV_SENTINEL}. The bridge touched the dev tree."
98 [[ -f "$CONFIG_SENTINEL" ]] || fail "Bridge safety sentinel disappeared: ${CONFIG_SENTINEL}. The bridge touched the dev tree."
99 [[ -f "$DATA_SENTINEL" ]] || fail "Bridge safety sentinel disappeared: ${DATA_SENTINEL}. The bridge touched the dev tree."
100 }
101
102 # ── Step 1: Security audit ─────────────────────────────────────────────────────
103 info "Step 1/4 — Running security audit (npm audit fix)..."
104
105 if [[ ! -f package.json ]]; then
106 warn "No package.json found — skipping npm audit. Add audits for your stack manually."
107 else
108 AUDIT_FIX_OUTPUT=$(npm audit fix --audit-level=moderate 2>&1 || true)
109 printf '%s\n' "$AUDIT_FIX_OUTPUT" | tail -5
110
111 # Check if high/critical vulns remain after auto-fix
112 HIGH_COUNT=$(npm audit --json 2>/dev/null \
113 | python3 -c "import json,sys; d=json.load(sys.stdin); \
114 v=d.get('metadata',{}).get('vulnerabilities',{}); \
115 print(v.get('high',0)+v.get('critical',0))" 2>/dev/null || echo "0")
116
117 if [[ "$HIGH_COUNT" -gt 0 ]]; then
118 fail "npm audit found $HIGH_COUNT high/critical vulnerabilities that could not be auto-fixed.\n\
119 Run 'npm audit' to review them, fix manually, then re-run this script.\n\
120 DO NOT bridge with known high/critical vulnerabilities."
121 fi
122 success "Audit clean — 0 high/critical vulnerabilities."
123 fi
124
125 # ── Step 2: Commit audit changes if any ───────────────────────────────────────
126 info "Step 2/4 — Checking for audit-generated changes..."
127
128 AUDIT_CHANGED=$(muse status --short 2>/dev/null | grep -E "package-lock\.json|package\.json" || true)
129
130 if [[ -n "$AUDIT_CHANGED" ]]; then
131 warn "Audit changed package files — committing to Muse before bridging."
132 muse code add package-lock.json package.json 2>/dev/null || true
133 muse commit -m "security: npm audit fix pre-bridge $(date '+%Y-%m-%d')"
134 muse push staging "${BASE_BRANCH}"
135 success "Audit commit pushed to Muse main."
136 else
137 success "No audit changes to commit."
138 fi
139
140 # ── Step 3: Bridge export ──────────────────────────────────────────────────────
141 info "Step 3/4 — Bridging Muse main → ${MIRROR_BRANCH} via isolated mirror checkout..."
142
143 ensure_mirror_checkout
144 prepare_sentinels
145
146 muse bridge git-export \
147 --git-dir "${MIRROR_DIR_ABS}" \
148 --git-branch "${MIRROR_BRANCH}" \
149 --git-remote origin \
150 --force-push \
151 --exclude '.env' \
152 --exclude '.env.local' \
153 --exclude '.env.*.local' \
154 --exclude 'config/local.yaml' \
155 --exclude 'config/*-local.*' \
156 --exclude 'data/*' \
157 --exclude 'data/**' \
158 --exclude '*.db' \
159 --exclude '*.sqlite'
160
161 verify_sentinels
162
163 success "Bridge complete. ${MIRROR_BRANCH} is up to date on origin."
164
165 # ── Step 4: GitHub/GitLab PR ──────────────────────────────────────────────────
166 info "Step 4/4 — Opening or updating PR: ${MIRROR_BRANCH} → ${BASE_BRANCH}..."
167
168 if ! command -v gh &>/dev/null; then
169 warn "gh CLI not found — skipping PR creation."
170 warn "Manually open a PR from '${MIRROR_BRANCH}' to '${BASE_BRANCH}' on your Git host."
171 exit 0
172 fi
173
174 # Check if a PR already exists for this branch
175 EXISTING_PR=$(gh pr list --head "${MIRROR_BRANCH}" --base "${BASE_BRANCH}" --json number,url \
176 --jq '.[0].url' 2>/dev/null || true)
177
178 if [[ -n "$EXISTING_PR" ]]; then
179 warn "PR already exists: $EXISTING_PR"
180 warn "Review and merge it at the link above. (The bridge already updated the branch.)"
181 else
182 PR_URL=$(gh pr create \
183 --base "${BASE_BRANCH}" \
184 --head "${MIRROR_BRANCH}" \
185 --title "${PR_TITLE}" \
186 --body "Automated mirror from MuseHub. All development and review happened there.
187
188 - Security audit passed (0 high/critical vulnerabilities)
189 - Bridged via: \`muse bridge git-export\`
190 - Source: \`muse status\` on Muse main at time of bridge
191
192 Merge this PR to trigger your deployment platform." \
193 2>&1)
194 success "PR created: $PR_URL"
195 fi
196
197 echo ""
198 success "Deploy pipeline complete."
199 echo -e " Next step: merge the PR on GitHub/GitLab and your deployment platform will pick it up."
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 18 hours ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago