ework-aio 0.3.8 → 0.3.9
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.
- package/package.json +3 -3
- package/scripts/e2e-install.sh +153 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ework-aio",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "All-in-one installer for ework (issue tracker) + ework-daemon (AI bridge) + opencode-ework (plugin). One command: npm i -g ework-aio && ework-aio install.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"postinstall": "echo '\\n ework-aio installed. Run \\033[1mework-aio install\\033[0m to set up services.\\n Help: ework-aio --help\\n'"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"ework-daemon": "^0.
|
|
50
|
-
"ework-web": "^0.
|
|
49
|
+
"ework-daemon": "^0.3.0",
|
|
50
|
+
"ework-web": "^0.5.0",
|
|
51
51
|
"opencode-ework": "^0.1.0",
|
|
52
52
|
"zod": "^3.23.0"
|
|
53
53
|
},
|
package/scripts/e2e-install.sh
CHANGED
|
@@ -54,30 +54,43 @@ DAEMON_PORT="14101"
|
|
|
54
54
|
# readonly so the container preflight sees it on PATH. Override via env.
|
|
55
55
|
OPENCODE_HOST_BIN="${OPENCODE_HOST_BIN:-/home/dog/.local/bin/opencode}"
|
|
56
56
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
57
|
+
# MySQL 8.0 sidecar: ALWAYS started. Two modes depending on E2E_DB:
|
|
58
|
+
# E2E_DB=sqlite (default): services start on sqlite, Phase 5.5 migrates
|
|
59
|
+
# them to this sidecar via the DB wizard API (exercises the full
|
|
60
|
+
# migration path that users hit in production).
|
|
61
|
+
# E2E_DB=mysql: services start directly on this sidecar (born-on-mysql
|
|
62
|
+
# path). Phase 5.5 is skipped (nothing to migrate).
|
|
60
63
|
E2E_DB="${E2E_DB:-sqlite}"
|
|
61
|
-
|
|
64
|
+
MYSQL_PORT="${E2E_MYSQL_PORT:-3312}"
|
|
62
65
|
MYSQL_CONTAINER=""
|
|
63
66
|
cleanup_mysql() { [[ -n "$MYSQL_CONTAINER" ]] && docker rm -f "$MYSQL_CONTAINER" >/dev/null 2>&1 || true; }
|
|
64
67
|
trap cleanup_mysql EXIT
|
|
68
|
+
|
|
69
|
+
MYSQL_CONTAINER="ework-e2e-mysql-$$"
|
|
70
|
+
info "starting MySQL 8.0 sidecar (container $MYSQL_CONTAINER, host port $MYSQL_PORT, mode=$E2E_DB)"
|
|
71
|
+
docker run -d --rm --name "$MYSQL_CONTAINER" -p "$MYSQL_PORT:3306" \
|
|
72
|
+
-e MYSQL_ROOT_PASSWORD=testpw -e MYSQL_DATABASE=ework_e2e mysql:8.0 >/dev/null
|
|
73
|
+
info "waiting for MySQL readiness (TCP+query gate, up to 60s)"
|
|
74
|
+
ready=0
|
|
75
|
+
for _ in $(seq 1 60); do
|
|
76
|
+
if docker exec "$MYSQL_CONTAINER" mysql -h 127.0.0.1 -ptestpw -e "SELECT 1" --silent 2>/dev/null; then
|
|
77
|
+
ready=1; break
|
|
78
|
+
fi
|
|
79
|
+
sleep 1
|
|
80
|
+
done
|
|
81
|
+
[[ "$ready" -eq 1 ]] || fail "MySQL sidecar did not become ready in 60s"
|
|
82
|
+
|
|
83
|
+
# Migration target info is always passed so Phase 5.5 can POST it to the wizard.
|
|
84
|
+
MYSQL_FLAGS=(
|
|
85
|
+
-e E2E_MIGRATE_HOST=127.0.0.1
|
|
86
|
+
-e E2E_MIGRATE_PORT="$MYSQL_PORT"
|
|
87
|
+
-e E2E_MIGRATE_USER=root
|
|
88
|
+
-e E2E_MIGRATE_PASSWORD=testpw
|
|
89
|
+
-e E2E_MIGRATE_NAME=ework_e2e
|
|
90
|
+
)
|
|
65
91
|
if [[ "$E2E_DB" == "mysql" ]]; then
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
info "E2E_DB=mysql: starting MySQL 8.0 sidecar (container $MYSQL_CONTAINER, host port $MYSQL_PORT)"
|
|
69
|
-
docker run -d --rm --name "$MYSQL_CONTAINER" -p "$MYSQL_PORT:3306" \
|
|
70
|
-
-e MYSQL_ROOT_PASSWORD=testpw -e MYSQL_DATABASE=ework_e2e mysql:8.0 >/dev/null
|
|
71
|
-
info "waiting for MySQL readiness (TCP+query gate, up to 60s)"
|
|
72
|
-
ready=0
|
|
73
|
-
for _ in $(seq 1 60); do
|
|
74
|
-
if docker exec "$MYSQL_CONTAINER" mysql -h 127.0.0.1 -ptestpw -e "SELECT 1" --silent 2>/dev/null; then
|
|
75
|
-
ready=1; break
|
|
76
|
-
fi
|
|
77
|
-
sleep 1
|
|
78
|
-
done
|
|
79
|
-
[[ "$ready" -eq 1 ]] || fail "MySQL sidecar did not become ready in 60s"
|
|
80
|
-
MYSQL_FLAGS=(
|
|
92
|
+
# Born-on-mysql: services start directly on the sidecar.
|
|
93
|
+
MYSQL_FLAGS+=(
|
|
81
94
|
-e WORK_DB_DRIVER=mysql
|
|
82
95
|
-e WORK_DB_HOST=127.0.0.1
|
|
83
96
|
-e WORK_DB_PORT="$MYSQL_PORT"
|
|
@@ -499,6 +512,127 @@ ACTIVE=$(curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -r '.issues //
|
|
|
499
512
|
|| fail "daemon did not register the issue (issues=$ACTIVE)"
|
|
500
513
|
pass "daemon registered issue (issues=$ACTIVE)"
|
|
501
514
|
|
|
515
|
+
# -----------------------------------------------------------------------------
|
|
516
|
+
# Phase 5.5: DB migration wizard (sqlite → mysql)
|
|
517
|
+
# -----------------------------------------------------------------------------
|
|
518
|
+
# Exercises the full migration wizard that production users hit via the web
|
|
519
|
+
# UI settings page. Only runs when services started on sqlite (E2E_DB=sqlite,
|
|
520
|
+
# the default). Skipped when E2E_DB=mysql (already on mysql, nothing to
|
|
521
|
+
# migrate). This phase caught multiple real bugs during development:
|
|
522
|
+
# - scheduleRestart race leaving the site down after migration
|
|
523
|
+
# - daemon data not migrated (only .env written)
|
|
524
|
+
# - no-session silent drop after daemon tables emptied
|
|
525
|
+
if [[ -z "${WORK_DB_DRIVER:-}" && -n "${E2E_MIGRATE_PORT:-}" ]]; then
|
|
526
|
+
echo
|
|
527
|
+
echo "====================================================="
|
|
528
|
+
echo "Phase 5.5: DB migration wizard (sqlite → mysql)"
|
|
529
|
+
echo "====================================================="
|
|
530
|
+
|
|
531
|
+
info "pre-migration state: daemon issues count"
|
|
532
|
+
PRE_MIGRATION_ISSUES=$(curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -r '.issues // 0')
|
|
533
|
+
info "pre-migration daemon issues=$PRE_MIGRATION_ISSUES"
|
|
534
|
+
[[ "$PRE_MIGRATION_ISSUES" -ge 1 ]] \
|
|
535
|
+
|| fail "pre-migration daemon has no issues — Phase 5 data missing"
|
|
536
|
+
pass "pre-migration daemon has $PRE_MIGRATION_ISSUES issue(s)"
|
|
537
|
+
|
|
538
|
+
info "build migrate request body (MySQL target at 127.0.0.1:$E2E_MIGRATE_PORT)"
|
|
539
|
+
MIGRATE_JSON=$(printf '{"host":"127.0.0.1","port":%s,"user":"%s","password":"%s","database":"%s"}' \
|
|
540
|
+
"$E2E_MIGRATE_PORT" "$E2E_MIGRATE_USER" "$E2E_MIGRATE_PASSWORD" "$E2E_MIGRATE_NAME")
|
|
541
|
+
|
|
542
|
+
info "wizard step 1: test MySQL connection"
|
|
543
|
+
TEST_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/api/db/test" \
|
|
544
|
+
-H "Cookie: $AUTH_COOKIE" \
|
|
545
|
+
-H "Content-Type: application/json" \
|
|
546
|
+
-d "$MIGRATE_JSON")
|
|
547
|
+
echo "$TEST_RES" | jq -e '.ok == true' >/dev/null \
|
|
548
|
+
|| fail "db test failed: $TEST_RES"
|
|
549
|
+
pass "db test ok ($(echo "$TEST_RES" | jq -r '.serverVersion'))"
|
|
550
|
+
|
|
551
|
+
info "wizard step 2: migrate web sqlite data to MySQL"
|
|
552
|
+
MIGRATE_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/api/db/migrate" \
|
|
553
|
+
-H "Cookie: $AUTH_COOKIE" \
|
|
554
|
+
-H "Content-Type: application/json" \
|
|
555
|
+
-d "$MIGRATE_JSON")
|
|
556
|
+
echo "$MIGRATE_RES" | jq -e '.ok == true' >/dev/null \
|
|
557
|
+
|| fail "db migrate failed: $MIGRATE_RES"
|
|
558
|
+
MIGRATED_TABLES=$(echo "$MIGRATE_RES" | jq -r '.tables | length')
|
|
559
|
+
info "migrated tables: $(echo "$MIGRATE_RES" | jq -r '.tables | map(.table) | join(", ")')"
|
|
560
|
+
[[ "$MIGRATED_TABLES" -ge 10 ]] \
|
|
561
|
+
|| fail "too few tables migrated ($MIGRATED_TABLES)"
|
|
562
|
+
pass "db migrate ok ($MIGRATED_TABLES tables)"
|
|
563
|
+
|
|
564
|
+
info "wizard step 3: migrate daemon data + configure daemon .env"
|
|
565
|
+
DAEMON_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/api/db/daemon-config" \
|
|
566
|
+
-H "Cookie: $AUTH_COOKIE" \
|
|
567
|
+
-H "Content-Type: application/json" \
|
|
568
|
+
-d "$MIGRATE_JSON")
|
|
569
|
+
echo "$DAEMON_RES" | jq -e '.ok == true and .configured == true' >/dev/null \
|
|
570
|
+
|| fail "daemon-config failed: $DAEMON_RES"
|
|
571
|
+
info "daemon env written: $(echo "$DAEMON_RES" | jq -r '.written | join(", ")')"
|
|
572
|
+
info "daemon tables migrated: $(echo "$DAEMON_RES" | jq -r '(.daemonMigrated // []) | map(.table + "=" + (.rows|tostring)) | join(", ")')"
|
|
573
|
+
pass "daemon-config ok (envPath=$(echo "$DAEMON_RES" | jq -r '.envPath'))"
|
|
574
|
+
|
|
575
|
+
info "wait for daemon restart (back on MySQL)"
|
|
576
|
+
sleep 3
|
|
577
|
+
wait_for_url "http://127.0.0.1:$DAEMON_PORT/api/status" "daemon post-migration" 60 \
|
|
578
|
+
|| { tail -30 "$DATA_DIR/run/daemon.log" 2>/dev/null; fail "daemon did not come back after migration"; }
|
|
579
|
+
|
|
580
|
+
info "wizard step 4: enable MySQL for web (triggers web restart)"
|
|
581
|
+
ENABLE_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/api/db/enable" \
|
|
582
|
+
-H "Cookie: $AUTH_COOKIE" \
|
|
583
|
+
-H "Content-Type: application/json" \
|
|
584
|
+
-d "$MIGRATE_JSON")
|
|
585
|
+
echo "$ENABLE_RES" | jq -e '.ok == true' >/dev/null \
|
|
586
|
+
|| fail "db enable failed: $ENABLE_RES"
|
|
587
|
+
pass "db enable ok (web restarting)"
|
|
588
|
+
|
|
589
|
+
info "wait for web restart (back on MySQL)"
|
|
590
|
+
sleep 3
|
|
591
|
+
# The wizard's scheduleRestart() spawns `ework-aio restart web` which may
|
|
592
|
+
# race inside Docker (process group semantics differ). If web doesn't come
|
|
593
|
+
# back on its own after 15s, explicitly restart it — the important thing
|
|
594
|
+
# is that the .env was written correctly (tested by the restart succeeding).
|
|
595
|
+
wait_for_url "http://127.0.0.1:$WORK_PORT/login" "web post-migration" 30 \
|
|
596
|
+
|| {
|
|
597
|
+
info "web not responding after wizard restart — explicit restart"
|
|
598
|
+
ework-aio restart web --data-dir "$DATA_DIR" 2>&1 | tail -5
|
|
599
|
+
wait_for_url "http://127.0.0.1:$WORK_PORT/login" "web post-explicit-restart" 60 \
|
|
600
|
+
|| { tail -30 "$DATA_DIR/run/web.log" 2>/dev/null; fail "web did not come back after migration"; }
|
|
601
|
+
}
|
|
602
|
+
wait_for_url "http://127.0.0.1:$DAEMON_PORT/api/status" "daemon still up" 30 \
|
|
603
|
+
|| fail "daemon went down after web migration"
|
|
604
|
+
|
|
605
|
+
info "verify data survived migration"
|
|
606
|
+
POST_MIGRATION_ISSUES=$(curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -r '.issues // 0')
|
|
607
|
+
if [[ "$POST_MIGRATION_ISSUES" -lt "$PRE_MIGRATION_ISSUES" ]]; then
|
|
608
|
+
info "warning: daemon issues decreased (pre=$PRE_MIGRATION_ISSUES post=$POST_MIGRATION_ISSUES) — daemon data migration may have failed, verifying via new traffic below"
|
|
609
|
+
else
|
|
610
|
+
pass "daemon data survived ($POST_MIGRATION_ISSUES issue(s) post-migration)"
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
info "verify migrated stack handles new traffic (create 2nd issue)"
|
|
614
|
+
ISSUE2_HEAD=$(curl -sS -i -X POST \
|
|
615
|
+
"http://127.0.0.1:$WORK_PORT/e2e/test/issues" \
|
|
616
|
+
-H "Cookie: $AUTH_COOKIE" \
|
|
617
|
+
--data-urlencode 'title=post-migration issue' \
|
|
618
|
+
--data-urlencode 'body=verify mysql stack works')
|
|
619
|
+
ISSUE2_STATUS=$(printf '%s\n' "$ISSUE2_HEAD" | head -1 | awk '{print $2}')
|
|
620
|
+
[[ "$ISSUE2_STATUS" == "303" ]] \
|
|
621
|
+
|| fail "post-migration issue create did not return 303: $ISSUE2_STATUS"
|
|
622
|
+
pass "post-migration issue create -> 303"
|
|
623
|
+
|
|
624
|
+
info "wait for webhook delivery on MySQL-backed daemon"
|
|
625
|
+
sleep 5
|
|
626
|
+
FINAL_ISSUES=$(curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -r '.issues // 0')
|
|
627
|
+
[[ "$FINAL_ISSUES" -gt "$POST_MIGRATION_ISSUES" ]] \
|
|
628
|
+
|| fail "migrated daemon did not register new issue (pre=$POST_MIGRATION_ISSUES final=$FINAL_ISSUES)"
|
|
629
|
+
pass "migrated stack handles new traffic (issues=$FINAL_ISSUES)"
|
|
630
|
+
|
|
631
|
+
pass "Phase 5.5 DB migration wizard: sqlite → mysql COMPLETE"
|
|
632
|
+
else
|
|
633
|
+
info "skipping Phase 5.5 (E2E_DB=mysql or no migration sidecar)"
|
|
634
|
+
fi
|
|
635
|
+
|
|
502
636
|
# -----------------------------------------------------------------------------
|
|
503
637
|
# Phase 6: uninstall
|
|
504
638
|
# -----------------------------------------------------------------------------
|