From a159c034f8a039a78491b1c5f3b7ae9689d1b00d Mon Sep 17 00:00:00 2001 From: michele Date: Mon, 10 Aug 2026 00:49:17 +0200 Subject: [PATCH] fix(ops): AGY 1.1.11 definitive wrapper and parser strict validation fix --- .../parse_205_runner_output.py | 138 ++++++++++++++-- .../run_205_followup_via_agy.sh | 51 +++--- .../antigravity-cli/test_runner_and_parser.sh | 151 ++++++++++++++++++ 3 files changed, 305 insertions(+), 35 deletions(-) create mode 100755 scripts/ops/antigravity-cli/test_runner_and_parser.sh diff --git a/scripts/ops/antigravity-cli/parse_205_runner_output.py b/scripts/ops/antigravity-cli/parse_205_runner_output.py index 591c39a..480422a 100755 --- a/scripts/ops/antigravity-cli/parse_205_runner_output.py +++ b/scripts/ops/antigravity-cli/parse_205_runner_output.py @@ -2,39 +2,143 @@ import sys import json import re +import argparse def parse_output(text: str) -> dict: fields = { "task_id": None, "esito_205": None, - "repository": "ssh://git@git.netgescon.it:2222/michele/netgescon-day0.git", - "branch": "stabilization/205-zero", + "repository": None, + "branch": None, "commit": None, + "consolidated_unit_id": None, + "blocco_dati": None, + "dates_used": None, "test_eseguiti": None, - "blocco_dati": "no", "note": None, + "absorbed_legacy_fragments": None, + "open_legacy_fragments": None, "raw_text": text } + try: + json_match = re.search(r'\{.*\}', text, re.DOTALL) + if json_match: + data = json.loads(json_match.group(0)) + if isinstance(data, dict): + source_dict = data.get("parsed", data) + for k in fields: + if k in source_dict and source_dict[k] is not None: + fields[k] = source_dict[k] + except Exception: + pass + patterns = { - "esito_205": r"ESITO_205:\s*(.+)", - "task_id": r"TASK_ID:\s*(.+)", - "repository": r"REPOSITORY:\s*(.+)", - "branch": r"BRANCH:\s*(.+)", - "commit": r"COMMIT:\s*(.+)", - "test_eseguiti": r"TEST_ESEGUITI:\s*(.+)", - "blocco_dati": r"BLOCCO_DATI:\s*(.+)", - "note": r"NOTE:\s*(.+)" + "task_id": r"(?:TASK_ID|task_id):\s*(.+)", + "esito_205": r"(?:ESITO_205|esito_205):\s*(.+)", + "repository": r"(?:REPOSITORY|repository):\s*(.+)", + "branch": r"(?:BRANCH|branch):\s*(.+)", + "commit": r"(?:COMMIT|commit):\s*(.+)", + "consolidated_unit_id": r"(?:CONSOLIDATED_UNIT_ID|UNITA_ID|consolidated_unit_id):\s*(.+)", + "blocco_dati": r"(?:BLOCCO_DATI|blocco_dati):\s*(.+)", + "dates_used": r"(?:DATES_USED|dates_used):\s*(.+)", + "test_eseguiti": r"(?:TEST_ESEGUITI|test_eseguiti):\s*(.+)", + "note": r"(?:NOTE|note):\s*(.+)", + "absorbed_legacy_fragments": r"(?:ABSORBED_LEGACY_FRAGMENTS|absorbed_legacy_fragments):\s*(.+)", + "open_legacy_fragments": r"(?:OPEN_LEGACY_FRAGMENTS|open_legacy_fragments):\s*(.+)" } for key, pat in patterns.items(): - m = re.search(pat, text, re.IGNORECASE) - if m: - fields[key] = m.group(1).strip() + if fields[key] is None: + m = re.search(pat, text, re.IGNORECASE) + if m: + val = m.group(1).strip() + fields[key] = val return fields -if __name__ == "__main__": + +def validate_fields(fields: dict, expected_repo: str = None, expected_branch: str = None, expected_commit: str = None) -> tuple[bool, list[str]]: + missing = [] + + if expected_repo and fields.get("repository") != expected_repo: + missing.append("repository") + elif not fields.get("repository"): + missing.append("repository") + + if expected_branch and fields.get("branch") != expected_branch: + missing.append("branch") + elif not fields.get("branch"): + missing.append("branch") + + if expected_commit and fields.get("commit") != expected_commit: + missing.append("commit") + elif not fields.get("commit"): + missing.append("commit") + + mandatory_single_fields = [ + "task_id", + "esito_205", + "consolidated_unit_id", + "blocco_dati", + "dates_used", + "test_eseguiti", + "note", + ] + + for key in mandatory_single_fields: + val = fields.get(key) + if val is None or (isinstance(val, str) and not val.strip()): + missing.append(key) + + abs_frag = fields.get("absorbed_legacy_fragments") + open_frag = fields.get("open_legacy_fragments") + has_abs = abs_frag is not None and (not isinstance(abs_frag, str) or bool(str(abs_frag).strip())) + has_open = open_frag is not None and (not isinstance(open_frag, str) or bool(str(open_frag).strip())) + + if not (has_abs or has_open): + missing.append("absorbed_legacy_fragments|open_legacy_fragments") + + dedup_missing = [] + for item in missing: + if item not in dedup_missing: + dedup_missing.append(item) + + is_valid = len(dedup_missing) == 0 + return is_valid, dedup_missing + + +def main(): + parser = argparse.ArgumentParser(description="Parse and validate 205 runner output.") + parser.add_argument("--expected-repo", help="Expected git repository URL") + parser.add_argument("--expected-branch", help="Expected git branch name") + parser.add_argument("--expected-commit", help="Expected git commit hash") + args = parser.parse_args() + input_text = sys.stdin.read() - parsed = parse_output(input_text) - print(json.dumps(parsed, ensure_ascii=False, indent=2)) + fields = parse_output(input_text) + is_valid, missing = validate_fields( + fields, + expected_repo=args.expected_repo, + expected_branch=args.expected_branch, + expected_commit=args.expected_commit + ) + + if is_valid: + output_data = { + "ok": True, + "parsed": fields + } + print(json.dumps(output_data, ensure_ascii=False, indent=2)) + sys.exit(0) + else: + output_data = { + "ok": False, + "missing": missing, + "parsed": fields + } + print(json.dumps(output_data, ensure_ascii=False, indent=2)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/scripts/ops/antigravity-cli/run_205_followup_via_agy.sh b/scripts/ops/antigravity-cli/run_205_followup_via_agy.sh index 78f65ce..caeddf3 100755 --- a/scripts/ops/antigravity-cli/run_205_followup_via_agy.sh +++ b/scripts/ops/antigravity-cli/run_205_followup_via_agy.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -export PATH="$HOME/.local/bin:$PATH" +export PATH="$PATH:$HOME/.local/bin" unset ANTIGRAVITY_LS_ADDRESS ANTIGRAVITY_CSRF_TOKEN ANTIGRAVITY_AGENT SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -43,24 +43,39 @@ else fi fi -RAW_OUTPUT="$("$EXEC_BIN" -p "$PROMPT" --model "$MODEL" --add-dir "$REPO_DIR" 2>&1)" || true +set +e +RAW_OUTPUT="$("$EXEC_BIN" -p "$PROMPT" --model "$MODEL" --add-dir "$REPO_DIR" 2>&1)" +AGY_EXIT=$? +set -e -REPO_VAL=$(git -C "$REPO_DIR" config --get remote.origin.url 2>/dev/null || echo "ssh://git@git.netgescon.it:2222/michele/netgescon-day0.git") -BRANCH_VAL=$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "stabilization/205-zero") -COMMIT_VAL=$(git -C "$REPO_DIR" rev-parse HEAD 2>/dev/null || echo "") - -if ! grep -q "REPOSITORY:" <<< "$RAW_OUTPUT"; then - RAW_OUTPUT="${RAW_OUTPUT}"$'\n'"REPOSITORY: ${REPO_VAL}" -fi -if ! grep -q "BRANCH:" <<< "$RAW_OUTPUT"; then - RAW_OUTPUT="${RAW_OUTPUT}"$'\n'"BRANCH: ${BRANCH_VAL}" -fi -if ! grep -q "COMMIT:" <<< "$RAW_OUTPUT"; then - RAW_OUTPUT="${RAW_OUTPUT}"$'\n'"COMMIT: ${COMMIT_VAL}" +if [ $AGY_EXIT -ne 0 ]; then + echo "ERROR: agy CLI binary failed with exit code $AGY_EXIT" >&2 + printf '%s\n' "$RAW_OUTPUT" >&2 + exit $AGY_EXIT fi -if [ -f "$PARSER_SCRIPT" ]; then - python3 "$PARSER_SCRIPT" <<< "$RAW_OUTPUT" -else - echo "$RAW_OUTPUT" +if [[ -z "${RAW_OUTPUT//[[:space:]]/}" ]]; then + echo "ERROR: agy produced empty output" >&2 + exit 1 fi + +LOWER_OUTPUT="$(echo "$RAW_OUTPUT" | tr '[:upper:]' '[:lower:]')" +if [[ "$LOWER_OUTPUT" == *"jetski: no output produced"* ]] || \ + [[ "$LOWER_OUTPUT" == *"quota reached"* ]] || \ + [[ "$LOWER_OUTPUT" == *"permission auto-denied"* ]]; then + echo "ERROR: agy output contains invalid execution condition (jetski/quota/permission)" >&2 + printf '%s\n' "$RAW_OUTPUT" >&2 + exit 1 +fi + +REAL_REPO="$(git -C "$REPO_DIR" config --get remote.origin.url)" +REAL_BRANCH="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" +REAL_COMMIT="$(git -C "$REPO_DIR" rev-parse HEAD)" + +set +e +PARSER_OUT="$(python3 "$PARSER_SCRIPT" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$RAW_OUTPUT")" +PARSER_EXIT=$? +set -e + +printf '%s\n' "$PARSER_OUT" +exit $PARSER_EXIT diff --git a/scripts/ops/antigravity-cli/test_runner_and_parser.sh b/scripts/ops/antigravity-cli/test_runner_and_parser.sh new file mode 100755 index 0000000..70e4a96 --- /dev/null +++ b/scripts/ops/antigravity-cli/test_runner_and_parser.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" +PARSER="$SCRIPT_DIR/parse_205_runner_output.py" +WRAPPER="$SCRIPT_DIR/run_205_followup_via_agy.sh" + +REAL_REPO="$(git -C "$REPO_DIR" config --get remote.origin.url)" +REAL_BRANCH="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" +REAL_COMMIT="$(git -C "$REPO_DIR" rev-parse HEAD)" + +echo "=== ESECUZIONE TEST AUTOMATICI WRAPPER E PARSER AGY 1.1.11 ===" + +# ------------------------------------------------------------- +# Test 1: Output Jetski negato -> wrapper fallisce +# ------------------------------------------------------------- +echo -n "Test 1: Output Jetski negato -> " +TEST_BIN_DIR="$(mktemp -d)" +cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra" +#!/usr/bin/env bash +echo 'jetski: no output produced — a tool required permission auto-denied' +exit 0 +EOF +chmod +x "$TEST_BIN_DIR/agy-ultra" + +set +e +TEST1_OUT="$(PATH="$TEST_BIN_DIR:$PATH" "$WRAPPER" <<< "test prompt" 2>&1)" +TEST1_EXIT=$? +set -e +rm -rf "$TEST_BIN_DIR" + +if [ $TEST1_EXIT -ne 0 ]; then + echo "PASS (Exit Code: $TEST1_EXIT)" +else + echo "FAIL (Expected non-zero exit code, got 0)" + exit 1 +fi + +# ------------------------------------------------------------- +# Test 2: Output vuoto -> wrapper fallisce +# ------------------------------------------------------------- +echo -n "Test 2: Output vuoto -> " +TEST_BIN_DIR="$(mktemp -d)" +cat << 'EOF' > "$TEST_BIN_DIR/agy-ultra" +#!/usr/bin/env bash +echo '' +exit 0 +EOF +chmod +x "$TEST_BIN_DIR/agy-ultra" + +set +e +TEST2_OUT="$(PATH="$TEST_BIN_DIR:$PATH" "$WRAPPER" <<< "test prompt" 2>&1)" +TEST2_EXIT=$? +set -e +rm -rf "$TEST_BIN_DIR" + +if [ $TEST2_EXIT -ne 0 ]; then + echo "PASS (Exit Code: $TEST2_EXIT)" +else + echo "FAIL (Expected non-zero exit code, got 0)" + exit 1 +fi + +# ------------------------------------------------------------- +# Test 3: task_id null -> parser fallisce +# ------------------------------------------------------------- +echo -n "Test 3: task_id null -> " +INCOMPLETE_REPORT="ESITO_205: riuscito +REPOSITORY: ${REAL_REPO} +BRANCH: ${REAL_BRANCH} +COMMIT: ${REAL_COMMIT} +CONSOLIDATED_UNIT_ID: 1545 +DATES_USED: 2026-08-10 +TEST_ESEGUITI: ./vendor/bin/pest +BLOCCO_DATI: no +NOTE: Incompleto +ABSORBED_LEGACY_FRAGMENTS: id_cond=12" + +set +e +TEST3_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$INCOMPLETE_REPORT" 2>&1)" +TEST3_EXIT=$? +set -e + +if [ $TEST3_EXIT -eq 1 ] && [[ "$TEST3_OUT" == *'"ok": false'* ]] && [[ "$TEST3_OUT" == *'"task_id"'* ]]; then + echo "PASS (Exit Code: $TEST3_EXIT)" +else + echo "FAIL (Expected exit code 1 with task_id in missing list)" + echo "$TEST3_OUT" + exit 1 +fi + +# ------------------------------------------------------------- +# Test 4: Report completo -> passa +# ------------------------------------------------------------- +echo -n "Test 4: Report completo -> " +COMPLETE_REPORT="TASK_ID: CT-2026-08-10-TEST-001 +ESITO_205: riuscito +REPOSITORY: ${REAL_REPO} +BRANCH: ${REAL_BRANCH} +COMMIT: ${REAL_COMMIT} +CONSOLIDATED_UNIT_ID: 1545 +DATES_USED: 2026-08-10 +TEST_ESEGUITI: ./vendor/bin/pest +BLOCCO_DATI: no +NOTE: Task completato correttamente +ABSORBED_LEGACY_FRAGMENTS: id_cond=12" + +set +e +TEST4_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$COMPLETE_REPORT" 2>&1)" +TEST4_EXIT=$? +set -e + +if [ $TEST4_EXIT -eq 0 ] && [[ "$TEST4_OUT" == *'"ok": true'* ]]; then + echo "PASS (Exit Code: $TEST4_EXIT)" +else + echo "FAIL (Expected exit code 0 with ok: true)" + echo "$TEST4_OUT" + exit 1 +fi + +# ------------------------------------------------------------- +# Test 5: Git diverso dal reale -> fallisce +# ------------------------------------------------------------- +echo -n "Test 5: Git diverso dal reale -> " +MISMATCH_REPORT="TASK_ID: CT-2026-08-10-TEST-001 +ESITO_205: riuscito +REPOSITORY: ssh://git@wrong-repo.git +BRANCH: wrong-branch +COMMIT: deadbeef1234 +CONSOLIDATED_UNIT_ID: 1545 +DATES_USED: 2026-08-10 +TEST_ESEGUITI: ./vendor/bin/pest +BLOCCO_DATI: no +NOTE: Mismatch test +ABSORBED_LEGACY_FRAGMENTS: id_cond=12" + +set +e +TEST5_OUT="$(python3 "$PARSER" --expected-repo "$REAL_REPO" --expected-branch "$REAL_BRANCH" --expected-commit "$REAL_COMMIT" <<< "$MISMATCH_REPORT" 2>&1)" +TEST5_EXIT=$? +set -e + +if [ $TEST5_EXIT -eq 1 ] && [[ "$TEST5_OUT" == *'"ok": false'* ]] && [[ "$TEST5_OUT" == *'"repository"'* ]]; then + echo "PASS (Exit Code: $TEST5_EXIT)" +else + echo "FAIL (Expected exit code 1 with git mismatch)" + echo "$TEST5_OUT" + exit 1 +fi + +echo "=== TUTTI I TEST HANNO AVUTO ESITO POSITIVO ==="