amsd-pipeline 2.0.76 → 2.0.77

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.
Files changed (2) hide show
  1. package/install.sh +70 -11
  2. package/package.json +1 -1
package/install.sh CHANGED
@@ -237,8 +237,17 @@ if [ -n "$DEST" ]; then
237
237
  fi
238
238
  fi
239
239
  _PKG_REF="${REF:-HEAD}"
240
- # FETCH FIRST: a colleague packaging a release just tagged by someone else may not have it yet.
241
- git -C "$_GIT_ROOT" fetch --tags --quiet >/dev/null 2>&1 || true
240
+ # FETCH ONLY FOR A REF THAT IS NOT HERE: a colleague packaging a release just tagged by someone
241
+ # else may not have it yet. Never for a ref this checkout already holds, and never unbounded or
242
+ # interactive — an unconditional `git fetch --tags` over ssh sat for 20 minutes with no output
243
+ # while packaging a local HEAD (amsd-2974 install, 2026-09-28). A fetch that cannot finish in
244
+ # EPAM_INSTALL_FETCH_TIMEOUT seconds is reported, and the ref check below decides.
245
+ if ! git -C "$_GIT_ROOT" rev-parse --verify "${_PKG_REF}^{commit}" >/dev/null 2>&1; then
246
+ _warn "ref '$_PKG_REF' is not in this checkout — fetching tags (at most ${EPAM_INSTALL_FETCH_TIMEOUT:-120}s)"
247
+ GIT_TERMINAL_PROMPT=0 GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh} -o BatchMode=yes -o ConnectTimeout=15" \
248
+ timeout "${EPAM_INSTALL_FETCH_TIMEOUT:-120}" git -C "$_GIT_ROOT" fetch --tags --quiet >/dev/null 2>&1 \
249
+ || _warn "fetching tags did not complete"
250
+ fi
242
251
  if ! git -C "$_GIT_ROOT" rev-parse --verify "${_PKG_REF}^{commit}" >/dev/null 2>&1; then
243
252
  _bad "ref '$_PKG_REF' does not exist in this checkout, even after fetching tags"
244
253
  exit 1
@@ -303,7 +312,9 @@ if [ -n "$DEST" ]; then
303
312
  fi
304
313
 
305
314
  if [ -n "$_OPCFG_TMP" ]; then
306
- restore_operator_config "$DEST" "$_OPCFG_TMP"
315
+ # The engine's own history of each preserved file, so a value it shipped is never mistaken for an
316
+ # operator edit (an unedited agent-contract.json froze the 10-01 relative-path fix out of an install).
317
+ restore_operator_config "$DEST" "$_OPCFG_TMP" "${_GIT_ROOT:-}"
307
318
  rm -rf "$_OPCFG_TMP"
308
319
  fi
309
320
  # RESTORED AFTER the operator config, and cleaned up on its own: the two snapshots are
@@ -371,7 +382,16 @@ RUNNER="$("$NODE_BIN" -e '
371
382
  const reg = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
372
383
  const set = reg.sets[process.argv[2]];
373
384
  const s = JSON.parse(fs.readFileSync(path.join(path.dirname(process.argv[1]), set.settingsFile), "utf8"));
374
- process.stdout.write(Object.keys(s.runners || {})[0] || "");
385
+ const runner = Object.keys(s.runners || {})[0] || "";
386
+ // THE BINARY THE RUNNER NEEDS, from the map the engine itself reads (providers.json cliBinary).
387
+ // A runner is usually its own binary (claude, copilot) and then this changes nothing; codemie-web
388
+ // is not a binary — it needs `codemie` (2026-09-28). $EPAM_CLI entries keep the runner name.
389
+ let bin = runner;
390
+ try {
391
+ const cb = (JSON.parse(fs.readFileSync(path.join(path.dirname(process.argv[1]), "providers.json"), "utf8")).cliBinary || {})[runner];
392
+ if (cb && !String(cb).startsWith("$")) bin = cb;
393
+ } catch { /* no map: the runner name is the binary, as before */ }
394
+ process.stdout.write(bin);
375
395
  ' "$CONFIG/provider-sets.json" "$STACK" 2>/dev/null)"
376
396
  if [ -n "$RUNNER" ]; then
377
397
  if command -v "$RUNNER" >/dev/null 2>&1; then _ok "$RUNNER (the '$STACK' runner)"
@@ -397,6 +417,39 @@ else
397
417
  _bad "python3 is not on PATH — 88 pipeline handlers cannot run without it"; FAILED=1
398
418
  fi
399
419
 
420
+ # A PYTHON ENVIRONMENT THE STACK DECLARES (provider-sets.json pythonEnv): built here, never by hand.
421
+ # codemie-web declares one — the codemie-plugins toolkit its agents reach the codeline through. The
422
+ # path is relative to the install root; the runner reads the same declaration. pip is idempotent, so
423
+ # a re-install converges on the declared (pinned) packages.
424
+ IFS=$'\t' read -r _PYENV_PATH _PYENV_PKGS <<<"$("$NODE_BIN" -e '
425
+ const reg = require(process.argv[1]);
426
+ const e = (reg.sets[process.argv[2]] || {}).pythonEnv;
427
+ if (e && e.path) process.stdout.write(e.path + "\t" + (e.packages || []).join(" "));
428
+ ' "$CONFIG/provider-sets.json" "$STACK" 2>/dev/null)"
429
+ if [ -n "${_PYENV_PATH:-}" ] && [ "$CHECK_ONLY" = "1" ]; then
430
+ # --check builds nothing: the environment and each pinned package must already be there.
431
+ _pyenv="$ROOT/$_PYENV_PATH"; _pymissing=""
432
+ for _pkg in ${_PYENV_PKGS:-}; do
433
+ _want="${_pkg#*==}"; [ "$_want" = "$_pkg" ] && _want=""
434
+ _have="$("$_pyenv/bin/python" -m pip show "${_pkg%%==*}" 2>/dev/null | sed -n 's/^Version: //p')"
435
+ { [ -n "$_have" ] && { [ -z "$_want" ] || [ "$_have" = "$_want" ]; }; } || _pymissing="$_pymissing $_pkg"
436
+ done
437
+ if [ ! -x "$_pyenv/bin/python" ] || [ -n "$_pymissing" ]; then
438
+ _bad "the '$STACK' stack's python environment at $_PYENV_PATH is missing or incomplete (${_pymissing# }) — run the installer without --check"; FAILED=1
439
+ else
440
+ _ok "python environment for the '$STACK' stack: $_PYENV_PATH (${_PYENV_PKGS:-no packages})"
441
+ fi
442
+ elif [ -n "${_PYENV_PATH:-}" ]; then
443
+ _pyenv="$ROOT/$_PYENV_PATH"
444
+ if [ ! -x "$_pyenv/bin/python" ] && ! python3 -m venv "$_pyenv" >/dev/null 2>&1; then
445
+ _bad "could not create the '$STACK' stack's python environment at $_pyenv (python3 -m venv)"; FAILED=1
446
+ elif [ -n "${_PYENV_PKGS:-}" ] && ! "$_pyenv/bin/python" -m pip install -q --disable-pip-version-check $_PYENV_PKGS >/dev/null 2>&1; then
447
+ _bad "could not install ${_PYENV_PKGS} into $_pyenv — the '$STACK' stack needs them"; FAILED=1
448
+ else
449
+ _ok "python environment for the '$STACK' stack: $_PYENV_PATH (${_PYENV_PKGS:-no packages})"
450
+ fi
451
+ fi
452
+
400
453
  # ── Credentials: what this stack needs, from what it declares ───────────────
401
454
  _head "Credentials"
402
455
  # .env.example IS GENERATED, never hand-maintained — it went stale in both directions: this repo's
@@ -608,13 +661,19 @@ if [ -n "$EPAM_PROJECT" ] && [ -d "$ROOT/orchestrations/projects/$EPAM_PROJECT"
608
661
  if [ -n "$_missing" ]; then
609
662
  _warn "project '$EPAM_PROJECT' not configured — missing:$_missing"
610
663
  else
611
- {
612
- echo "# Written by install.sh. NON-SECRET VALUES ONLY — credentials live in .env."
613
- echo "PROJECT_NAME=$EPAM_PROJECT"
614
- echo "JIRA_URL=$JIRA_URL"
615
- echo "JIRA_PROJECT_KEY=$JIRA_PROJECT_KEY"
616
- echo "JIRA_CODELINE_ROOT=$JIRA_CODELINE_ROOT"
617
- } > "$_cfg"
664
+ # SET THESE KEYS, KEEP EVERYTHING ELSE. This replaced the whole file: a project's
665
+ # config.env carries its pauses, baseline branch, JQL and field mappings, and every one
666
+ # of them was lost to five lines. Each key is replaced where it stands, or appended.
667
+ "$NODE_BIN" -e '
668
+ const fs = require("fs"); const [file, ...kv] = process.argv.slice(1);
669
+ let lines = fs.existsSync(file) ? fs.readFileSync(file, "utf8").split("\n") : [];
670
+ for (let i = 0; i < kv.length; i += 2) {
671
+ const k = kv[i], line = k + "=" + kv[i + 1];
672
+ const at = lines.findIndex((l) => new RegExp("^(export\\s+)?" + k + "=").test(l));
673
+ if (at >= 0) lines[at] = line; else lines.splice(lines.length && lines[lines.length - 1] === "" ? lines.length - 1 : lines.length, 0, line);
674
+ }
675
+ fs.writeFileSync(file, lines.join("\n").replace(/\n*$/, "\n"));
676
+ ' "$_cfg" PROJECT_NAME "$EPAM_PROJECT" JIRA_URL "$JIRA_URL" JIRA_PROJECT_KEY "$JIRA_PROJECT_KEY" JIRA_CODELINE_ROOT "$JIRA_CODELINE_ROOT"
618
677
  _ok "wrote $_cfg (JIRA_URL, JIRA_PROJECT_KEY, JIRA_CODELINE_ROOT)"
619
678
  fi
620
679
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amsd-pipeline",
3
- "version": "2.0.76",
3
+ "version": "2.0.77",
4
4
  "description": "Installer for the amsd-pipeline orchestration stack. Clones, packages and provisions the full stack with one command — no separate git clone step.",
5
5
  "bin": {
6
6
  "amsd-pipeline": "bin/amsd-pipeline.js"