@osfactory/har 0.4.0 → 0.6.0
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/dist/index.js +7 -4
- package/dist/templates/adaptation-prompt-init.md +42 -3
- package/dist/templates/adaptation-prompt-maintain.md +2 -2
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +5 -3
- package/dist/templates/har-boilerplate/README.md +17 -7
- package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +2 -0
- package/dist/templates/har-boilerplate/env.template +3 -0
- package/dist/templates/har-boilerplate/harness.env +5 -0
- package/dist/templates/har-boilerplate/launch.sh +9 -15
- package/dist/templates/har-boilerplate/provision-toolchain.sh +285 -0
- package/dist/templates/har-boilerplate/verify.sh +92 -6
- package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +5 -2
- package/dist/templates/har-boilerplate-cli/README.md +17 -7
- package/dist/templates/har-boilerplate-cli/agent-cli.sh +1 -1
- package/dist/templates/har-boilerplate-cli/harness.env +6 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +8 -13
- package/dist/templates/har-boilerplate-cli/provision-toolchain.sh +285 -0
- package/dist/templates/har-boilerplate-cli/verify.sh +95 -7
- package/dist/templates/har-boilerplate-ios/README.md +11 -7
- package/dist/templates/har-boilerplate-ios/harness.env +4 -0
- package/dist/templates/har-boilerplate-ios/launch.sh +8 -13
- package/dist/templates/har-boilerplate-ios/provision-toolchain.sh +285 -0
- package/dist/templates/har-boilerplate-ios/verify.sh +27 -26
- package/package.json +1 -1
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
# Outputs JSON to stdout, human-readable progress to stderr.
|
|
4
4
|
#
|
|
5
5
|
# Usage: ./.har/verify.sh <agent-id> [--full]
|
|
6
|
+
#
|
|
7
|
+
# Quick (default): ecosystem smoke + health only
|
|
8
|
+
# Full (--full): + conventional tests, lint, optional readiness + browser-e2e
|
|
9
|
+
# Stock steps are examples. Replace them during adaptation to match this repo.
|
|
6
10
|
set -euo pipefail
|
|
7
11
|
|
|
8
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -132,16 +136,98 @@ process.stdout.write(JSON.stringify(arr));
|
|
|
132
136
|
fi
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
node_package_script_exists() {
|
|
140
|
+
local script="$1"
|
|
141
|
+
"${NODE_BIN:-node}" -e "
|
|
142
|
+
const fs = require('fs');
|
|
143
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
144
|
+
const script = process.argv[1];
|
|
145
|
+
process.exit(pkg.scripts && pkg.scripts[script] ? 0 : 1);
|
|
146
|
+
" "$script"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
run_node_script_or_skip() {
|
|
150
|
+
local name="$1"
|
|
151
|
+
local script="$2"
|
|
152
|
+
if node_package_script_exists "$script"; then
|
|
153
|
+
run_step "$name" "${NPM_BIN:-npm} run ${script}"
|
|
154
|
+
else
|
|
155
|
+
run_step "$name" "echo 'No ${script} script configured; skipping.'"
|
|
156
|
+
fi
|
|
157
|
+
}
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
159
|
+
run_node_quick_smoke() {
|
|
160
|
+
if node_package_script_exists typecheck; then
|
|
161
|
+
run_step "node-typecheck" '${NPM_BIN:-npm} run typecheck'
|
|
162
|
+
elif node_package_script_exists build; then
|
|
163
|
+
run_step "node-build" '${NPM_BIN:-npm} run build'
|
|
164
|
+
else
|
|
165
|
+
run_step "node-package-load" '${NODE_BIN:-node} -e "require(\"./package.json\")"'
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
run_quick_smoke() {
|
|
170
|
+
case "${HARNESS_ECOSYSTEM:-none}" in
|
|
171
|
+
node)
|
|
172
|
+
run_node_quick_smoke
|
|
173
|
+
;;
|
|
174
|
+
python)
|
|
175
|
+
run_step "python-compile" '${PYTHON_BIN:-python3} -m compileall -q .'
|
|
176
|
+
;;
|
|
177
|
+
go)
|
|
178
|
+
run_step "go-build" '${GO_BIN:-go} build ./...'
|
|
179
|
+
;;
|
|
180
|
+
rust)
|
|
181
|
+
run_step "rust-check" '${CARGO_BIN:-cargo} check'
|
|
182
|
+
;;
|
|
183
|
+
java)
|
|
184
|
+
run_step "java-compile" 'if [ -x ./mvnw ]; then ./mvnw -q -DskipTests compile; elif command -v mvn >/dev/null 2>&1; then mvn -q -DskipTests compile; elif [ -x ./gradlew ]; then ./gradlew classes; elif command -v gradle >/dev/null 2>&1; then gradle classes; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi'
|
|
185
|
+
;;
|
|
186
|
+
ruby)
|
|
187
|
+
run_step "ruby-smoke" '${RUBY_BIN:-ruby} -e "puts RUBY_VERSION"'
|
|
188
|
+
;;
|
|
189
|
+
custom|none|*)
|
|
190
|
+
run_step "smoke-not-configured" 'echo "No stock smoke for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."'
|
|
191
|
+
;;
|
|
192
|
+
esac
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
run_full_checks() {
|
|
196
|
+
case "${HARNESS_ECOSYSTEM:-none}" in
|
|
197
|
+
node)
|
|
198
|
+
run_node_script_or_skip "unit-tests" test || true
|
|
199
|
+
run_node_script_or_skip "lint" lint || true
|
|
200
|
+
;;
|
|
201
|
+
python)
|
|
202
|
+
run_step "unit-tests" 'if ${PYTHON_BIN:-python3} -c "import pytest" >/dev/null 2>&1; then ${PYTHON_BIN:-python3} -m pytest -q; else echo "pytest not installed; adapt verify.sh for this Python repo."; fi' || true
|
|
203
|
+
;;
|
|
204
|
+
go)
|
|
205
|
+
run_step "unit-tests" '${GO_BIN:-go} test ./...' || true
|
|
206
|
+
;;
|
|
207
|
+
rust)
|
|
208
|
+
run_step "unit-tests" '${CARGO_BIN:-cargo} test' || true
|
|
209
|
+
;;
|
|
210
|
+
java)
|
|
211
|
+
run_step "unit-tests" 'if [ -x ./mvnw ]; then ./mvnw -q test; elif command -v mvn >/dev/null 2>&1; then mvn -q test; elif [ -x ./gradlew ]; then ./gradlew test; elif command -v gradle >/dev/null 2>&1; then gradle test; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi' || true
|
|
212
|
+
;;
|
|
213
|
+
ruby)
|
|
214
|
+
run_step "unit-tests" 'if command -v "${BUNDLE_BIN:-bundle}" >/dev/null 2>&1 && [ -f Gemfile ]; then "${BUNDLE_BIN:-bundle}" exec rake test 2>/dev/null || "${BUNDLE_BIN:-bundle}" exec rspec; else echo "No Ruby test command detected; adapt verify.sh for this Ruby repo."; fi' || true
|
|
215
|
+
;;
|
|
216
|
+
custom|none|*)
|
|
217
|
+
run_step "unit-tests" 'echo "No stock full checks for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."' || true
|
|
218
|
+
;;
|
|
219
|
+
esac
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# ── Verification stages ─────────────────────────────────────────────────────
|
|
223
|
+
# These stock steps are intentionally generic conventions. Adapt this section
|
|
224
|
+
# to the repository's real commands from package.json, Makefile, CI, pyproject,
|
|
225
|
+
# Cargo.toml, go.mod, pom.xml, etc.
|
|
226
|
+
run_quick_smoke || { [ -z "$FULL" ] && true; }
|
|
141
227
|
run_http_step "api-health" "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_PATH}" || { [ -z "$FULL" ] && true; }
|
|
142
228
|
|
|
143
229
|
if [ -n "$FULL" ]; then
|
|
144
|
-
|
|
230
|
+
run_full_checks
|
|
145
231
|
run_step "readiness" "run_readiness_if_configured \"$AGENT_ID\"" || true
|
|
146
232
|
run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
|
|
147
233
|
fi
|
|
@@ -36,9 +36,12 @@ Quick loop: MCP `har_run_verification`, `har env verify ${AGENT_ID}`, or `./.har
|
|
|
36
36
|
## Project commands
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
# TODO: adapt for this repository
|
|
40
|
-
# npm run typecheck
|
|
39
|
+
# TODO: adapt for this repository. Examples:
|
|
41
40
|
# npm test
|
|
41
|
+
# pytest -q
|
|
42
|
+
# go test ./...
|
|
43
|
+
# cargo test
|
|
44
|
+
# make test
|
|
42
45
|
```
|
|
43
46
|
|
|
44
47
|
## Do not
|
|
@@ -12,15 +12,16 @@ Generated and maintained by [`har`](https://github.com/antoineFrau/har). Run `ha
|
|
|
12
12
|
|------|---------|
|
|
13
13
|
| `README.md` | This file — index of the harness |
|
|
14
14
|
| `manifest.json` | Generator metadata (version, profile, checksums) — do not edit |
|
|
15
|
-
| `harness.env` | Shared config: worktree default, `HARNESS_INFRA_SERVICES`, migrate/seed commands |
|
|
15
|
+
| `harness.env` | Shared config: worktree default, `HARNESS_INFRA_SERVICES`, toolchain provisioning (`HARNESS_ECOSYSTEM`, `HARNESS_INSTALL_CMD`), migrate/seed commands |
|
|
16
16
|
| `stages.json` | Machine-readable registry of runnable harness stages |
|
|
17
17
|
| `stages/` | Optional custom stage scripts registered from `stages.json` |
|
|
18
18
|
| `runs/` | Run history from `har env` / MCP only — `.har/runs/YYYY-MM-DD/HH-mm-ss_<stageId>_agent-<id>.json` (gitignore) |
|
|
19
19
|
| `artifacts/` | Stage outputs: reports, traces, screenshots, logs |
|
|
20
20
|
| `agent-slot.sh` | Shared agent-id validation (reads limits from `harness.env`) |
|
|
21
21
|
| `setup-infra.sh` | Start optional Docker Compose stack + template database |
|
|
22
|
-
| `launch.sh` | Launch one agent slot (git worktree by default,
|
|
23
|
-
| `
|
|
22
|
+
| `launch.sh` | Launch one agent slot (git worktree by default, toolchain provisioning, env file) |
|
|
23
|
+
| `provision-toolchain.sh` | Install deps and write toolchain paths (`PYTHON_BIN`, `NPM_BIN`, …) to `.env.agent.<id>` |
|
|
24
|
+
| `verify.sh` | Verification pipeline (smoke by default; --full adds tests, lint, e2e) |
|
|
24
25
|
| `teardown.sh` | Tear down one agent slot (worktree + env file) |
|
|
25
26
|
| `agent-cli.sh` | Inspect slot status, run commands in the work dir |
|
|
26
27
|
| `docker-compose.agent.yml` | Shared infrastructure containers (services listed in `HARNESS_INFRA_SERVICES`) |
|
|
@@ -47,8 +48,8 @@ In Cursor with HAR MCP configured: use `har_launch_environment`, `har_run_verifi
|
|
|
47
48
|
```bash
|
|
48
49
|
./.har/setup-infra.sh # when HARNESS_INFRA_SERVICES is non-empty
|
|
49
50
|
./.har/launch.sh 1
|
|
50
|
-
./.har/verify.sh 1
|
|
51
|
-
./.har/verify.sh 1 --full # +
|
|
51
|
+
./.har/verify.sh 1 # quick: ecosystem smoke (compile/import/build)
|
|
52
|
+
./.har/verify.sh 1 --full # + conventional tests, lint, browser-e2e (if installed)
|
|
52
53
|
./.har/teardown.sh 1
|
|
53
54
|
```
|
|
54
55
|
|
|
@@ -56,10 +57,19 @@ Read **`stages.json`** and **`verificationStages`**. Optional: `har env add-stag
|
|
|
56
57
|
|
|
57
58
|
## Verification contract
|
|
58
59
|
|
|
60
|
+
Steps in `verify.sh` are **project-specific examples** — adapt them to your stack
|
|
61
|
+
during `har env init` / `har env maintain` / benchmark setup. The table describes
|
|
62
|
+
each tier's intent, not a fixed command list.
|
|
63
|
+
|
|
59
64
|
| Mode | Command | Typical steps |
|
|
60
65
|
|------|---------|---------------|
|
|
61
|
-
| Quick | `har env verify <id>` or `verify.sh <id>` |
|
|
62
|
-
| Full | `har env verify <id> --full` or `verify.sh <id> --full` |
|
|
66
|
+
| Quick | `har env verify <id>` or `verify.sh <id>` | Stock ecosystem smoke: compile / import / build conventions |
|
|
67
|
+
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | Stock conventional tests/lint + optional readiness smoke, **browser-e2e** when `stages/browser-e2e.sh` exists |
|
|
68
|
+
|
|
69
|
+
The stock commands are deliberately generic conventions keyed by
|
|
70
|
+
`HARNESS_ECOSYSTEM`. Replace them with the repository's real commands during
|
|
71
|
+
adaptation; do not leave Node/npm, Python, Go, Rust, Java, or Ruby defaults in
|
|
72
|
+
place when they do not match the project.
|
|
63
73
|
|
|
64
74
|
For repos that need runtime services, distinguish health from usability. If the
|
|
65
75
|
harness skips slow local-dev setup, document the skipped steps and add a minimal
|
|
@@ -61,7 +61,7 @@ case "$COMMAND" in
|
|
|
61
61
|
logs)
|
|
62
62
|
echo "CLI profile has no managed processes (no PM2)." >&2
|
|
63
63
|
echo "Run project commands in the work dir, e.g.:" >&2
|
|
64
|
-
echo " ./.har/agent-cli.sh ${AGENT_ID} exec
|
|
64
|
+
echo " ./.har/agent-cli.sh ${AGENT_ID} exec make test" >&2
|
|
65
65
|
exit 1
|
|
66
66
|
;;
|
|
67
67
|
|
|
@@ -11,6 +11,12 @@ export HARNESS_HEALTH_CHECK_PATH=
|
|
|
11
11
|
export HARNESS_AGENT_SLOT_MIN=1
|
|
12
12
|
export HARNESS_AGENT_SLOT_MAX=3
|
|
13
13
|
|
|
14
|
+
# Toolchain provisioning — launch writes resolved paths into .env.agent.<id>
|
|
15
|
+
# Ecosystem: auto (detect from manifests) | node | python | go | rust | java | ruby | none
|
|
16
|
+
export HARNESS_ECOSYSTEM="auto"
|
|
17
|
+
# Optional install override (eval in work dir after env file is created)
|
|
18
|
+
# export HARNESS_INSTALL_CMD="make deps"
|
|
19
|
+
|
|
14
20
|
# Shared infrastructure — space-separated docker compose service names from
|
|
15
21
|
# docker-compose.agent.yml, started ONCE by setup-infra.sh and shared by all
|
|
16
22
|
# agent slots on fixed ports. Empty = no containers. Prune/add services in the
|
|
@@ -79,7 +79,7 @@ fi
|
|
|
79
79
|
# whose .gitignore doesn't know about har (applies to all worktrees too).
|
|
80
80
|
GIT_EXCLUDE="$(git -C "$REPO_ROOT" rev-parse --git-common-dir 2>/dev/null)/info/exclude"
|
|
81
81
|
if [ -n "$GIT_EXCLUDE" ] && [ -d "$(dirname "$GIT_EXCLUDE")" ]; then
|
|
82
|
-
for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs'; do
|
|
82
|
+
for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs' '.har/venv'; do
|
|
83
83
|
grep -qxF "$pattern" "$GIT_EXCLUDE" 2>/dev/null || echo "$pattern" >> "$GIT_EXCLUDE"
|
|
84
84
|
done
|
|
85
85
|
fi
|
|
@@ -134,18 +134,13 @@ SLOT_STATUS="starting" \
|
|
|
134
134
|
write_slot_registry
|
|
135
135
|
REGISTRY_WRITTEN=true
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# Monorepo: file:/workspace deps may resolve modules from the repo root — install there too
|
|
145
|
-
if [ -n "${REL_PREFIX:-}" ] && [ -f "$WORKTREE_DIR/package.json" ] && [ ! -d "$WORKTREE_DIR/node_modules" ]; then
|
|
146
|
-
log "Installing monorepo root dependencies in $WORKTREE_DIR..."
|
|
147
|
-
(cd "$WORKTREE_DIR" && npm install --silent)
|
|
148
|
-
fi
|
|
137
|
+
log "Provisioning toolchain (see harness.env: HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD)..."
|
|
138
|
+
HAR_WORK_DIR="$WORK_DIR" \
|
|
139
|
+
HAR_ENV_FILE="$ENV_FILE" \
|
|
140
|
+
HAR_WORKTREE_DIR="${WORKTREE_DIR:-}" \
|
|
141
|
+
HAR_REL_PREFIX="${REL_PREFIX:-}" \
|
|
142
|
+
HAR_AGENT_ID="$AGENT_ID" \
|
|
143
|
+
"$SCRIPT_DIR/provision-toolchain.sh"
|
|
149
144
|
|
|
150
145
|
# Record the session in the slot registry — the source of truth for where
|
|
151
146
|
# this slot's code lives (status/verify/teardown resolve through it).
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Provision the project toolchain and append resolved paths to .env.agent.<id>.
|
|
3
|
+
# Called from launch.sh after the agent env file is created.
|
|
4
|
+
#
|
|
5
|
+
# Configure in harness.env:
|
|
6
|
+
# HARNESS_ECOSYSTEM — auto (default) | node | python | go | rust | java | ruby | ios | none
|
|
7
|
+
# HARNESS_INSTALL_CMD — optional override (eval in HAR_WORK_DIR)
|
|
8
|
+
# Ecosystem-specific optional overrides:
|
|
9
|
+
# HARNESS_PYTHON_VENV_DIR — Python venv path relative to HAR_WORK_DIR (default: .har/venv)
|
|
10
|
+
#
|
|
11
|
+
# Required env from caller: HAR_WORK_DIR, HAR_ENV_FILE
|
|
12
|
+
# Optional: HAR_WORKTREE_DIR, HAR_REL_PREFIX, HAR_AGENT_ID (for logging)
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
: "${HAR_WORK_DIR:?HAR_WORK_DIR is required}"
|
|
16
|
+
: "${HAR_ENV_FILE:?HAR_ENV_FILE is required}"
|
|
17
|
+
|
|
18
|
+
HAR_WORKTREE_DIR="${HAR_WORKTREE_DIR:-}"
|
|
19
|
+
HAR_REL_PREFIX="${HAR_REL_PREFIX:-}"
|
|
20
|
+
HAR_AGENT_ID="${HAR_AGENT_ID:-}"
|
|
21
|
+
|
|
22
|
+
pt_log() {
|
|
23
|
+
if [ -n "$HAR_AGENT_ID" ]; then
|
|
24
|
+
echo "==> [agent-${HAR_AGENT_ID}] toolchain: $*" >&2
|
|
25
|
+
else
|
|
26
|
+
echo "==> [provision-toolchain] $*" >&2
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
append_env() {
|
|
31
|
+
local key="$1"
|
|
32
|
+
local value="$2"
|
|
33
|
+
printf '%s=%s\n' "$key" "$value" >> "$HAR_ENV_FILE"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
append_path_prefix() {
|
|
37
|
+
local prefix="$1"
|
|
38
|
+
[ -n "$prefix" ] && [ -d "$prefix" ] || return 0
|
|
39
|
+
append_env "PATH" "${prefix}:${PATH:-$PATH}"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
detect_ecosystem() {
|
|
43
|
+
local dir="$1"
|
|
44
|
+
local configured="${HARNESS_ECOSYSTEM:-auto}"
|
|
45
|
+
if [ -n "$configured" ] && [ "$configured" != "auto" ]; then
|
|
46
|
+
echo "$configured"
|
|
47
|
+
return
|
|
48
|
+
fi
|
|
49
|
+
if [ -f "$dir/package.json" ]; then echo node; return; fi
|
|
50
|
+
if [ -f "$dir/pyproject.toml" ] || [ -f "$dir/setup.py" ] || [ -f "$dir/setup.cfg" ] \
|
|
51
|
+
|| [ -f "$dir/requirements.txt" ] || [ -f "$dir/Pipfile" ]; then
|
|
52
|
+
echo python
|
|
53
|
+
return
|
|
54
|
+
fi
|
|
55
|
+
if [ -f "$dir/go.mod" ]; then echo go; return; fi
|
|
56
|
+
if [ -f "$dir/Cargo.toml" ]; then echo rust; return; fi
|
|
57
|
+
if [ -f "$dir/pom.xml" ] || [ -f "$dir/build.gradle" ] || [ -f "$dir/build.gradle.kts" ]; then
|
|
58
|
+
echo java
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
if [ -f "$dir/Gemfile" ]; then echo ruby; return; fi
|
|
62
|
+
if [ -n "${HARNESS_XCODE_SCHEME:-}" ] || [ -n "${HARNESS_XCODE_PROJECT:-}" ] \
|
|
63
|
+
|| [ -n "${HARNESS_XCODE_WORKSPACE:-}" ]; then
|
|
64
|
+
echo ios
|
|
65
|
+
return
|
|
66
|
+
fi
|
|
67
|
+
echo none
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
run_install_cmd() {
|
|
71
|
+
local dir="$1"
|
|
72
|
+
if [ -n "${HARNESS_INSTALL_CMD:-}" ]; then
|
|
73
|
+
pt_log "Running HARNESS_INSTALL_CMD..."
|
|
74
|
+
(cd "$dir" && eval "$HARNESS_INSTALL_CMD")
|
|
75
|
+
return
|
|
76
|
+
fi
|
|
77
|
+
return 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
provision_node() {
|
|
81
|
+
local dir="$1"
|
|
82
|
+
local npm_bin="npm"
|
|
83
|
+
local node_bin="node"
|
|
84
|
+
|
|
85
|
+
if ! run_install_cmd "$dir"; then
|
|
86
|
+
pt_log "Installing Node dependencies..."
|
|
87
|
+
if [ -f "$dir/pnpm-lock.yaml" ] && command -v pnpm >/dev/null 2>&1; then
|
|
88
|
+
(cd "$dir" && pnpm install --silent)
|
|
89
|
+
npm_bin="pnpm"
|
|
90
|
+
elif [ -f "$dir/yarn.lock" ] && command -v yarn >/dev/null 2>&1; then
|
|
91
|
+
(cd "$dir" && yarn install --silent)
|
|
92
|
+
npm_bin="yarn"
|
|
93
|
+
else
|
|
94
|
+
(cd "$dir" && npm install --silent)
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if command -v node >/dev/null 2>&1; then
|
|
99
|
+
node_bin="$(command -v node)"
|
|
100
|
+
fi
|
|
101
|
+
if [ "$npm_bin" = "npm" ] && command -v npm >/dev/null 2>&1; then
|
|
102
|
+
npm_bin="$(command -v npm)"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
append_env "HARNESS_ECOSYSTEM" "node"
|
|
106
|
+
append_env "NODE_BIN" "$node_bin"
|
|
107
|
+
append_env "NPM_BIN" "$npm_bin"
|
|
108
|
+
append_path_prefix "$dir/node_modules/.bin"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
provision_python() {
|
|
112
|
+
local dir="$1"
|
|
113
|
+
local venv_rel="${HARNESS_PYTHON_VENV_DIR:-.har/venv}"
|
|
114
|
+
local venv_dir="$dir/$venv_rel"
|
|
115
|
+
local python_bin="python3"
|
|
116
|
+
|
|
117
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
118
|
+
pt_log "python3 not found on PATH — skipping venv provisioning"
|
|
119
|
+
append_env "HARNESS_ECOSYSTEM" "python"
|
|
120
|
+
append_env "PYTHON_BIN" "${PYTHON_BIN:-python3}"
|
|
121
|
+
return
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [ ! -d "$venv_dir" ]; then
|
|
125
|
+
pt_log "Creating Python venv at $venv_rel..."
|
|
126
|
+
if ! python3 -m venv "$venv_dir"; then
|
|
127
|
+
rm -rf "$venv_dir"
|
|
128
|
+
pt_log "python3 -m venv failed (on Debian/Ubuntu install python3-venv) — using system python3"
|
|
129
|
+
append_env "HARNESS_ECOSYSTEM" "python"
|
|
130
|
+
append_env "PYTHON_BIN" "$(command -v python3)"
|
|
131
|
+
return
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if [ ! -x "$venv_dir/bin/python" ]; then
|
|
136
|
+
pt_log "Python venv at $venv_rel is missing or broken — using system python3"
|
|
137
|
+
append_env "HARNESS_ECOSYSTEM" "python"
|
|
138
|
+
append_env "PYTHON_BIN" "$(command -v python3)"
|
|
139
|
+
return
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
python_bin="$venv_dir/bin/python"
|
|
143
|
+
# shellcheck disable=SC1091
|
|
144
|
+
source "$venv_dir/bin/activate"
|
|
145
|
+
|
|
146
|
+
if ! run_install_cmd "$dir"; then
|
|
147
|
+
pt_log "Installing Python dependencies..."
|
|
148
|
+
if [ -f "$dir/pyproject.toml" ]; then
|
|
149
|
+
(cd "$dir" && pip install -q -e ".[dev]" 2>/dev/null) || (cd "$dir" && pip install -q -e .)
|
|
150
|
+
elif [ -f "$dir/requirements.txt" ]; then
|
|
151
|
+
(cd "$dir" && pip install -q -r requirements.txt)
|
|
152
|
+
elif [ -f "$dir/setup.py" ] || [ -f "$dir/setup.cfg" ]; then
|
|
153
|
+
(cd "$dir" && pip install -q -e .)
|
|
154
|
+
elif [ -f "$dir/Pipfile" ] && command -v pipenv >/dev/null 2>&1; then
|
|
155
|
+
(cd "$dir" && pipenv install --dev)
|
|
156
|
+
python_bin="$(cd "$dir" && pipenv --py)"
|
|
157
|
+
fi
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
append_env "HARNESS_ECOSYSTEM" "python"
|
|
161
|
+
append_env "PYTHON_BIN" "$python_bin"
|
|
162
|
+
append_env "VIRTUAL_ENV" "$venv_dir"
|
|
163
|
+
append_path_prefix "$venv_dir/bin"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
provision_go() {
|
|
167
|
+
local dir="$1"
|
|
168
|
+
if ! run_install_cmd "$dir"; then
|
|
169
|
+
if command -v go >/dev/null 2>&1; then
|
|
170
|
+
pt_log "Downloading Go modules..."
|
|
171
|
+
(cd "$dir" && go mod download)
|
|
172
|
+
else
|
|
173
|
+
pt_log "go not found on PATH — record paths only"
|
|
174
|
+
fi
|
|
175
|
+
fi
|
|
176
|
+
append_env "HARNESS_ECOSYSTEM" "go"
|
|
177
|
+
append_env "GO_BIN" "$(command -v go 2>/dev/null || echo go)"
|
|
178
|
+
if [ -n "${GOPATH:-}" ]; then append_env "GOPATH" "$GOPATH"; fi
|
|
179
|
+
if [ -n "${GOROOT:-}" ]; then append_env "GOROOT" "$GOROOT"; fi
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
provision_rust() {
|
|
183
|
+
local dir="$1"
|
|
184
|
+
if ! run_install_cmd "$dir"; then
|
|
185
|
+
if command -v cargo >/dev/null 2>&1; then
|
|
186
|
+
pt_log "Fetching Rust dependencies..."
|
|
187
|
+
(cd "$dir" && cargo fetch)
|
|
188
|
+
else
|
|
189
|
+
pt_log "cargo not found on PATH — record paths only"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
append_env "HARNESS_ECOSYSTEM" "rust"
|
|
193
|
+
append_env "CARGO_BIN" "$(command -v cargo 2>/dev/null || echo cargo)"
|
|
194
|
+
append_env "RUSTC_BIN" "$(command -v rustc 2>/dev/null || echo rustc)"
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
provision_java() {
|
|
198
|
+
local dir="$1"
|
|
199
|
+
run_install_cmd "$dir" || true
|
|
200
|
+
append_env "HARNESS_ECOSYSTEM" "java"
|
|
201
|
+
if [ -n "${JAVA_HOME:-}" ]; then
|
|
202
|
+
append_env "JAVA_HOME" "$JAVA_HOME"
|
|
203
|
+
append_path_prefix "$JAVA_HOME/bin"
|
|
204
|
+
fi
|
|
205
|
+
if command -v mvn >/dev/null 2>&1; then
|
|
206
|
+
append_env "MVN_BIN" "$(command -v mvn)"
|
|
207
|
+
elif [ -f "$dir/gradlew" ]; then
|
|
208
|
+
append_env "GRADLE_BIN" "$dir/gradlew"
|
|
209
|
+
elif command -v gradle >/dev/null 2>&1; then
|
|
210
|
+
append_env "GRADLE_BIN" "$(command -v gradle)"
|
|
211
|
+
fi
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
provision_ruby() {
|
|
215
|
+
local dir="$1"
|
|
216
|
+
if ! run_install_cmd "$dir"; then
|
|
217
|
+
if command -v bundle >/dev/null 2>&1; then
|
|
218
|
+
pt_log "Installing Ruby gems..."
|
|
219
|
+
(cd "$dir" && bundle install --quiet)
|
|
220
|
+
else
|
|
221
|
+
pt_log "bundle not found on PATH — record paths only"
|
|
222
|
+
fi
|
|
223
|
+
fi
|
|
224
|
+
append_env "HARNESS_ECOSYSTEM" "ruby"
|
|
225
|
+
append_env "RUBY_BIN" "$(command -v ruby 2>/dev/null || echo ruby)"
|
|
226
|
+
append_env "BUNDLE_BIN" "$(command -v bundle 2>/dev/null || echo bundle)"
|
|
227
|
+
append_path_prefix "$dir/vendor/bundle/bin"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
provision_ios() {
|
|
231
|
+
local dir="$1"
|
|
232
|
+
run_install_cmd "$dir" || true
|
|
233
|
+
append_env "HARNESS_ECOSYSTEM" "ios"
|
|
234
|
+
append_env "XCODEBUILD_BIN" "$(command -v xcodebuild 2>/dev/null || echo xcodebuild)"
|
|
235
|
+
if [ -n "${HARNESS_XCODE_SCHEME:-}" ]; then
|
|
236
|
+
append_env "HARNESS_XCODE_SCHEME" "$HARNESS_XCODE_SCHEME"
|
|
237
|
+
fi
|
|
238
|
+
if [ -n "${HARNESS_SIMULATOR_NAME:-}" ]; then
|
|
239
|
+
append_env "HARNESS_SIMULATOR_NAME" "$HARNESS_SIMULATOR_NAME"
|
|
240
|
+
fi
|
|
241
|
+
if [ -n "${HARNESS_BUNDLE_ID:-}" ]; then
|
|
242
|
+
append_env "HARNESS_BUNDLE_ID" "$HARNESS_BUNDLE_ID"
|
|
243
|
+
fi
|
|
244
|
+
if [ -n "${DEVELOPER_DIR:-}" ]; then
|
|
245
|
+
append_env "DEVELOPER_DIR" "$DEVELOPER_DIR"
|
|
246
|
+
fi
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
provision_monorepo_root() {
|
|
250
|
+
[ -n "$HAR_REL_PREFIX" ] || return 0
|
|
251
|
+
[ -n "$HAR_WORKTREE_DIR" ] || return 0
|
|
252
|
+
[ -f "$HAR_WORKTREE_DIR/package.json" ] || return 0
|
|
253
|
+
[ -d "$HAR_WORKTREE_DIR/node_modules" ] && return 0
|
|
254
|
+
pt_log "Installing monorepo root dependencies in $HAR_WORKTREE_DIR..."
|
|
255
|
+
(cd "$HAR_WORKTREE_DIR" && npm install --silent)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
provision_ecosystem() {
|
|
259
|
+
local dir="$1"
|
|
260
|
+
local ecosystem
|
|
261
|
+
ecosystem="$(detect_ecosystem "$dir")"
|
|
262
|
+
pt_log "Toolchain ecosystem: ${ecosystem} (work dir: ${dir})"
|
|
263
|
+
|
|
264
|
+
case "$ecosystem" in
|
|
265
|
+
node) provision_node "$dir" ;;
|
|
266
|
+
python) provision_python "$dir" ;;
|
|
267
|
+
go) provision_go "$dir" ;;
|
|
268
|
+
rust) provision_rust "$dir" ;;
|
|
269
|
+
java) provision_java "$dir" ;;
|
|
270
|
+
ruby) provision_ruby "$dir" ;;
|
|
271
|
+
ios) provision_ios "$dir" ;;
|
|
272
|
+
none)
|
|
273
|
+
if run_install_cmd "$dir"; then
|
|
274
|
+
append_env "HARNESS_ECOSYSTEM" "custom"
|
|
275
|
+
else
|
|
276
|
+
pt_log "No ecosystem manifest detected — set HARNESS_ECOSYSTEM or HARNESS_INSTALL_CMD in harness.env"
|
|
277
|
+
append_env "HARNESS_ECOSYSTEM" "none"
|
|
278
|
+
fi
|
|
279
|
+
;;
|
|
280
|
+
esac
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
append_env "HARNESS_TOOLCHAIN_PROVISIONED" "true"
|
|
284
|
+
provision_ecosystem "$HAR_WORK_DIR"
|
|
285
|
+
provision_monorepo_root
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Verification pipeline for CLI/library repos
|
|
2
|
+
# Verification pipeline for CLI/library repos.
|
|
3
3
|
# Outputs JSON to stdout, human-readable progress to stderr.
|
|
4
4
|
#
|
|
5
5
|
# Usage: ./.har/verify.sh <agent-id> [--full]
|
|
6
|
+
#
|
|
7
|
+
# Quick (default): ecosystem smoke — compile/import/build only
|
|
8
|
+
# Full (--full): + conventional tests, lint, optional readiness + browser-e2e
|
|
9
|
+
# Stock steps are examples. Replace them during adaptation to match this repo.
|
|
6
10
|
set -euo pipefail
|
|
7
11
|
|
|
8
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -57,7 +61,7 @@ run_step() {
|
|
|
57
61
|
start=$(now_ms)
|
|
58
62
|
|
|
59
63
|
set +e
|
|
60
|
-
output=$(cd "$WORK_DIR" && eval "$cmd" 2>&1)
|
|
64
|
+
output=$(cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$cmd" 2>&1)
|
|
61
65
|
exit_code=$?
|
|
62
66
|
set -e
|
|
63
67
|
|
|
@@ -89,13 +93,97 @@ process.stdout.write(JSON.stringify(arr));
|
|
|
89
93
|
fi
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
node_package_script_exists() {
|
|
97
|
+
local script="$1"
|
|
98
|
+
"${NODE_BIN:-node}" -e "
|
|
99
|
+
const fs = require('fs');
|
|
100
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
101
|
+
const script = process.argv[1];
|
|
102
|
+
process.exit(pkg.scripts && pkg.scripts[script] ? 0 : 1);
|
|
103
|
+
" "$script"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
run_node_script_or_skip() {
|
|
107
|
+
local name="$1"
|
|
108
|
+
local script="$2"
|
|
109
|
+
if node_package_script_exists "$script"; then
|
|
110
|
+
run_step "$name" "${NPM_BIN:-npm} run ${script}"
|
|
111
|
+
else
|
|
112
|
+
run_step "$name" "echo 'No ${script} script configured; skipping.'"
|
|
113
|
+
fi
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
run_node_quick_smoke() {
|
|
117
|
+
if node_package_script_exists typecheck; then
|
|
118
|
+
run_step "node-typecheck" '${NPM_BIN:-npm} run typecheck'
|
|
119
|
+
elif node_package_script_exists build; then
|
|
120
|
+
run_step "node-build" '${NPM_BIN:-npm} run build'
|
|
121
|
+
else
|
|
122
|
+
run_step "node-package-load" '${NODE_BIN:-node} -e "require(\"./package.json\")"'
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
run_quick_smoke() {
|
|
127
|
+
case "${HARNESS_ECOSYSTEM:-none}" in
|
|
128
|
+
node)
|
|
129
|
+
run_node_quick_smoke
|
|
130
|
+
;;
|
|
131
|
+
python)
|
|
132
|
+
run_step "python-compile" '${PYTHON_BIN:-python3} -m compileall -q .'
|
|
133
|
+
;;
|
|
134
|
+
go)
|
|
135
|
+
run_step "go-build" '${GO_BIN:-go} build ./...'
|
|
136
|
+
;;
|
|
137
|
+
rust)
|
|
138
|
+
run_step "rust-check" '${CARGO_BIN:-cargo} check'
|
|
139
|
+
;;
|
|
140
|
+
java)
|
|
141
|
+
run_step "java-compile" 'if [ -x ./mvnw ]; then ./mvnw -q -DskipTests compile; elif command -v mvn >/dev/null 2>&1; then mvn -q -DskipTests compile; elif [ -x ./gradlew ]; then ./gradlew classes; elif command -v gradle >/dev/null 2>&1; then gradle classes; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi'
|
|
142
|
+
;;
|
|
143
|
+
ruby)
|
|
144
|
+
run_step "ruby-smoke" '${RUBY_BIN:-ruby} -e "puts RUBY_VERSION"'
|
|
145
|
+
;;
|
|
146
|
+
custom|none|*)
|
|
147
|
+
run_step "smoke-not-configured" 'echo "No stock smoke for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."'
|
|
148
|
+
;;
|
|
149
|
+
esac
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
run_full_checks() {
|
|
153
|
+
case "${HARNESS_ECOSYSTEM:-none}" in
|
|
154
|
+
node)
|
|
155
|
+
run_node_script_or_skip "unit-tests" test || true
|
|
156
|
+
run_node_script_or_skip "lint" lint || true
|
|
157
|
+
;;
|
|
158
|
+
python)
|
|
159
|
+
run_step "unit-tests" 'if ${PYTHON_BIN:-python3} -c "import pytest" >/dev/null 2>&1; then ${PYTHON_BIN:-python3} -m pytest -q; else echo "pytest not installed; adapt verify.sh for this Python repo."; fi' || true
|
|
160
|
+
;;
|
|
161
|
+
go)
|
|
162
|
+
run_step "unit-tests" '${GO_BIN:-go} test ./...' || true
|
|
163
|
+
;;
|
|
164
|
+
rust)
|
|
165
|
+
run_step "unit-tests" '${CARGO_BIN:-cargo} test' || true
|
|
166
|
+
;;
|
|
167
|
+
java)
|
|
168
|
+
run_step "unit-tests" 'if [ -x ./mvnw ]; then ./mvnw -q test; elif command -v mvn >/dev/null 2>&1; then mvn -q test; elif [ -x ./gradlew ]; then ./gradlew test; elif command -v gradle >/dev/null 2>&1; then gradle test; else echo "No Maven/Gradle command found; adapt verify.sh for this Java repo."; fi' || true
|
|
169
|
+
;;
|
|
170
|
+
ruby)
|
|
171
|
+
run_step "unit-tests" 'if command -v "${BUNDLE_BIN:-bundle}" >/dev/null 2>&1 && [ -f Gemfile ]; then "${BUNDLE_BIN:-bundle}" exec rake test 2>/dev/null || "${BUNDLE_BIN:-bundle}" exec rspec; else echo "No Ruby test command detected; adapt verify.sh for this Ruby repo."; fi' || true
|
|
172
|
+
;;
|
|
173
|
+
custom|none|*)
|
|
174
|
+
run_step "unit-tests" 'echo "No stock full checks for HARNESS_ECOSYSTEM=${HARNESS_ECOSYSTEM:-none}; adapt .har/verify.sh for this repo."' || true
|
|
175
|
+
;;
|
|
176
|
+
esac
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# ── Verification stages ─────────────────────────────────────────────────────
|
|
180
|
+
# These stock steps are intentionally generic conventions. Adapt this section
|
|
181
|
+
# to the repository's real commands from package.json, Makefile, CI, pyproject,
|
|
182
|
+
# Cargo.toml, go.mod, pom.xml, etc.
|
|
183
|
+
run_quick_smoke || { [ -z "$FULL" ] && true; }
|
|
96
184
|
|
|
97
185
|
if [ -n "$FULL" ]; then
|
|
98
|
-
|
|
186
|
+
run_full_checks
|
|
99
187
|
run_step "readiness" "run_readiness_if_configured \"$AGENT_ID\"" || true
|
|
100
188
|
run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
|
|
101
189
|
fi
|