@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
package/dist/index.js
CHANGED
|
@@ -26921,6 +26921,7 @@ var REQUIRED_FILES_DEFAULT = [
|
|
|
26921
26921
|
"harness.env",
|
|
26922
26922
|
"setup-infra.sh",
|
|
26923
26923
|
"launch.sh",
|
|
26924
|
+
"provision-toolchain.sh",
|
|
26924
26925
|
"verify.sh",
|
|
26925
26926
|
"teardown.sh",
|
|
26926
26927
|
"agent-cli.sh",
|
|
@@ -26939,6 +26940,7 @@ var REQUIRED_FILES_IOS = [
|
|
|
26939
26940
|
"harness.env",
|
|
26940
26941
|
"setup-infra.sh",
|
|
26941
26942
|
"launch.sh",
|
|
26943
|
+
"provision-toolchain.sh",
|
|
26942
26944
|
"verify.sh",
|
|
26943
26945
|
"teardown.sh",
|
|
26944
26946
|
"agent-cli.sh",
|
|
@@ -26955,6 +26957,7 @@ function getRequiredFiles(repoPath) {
|
|
|
26955
26957
|
var SHELL_SCRIPTS = [
|
|
26956
26958
|
"setup-infra.sh",
|
|
26957
26959
|
"launch.sh",
|
|
26960
|
+
"provision-toolchain.sh",
|
|
26958
26961
|
"verify.sh",
|
|
26959
26962
|
"teardown.sh",
|
|
26960
26963
|
"agent-cli.sh",
|
|
@@ -27244,7 +27247,7 @@ function patchStageRegistry(repoPath, manifest, force) {
|
|
|
27244
27247
|
if (verifyIdx >= 0) {
|
|
27245
27248
|
stages[verifyIdx] = {
|
|
27246
27249
|
...stages[verifyIdx],
|
|
27247
|
-
description: "Verification pipeline (quick by default; --full adds lint and browser-e2e when installed)",
|
|
27250
|
+
description: "Verification pipeline (quick smoke by default; --full adds tests, lint, and browser-e2e when installed)",
|
|
27248
27251
|
acceptsArgs: ["--full"]
|
|
27249
27252
|
};
|
|
27250
27253
|
}
|
|
@@ -27466,9 +27469,9 @@ var fs17 = __toESM(require("fs"));
|
|
|
27466
27469
|
var path15 = __toESM(require("path"));
|
|
27467
27470
|
var ADAPTATION_PROMPT_FILE = "ADAPT-PROMPT.md";
|
|
27468
27471
|
var PROFILE_HINTS = {
|
|
27469
|
-
default: "Web app profile \u2014 Docker Compose for shared infra (HARNESS_INFRA_SERVICES), PM2 for the primary application only, git worktree per agent slot by default. Identify the primary app agents modify; run supporting services shared.
|
|
27470
|
-
cli: "CLI/library profile \u2014 no PM2. Optional Docker Compose via
|
|
27471
|
-
ios: "iOS mobile app profile \u2014
|
|
27472
|
+
default: "Web app profile (SaaS/full-stack) \u2014 Docker Compose for shared infra (HARNESS_INFRA_SERVICES), PM2 for the primary application only, git worktree per agent slot by default. Launch provisions toolchain via harness.env (HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD) and writes paths to .env.agent.<id>. Identify the primary app agents modify; run supporting services shared.",
|
|
27473
|
+
cli: "CLI/library profile (typical SWE-bench) \u2014 no PM2. Optional Docker Compose via HARNESS_INFRA_SERVICES. Git worktree by default. Launch provisions toolchain declaratively (HARNESS_ECOSYSTEM auto-detects common ecosystems); verify must use resolved tool paths from .env.agent.<id>, never hardcoded interpreter or package-manager paths.",
|
|
27474
|
+
ios: "iOS mobile app profile \u2014 xcodebuild + iOS Simulator in an isolated git worktree. Set HARNESS_XCODE_SCHEME, workspace/project, HARNESS_SIMULATOR_NAME, HARNESS_BUNDLE_ID. Launch writes XCODEBUILD_BIN to .env.agent.<id>; verify uses it. Install RocketSim stage (har env add-stage rocketsim) for user-flow validation."
|
|
27472
27475
|
};
|
|
27473
27476
|
function loadTemplate(name) {
|
|
27474
27477
|
const filePath = resolveTemplateFile(name);
|
|
@@ -6,6 +6,24 @@ Explore this repository, then edit files in `.har/` directly to make the harness
|
|
|
6
6
|
|
|
7
7
|
**Do NOT** create a YAML config or JSON mapping file for runtime behavior. Put behavior directly in the harness scripts and templates.
|
|
8
8
|
|
|
9
|
+
## HAR profiles (pick the right one at init)
|
|
10
|
+
|
|
11
|
+
`har env init` scaffolds from one of three boilerplate profiles — **choose the profile that matches this repository**:
|
|
12
|
+
|
|
13
|
+
| Profile | Best for | What you get |
|
|
14
|
+
|---------|----------|--------------|
|
|
15
|
+
| `default` | **SaaS / web apps** (Next.js, Rails, full-stack, etc.) | Docker Compose shared infra, PM2 for primary app, per-slot ports and preview URLs |
|
|
16
|
+
| `cli` | **CLI tools, libraries, test-suite repos** (typical SWE-bench) | Git worktree by default, no PM2; optional Docker for databases; run project commands in isolation |
|
|
17
|
+
| `ios` | **Mobile iOS / Swift** | xcodebuild + iOS Simulator; scheme/project/simulator in `harness.env` |
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
har env init # default (SaaS/web)
|
|
21
|
+
har env init --profile cli # libraries / CLI / polyglot test repos
|
|
22
|
+
har env init --profile ios # iOS apps
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Do **not** disable worktrees, rewrite launch into repo-root-only mode, or pick the wrong profile unless the project truly requires it.
|
|
26
|
+
|
|
9
27
|
## Profile: {{PROFILE}}
|
|
10
28
|
|
|
11
29
|
{{PROFILE_HINT}}
|
|
@@ -38,11 +56,31 @@ Clear index of the harness: what each file does, quick start, architecture, how
|
|
|
38
56
|
### `.har/harness.env`
|
|
39
57
|
Primary app, ports, `HARNESS_INFRA_SERVICES`, migrate/seed commands, health check path.
|
|
40
58
|
|
|
59
|
+
**Toolchain provisioning:** set `HARNESS_ECOSYSTEM` (`auto` detects from manifests) and optional `HARNESS_INSTALL_CMD`. Launch runs `provision-toolchain.sh`, writes resolved paths (`PYTHON_BIN`, `NODE_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) into `.env.agent.<id>`. Verify steps **must** use those paths — never hardcode venv or interpreter locations.
|
|
60
|
+
|
|
41
61
|
### `.har/ecosystem.agent.template.cjs` (default profile only)
|
|
42
62
|
PM2 processes for the primary application only, matching how it runs in dev. Skip entirely for the CLI profile.
|
|
43
63
|
|
|
44
64
|
### `.har/verify.sh`
|
|
45
|
-
|
|
65
|
+
Adapt verification for this repository's toolchain. Step lists in the template are
|
|
66
|
+
**examples, not exhaustive** — add, remove, or reorder `run_step` calls to match
|
|
67
|
+
how this project is built and tested. Use toolchain variables from `.env.agent.<id>`
|
|
68
|
+
(e.g. `${NPM_BIN:-npm}`, `${PYTHON_BIN:-python3}`, `${XCODEBUILD_BIN:-xcodebuild}`).
|
|
69
|
+
The stock verify section is keyed by `HARNESS_ECOSYSTEM`; it is a starting point,
|
|
70
|
+
not the repo's final contract. Replace conventions that do not match this project.
|
|
71
|
+
|
|
72
|
+
**Tier contract:**
|
|
73
|
+
|
|
74
|
+
| Mode | Command | Intent |
|
|
75
|
+
|------|---------|--------|
|
|
76
|
+
| Quick (default) | `har env verify 1` | Smoke — compile / import / build / health only |
|
|
77
|
+
| Full | `har env verify 1 --full` | Stricter — unit tests, lint, readiness, optional browser-e2e |
|
|
78
|
+
|
|
79
|
+
- **Quick** must stay fast and minimal (syntax, compile, import smoke) — not the full test suite.
|
|
80
|
+
- **Full** holds unit tests, lint, and heavier checks; optional Playwright runs on `--full` when installed.
|
|
81
|
+
- Reuse real commands from `package.json`, `Makefile`, CI, `pyproject.toml`, etc.
|
|
82
|
+
- Remove stock npm/pytest/go/cargo/maven/gradle examples that do not apply.
|
|
83
|
+
- Replace all TODO placeholders in both tiers.
|
|
46
84
|
|
|
47
85
|
### Readiness vs liveness (required)
|
|
48
86
|
Do not treat a passing health check as adaptation complete. Before finishing,
|
|
@@ -74,8 +112,9 @@ scripts or document why no substitute is needed. In particular:
|
|
|
74
112
|
choose and document an agent-friendly asset mode.
|
|
75
113
|
- Put Layer 3 checks in `verify --full`, a project-owned readiness script, or
|
|
76
114
|
documented smoke URLs. Health alone is not sufficient for UI/auth apps.
|
|
77
|
-
- Ensure `launch.sh` writes the slot registry before slow or fragile steps,
|
|
78
|
-
`
|
|
115
|
+
- Ensure `launch.sh` writes the slot registry before slow or fragile steps, runs
|
|
116
|
+
`provision-toolchain.sh` to install deps and record toolchain paths in
|
|
117
|
+
`.env.agent.<id>`, and `verify.sh` resolves env/work dir through `agent-slot.sh`.
|
|
79
118
|
|
|
80
119
|
### Optional Playwright stage
|
|
81
120
|
If the user ran `har env add-stage playwright` (or `@playwright/test` is in package.json):
|
|
@@ -22,8 +22,8 @@ Prefer targeted edits over full rewrites. Key files to review:
|
|
|
22
22
|
### `.har/README.md` (required)
|
|
23
23
|
Keep this accurate — it is the harness index. Update whenever scripts, stages, or workflow change.
|
|
24
24
|
|
|
25
|
-
### `.har/harness.env`, `verify.sh`, `ecosystem.agent.template.cjs`, `CLAUDE.agent.md`
|
|
26
|
-
Align commands and instructions with the current stack.
|
|
25
|
+
### `.har/harness.env`, `verify.sh`, `provision-toolchain.sh`, `ecosystem.agent.template.cjs`, `CLAUDE.agent.md`
|
|
26
|
+
Align commands and instructions with the current stack. Verify steps must use toolchain paths from `.env.agent.<id>` (`PYTHON_BIN`, `NPM_BIN`, `XCODEBUILD_BIN`, …) — never hardcoded venv or interpreter paths. Replace stock ecosystem conventions that do not match the repository; do not leave npm/pytest/go/cargo/maven/gradle examples in place by accident.
|
|
27
27
|
|
|
28
28
|
### `.har/env.template`, `setup-infra.sh`, `docker-compose.agent.yml`
|
|
29
29
|
Update only if infra changed.
|
|
@@ -50,10 +50,12 @@ Quick loop during development: MCP `har_run_verification`, `har env verify ${AGE
|
|
|
50
50
|
## Project commands
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
# TODO: adapt for this repository (see package.json, Makefile, etc.)
|
|
53
|
+
# TODO: adapt for this repository (see package.json, Makefile, pyproject.toml, CI, etc.)
|
|
54
54
|
# npm run typecheck
|
|
55
|
-
#
|
|
56
|
-
#
|
|
55
|
+
# pytest -q
|
|
56
|
+
# go test ./...
|
|
57
|
+
# cargo test
|
|
58
|
+
# make test
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
## 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, checksums) — do not edit |
|
|
15
|
-
| `harness.env` | Shared config: primary app, ports, agent slot limits, `HARNESS_INFRA_SERVICES`, migrate/seed commands |
|
|
15
|
+
| `harness.env` | Shared config: primary app, ports, agent slot limits, `HARNESS_INFRA_SERVICES`, toolchain provisioning, 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 shared Docker infra + create template database |
|
|
22
|
-
| `launch.sh` | Launch one agent slot (ports, DB clone, PM2 processes) |
|
|
23
|
-
| `
|
|
22
|
+
| `launch.sh` | Launch one agent slot (ports, DB clone, toolchain provisioning, PM2 processes) |
|
|
23
|
+
| `provision-toolchain.sh` | Install deps and write toolchain paths 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 |
|
|
25
26
|
| `agent-cli.sh` | Manage a running agent (status, logs, psql, health) |
|
|
26
27
|
| `attach.sh` | Attach to agent tmux session |
|
|
@@ -49,8 +50,8 @@ In Cursor with HAR MCP configured: use `har_launch_environment`, `har_run_verifi
|
|
|
49
50
|
```bash
|
|
50
51
|
./.har/setup-infra.sh # when HARNESS_INFRA_SERVICES is non-empty
|
|
51
52
|
./.har/launch.sh 1
|
|
52
|
-
./.har/verify.sh 1 # quick:
|
|
53
|
-
./.har/verify.sh 1 --full #
|
|
53
|
+
./.har/verify.sh 1 # quick: ecosystem smoke + health
|
|
54
|
+
./.har/verify.sh 1 --full # + conventional tests/lint, browser-e2e (if installed)
|
|
54
55
|
./.har/teardown.sh 1
|
|
55
56
|
```
|
|
56
57
|
|
|
@@ -58,10 +59,19 @@ Read **`stages.json`** for registered stages and **`verificationStages`** for th
|
|
|
58
59
|
|
|
59
60
|
## Verification contract
|
|
60
61
|
|
|
62
|
+
Steps in `verify.sh` are **project-specific examples** — adapt them to your stack
|
|
63
|
+
during `har env init` / `har env maintain`. The table describes each tier's intent,
|
|
64
|
+
not a fixed command list.
|
|
65
|
+
|
|
61
66
|
| Mode | Command | Typical steps |
|
|
62
67
|
|------|---------|---------------|
|
|
63
|
-
| Quick | `har env verify <id>` or `verify.sh <id>` |
|
|
64
|
-
| Full | `har env verify <id> --full` or `verify.sh <id> --full` |
|
|
68
|
+
| Quick | `har env verify <id>` or `verify.sh <id>` | Stock ecosystem smoke + health (stops early on failure) |
|
|
69
|
+
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | Stock conventional tests/lint, optional readiness smoke + **`browser-e2e`** when `.har/stages/browser-e2e.sh` exists |
|
|
70
|
+
|
|
71
|
+
The stock commands are deliberately generic conventions keyed by
|
|
72
|
+
`HARNESS_ECOSYSTEM`. Replace them with the repository's real commands during
|
|
73
|
+
adaptation; do not leave Node/npm, Python, Go, Rust, Java, or Ruby defaults in
|
|
74
|
+
place when they do not match the project.
|
|
65
75
|
|
|
66
76
|
Install Playwright stage: `har env add-stage playwright` (optional). UI changes should add or update specs under `tests/`.
|
|
67
77
|
|
|
@@ -23,6 +23,8 @@ module.exports = {
|
|
|
23
23
|
// .har/ecosystem.shared.config.cjs (started by setup-infra.sh, processes
|
|
24
24
|
// named "har-shared-<name>", fixed ports). A primary app may still need two
|
|
25
25
|
// processes (e.g. api + frontend) — add one entry per process.
|
|
26
|
+
// Example Node dev process. Replace with this repo's actual primary app
|
|
27
|
+
// command (Rails, Django, Go, Java, etc. should not keep this npm entry).
|
|
26
28
|
{
|
|
27
29
|
name: 'agent-${AGENT_ID}-api',
|
|
28
30
|
script: 'npm',
|
|
@@ -32,6 +32,9 @@ S3_FORCE_PATH_STYLE=true
|
|
|
32
32
|
# Headless browser (if enabled)
|
|
33
33
|
HEADLESS_BROWSER_URL=http://localhost:${BROWSER_PORT}
|
|
34
34
|
|
|
35
|
+
# Toolchain paths — appended by provision-toolchain.sh during launch
|
|
36
|
+
# NODE_BIN= NPM_BIN= PYTHON_BIN= VIRTUAL_ENV= GO_BIN= CARGO_BIN= JAVA_HOME=
|
|
37
|
+
|
|
35
38
|
# Mail (if enabled)
|
|
36
39
|
SMTP_HOST=localhost
|
|
37
40
|
SMTP_PORT=11025
|
|
@@ -19,6 +19,11 @@ export HARNESS_HEALTH_CHECK_PATH=/health
|
|
|
19
19
|
export HARNESS_AGENT_SLOT_MIN=1
|
|
20
20
|
export HARNESS_AGENT_SLOT_MAX=5
|
|
21
21
|
|
|
22
|
+
# Toolchain provisioning — launch writes resolved paths into .env.agent.<id>
|
|
23
|
+
# Ecosystem: auto (detect from manifests) | node | python | go | rust | java | ruby | none
|
|
24
|
+
export HARNESS_ECOSYSTEM="auto"
|
|
25
|
+
# export HARNESS_INSTALL_CMD="make deps"
|
|
26
|
+
|
|
22
27
|
# Shared infrastructure — space-separated docker compose service names from
|
|
23
28
|
# docker-compose.agent.yml. Started ONCE by setup-infra.sh and shared by all
|
|
24
29
|
# agent slots on fixed ports. Prune/add services in the compose file, then
|
|
@@ -110,26 +110,12 @@ else
|
|
|
110
110
|
log "Using repo root (worktree disabled)"
|
|
111
111
|
fi
|
|
112
112
|
|
|
113
|
-
# Install dependencies (fresh worktrees have no node_modules — PM2 config needs them at load time)
|
|
114
|
-
if [ -f "$WORK_DIR/package.json" ] && [ ! -d "$WORK_DIR/node_modules" ]; then
|
|
115
|
-
log "Installing dependencies in $WORK_DIR..."
|
|
116
|
-
(cd "$WORK_DIR" && npm install --silent)
|
|
117
|
-
elif [ -f "$WORK_DIR/go.mod" ] && [ ! -d "$WORK_DIR/vendor" ]; then
|
|
118
|
-
log "Go module detected in work dir (run go mod download if needed)"
|
|
119
|
-
fi
|
|
120
|
-
|
|
121
|
-
# Monorepo: file:/workspace deps may resolve modules from the repo root — install there too
|
|
122
|
-
if [ -n "${REL_PREFIX:-}" ] && [ -f "$WORKTREE_DIR/package.json" ] && [ ! -d "$WORKTREE_DIR/node_modules" ]; then
|
|
123
|
-
log "Installing monorepo root dependencies in $WORKTREE_DIR..."
|
|
124
|
-
(cd "$WORKTREE_DIR" && npm install --silent)
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
113
|
# Generate .env.agent.N
|
|
128
114
|
# Keep harness-generated files out of accidental `git add -A` commits in repos
|
|
129
115
|
# whose .gitignore doesn't know about har (applies to all worktrees too).
|
|
130
116
|
GIT_EXCLUDE="$(git -C "$REPO_ROOT" rev-parse --git-common-dir 2>/dev/null)/info/exclude"
|
|
131
117
|
if [ -n "$GIT_EXCLUDE" ] && [ -d "$(dirname "$GIT_EXCLUDE")" ]; then
|
|
132
|
-
for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs'; do
|
|
118
|
+
for pattern in '.env.agent.*' 'ecosystem.agent.*.config.cjs' '.har/venv'; do
|
|
133
119
|
grep -qxF "$pattern" "$GIT_EXCLUDE" 2>/dev/null || echo "$pattern" >> "$GIT_EXCLUDE"
|
|
134
120
|
done
|
|
135
121
|
fi
|
|
@@ -191,6 +177,14 @@ SLOT_STATUS="starting" \
|
|
|
191
177
|
write_slot_registry
|
|
192
178
|
REGISTRY_WRITTEN=true
|
|
193
179
|
|
|
180
|
+
log "Provisioning toolchain (see harness.env: HARNESS_ECOSYSTEM, HARNESS_INSTALL_CMD)..."
|
|
181
|
+
HAR_WORK_DIR="$WORK_DIR" \
|
|
182
|
+
HAR_ENV_FILE="$ENV_FILE" \
|
|
183
|
+
HAR_WORKTREE_DIR="${WORKTREE_DIR:-}" \
|
|
184
|
+
HAR_REL_PREFIX="${REL_PREFIX:-}" \
|
|
185
|
+
HAR_AGENT_ID="$AGENT_ID" \
|
|
186
|
+
"$SCRIPT_DIR/provision-toolchain.sh"
|
|
187
|
+
|
|
194
188
|
if [ -n "${HARNESS_DB_MINIMAL_BOOTSTRAP_CMD:-}" ]; then
|
|
195
189
|
log "Running minimal data bootstrap..."
|
|
196
190
|
(cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$HARNESS_DB_MINIMAL_BOOTSTRAP_CMD")
|
|
@@ -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
|