ework-aio 0.1.7 → 0.1.10
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/README.md +46 -1
- package/bin/ework-aio +378 -3
- package/bin/install.sh +48 -1
- package/package.json +2 -1
- package/scripts/backfill-timestamps.ts +225 -0
- package/scripts/migrate-from-gitea.ts +640 -0
- package/scripts/regression.sh +249 -0
|
@@ -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}"
|