forgedock 1.32.0 → 1.33.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/bin/engine/outcome-codes.mjs +7 -3
- package/bin/engine/phases.mjs +29 -8
- package/bin/engine-cli.mjs +84 -24
- package/bin/forgedock.mjs +21 -1
- package/bin/lean/basesync.mjs +98 -0
- package/bin/lean/basesync.test.mjs +151 -0
- package/bin/lean/batch.mjs +9 -3
- package/bin/lean/batch.test.mjs +134 -3
- package/bin/lean/budget.mjs +7 -1
- package/bin/lean/budget.test.mjs +9 -1
- package/bin/lean/conformance.mjs +20 -7
- package/bin/lean/conformance.test.mjs +29 -2
- package/bin/lean/depcache.mjs +23 -3
- package/bin/lean/depcache.test.mjs +42 -1
- package/bin/lean/effects.mjs +29 -3
- package/bin/lean/execution.mjs +106 -14
- package/bin/lean/execution.test.mjs +101 -1
- package/bin/lean/hostkill.mjs +8 -0
- package/bin/lean/lean.mjs +65 -24
- package/bin/lean/lean.test.mjs +221 -10
- package/bin/lean/lightlane.mjs +38 -13
- package/bin/lean/lightlane.test.mjs +35 -1
- package/bin/lean/orchestrate.mjs +93 -26
- package/bin/lean/prompts.mjs +17 -7
- package/bin/lean/recovery.test.mjs +26 -0
- package/bin/lean/retry.mjs +15 -1
- package/bin/lean/sandbox-provider.contract.test.mjs +74 -1
- package/bin/lean/sandbox-remote.mjs +40 -7
- package/bin/lean/schedd.mjs +50 -14
- package/bin/lean/schedd.test.mjs +133 -21
- package/bin/lean/seqlease.mjs +49 -2
- package/bin/lean/seqlease.test.mjs +14 -0
- package/bin/lean/sequence.mjs +54 -6
- package/bin/lean/sequence.test.mjs +83 -1
- package/bin/pool/agent.mjs +435 -0
- package/bin/pool/cli.mjs +110 -0
- package/bin/pool/docker.mjs +56 -0
- package/bin/pool/http.mjs +55 -0
- package/bin/pool/router.mjs +299 -0
- package/bin/pool/store.mjs +25 -0
- package/bin/pool/version.mjs +44 -0
- package/bin/runner.mjs +1 -0
- package/bin/stats/aggregate.mjs +6 -0
- package/bin/stats/index.mjs +14 -2
- package/bin/stats/local-source.mjs +1 -1
- package/bin/stats/pool.mjs +51 -0
- package/bin/stats/pool.test.mjs +39 -0
- package/bin/stats/query.mjs +2 -2
- package/bin/stats/stats.test.mjs +15 -1
- package/commands/orchestrate.md +3 -3
- package/docs/CONFIG.md +23 -9
- package/docs/OUTCOME-CODES.md +5 -3
- package/docs/STATS.md +7 -0
- package/package.json +2 -1
- package/pool/README.md +13 -0
- package/pool/image/Dockerfile +46 -0
- package/pool/image/claude-shim +8 -0
- package/pool/image/cs-cred +12 -0
- package/pool/image/entrypoint.sh +61 -0
- package/pool/image/gh-shim +6 -0
- package/pool/install/install-agent-linux.sh +53 -0
- package/pool/install/install-agent.ps1 +225 -0
- package/pool/install/install-agent.sh +101 -0
- package/pool/install/install-router.sh +67 -0
- package/pool/install/keepalive.ps1 +36 -0
- package/pool/install/uninstall-agent.ps1 +20 -0
- package/scripts/hotspots.sh +33 -21
- package/scripts/hotspots.test.sh +22 -3
- package/scripts/refresh-bot-token.test.sh +56 -0
package/scripts/hotspots.sh
CHANGED
|
@@ -7,14 +7,17 @@
|
|
|
7
7
|
#
|
|
8
8
|
# Usage: hotspots.sh [--base <ref>] [--] <path>...
|
|
9
9
|
# Config: forge.yaml maintenance.hotspot {enabled, file_lines, growth_lines, exclude_globs}; defaults true/2000/150/[]
|
|
10
|
-
# (also when forge.yaml or yq is missing).
|
|
10
|
+
# (also when forge.yaml or yq is missing); maintenance.generated_paths (forge#4519): shell globs of generated,
|
|
11
|
+
# bot-owned files, default [].
|
|
11
12
|
# Output (stdout, one JSON object):
|
|
12
|
-
# {"enabled","file_lines","growth_lines","
|
|
13
|
+
# {"enabled","file_lines","growth_lines","generated_paths",
|
|
14
|
+
# "files":[{path,lines,commits_90d,hotspot,over,generated,excluded,base_lines,growth}]}
|
|
13
15
|
# lines = `wc -l` of the working-tree file (0 when missing); commits_90d = `git rev-list --count --since=90.days HEAD`;
|
|
14
|
-
# hotspot = lines × commits_90d; over = lines > file_lines;
|
|
15
|
-
#
|
|
16
|
+
# hotspot = lines × commits_90d; over = lines > file_lines; generated = linguist-generated set by .gitattributes, a
|
|
17
|
+
# match of a generated_paths glob, or `@generated` / `DO NOT EDIT` in the first 5 lines of the working-tree file;
|
|
18
|
+
# excluded = generated, linguist-vendored, or a match of an exclude_glob (shell pattern, `*` crosses `/`). Without --base no blob is read and
|
|
16
19
|
# base_lines/growth are null; with --base they are the line count at git merge-base(<ref>, HEAD) (0 when absent there)
|
|
17
|
-
# and lines - base_lines. enabled:false prints files:[] and does no per-file work.
|
|
20
|
+
# and lines - base_lines. enabled:false prints files:[] (generated_paths still set) and does no per-file work.
|
|
18
21
|
# Exit: 0 ok | 2 usage or bad ref. bash 3.2 portable.
|
|
19
22
|
set -uo pipefail
|
|
20
23
|
|
|
@@ -31,19 +34,21 @@ while [ $# -gt 0 ]; do
|
|
|
31
34
|
done
|
|
32
35
|
|
|
33
36
|
# ---- config: maintenance.hotspot as JSON, {} when unreadable ----
|
|
34
|
-
CFG='{}'
|
|
37
|
+
CFG='{}'; GEN_CFG='[]'
|
|
35
38
|
if [ -f forge.yaml ] && command -v yq >/dev/null 2>&1; then
|
|
36
39
|
c=$(yq -o=json '.maintenance.hotspot // {}' forge.yaml 2>/dev/null) || c=""
|
|
37
40
|
printf '%s' "$c" | jq -e 'type == "object"' >/dev/null 2>&1 && CFG="$c"
|
|
41
|
+
g=$(yq -o=json '.maintenance.generated_paths // []' forge.yaml 2>/dev/null) || g=""
|
|
42
|
+
printf '%s' "$g" | jq -e 'type == "array"' >/dev/null 2>&1 && GEN_CFG="$g"
|
|
38
43
|
fi
|
|
44
|
+
GEN_CFG=$(printf '%s' "$GEN_CFG" | jq -c '[.[] | strings | select(length > 0)]')
|
|
39
45
|
CFG=$(printf '%s' "$CFG" | jq -c '
|
|
40
46
|
def pos($d): if type == "number" and . > 0 then floor else $d end;
|
|
41
47
|
{ enabled: (.enabled != false), file_lines: (.file_lines | pos(2000)), growth_lines: (.growth_lines | pos(150)),
|
|
42
48
|
exclude_globs: ([(.exclude_globs // [])[]? | strings]) }')
|
|
43
49
|
ENABLED=$(printf '%s' "$CFG" | jq -r '.enabled')
|
|
44
50
|
FILE_LINES=$(printf '%s' "$CFG" | jq -r '.file_lines')
|
|
45
|
-
|
|
46
|
-
head_json() { printf '%s' "$CFG" | jq -c --argjson files "$1" '{enabled, file_lines, growth_lines, files: $files}'; }
|
|
51
|
+
head_json() { printf '%s' "$CFG" | jq -c --argjson files "$1" --argjson gen "$GEN_CFG" '{enabled, file_lines, growth_lines, generated_paths: $gen, files: $files}'; }
|
|
47
52
|
[ "$ENABLED" = true ] || { head_json '[]'; exit 0; }
|
|
48
53
|
|
|
49
54
|
MB=""
|
|
@@ -53,6 +58,19 @@ if [ -n "$BASE" ]; then
|
|
|
53
58
|
fi
|
|
54
59
|
HAS_HEAD=0; git rev-parse --verify -q "HEAD^{commit}" >/dev/null && HAS_HEAD=1
|
|
55
60
|
GLOBS=$(printf '%s' "$CFG" | jq -r '.exclude_globs[]')
|
|
61
|
+
GEN_GLOBS=$(printf '%s' "$GEN_CFG" | jq -r '.[]')
|
|
62
|
+
# 0 when $1 matches a newline-separated list of shell patterns in $2 (`*` crosses `/`)
|
|
63
|
+
glob_hit() {
|
|
64
|
+
[ -n "$2" ] || return 1
|
|
65
|
+
while IFS= read -r g; do
|
|
66
|
+
[ -n "$g" ] || continue
|
|
67
|
+
# shellcheck disable=SC2254
|
|
68
|
+
case "$1" in $g) return 0 ;; esac
|
|
69
|
+
done <<EOF
|
|
70
|
+
$2
|
|
71
|
+
EOF
|
|
72
|
+
return 1
|
|
73
|
+
}
|
|
56
74
|
|
|
57
75
|
T=$(mktemp -d "${TMPDIR:-/tmp}/hotspots.XXXXXX") || die "mktemp failed"
|
|
58
76
|
trap 'rm -rf "$T"' EXIT
|
|
@@ -72,29 +90,23 @@ for p in "$@"; do
|
|
|
72
90
|
[ -n "$p" ] || continue
|
|
73
91
|
lines=0; [ -f "$p" ] && lines=$(wc -l < "$p" | tr -d ' ')
|
|
74
92
|
commits=$(cat "$T/c.$i" 2>/dev/null); case "$commits" in ''|*[!0-9]*) commits=0 ;; esac
|
|
75
|
-
excluded=false
|
|
76
|
-
if git check-attr linguist-generated
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# shellcheck disable=SC2254
|
|
81
|
-
case "$p" in $g) excluded=true; break ;; esac
|
|
82
|
-
done <<EOF
|
|
83
|
-
$GLOBS
|
|
84
|
-
EOF
|
|
85
|
-
fi
|
|
93
|
+
generated=false; excluded=false
|
|
94
|
+
if git check-attr linguist-generated -- "$p" 2>/dev/null | grep -qE ': (set|true)$' || glob_hit "$p" "$GEN_GLOBS" \
|
|
95
|
+
|| { [ -f "$p" ] && head -n 5 "$p" 2>/dev/null | grep -qE '@generated|DO NOT EDIT'; }; then generated=true; excluded=true; fi
|
|
96
|
+
if [ "$excluded" = false ] && git check-attr linguist-vendored -- "$p" 2>/dev/null | grep -qE ': (set|true)$'; then excluded=true; fi
|
|
97
|
+
if [ "$excluded" = false ] && glob_hit "$p" "$GLOBS"; then excluded=true; fi
|
|
86
98
|
base_lines=null; growth=null
|
|
87
99
|
if [ -n "$MB" ]; then
|
|
88
100
|
base_lines=0
|
|
89
101
|
git cat-file -e "$MB:$p" 2>/dev/null && base_lines=$(git show "$MB:$p" | wc -l | tr -d ' ')
|
|
90
102
|
growth=$((lines - base_lines))
|
|
91
103
|
fi
|
|
92
|
-
printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$p" "$lines" "$commits" "$excluded" "$base_lines" "$growth" >> "$T/rows"
|
|
104
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$p" "$lines" "$commits" "$excluded" "$base_lines" "$growth" "$generated" >> "$T/rows"
|
|
93
105
|
done
|
|
94
106
|
|
|
95
107
|
FILES=$(jq -R -s -c --argjson fl "$FILE_LINES" '
|
|
96
108
|
[split("\n")[] | select(length > 0) | split("\t") |
|
|
97
109
|
{ path: .[0], lines: (.[1] | tonumber), commits_90d: (.[2] | tonumber) } as $r |
|
|
98
|
-
$r + { hotspot: ($r.lines * $r.commits_90d), over: ($r.lines > $fl), excluded: (.[3] == "true"),
|
|
110
|
+
$r + { hotspot: ($r.lines * $r.commits_90d), over: ($r.lines > $fl), generated: (.[6] == "true"), excluded: (.[3] == "true"),
|
|
99
111
|
base_lines: (.[4] | fromjson), growth: (.[5] | fromjson) }]' < "$T/rows")
|
|
100
112
|
head_json "$FILES"
|
package/scripts/hotspots.test.sh
CHANGED
|
@@ -27,7 +27,7 @@ echo y >> big.py; lines small.py 10; commit c2
|
|
|
27
27
|
OUT="$(bash "$S" big.py ./small.py missing/gone.py)"; rc=$?
|
|
28
28
|
eq "rc" "$rc" 0
|
|
29
29
|
eq "defaults" "$(q '[.enabled,.file_lines,.growth_lines]')" '[true,2000,150]'
|
|
30
|
-
eq "big facts" "$(q '.files[0]')" '{"path":"big.py","lines":2102,"commits_90d":2,"hotspot":4204,"over":true,"excluded":false,"base_lines":null,"growth":null}'
|
|
30
|
+
eq "big facts" "$(q '.files[0]')" '{"path":"big.py","lines":2102,"commits_90d":2,"hotspot":4204,"over":true,"generated":false,"excluded":false,"base_lines":null,"growth":null}'
|
|
31
31
|
eq "small facts (./ stripped)" "$(q '.files[1] | [.path,.lines,.commits_90d,.over]')" '["small.py",10,1,false]'
|
|
32
32
|
eq "missing file" "$(q '.files[2] | [.path,.lines,.commits_90d,.over]')" '["missing/gone.py",0,0,false]'
|
|
33
33
|
OUT="$(bash "$S")"; eq "no paths" "$(q '.files')" '[]'
|
|
@@ -49,6 +49,13 @@ printf 'gen/** linguist-generated\nvendor/** linguist-vendored=true\n' > .gitatt
|
|
|
49
49
|
commit gen
|
|
50
50
|
OUT="$(bash "$S" gen/api.py vendor/lib/x.js dist/bundle.js big.py)"
|
|
51
51
|
eq "attr exclusions" "$(q '[.files[] | .excluded]')" '[true,true,false,false]'
|
|
52
|
+
eq "attr generated" "$(q '[.files[] | .generated]')" '[true,false,false,false]'
|
|
53
|
+
# forge#4519: a generated-file header in the first 5 lines marks the file generated
|
|
54
|
+
{ echo '# @generated'; i=0; while [ $i -lt 2500 ]; do echo "l$i"; i=$((i+1)); done; } > hdr.py
|
|
55
|
+
{ i=0; while [ $i -lt 2500 ]; do echo "l$i"; i=$((i+1)); done; echo '# DO NOT EDIT'; } > late.py
|
|
56
|
+
OUT="$(bash "$S" hdr.py late.py)"
|
|
57
|
+
rm -f hdr.py late.py
|
|
58
|
+
eq "header generated" "$(q '[.files[] | [.generated,.excluded]]')" '[[true,true],[false,false]]'
|
|
52
59
|
if [ "$HAVE_YQ" = 1 ]; then
|
|
53
60
|
printf 'maintenance:\n hotspot:\n file_lines: 100\n growth_lines: 5\n exclude_globs: ["dist/*", "*.lock"]\n' > forge.yaml
|
|
54
61
|
OUT="$(bash "$S" dist/bundle.js small.py big.py)"
|
|
@@ -60,7 +67,19 @@ if [ "$HAVE_YQ" = 1 ]; then
|
|
|
60
67
|
eq "opt-out" "$(q '[.enabled,.files]')" '[false,[]]'
|
|
61
68
|
printf 'maintenance:\n hotspot:\n file_lines: nope\n' > forge.yaml
|
|
62
69
|
OUT="$(bash "$S" big.py)"; eq "bad value falls back" "$(q '.file_lines')" 2000
|
|
63
|
-
|
|
70
|
+
# forge#4519: maintenance.generated_paths marks bot-owned files generated (and so excluded), even over the threshold
|
|
71
|
+
mkdir -p web/public/openapi-versions
|
|
72
|
+
lines web/public/openapi.json 2500; lines web/public/openapi-versions/3.29.2.json 2500
|
|
73
|
+
printf 'maintenance:\n generated_paths: ["web/public/openapi*.json", "web/public/openapi-versions/*"]\n' > forge.yaml
|
|
74
|
+
OUT="$(bash "$S" web/public/openapi.json web/public/openapi-versions/3.29.2.json big.py gen/api.py)"
|
|
75
|
+
eq "generated_paths reported" "$(q '.generated_paths')" '["web/public/openapi*.json","web/public/openapi-versions/*"]'
|
|
76
|
+
eq "generated_paths: generated" "$(q '[.files[] | .generated]')" '[true,true,false,true]'
|
|
77
|
+
eq "generated_paths: excluded" "$(q '[.files[] | .excluded]')" '[true,true,false,true]'
|
|
78
|
+
printf 'maintenance:\n generated_paths: ["*.json"]\n hotspot:\n enabled: false\n' > forge.yaml
|
|
79
|
+
OUT="$(bash "$S" big.py)"; eq "generated_paths with the gate off" "$(q '[.enabled,.generated_paths,.files]')" '[false,["*.json"],[]]'
|
|
80
|
+
printf 'maintenance:\n generated_paths: nope\n' > forge.yaml
|
|
81
|
+
OUT="$(bash "$S" big.py)"; eq "bad generated_paths falls back" "$(q '.generated_paths')" '[]'
|
|
82
|
+
rm -rf forge.yaml web
|
|
64
83
|
else
|
|
65
84
|
echo "SKIP: forge.yaml cases need mikefarah yq v4"
|
|
66
85
|
fi
|
|
@@ -93,7 +112,7 @@ done
|
|
|
93
112
|
eq "30k exact commits" "$(q '[.files[] | .commits_90d]')" '[300,60,60,60]'
|
|
94
113
|
eq "30k exact lines" "$(q '[.files[] | .lines]')" '[1,1,1,1]'
|
|
95
114
|
if [ "$best" -lt 1000 ]; then ok; else bad "30k-commit run took ${best} ms (limit 1000)"; fi
|
|
96
|
-
grep -q '"rev-list"' "$TMP/trace.json"
|
|
115
|
+
if grep -q '"rev-list"' "$TMP/trace.json"; then ok; else bad "git trace not captured"; fi
|
|
97
116
|
if grep -qE '"(cat-file|show)"' "$TMP/trace.json"; then bad "blob read without --base"; else ok; fi
|
|
98
117
|
if [ -e "$TMP/net.log" ]; then bad "network tool invoked: $(tr '\n' ' ' < "$TMP/net.log")"; else ok; fi
|
|
99
118
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# Fixtures for scripts/refresh-bot-token.sh (#4507): hosts.yml is replaced by rename, never rewritten in place.
|
|
6
|
+
# Run: bash scripts/refresh-bot-token.test.sh (bash 3.2 compatible). Stubs `node` and `gh`; no network.
|
|
7
|
+
|
|
8
|
+
set -u
|
|
9
|
+
DIR=$(cd "$(dirname "$0")" && pwd)
|
|
10
|
+
S="$DIR/refresh-bot-token.sh"
|
|
11
|
+
T=$(mktemp -d "${TMPDIR:-/tmp}/refresh-bot-token-test.XXXXXX")
|
|
12
|
+
trap 'rm -rf "$T"' EXIT
|
|
13
|
+
pass=0; fail=0
|
|
14
|
+
ok() { pass=$((pass+1)); }
|
|
15
|
+
no() { fail=$((fail+1)); echo "FAIL: $1"; }
|
|
16
|
+
|
|
17
|
+
mkdir -p "$T/bin" "$T/cfg"
|
|
18
|
+
echo key > "$T/key.pem"
|
|
19
|
+
cat > "$T/bin/node" <<'STUB'
|
|
20
|
+
#!/usr/bin/env bash
|
|
21
|
+
printf 'tok-new'
|
|
22
|
+
STUB
|
|
23
|
+
cat > "$T/bin/gh" <<'STUB'
|
|
24
|
+
#!/usr/bin/env bash
|
|
25
|
+
echo "$GH_CONFIG_DIR $*" >> "$GH_LOG"
|
|
26
|
+
case "$1 $2" in
|
|
27
|
+
"auth login") [ -n "${GH_FAIL:-}" ] && exit 1; cat > /dev/null; printf 'github.com:\n oauth_token: tok-new\n' > "$GH_CONFIG_DIR/hosts.yml" ;;
|
|
28
|
+
"auth status") echo "Logged in to github.com account rapiercraft-forgedock[bot]" ;;
|
|
29
|
+
esac
|
|
30
|
+
exit 0
|
|
31
|
+
STUB
|
|
32
|
+
chmod +x "$T/bin/node" "$T/bin/gh"
|
|
33
|
+
inode() { ls -i "$1" | awk '{print $1}'; }
|
|
34
|
+
|
|
35
|
+
# (a) success: gh writes in a staging dir; hosts.yml is renamed into place
|
|
36
|
+
printf 'github.com:\n oauth_token: tok-old\n' > "$T/cfg/hosts.yml"
|
|
37
|
+
before=$(inode "$T/cfg/hosts.yml")
|
|
38
|
+
OUT=$(PATH="$T/bin:$PATH" GH_CONFIG_DIR="$T/cfg" GH_LOG="$T/log" bash "$S" --pem "$T/key.pem" 2>&1); RC=$?
|
|
39
|
+
[ "$RC" = 0 ] && ok || no "(a) exit $RC: $OUT"
|
|
40
|
+
grep -q 'tok-new' "$T/cfg/hosts.yml" && ok || no "(a) hosts.yml not updated"
|
|
41
|
+
[ "$(inode "$T/cfg/hosts.yml")" != "$before" ] && ok || no "(a) hosts.yml rewritten in place (same inode)"
|
|
42
|
+
if awk '{print $1}' "$T/log" | grep -qx "$T/cfg"; then no "(a) gh ran against the live config dir"; else ok; fi
|
|
43
|
+
grep -q "auth login" "$T/log" && ok || no "(a) gh auth login not run"
|
|
44
|
+
ls -a "$T/cfg" | grep -q '^\.hosts-stage\.' && no "(a) staging dir left behind" || ok
|
|
45
|
+
case "$OUT" in *"token refreshed"*) ok ;; *) no "(a) success message missing: $OUT" ;; esac
|
|
46
|
+
|
|
47
|
+
# (b) failed login: the live hosts.yml stays byte-identical, exit != 0
|
|
48
|
+
printf 'github.com:\n oauth_token: tok-keep\n' > "$T/cfg/hosts.yml"
|
|
49
|
+
cp "$T/cfg/hosts.yml" "$T/expect"
|
|
50
|
+
PATH="$T/bin:$PATH" GH_CONFIG_DIR="$T/cfg" GH_LOG="$T/log" GH_FAIL=1 bash "$S" --pem "$T/key.pem" >/dev/null 2>&1; RC=$?
|
|
51
|
+
[ "$RC" != 0 ] && ok || no "(b) failed login exited 0"
|
|
52
|
+
cmp -s "$T/expect" "$T/cfg/hosts.yml" && ok || no "(b) hosts.yml changed after a failed login"
|
|
53
|
+
ls -a "$T/cfg" | grep -q '^\.hosts-stage\.' && no "(b) staging dir left behind" || ok
|
|
54
|
+
|
|
55
|
+
echo "refresh-bot-token: $pass passed, $fail failed"
|
|
56
|
+
[ "$fail" = 0 ]
|