ework-aio 0.2.6 → 0.2.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-aio",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
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",
@@ -41,6 +41,8 @@
41
41
  "start": "bun bin/ework-aio.js",
42
42
  "check": "tsc --noEmit",
43
43
  "test": "bun test",
44
+ "test:e2e": "bash scripts/e2e-install.sh",
45
+ "test:regression": "bash scripts/regression.sh",
44
46
  "postinstall": "echo '\\n ework-aio installed. Run \\033[1mework-aio install\\033[0m to set up services.\\n Help: ework-aio --help\\n'"
45
47
  },
46
48
  "dependencies": {
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env bash
2
+ # End-to-end install test.
3
+ #
4
+ # Spawns a clean debian container (via Dockerfile.regression) and runs the
5
+ # FULL `ework-aio install` flow — not a hand-rolled .env like the older
6
+ # regression.sh. This is the test that would have caught every bug we
7
+ # shipped between v0.2.0 and v0.2.6:
8
+ #
9
+ # - v0.2.4 plugin key fix → caught by issue-create → daemon receives webhook
10
+ # - v0.2.5 daemon binary fix → caught by /api/status probe (was printing help)
11
+ # - v0.2.6 webhook secret → caught by "no invalid signature in daemon.log"
12
+ # - v0.2.6 access log → caught by "no EACCES in web.log"
13
+ #
14
+ # Usage: ./scripts/e2e-install.sh [container-runtime] [npm-tag]
15
+ # container-runtime: docker (default) | podman
16
+ # npm-tag: latest (default) | 0.2.6 | file:.. (for local builds)
17
+
18
+ set -euo pipefail
19
+
20
+ RUNTIME="${1:-docker}"
21
+ NPM_TAG="${2:-latest}"
22
+ IMAGE="${IMAGE:-ework-aio:e2e}"
23
+
24
+ c_grn=$'\033[32m'; c_red=$'\033[31m'; c_ylw=$'\033[33m'; c_rst=$'\033[0m'
25
+ pass() { printf '%sPASS%s %s\n' "$c_grn" "$c_rst" "$*"; }
26
+ fail() { printf '%sFAIL%s %s\n' "$c_red" "$c_rst" "$*"; exit 1; }
27
+ info() { printf '%s…%s %s\n' "$c_ylw" "$c_rst" "$*" >&2; }
28
+
29
+ if ! "$RUNTIME" image inspect "$IMAGE" >/dev/null 2>&1; then
30
+ info "Building $IMAGE"
31
+ # Build context = repo root (this script's parent dir + ..). Lets us invoke
32
+ # the script from anywhere (npm run test:e2e, CI, etc.) without cwd issues.
33
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
34
+ "$RUNTIME" build --network=host -f "$REPO_ROOT/Dockerfile.regression" \
35
+ -t "$IMAGE" "$REPO_ROOT"
36
+ fi
37
+
38
+ info "Using image: $IMAGE (testing npm tag: $NPM_TAG)"
39
+
40
+ # Unique ports so we don't collide with a host-side ework stack when run with
41
+ # --network host. Override via env if needed.
42
+ WORK_PORT="${WORK_PORT:-14002}"
43
+ DAEMON_PORT="${DAEMON_PORT:-14101}"
44
+
45
+ # opencode binary is host-specific (path may differ across machines). Mount
46
+ # readonly so the container preflight sees it on PATH. Override via env.
47
+ OPENCODE_HOST_BIN="${OPENCODE_HOST_BIN:-/home/dog/.local/bin/opencode}"
48
+
49
+ "$RUNTIME" run --rm -i --network host \
50
+ -v "$OPENCODE_HOST_BIN:/usr/local/bin/opencode:ro" \
51
+ -e WORK_PORT="$WORK_PORT" \
52
+ -e DAEMON_PORT="$DAEMON_PORT" \
53
+ -e NPM_TAG="$NPM_TAG" \
54
+ -e HTTP_PROXY="${HTTP_PROXY:-}" \
55
+ -e HTTPS_PROXY="${HTTPS_PROXY:-}" \
56
+ -e http_proxy="${HTTP_PROXY:-}" \
57
+ -e https_proxy="${HTTPS_PROXY:-}" \
58
+ -e NO_PROXY="127.0.0.1,localhost" \
59
+ "$IMAGE" bash -euo pipefail <<'EOSCRIPT'
60
+ set -euo pipefail
61
+
62
+ c_grn=$'\033[32m'; c_red=$'\033[31m'; c_ylw=$'\033[33m'; c_rst=$'\033[0m'
63
+ pass() { printf '%sPASS%s %s\n' "$c_grn" "$c_rst" "$*"; }
64
+ fail() { printf '%sFAIL%s %s\n' "$c_red" "$c_rst" "$*"; exit 1; }
65
+ info() { printf '%s…%s %s\n' "$c_ylw" "$c_rst" "$*" >&2; }
66
+
67
+ WORK_PORT="${WORK_PORT:-14002}"
68
+ DAEMON_PORT="${DAEMON_PORT:-14101}"
69
+ NPM_TAG="${NPM_TAG:-latest}"
70
+ DATA_DIR=/tmp/aio-e2e
71
+
72
+ info "clean state"
73
+ rm -rf "$DATA_DIR"
74
+ mkdir -p "$DATA_DIR"
75
+
76
+ info "opencode binary on PATH"
77
+ opencode --version | head -1
78
+
79
+ info "npm install -g ework-web@${NPM_TAG} ework-daemon@latest ework-aio@${NPM_TAG}"
80
+ # Note: ework-web and ework-daemon aren't version-locked to ework-aio. We
81
+ # always install latest of the services and only parameterize ework-aio so
82
+ # we can E2E-test a PR branch against published services.
83
+ for pkg in ework-web ework-daemon opencode-ework; do
84
+ npm install -g "$pkg@latest" 2>&1 | tail -2
85
+ done
86
+ npm install -g "ework-aio@${NPM_TAG}" 2>&1 | tail -2
87
+
88
+ info "v0.2.5 regression: all 4 bins on PATH"
89
+ for b in ework-aio ework-web ework-daemon ework-daemon-server; do
90
+ command -v "$b" >/dev/null 2>&1 || fail "$b not on PATH"
91
+ pass "$b -> $(command -v "$b")"
92
+ done
93
+
94
+ info "run ework-aio install (the real installer — no hand-rolled .env)"
95
+ # --allow-root: container runs as root; refusing would test an unrealistic
96
+ # path. The installer's data still lands in /tmp/aio-e2e via --data-dir.
97
+ ework-aio install \
98
+ --allow-root \
99
+ --data-dir "$DATA_DIR" \
100
+ --port "$WORK_PORT" \
101
+ --daemon-port "$DAEMON_PORT" \
102
+ --bot-name e2e-bot \
103
+ --yes 2>&1 | tee /tmp/install.log
104
+
105
+ pass "install exited 0"
106
+
107
+ info "v0.2.6 regression: webhook secrets match across web and daemon .env"
108
+ WEB_SEC=$(grep ^WORK_DAEMON_WEBHOOK_SECRET= "$DATA_DIR/ework-web/.env" | cut -d= -f2-)
109
+ DAE_SEC=$(grep ^GITEA_WEBHOOK_SECRET= "$DATA_DIR/ework-daemon/.env" | cut -d= -f2-)
110
+ [[ -n "$WEB_SEC" ]] || fail "WORK_DAEMON_WEBHOOK_SECRET empty in web .env"
111
+ [[ -n "$DAE_SEC" ]] || fail "GITEA_WEBHOOK_SECRET empty in daemon .env"
112
+ [[ "$WEB_SEC" == "$DAE_SEC" ]] \
113
+ || fail "webhook secrets differ (web=$WEB_SEC daemon=$DAE_SEC)"
114
+ pass "secrets match (${#WEB_SEC} chars)"
115
+
116
+ info "v0.2.6 regression: WORK_ACCESS_LOG set, not defaulting to /tmp/ework-access.log"
117
+ ACCESS_LOG_VAL=$(grep ^WORK_ACCESS_LOG= "$DATA_DIR/ework-web/.env" | cut -d= -f2-)
118
+ [[ -n "$ACCESS_LOG_VAL" ]] || fail "WORK_ACCESS_LOG missing from web .env"
119
+ [[ "$ACCESS_LOG_VAL" != "/tmp/ework-access.log" ]] \
120
+ || fail "WORK_ACCESS_LOG still defaults to /tmp/ework-access.log"
121
+ pass "WORK_ACCESS_LOG=$ACCESS_LOG_VAL"
122
+
123
+ info "wait for ework-web /login"
124
+ for i in $(seq 1 60); do
125
+ if curl -sf -o /dev/null "http://127.0.0.1:$WORK_PORT/login"; then
126
+ pass "/login responds (after ${i} half-seconds)"
127
+ break
128
+ fi
129
+ sleep 0.5
130
+ [[ $i -eq 60 ]] && { tail -30 "$DATA_DIR/run/web.log" 2>/dev/null; fail "ework-web did not come up"; }
131
+ done
132
+
133
+ info "wait for daemon /api/status"
134
+ for i in $(seq 1 60); do
135
+ if curl -sf -o /dev/null "http://127.0.0.1:$DAEMON_PORT/api/status"; then
136
+ pass "/api/status responds (after ${i} half-seconds)"
137
+ break
138
+ fi
139
+ sleep 0.5
140
+ [[ $i -eq 60 ]] && { tail -30 "$DATA_DIR/run/daemon.log" 2>/dev/null; fail "daemon did not come up"; }
141
+ done
142
+
143
+ info "v0.2.6 regression: no EACCES in web.log"
144
+ sleep 1
145
+ if grep -q "EACCES" "$DATA_DIR/run/web.log" 2>/dev/null; then
146
+ fail "EACCES in web.log:"
147
+ grep EACCES "$DATA_DIR/run/web.log" | head -3
148
+ fi
149
+ pass "no EACCES in web.log"
150
+
151
+ info "v0.2.6 regression: no invalid signature in daemon.log (pre-issue-create)"
152
+ if grep -q "invalid signature" "$DATA_DIR/run/daemon.log" 2>/dev/null; then
153
+ fail "invalid signature BEFORE issue create — autoWire webhook must have fired"
154
+ grep "invalid signature" "$DATA_DIR/run/daemon.log" | head -3
155
+ fi
156
+ pass "no invalid signature yet"
157
+
158
+ info "build admin auth cookie"
159
+ WORK_TOKEN=$(grep ^WORK_TOKEN= "$DATA_DIR/ework-web/.env" | cut -d= -f2-)
160
+ WORK_COOKIE_SECRET=$(grep ^WORK_COOKIE_SECRET= "$DATA_DIR/ework-web/.env" | cut -d= -f2-)
161
+ COOKIE_SIG=$(printf '%s' "$WORK_TOKEN" \
162
+ | openssl dgst -sha256 -hmac "$WORK_COOKIE_SECRET" -binary \
163
+ | base64 | tr '+/' '-_' | tr -d '=')
164
+ AUTH_COOKIE="ework_auth=${WORK_TOKEN}.${COOKIE_SIG}"
165
+
166
+ info "create test project (triggers autoWire webhook registration)"
167
+ PROJ_CODE=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
168
+ "http://127.0.0.1:$WORK_PORT/projects" \
169
+ -H "Cookie: $AUTH_COOKIE" \
170
+ --data-urlencode 'owner=e2e' \
171
+ --data-urlencode 'name=test')
172
+ [[ "$PROJ_CODE" == "303" || "$PROJ_CODE" == "302" ]] \
173
+ || fail "project create failed: HTTP $PROJ_CODE"
174
+ pass "project create -> $PROJ_CODE"
175
+
176
+ info "create issue (triggers webhook POST to daemon)"
177
+ ISSUE_HEAD=$(curl -sS -i -X POST \
178
+ "http://127.0.0.1:$WORK_PORT/e2e/test/issues" \
179
+ -H "Cookie: $AUTH_COOKIE" \
180
+ --data-urlencode 'title=e2e test' \
181
+ --data-urlencode 'body=trigger webhook')
182
+ ISSUE_STATUS=$(printf '%s\n' "$ISSUE_HEAD" | head -1 | awk '{print $2}')
183
+ [[ "$ISSUE_STATUS" == "303" ]] \
184
+ || fail "issue create did not return 303: $ISSUE_STATUS"
185
+ pass "issue create -> 303"
186
+
187
+ info "wait for webhook delivery + daemon processing"
188
+ sleep 5
189
+
190
+ info "v0.2.6 regression: webhook was accepted (no invalid signature after issue create)"
191
+ if grep -q "invalid signature" "$DATA_DIR/run/daemon.log" 2>/dev/null; then
192
+ fail "invalid signature AFTER issue create — webhook secret mismatch"
193
+ grep "invalid signature" "$DATA_DIR/run/daemon.log" | head -5
194
+ fi
195
+ pass "no invalid signature after issue create"
196
+
197
+ info "daemon received the issue event"
198
+ ACTIVE=$(curl -sS "http://127.0.0.1:$DAEMON_PORT/api/status" | jq -r '.issues // 0')
199
+ [[ "$ACTIVE" -ge 1 ]] \
200
+ || fail "daemon did not register the issue (issues=$ACTIVE)"
201
+ pass "daemon registered issue (issues=$ACTIVE)"
202
+
203
+ echo
204
+ echo "===== E2E PASSED ====="
205
+ echo " ework-aio version: $(ework-aio --version 2>/dev/null || echo unknown)"
206
+ echo " data dir: $DATA_DIR"
207
+ echo " web: http://127.0.0.1:$WORK_PORT/login"
208
+ echo " daemon: http://127.0.0.1:$DAEMON_PORT/api/status"
209
+ echo " logs: $DATA_DIR/run/{web,daemon}.log"
210
+ EOSCRIPT
211
+
212
+ echo
213
+ echo "${c_grn}E2E COMPLETE${c_rst}"
package/src/cli.ts CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  type ConfigArgs,
36
36
  } from "./commands/config.ts";
37
37
 
38
- const VERSION = "0.2.6-dev";
38
+ const VERSION = "0.2.7-dev";
39
39
 
40
40
  const USAGE = `ework-aio <command> [options]
41
41