ework-aio 0.1.7 → 0.1.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.
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env bash
2
+ # Regression test: install published npm packages in a fresh container and
3
+ # verify each component launches + responds.
4
+ #
5
+ # Skips systemd (containers don't have it). Tests:
6
+ # 1. npm install -g ework-aio resolves all 3 deps
7
+ # 2. Bin shims (ework-aio, ework-web, ework-daemon-server) are on PATH
8
+ # 3. ework-web launches and /login responds 200
9
+ # 4. Bot user creation + PAT mint via API works
10
+ # 5. ework-daemon launches and /api/status responds
11
+ # 6. Plugin source loadable (opencode-ework resolves)
12
+ #
13
+ # Usage: ./scripts/regression.sh [container-runtime]
14
+ # container-runtime: docker (default) | podman
15
+
16
+ set -euo pipefail
17
+
18
+ RUNTIME="${1:-docker}"
19
+ IMAGE="${IMAGE:-ework-aio:regression}"
20
+
21
+ c_grn=$'\033[32m'; c_red=$'\033[31m'; c_ylw=$'\033[33m'; c_rst=$'\033[0m'
22
+ pass() { printf '%sPASS%s %s\n' "$c_grn" "$c_rst" "$*"; }
23
+ fail() { printf '%sFAIL%s %s\n' "$c_red" "$c_rst" "$*"; exit 1; }
24
+ info() { printf '%s…%s %s\n' "$c_ylw" "$c_rst" "$*"; }
25
+
26
+ if ! $RUNTIME image inspect "$IMAGE" >/dev/null 2>&1; then
27
+ info "Building $IMAGE (apt deps + npm pre-installed)"
28
+ docker build --network=host -f Dockerfile.regression -t "$IMAGE" .
29
+ fi
30
+
31
+ info "Using image: $IMAGE"
32
+
33
+ NPM_VER=$($RUNTIME run --rm "$IMAGE" bash -c 'npm --version 2>&1 || echo MISSING' 2>&1)
34
+ info "container npm version: $NPM_VER"
35
+ [[ "$NPM_VER" != "MISSING" ]] || fail "npm not in $IMAGE"
36
+
37
+ info "Running regression in $RUNTIME / $IMAGE"
38
+
39
+ # Pick unique ports to avoid collisions with host's existing ework instances
40
+ # when running with --network host. Override via WORK_PORT/DAEMON_PORT env.
41
+ WORK_PORT="${WORK_PORT:-14002}"
42
+ DAEMON_PORT="${DAEMON_PORT:-14101}"
43
+
44
+ # --network host so container can reach host's HTTP proxy (HTTP_PROXY).
45
+ $RUNTIME run --rm -i --network host \
46
+ -v /home/dog/.local/bin/opencode:/usr/local/bin/opencode:ro \
47
+ -e WORK_PORT="$WORK_PORT" \
48
+ -e DAEMON_PORT="$DAEMON_PORT" \
49
+ -e HTTP_PROXY="${HTTP_PROXY:-}" \
50
+ -e HTTPS_PROXY="${HTTPS_PROXY:-}" \
51
+ -e http_proxy="${HTTP_PROXY:-}" \
52
+ -e https_proxy="${HTTPS_PROXY:-}" \
53
+ -e NO_PROXY="127.0.0.1,localhost" \
54
+ "$IMAGE" bash -euo pipefail <<'EOSCRIPT'
55
+ set -euo pipefail
56
+
57
+ c_grn=$'\033[32m'; c_red=$'\033[31m'; c_ylw=$'\033[33m'; c_rst=$'\033[0m'
58
+ pass() { printf '%sPASS%s %s\n' "$c_grn" "$c_rst" "$*"; }
59
+ fail() { printf '%sFAIL%s %s\n' "$c_red" "$c_rst" "$*"; exit 1; }
60
+ info() { printf '%s…%s %s\n' "$c_ylw" "$c_rst" "$*" >&2; }
61
+
62
+ WORK_PORT="${WORK_PORT:-3002}"
63
+ DAEMON_PORT="${DAEMON_PORT:-3101}"
64
+
65
+ info "apt deps already in image (curl/jq/openssl present)"
66
+ command -v curl >/dev/null && pass "curl present"
67
+ command -v jq >/dev/null && pass "jq present"
68
+
69
+ info "opencode binary"
70
+ opencode --version | head -1
71
+
72
+ info "npm install -g (each pkg top-level so bins link to PATH)"
73
+ for pkg in ework-web ework-daemon opencode-ework ework-aio; do
74
+ npm install -g "$pkg" 2>&1 | tail -2
75
+ done
76
+
77
+ info "verify bin shims"
78
+ for b in ework-aio ework-web ework-daemon-server; do
79
+ command -v "$b" >/dev/null 2>&1 || fail "$b not on PATH"
80
+ pass "$b -> $(command -v "$b")"
81
+ done
82
+
83
+ info "ework-aio CLI smoke"
84
+ ework-aio --version
85
+ ework-aio env
86
+
87
+ info "preparing data dir"
88
+ mkdir -p /tmp/aio/ework-web /tmp/aio/ework-daemon /tmp/aio/opencode-workdir
89
+ cd /tmp/aio
90
+
91
+ WORK_TOKEN="$(openssl rand -hex 20)"
92
+ WORK_COOKIE_SECRET="$(openssl rand -hex 24)"
93
+ WEBHOOK_SECRET="$(openssl rand -hex 20)"
94
+ BOT_NAME="regression-bot"
95
+
96
+ cat > ework-web/.env <<EOF
97
+ WORK_PORT=$WORK_PORT
98
+ WORK_HOST=127.0.0.1
99
+ WORK_TOKEN=$WORK_TOKEN
100
+ WORK_COOKIE_SECRET=$WORK_COOKIE_SECRET
101
+ WORK_OPERATOR_LOGIN=root
102
+ WORK_WRITES_ENABLED=true
103
+ WORK_DB_PATH=/tmp/aio/ework-web/ework.db
104
+ WORK_ATTACHMENT_ROOT=/tmp/aio/ework-web/attachments
105
+ WORK_FILE_ROOTS=/tmp
106
+ WORK_DAEMON_BOT_LOGIN=$BOT_NAME
107
+ WORK_DAEMON_WEBHOOK_URL=http://127.0.0.1:$DAEMON_PORT
108
+ WORK_DAEMON_WEBHOOK_SECRET=$WEBHOOK_SECRET
109
+ EOF
110
+ chmod 600 ework-web/.env
111
+ info "WORK_TOKEN=$WORK_TOKEN"
112
+
113
+ info "launch ework-web in background"
114
+ cd /tmp/aio/ework-web
115
+ set -a; source .env; set +a
116
+ bun "$(npm root -g)/ework-web/bin/ework-web.js" \
117
+ > /tmp/aio/ework-web.log 2>&1 &
118
+ EWEB_PID=$!
119
+ info "ework-web pid=$EWEB_PID"
120
+
121
+ cleanup() { kill $EWEB_PID ${EDAEMON_PID:-} 2>/dev/null || true; }
122
+ trap cleanup EXIT
123
+
124
+ info "wait for ework-web /login"
125
+ for i in $(seq 1 60); do
126
+ if curl -sf -o /dev/null "http://127.0.0.1:$WORK_PORT/login"; then
127
+ pass "/login responds (after ${i} half-seconds)"
128
+ break
129
+ fi
130
+ sleep 0.5
131
+ [[ $i -eq 60 ]] && { tail -50 /tmp/aio/ework-web.log; fail "ework-web did not come up"; }
132
+ done
133
+
134
+ info "build auth cookie"
135
+ COOKIE_SIG=$(printf '%s' "$WORK_TOKEN" \
136
+ | openssl dgst -sha256 -hmac "$WORK_COOKIE_SECRET" -binary \
137
+ | base64 | tr '+/' '-_' | tr -d '=')
138
+ AUTH_COOKIE="ework_auth=${WORK_TOKEN}.${COOKIE_SIG}"
139
+
140
+ info "create bot user"
141
+ BOT_PW="$(openssl rand -hex 24)"
142
+ CREATE_CODE=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
143
+ "http://127.0.0.1:$WORK_PORT/admin/users/create" \
144
+ -H "Cookie: $AUTH_COOKIE" \
145
+ --data-urlencode "login=$BOT_NAME" \
146
+ --data-urlencode "password=$BOT_PW" \
147
+ --data-urlencode "kind=bot" \
148
+ --data-urlencode "is_admin=0")
149
+ [[ "$CREATE_CODE" == "303" || "$CREATE_CODE" == "400" || "$CREATE_CODE" == "409" ]] \
150
+ || fail "bot user create failed: HTTP $CREATE_CODE"
151
+ pass "bot user create returned $CREATE_CODE"
152
+
153
+ info "login as bot + mint PAT"
154
+ COOKIE_JAR=$(mktemp)
155
+ LOGIN_CODE=$(curl -sS -c "$COOKIE_JAR" -X POST "http://127.0.0.1:$WORK_PORT/login" \
156
+ --data-urlencode "login=$BOT_NAME" \
157
+ --data-urlencode "password=$BOT_PW" \
158
+ -o /dev/null -w '%{http_code}')
159
+ BOT_COOKIE=$(awk '/ework_auth/ {print $7}' "$COOKIE_JAR")
160
+ rm -f "$COOKIE_JAR"
161
+ [[ "$LOGIN_CODE" == "302" && -n "$BOT_COOKIE" ]] || fail "bot login failed: HTTP $LOGIN_CODE"
162
+ PAT_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/me/tokens/create" \
163
+ -H "Cookie: ework_auth=$BOT_COOKIE" \
164
+ --data-urlencode "name=aio-regression")
165
+ BOT_TOKEN=$(printf '%s' "$PAT_RES" | grep -oE 'id="t">[a-f0-9]{40}<' | grep -oE '[a-f0-9]{40}' | head -1 || true)
166
+ [[ -n "$BOT_TOKEN" ]] || fail "could not extract PAT from token-create page"
167
+ pass "bot PAT minted (${#BOT_TOKEN} chars)"
168
+
169
+ info "write daemon .env + launch"
170
+ cat > /tmp/aio/ework-daemon/.env <<EOF
171
+ DAEMON_ENV=production
172
+ DAEMON_PORT=$DAEMON_PORT
173
+ DAEMON_HOST=127.0.0.1
174
+ DAEMON_DB_PATH=/tmp/aio/ework-daemon/ework-daemon.db
175
+ GITEA_URL=http://127.0.0.1:$WORK_PORT
176
+ GITEA_TOKEN=$BOT_TOKEN
177
+ GITEA_WEBHOOK_SECRET=$WEBHOOK_SECRET
178
+ BOT_USERNAME=$BOT_NAME
179
+ BOT_TOKEN=$BOT_TOKEN
180
+ OPENCODE_BINARY=$(command -v opencode)
181
+ OPENCODE_BASE_WORKDIR=/tmp/aio/opencode-workdir
182
+ EOF
183
+ chmod 600 /tmp/aio/ework-daemon/.env
184
+
185
+ cd /tmp/aio/ework-daemon
186
+ set -a; source .env; set +a
187
+ bun "$(npm root -g)/ework-daemon/bin/ework-daemon-server.js" \
188
+ > /tmp/aio/ework-daemon.log 2>&1 &
189
+ EDAEMON_PID=$!
190
+ info "ework-daemon pid=$EDAEMON_PID"
191
+
192
+ info "wait for daemon /api/status"
193
+ for i in $(seq 1 60); do
194
+ if curl -sf -o /dev/null "http://127.0.0.1:$DAEMON_PORT/api/status"; then
195
+ pass "/api/status responds (after ${i} half-seconds)"
196
+ break
197
+ fi
198
+ sleep 0.5
199
+ [[ $i -eq 60 ]] && { tail -30 /tmp/aio/ework-daemon.log; fail "daemon did not come up"; }
200
+ done
201
+
202
+ info "daemon status:"
203
+ curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -c '{env,daemon,db,running,issues}'
204
+
205
+ info "verify opencode-ework plugin loadable from node_modules"
206
+ PLUGIN_PATH="$(npm root -g)/ework-aio/node_modules/opencode-ework/src/index.ts"
207
+ [[ -f "$PLUGIN_PATH" ]] || PLUGIN_PATH="$(npm root -g)/opencode-ework/src/index.ts"
208
+ [[ -f "$PLUGIN_PATH" ]] || fail "opencode-ework/src/index.ts not found"
209
+ pass "plugin source at $PLUGIN_PATH"
210
+
211
+ info "create test project + issue to exercise end-to-end API"
212
+ PROJ_RES=$(curl -sS -X POST "http://127.0.0.1:$WORK_PORT/projects" \
213
+ -H "Cookie: $AUTH_COOKIE" \
214
+ --data-urlencode 'owner=regression' \
215
+ --data-urlencode 'name=test' \
216
+ -o /dev/null -w '%{http_code}')
217
+ [[ "$PROJ_RES" == "303" || "$PROJ_RES" == "302" ]] || fail "project create failed: HTTP $PROJ_RES"
218
+ pass "project create -> $PROJ_RES"
219
+
220
+ info "issue POST (no -L) to capture redirect:"
221
+ ISSUE_HEAD=$(curl -sS -i -X POST "http://127.0.0.1:$WORK_PORT/regression/test/issues" \
222
+ -H "Cookie: $AUTH_COOKIE" \
223
+ --data-urlencode 'title=regression test' \
224
+ --data-urlencode 'body=verify npm-installed stack works')
225
+ ISSUE_LOCATION=$(printf '%s\n' "$ISSUE_HEAD" | grep -i '^Location:' | awk '{print $2}' | tr -d '\r\n')
226
+ info "issue create redirect -> $ISSUE_LOCATION"
227
+
228
+ ISSUE_RES=$(printf '%s\n' "$ISSUE_HEAD" | head -1 | awk '{print $2}')
229
+ [[ "$ISSUE_RES" == "303" ]] || fail "issue create failed: HTTP $ISSUE_RES"
230
+ pass "issue create -> $ISSUE_RES"
231
+
232
+ info "GET issue page via Location:"
233
+ ISSUE_VIEW_CODE=$(curl -sS -o /dev/null -w '%{http_code}' "$ISSUE_LOCATION" -H "Cookie: $AUTH_COOKIE")
234
+ [[ "$ISSUE_VIEW_CODE" == "200" ]] || fail "issue view GET failed: HTTP $ISSUE_VIEW_CODE"
235
+ pass "issue view GET -> $ISSUE_VIEW_CODE"
236
+
237
+ ISSUE_LIST=$(curl -sS "http://127.0.0.1:$WORK_PORT/regression/test/issues" \
238
+ -H "Cookie: $AUTH_COOKIE" -L | grep -oE 'issues/[0-9]+' | head -3)
239
+ info "found issue links: $ISSUE_LIST"
240
+
241
+ echo
242
+ echo "===== REGRESSION PASSED ====="
243
+ echo " ework-web: http://127.0.0.1:$WORK_PORT/login (token: $WORK_TOKEN)"
244
+ echo " ework-daemon: http://127.0.0.1:$DAEMON_PORT/api/status"
245
+ echo " logs: /tmp/aio/ework-web.log, /tmp/aio/ework-daemon.log"
246
+ EOSCRIPT
247
+
248
+ echo
249
+ echo "${c_grn}REGRESSION COMPLETE${c_rst}"