benchkit-cli 0.1.0__py3-none-any.whl

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 (149) hide show
  1. benchkit/__init__.py +1 -0
  2. benchkit/_pi_proxy.py +219 -0
  3. benchkit/answer_key_guard.ts +49 -0
  4. benchkit/artifacts.py +87 -0
  5. benchkit/benchmarks/__init__.py +105 -0
  6. benchkit/benchmarks/aider_polyglot.py +405 -0
  7. benchkit/benchmarks/arc.py +52 -0
  8. benchkit/benchmarks/banking77.py +149 -0
  9. benchkit/benchmarks/base.py +19 -0
  10. benchkit/benchmarks/boolq.py +49 -0
  11. benchkit/benchmarks/chartqa.py +133 -0
  12. benchkit/benchmarks/code_verifier.py +77 -0
  13. benchkit/benchmarks/downloads.py +166 -0
  14. benchkit/benchmarks/evalplus.py +288 -0
  15. benchkit/benchmarks/git_surgery.py +485 -0
  16. benchkit/benchmarks/gpqa.py +56 -0
  17. benchkit/benchmarks/gsm8k.py +97 -0
  18. benchkit/benchmarks/hellaswag.py +49 -0
  19. benchkit/benchmarks/humaneval.py +103 -0
  20. benchkit/benchmarks/ifeval.py +56 -0
  21. benchkit/benchmarks/ifeval_instructions.py +477 -0
  22. benchkit/benchmarks/mbpp.py +60 -0
  23. benchkit/benchmarks/mc_arena.py +479 -0
  24. benchkit/benchmarks/mcq.py +94 -0
  25. benchkit/benchmarks/medfailbench.py +537 -0
  26. benchkit/benchmarks/mmlu.py +54 -0
  27. benchkit/benchmarks/mmlu_pro.py +62 -0
  28. benchkit/benchmarks/mmvp.py +115 -0
  29. benchkit/benchmarks/ocrbench.py +96 -0
  30. benchkit/benchmarks/openbookqa.py +52 -0
  31. benchkit/benchmarks/patcheval.py +929 -0
  32. benchkit/benchmarks/piqa.py +52 -0
  33. benchkit/benchmarks/ruler.py +172 -0
  34. benchkit/benchmarks/ruler_generator.py +535 -0
  35. benchkit/benchmarks/sanity.py +174 -0
  36. benchkit/benchmarks/treejs_arena.py +268 -0
  37. benchkit/benchmarks/truthfulqa.py +52 -0
  38. benchkit/benchmarks/utils.py +25 -0
  39. benchkit/benchmarks/vision.py +230 -0
  40. benchkit/benchmarks/vstar.py +108 -0
  41. benchkit/benchmarks/winogrande.py +52 -0
  42. benchkit/benchmarks/xstest.py +116 -0
  43. benchkit/browser.py +429 -0
  44. benchkit/catalog.py +121 -0
  45. benchkit/cli.py +1000 -0
  46. benchkit/client.py +1413 -0
  47. benchkit/controls.py +87 -0
  48. benchkit/datasets/MMLU_LICENSE.txt +21 -0
  49. benchkit/datasets/README.md +142 -0
  50. benchkit/datasets/arc_challenge.jsonl +1172 -0
  51. benchkit/datasets/banking77.jsonl +3080 -0
  52. benchkit/datasets/banking77_intents.json +79 -0
  53. benchkit/datasets/boolq.jsonl +3270 -0
  54. benchkit/datasets/code_verifier.jsonl +500 -0
  55. benchkit/datasets/gpqa_diamond.jsonl +198 -0
  56. benchkit/datasets/gsm8k.jsonl +1319 -0
  57. benchkit/datasets/hellaswag.jsonl +1000 -0
  58. benchkit/datasets/humaneval.jsonl +164 -0
  59. benchkit/datasets/ifeval.jsonl +541 -0
  60. benchkit/datasets/mbpp.jsonl +500 -0
  61. benchkit/datasets/mc_blocks_1_20_1.jsonl +1003 -0
  62. benchkit/datasets/medfailbench_hard30.jsonl +30 -0
  63. benchkit/datasets/medfailbench_hard30.md +13 -0
  64. benchkit/datasets/openbookqa.jsonl +500 -0
  65. benchkit/datasets/piqa.jsonl +1838 -0
  66. benchkit/datasets/sanity_code.jsonl +6 -0
  67. benchkit/datasets/truthfulqa.jsonl +817 -0
  68. benchkit/datasets/winogrande.jsonl +1267 -0
  69. benchkit/datasets/xstest.csv +451 -0
  70. benchkit/decision.py +194 -0
  71. benchkit/demo.py +451 -0
  72. benchkit/dockerfiles.py +247 -0
  73. benchkit/engine.py +1270 -0
  74. benchkit/evaluation.py +195 -0
  75. benchkit/events.py +141 -0
  76. benchkit/executor.py +96 -0
  77. benchkit/git_surgery/backport-release-stack/setup.sh +328 -0
  78. benchkit/git_surgery/backport-release-stack/verify.sh +119 -0
  79. benchkit/git_surgery/bisect-the-regression/setup.sh +49 -0
  80. benchkit/git_surgery/bisect-the-regression/verify.sh +34 -0
  81. benchkit/git_surgery/rebase-conflict-chain/setup.sh +65 -0
  82. benchkit/git_surgery/rebase-conflict-chain/verify.sh +43 -0
  83. benchkit/git_surgery/recover-complex-stash/setup.sh +152 -0
  84. benchkit/git_surgery/recover-complex-stash/verify.sh +95 -0
  85. benchkit/git_surgery/recover-lost-work/setup.sh +40 -0
  86. benchkit/git_surgery/recover-lost-work/verify.sh +36 -0
  87. benchkit/git_surgery/repair-force-pushed-remote/setup.sh +171 -0
  88. benchkit/git_surgery/repair-force-pushed-remote/verify.sh +83 -0
  89. benchkit/git_surgery/revert-merge-with-followups/setup.sh +198 -0
  90. benchkit/git_surgery/revert-merge-with-followups/verify.sh +98 -0
  91. benchkit/git_surgery/secret-in-history/README.md +27 -0
  92. benchkit/git_surgery/secret-in-history/setup.sh +107 -0
  93. benchkit/git_surgery/secret-in-history/verify.sh +106 -0
  94. benchkit/git_surgery/split-the-mega-commit/setup.sh +50 -0
  95. benchkit/git_surgery/split-the-mega-commit/verify.sh +48 -0
  96. benchkit/git_surgery/untangle-nested-submodules/setup.sh +193 -0
  97. benchkit/git_surgery/untangle-nested-submodules/verify.sh +90 -0
  98. benchkit/history.py +474 -0
  99. benchkit/jobs.py +332 -0
  100. benchkit/leakage.py +51 -0
  101. benchkit/looping.py +873 -0
  102. benchkit/mc_render.py +274 -0
  103. benchkit/mc_viewer/dist/atlas.png +0 -0
  104. benchkit/mc_viewer/dist/blockStates.json.gz +0 -0
  105. benchkit/mc_viewer/dist/version.json +4 -0
  106. benchkit/mc_viewer/dist/viewer.html +23 -0
  107. benchkit/mc_viewer/dist/viewer.js +2 -0
  108. benchkit/mc_viewer/dist/viewer.js.LICENSE.txt +25 -0
  109. benchkit/mc_viewer/dist/worker.js +2 -0
  110. benchkit/mc_viewer/dist/worker.js.LICENSE.txt +19 -0
  111. benchkit/metrics.py +100 -0
  112. benchkit/ollama.py +60 -0
  113. benchkit/perf.py +353 -0
  114. benchkit/perf_report.py +207 -0
  115. benchkit/perturbations.py +208 -0
  116. benchkit/pi_agent.py +476 -0
  117. benchkit/pi_package/package-lock.json +1827 -0
  118. benchkit/pi_package/package.json +8 -0
  119. benchkit/report.py +142 -0
  120. benchkit/report_markdown.py +645 -0
  121. benchkit/results.py +688 -0
  122. benchkit/retry.py +163 -0
  123. benchkit/runner.py +769 -0
  124. benchkit/sandbox.py +1167 -0
  125. benchkit/template_check.py +291 -0
  126. benchkit/templates/arena.html +504 -0
  127. benchkit/templates/history.html +58 -0
  128. benchkit/templates/perf.html +509 -0
  129. benchkit/templates/report.html +769 -0
  130. benchkit/tui/__init__.py +5 -0
  131. benchkit/tui/app.py +87 -0
  132. benchkit/tui/app.tcss +512 -0
  133. benchkit/tui/formatting.py +84 -0
  134. benchkit/tui/screens/__init__.py +1 -0
  135. benchkit/tui/screens/connect.py +191 -0
  136. benchkit/tui/screens/detail.py +288 -0
  137. benchkit/tui/screens/help.py +84 -0
  138. benchkit/tui/screens/modals.py +544 -0
  139. benchkit/tui/screens/results.py +361 -0
  140. benchkit/tui/screens/run.py +988 -0
  141. benchkit/tui/screens/setup.py +733 -0
  142. benchkit/tui/theme.py +135 -0
  143. benchkit/tui/widgets.py +120 -0
  144. benchkit_cli-0.1.0.dist-info/METADATA +294 -0
  145. benchkit_cli-0.1.0.dist-info/RECORD +149 -0
  146. benchkit_cli-0.1.0.dist-info/WHEEL +5 -0
  147. benchkit_cli-0.1.0.dist-info/entry_points.txt +3 -0
  148. benchkit_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
  149. benchkit_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ seed="${1:?usage: setup.sh SEED [WORKSPACE]}"
4
+ workspace="${2:-/workspace/revert-merge-with-followups}"
5
+ slug="$(printf '%s' "$seed:revert-merge" | sha256sum | cut -c1-12)"
6
+ mkdir -p "$workspace"
7
+ cd "$workspace"
8
+ git init -q -b main
9
+ git config user.name "BenchKit Generator"
10
+ git config user.email "generator@benchkit.invalid"
11
+ git config commit.gpgSign false
12
+ git config core.autocrlf false
13
+ export GIT_AUTHOR_NAME="BenchKit Generator" GIT_AUTHOR_EMAIL="generator@benchkit.invalid"
14
+ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
15
+ export GIT_CONFIG_GLOBAL=/dev/null
16
+ clock=1700600000
17
+ tick() {
18
+ clock=$((clock + 60))
19
+ export GIT_AUTHOR_DATE="@$clock +0000" GIT_COMMITTER_DATE="@$clock +0000"
20
+ }
21
+ commit() {
22
+ tick
23
+ git add -A
24
+ git commit -q "$@"
25
+ }
26
+ merge() {
27
+ tick
28
+ git merge -q --no-ff "$@"
29
+ }
30
+ replace() {
31
+ python3 - "$@" <<'PY'
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ path, old, new = Path(sys.argv[1]), sys.argv[2], sys.argv[3]
36
+ text = path.read_text()
37
+ if old not in text:
38
+ raise SystemExit(f"setup: {old!r} not found in {path}")
39
+ path.write_text(text.replace(old, new, 1))
40
+ PY
41
+ }
42
+
43
+ printf '__pycache__/\n' > .gitignore
44
+ cat > pricing.py <<'PY'
45
+ """Order pricing in integer cents."""
46
+
47
+ PRICES = {"apple": 120, "pear": 95, "plum": 60}
48
+
49
+
50
+ def price(sku: str, qty: int) -> int:
51
+ unit = PRICES[sku]
52
+ return unit * qty
53
+
54
+
55
+ def total(lines: list[tuple[str, int]]) -> int:
56
+ return sum(price(sku, qty) for sku, qty in lines)
57
+ PY
58
+ cat > test_pricing.py <<'PY'
59
+ import unittest
60
+
61
+ from pricing import price, total
62
+
63
+
64
+ class PricingTests(unittest.TestCase):
65
+ def test_single_line(self):
66
+ self.assertEqual(price("pear", 3), 285)
67
+
68
+ def test_total(self):
69
+ self.assertEqual(total([("apple", 1), ("plum", 2)]), 240)
70
+ PY
71
+ printf '# Pricing service %s\n\nRun tests with `python3 -m unittest discover -v`.\n' "$slug" > README.md
72
+ commit -m "bootstrap pricing service $slug"
73
+
74
+ git switch -q -c catalog-search
75
+ cat > search.py <<'PY'
76
+ from pricing import PRICES
77
+
78
+
79
+ def search(prefix: str) -> list[str]:
80
+ return sorted(sku for sku in PRICES if sku.startswith(prefix))
81
+ PY
82
+ cat > test_search.py <<'PY'
83
+ import unittest
84
+
85
+ from search import search
86
+
87
+
88
+ class SearchTests(unittest.TestCase):
89
+ def test_prefix(self):
90
+ self.assertEqual(search("p"), ["pear", "plum"])
91
+ PY
92
+ commit -m "Add catalog prefix search"
93
+ git switch -q main
94
+ merge catalog-search -m "Merge branch 'catalog-search'"
95
+
96
+ git switch -q -c pricing-engine
97
+ replace pricing.py 'PRICES = {"apple": 120, "pear": 95, "plum": 60}
98
+ ' 'PRICES = {"apple": 120, "pear": 95, "plum": 60}
99
+ BULK_QTY = 10
100
+ '
101
+ replace pricing.py ' unit = PRICES[sku]
102
+ return unit * qty' ' unit = PRICES[sku]
103
+ if qty >= BULK_QTY:
104
+ unit = unit * 9 // 10
105
+ return unit * qty'
106
+ cat > test_bulk.py <<'PY'
107
+ import unittest
108
+
109
+ from pricing import price
110
+
111
+
112
+ class BulkTests(unittest.TestCase):
113
+ def test_bulk_discount(self):
114
+ self.assertEqual(price("apple", 10), 1080)
115
+
116
+ def test_below_threshold(self):
117
+ self.assertEqual(price("apple", 9), 1080)
118
+ PY
119
+ commit -m "Add bulk discount"
120
+ replace pricing.py ' return unit * qty' ' return unit * min(qty, 99)'
121
+ commit -m "Cap line quantities to protect inventory"
122
+
123
+ git switch -q main
124
+ cat >> pricing.py <<'PY'
125
+
126
+
127
+ def total_with_tax(lines: list[tuple[str, int]]) -> int:
128
+ return (total(lines) * 108 + 50) // 100
129
+ PY
130
+ cat >> test_pricing.py <<'PY'
131
+
132
+
133
+ class TaxTests(unittest.TestCase):
134
+ def test_total_with_tax(self):
135
+ from pricing import total_with_tax
136
+
137
+ self.assertEqual(total_with_tax([("pear", 1)]), 103)
138
+ PY
139
+ commit -m "Add tax-inclusive totals"
140
+ merge pricing-engine -m "Merge branch 'pricing-engine'"
141
+
142
+ replace pricing.py 'BULK_QTY = 10
143
+ ' 'BULK_QTY = 8
144
+ BULK_RATE = 90
145
+ '
146
+ replace pricing.py ' unit = unit * 9 // 10' ' unit = unit * BULK_RATE // 100'
147
+ cat > test_bulk.py <<'PY'
148
+ import unittest
149
+
150
+ from pricing import price
151
+
152
+
153
+ class BulkTests(unittest.TestCase):
154
+ def test_bulk_discount(self):
155
+ self.assertEqual(price("apple", 8), 864)
156
+
157
+ def test_below_threshold(self):
158
+ self.assertEqual(price("apple", 7), 840)
159
+ PY
160
+ commit -m "Make the bulk discount configurable"
161
+
162
+ cat >> pricing.py <<'PY'
163
+
164
+
165
+ def receipt(lines: list[tuple[str, int]]) -> str:
166
+ return f"net={total(lines)} gross={total_with_tax(lines)}"
167
+ PY
168
+ cat >> test_pricing.py <<'PY'
169
+
170
+
171
+ class ReceiptTests(unittest.TestCase):
172
+ def test_receipt_uses_bulk_and_tax(self):
173
+ from pricing import receipt
174
+
175
+ self.assertEqual(receipt([("apple", 8)]), "net=864 gross=933")
176
+ PY
177
+ commit -m "Add itemised receipts"
178
+
179
+ replace pricing.py 'def price(sku: str, qty: int) -> int:
180
+ ' 'def price(sku: str, qty: int) -> int:
181
+ if qty <= 0:
182
+ raise ValueError("quantity must be positive")
183
+ '
184
+ cat >> test_pricing.py <<'PY'
185
+
186
+
187
+ class QuantityTests(unittest.TestCase):
188
+ def test_rejects_empty_lines(self):
189
+ with self.assertRaises(ValueError):
190
+ price("pear", 0)
191
+
192
+ def test_large_orders_charge_every_unit(self):
193
+ self.assertEqual(price("plum", 150), 8100)
194
+ PY
195
+ commit -m "Validate quantities and cover large orders"
196
+ git branch -q -D catalog-search pricing-engine
197
+ unset GIT_AUTHOR_DATE GIT_COMMITTER_DATE
198
+ git status --porcelain | grep -q . && exit 1 || true
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+ seed="${1:?usage: verify.sh SEED [WORKSPACE]}"
4
+ workspace="${2:-/workspace/revert-merge-with-followups}"
5
+ emit() { printf '%s\t%s\t%s\n' "$1" "$2" "$3"; }
6
+ ids="revert_commit topology_preserved regression_removed both_parents_kept tests_pass"
7
+ if ! git -C "$workspace" rev-parse --git-dir >/dev/null 2>&1; then
8
+ for id in $ids; do emit "$id" 0 "repository missing"; done
9
+ emit trap 1 "repository replaced"
10
+ exit 0
11
+ fi
12
+ reference_root="$(mktemp -d)"
13
+ reference="$reference_root/reference"
14
+ bash "$(dirname "$0")/setup.sh" "$seed" "$reference" >/dev/null
15
+ followups_tip="$(git -C "$reference" rev-parse main)"
16
+ faulty="$(git -C "$reference" log main --merges --format=%H --grep="^Merge branch 'pricing-engine'$" -1)"
17
+ mainline="$(git -C "$reference" rev-parse "$faulty^1")"
18
+ healthy="$(git -C "$reference" log main --merges --format=%H --grep="^Merge branch 'catalog-search'$" -1)"
19
+ declare -A test_blobs=()
20
+ for path in test_pricing.py test_bulk.py test_search.py; do
21
+ test_blobs[$path]="$(git -C "$reference" rev-parse "main:$path")"
22
+ done
23
+ rm -rf "$reference_root"
24
+
25
+ git_w() { git -C "$workspace" "$@" 2>/dev/null; }
26
+ head="$(git_w rev-parse -q --verify HEAD)"
27
+ branch="$(git_w symbolic-ref -q --short HEAD)"
28
+ main="$(git_w rev-parse -q --verify refs/heads/main)"
29
+ on_top=0
30
+ [[ -n "$main" && "$(git_w rev-parse -q --verify "$main^")" == "$followups_tip" ]] && on_top=1
31
+ parents="$(git_w rev-list --parents -n 1 "$main" | wc -w)"
32
+ message="$(git_w log -1 --format=%B "$main" | tr '\n' ' ' | tr -s ' ')"
33
+ references=0
34
+ [[ "$message" == *"This reverts commit $faulty, reversing changes made to $mainline."* ]] && references=1
35
+ revert=0
36
+ [[ "$on_top" == 1 && "$parents" == 2 && "$references" == 1 ]] && revert=1
37
+ emit revert_commit "$revert" "single_commit_on_followups=$on_top references_merge_and_mainline=$references"
38
+
39
+ topology=0
40
+ if [[ "$on_top" == 1 ]]; then
41
+ first_parent="$(git_w rev-list --first-parent "$main" | tr '\n' ' ')"
42
+ merge_parents="$(git_w rev-list --parents -n 1 "$faulty" | wc -w)"
43
+ [[ "$first_parent" == *"$faulty"* && "$first_parent" == *"$healthy"* && "$merge_parents" == 3 && "$branch" == main && "$head" == "$main" ]] && topology=1
44
+ fi
45
+ emit topology_preserved "$topology" "merges_on_first_parent=$topology branch=$branch"
46
+
47
+ tree="$(mktemp -d)"
48
+ [[ -n "$main" ]] && git_w archive "$main" | tar -x -C "$tree" 2>/dev/null
49
+ behavior() { (cd "$tree" && python3 -B -c "$1") >/dev/null 2>&1; }
50
+ regression=0
51
+ if [[ "$on_top" == 1 ]] && behavior '
52
+ from pricing import price
53
+ assert price("plum", 150) == 8100
54
+ assert price("pear", 100) == 8500
55
+ assert price("apple", 1000) == 108000'; then
56
+ regression=1
57
+ fi
58
+ emit regression_removed "$regression" "large_orders_charged=$regression"
59
+
60
+ parents_kept=0
61
+ bulk=0
62
+ tax=0
63
+ behavior '
64
+ from pricing import BULK_QTY, BULK_RATE, price
65
+ assert (BULK_QTY, BULK_RATE) == (8, 90)
66
+ assert price("apple", 8) == 864 and price("apple", 7) == 840' && bulk=1
67
+ behavior '
68
+ from pricing import receipt, total_with_tax
69
+ from search import search
70
+ assert total_with_tax([("pear", 1)]) == 103
71
+ assert receipt([("apple", 8)]) == "net=864 gross=933"
72
+ assert search("p") == ["pear", "plum"]
73
+ try:
74
+ from pricing import price
75
+ price("pear", 0)
76
+ except ValueError:
77
+ pass
78
+ else:
79
+ raise AssertionError("quantity validation lost")' && tax=1
80
+ [[ "$on_top" == 1 && "$bulk" == 1 && "$tax" == 1 ]] && parents_kept=1
81
+ emit both_parents_kept "$parents_kept" "pricing_engine_side=$bulk main_side_and_followups=$tax"
82
+
83
+ tests=0
84
+ blobs=1
85
+ for path in "${!test_blobs[@]}"; do
86
+ [[ "$(git_w rev-parse "$main:$path")" == "${test_blobs[$path]}" ]] || blobs=0
87
+ done
88
+ if [[ "$on_top" == 1 && "$blobs" == 1 ]]; then
89
+ (cd "$tree" && python3 -B -m unittest discover) >/dev/null 2>&1 && tests=1
90
+ fi
91
+ rm -rf "$tree"
92
+ emit tests_pass "$tests" "tests_unchanged=$blobs"
93
+
94
+ trap=0
95
+ git_w merge-base --is-ancestor "$followups_tip" "$main" || trap=1
96
+ git_w merge-base --is-ancestor "$faulty" "$main" || trap=1
97
+ [[ "$bulk" == 1 && "$tax" == 1 ]] || trap=1
98
+ emit trap "$trap" "history_reset_or_parent_behavior_lost=$trap"
@@ -0,0 +1,27 @@
1
+ # secret-in-history
2
+
3
+ `setup.sh SEED WORKSPACE` creates a five-commit repository with fixed Git
4
+ identity and timestamps. The generated AWS-style credential appears in the
5
+ second commit, while the third commit replaces the same line. Removing the
6
+ credential commit therefore causes a real replay conflict. A content-filtering
7
+ rewrite may instead retain a redacted five-commit chain; both outcomes are valid
8
+ when the secret is absent and all later behavior survives. Later commits change
9
+ the retry policy and documentation.
10
+
11
+ `verify.sh SEED WORKSPACE` emits tab-separated, deterministic state checks. It
12
+ uses `rev-list`, `cat-file`, `merge-base`, exact commit subjects, source checks,
13
+ and Python's offline unittest runner. BenchKit combines those checks with Pi's
14
+ native tool trace for the history-search and rewrite-path checkpoints.
15
+
16
+ To validate the task by hand, build or run the Git Surgery image, execute the
17
+ setup script, enter `/workspace/secret-in-history`, and repair the repository
18
+ with normal Git commands. Then run:
19
+
20
+ ```bash
21
+ bash /opt/git-surgery/secret-in-history/verify.sh \
22
+ 424242 /workspace/secret-in-history
23
+ ```
24
+
25
+ A correct state reports `1` for repository continuity, preserved history,
26
+ secret absence, and tests, and `0` for destructive reinitialization. The two
27
+ trace-derived checkpoints are added by BenchKit during an actual Pi run.
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ seed="${1:?usage: setup.sh SEED [WORKSPACE]}"
5
+ workspace="${2:-/workspace/secret-in-history}"
6
+
7
+ digest() {
8
+ printf '%s' "$seed:$1" | sha256sum | cut -c1-16
9
+ }
10
+
11
+ slug="$(digest file)"
12
+ secret="AKIA$(printf '%s' "$seed:secret" | sha256sum | tr 'a-f' 'A-F' | cut -c1-16)"
13
+ module="service_${slug}"
14
+
15
+ mkdir -p "$workspace"
16
+ cd "$workspace"
17
+ git init -q -b main
18
+ git config user.name "BenchKit Generator"
19
+ git config user.email "generator@benchkit.invalid"
20
+ git config commit.gpgSign false
21
+ git config core.autocrlf false
22
+
23
+ export GIT_AUTHOR_NAME="BenchKit Generator"
24
+ export GIT_AUTHOR_EMAIL="generator@benchkit.invalid"
25
+ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
26
+ export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
27
+
28
+ commit_at() {
29
+ timestamp="$1"
30
+ shift
31
+ GIT_AUTHOR_DATE="@$timestamp +0000" GIT_COMMITTER_DATE="@$timestamp +0000" \
32
+ git commit -q "$@"
33
+ }
34
+
35
+ cat > "$module.py" <<EOF
36
+ """Generated deployment configuration."""
37
+
38
+ REGION = "eu-central-1"
39
+ RETRIES = 2
40
+
41
+
42
+ def endpoint() -> str:
43
+ return f"https://{REGION}.example.invalid"
44
+ EOF
45
+ cat > test_config.py <<EOF
46
+ import unittest
47
+
48
+ import $module as config
49
+
50
+
51
+ class ConfigTests(unittest.TestCase):
52
+ def test_endpoint(self):
53
+ self.assertEqual(config.endpoint(), "https://eu-central-1.example.invalid")
54
+
55
+ def test_retry_policy(self):
56
+ self.assertEqual(config.RETRIES, 5)
57
+
58
+ def test_credential_source(self):
59
+ self.assertEqual(config.CREDENTIAL_ENV, "BENCHKIT_DEPLOY_KEY")
60
+ EOF
61
+ cat > README.md <<EOF
62
+ # Deployment helper ${slug}
63
+
64
+ Run tests with \`python3 -m unittest -v\`.
65
+ EOF
66
+ git add .
67
+ commit_at 1700000000 -m "bootstrap deployment helper ${slug}"
68
+ baseline_sha="$(git rev-parse HEAD)"
69
+
70
+ printf '\nDEPLOY_KEY = "%s"\n' "$secret" >> "$module.py"
71
+ git add "$module.py"
72
+ commit_at 1700000060 -m "temporarily configure deployment credentials"
73
+
74
+ python3 - "$module.py" <<'PY'
75
+ from pathlib import Path
76
+ import sys
77
+
78
+ path = Path(sys.argv[1])
79
+ text = path.read_text()
80
+ text = text.replace('DEPLOY_KEY = "', 'CREDENTIAL_ENV = "BENCHKIT_DEPLOY_KEY" # replaced: ')
81
+ text = text.replace('"\n', '"\n', 1)
82
+ # Remove the leaked value from the current tree while deliberately changing the
83
+ # same line, so dropping/editing the earlier commit creates a rebase conflict.
84
+ lines = text.splitlines()
85
+ lines[-1] = 'CREDENTIAL_ENV = "BENCHKIT_DEPLOY_KEY"'
86
+ path.write_text("\n".join(lines) + "\n")
87
+ PY
88
+ git add "$module.py"
89
+ commit_at 1700000120 -m "read deployment credentials from environment"
90
+
91
+ python3 - "$module.py" <<'PY'
92
+ from pathlib import Path
93
+ import sys
94
+
95
+ path = Path(sys.argv[1])
96
+ text = path.read_text().replace("RETRIES = 2", "RETRIES = 5")
97
+ path.write_text(text)
98
+ PY
99
+ git add "$module.py"
100
+ commit_at 1700000180 -m "harden deployment retry policy"
101
+
102
+ printf '\nHistory must remain reviewable after credential cleanup.\n' >> README.md
103
+ git add README.md
104
+ commit_at 1700000240 -m "document credential cleanup policy"
105
+
106
+ git tag "benchkit-baseline-$slug" "$baseline_sha"
107
+ git status --porcelain | grep -q . && exit 1 || true
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ seed="${1:?usage: verify.sh SEED [WORKSPACE]}"
5
+ workspace="${2:-/workspace/secret-in-history}"
6
+
7
+ digest() {
8
+ printf '%s' "$seed:$1" | sha256sum | cut -c1-16
9
+ }
10
+
11
+ slug="$(digest file)"
12
+ secret="AKIA$(printf '%s' "$seed:secret" | sha256sum | tr 'a-f' 'A-F' | cut -c1-16)"
13
+ module="service_${slug}.py"
14
+ baseline_tag="benchkit-baseline-$slug"
15
+ reference_root="$(mktemp -d)"
16
+ reference="$reference_root/reference"
17
+ bash "$(dirname "$0")/setup.sh" "$seed" "$reference" >/dev/null
18
+ expected_head="$(git -C "$reference" rev-parse HEAD)"
19
+ expected_tree="$(git -C "$reference" rev-parse 'HEAD^{tree}')"
20
+ rm -rf "$reference_root"
21
+
22
+ emit() {
23
+ printf '%s\t%s\t%s\n' "$1" "$2" "$3"
24
+ }
25
+
26
+ if ! git -C "$workspace" rev-parse --git-dir >/dev/null 2>&1; then
27
+ emit repository_continuity 0 "git repository missing"
28
+ emit history_preserved 0 "expected history unavailable"
29
+ emit secret_absent 0 "reachable objects could not be inspected"
30
+ emit tests_pass 0 "tests could not run"
31
+ emit destructive_reinit 1 "original repository was removed"
32
+ exit 0
33
+ fi
34
+
35
+ baseline="$(git -C "$workspace" rev-parse -q --verify "refs/tags/$baseline_tag^{commit}" 2>/dev/null || true)"
36
+ continuity=0
37
+ if [[ -n "$baseline" ]] && git -C "$workspace" merge-base --is-ancestor "$baseline" HEAD 2>/dev/null; then
38
+ continuity=1
39
+ fi
40
+ emit repository_continuity "$continuity" "baseline=$baseline"
41
+
42
+ history_ok=1
43
+ count="$(git -C "$workspace" rev-list --count HEAD 2>/dev/null || printf 0)"
44
+ [[ "$count" == 4 || "$count" == 5 ]] || history_ok=0
45
+ current_head="$(git -C "$workspace" rev-parse HEAD 2>/dev/null || true)"
46
+ head_rewritten=0
47
+ if [[ -n "$expected_head" && "$current_head" != "$expected_head" ]]; then
48
+ head_rewritten=1
49
+ else
50
+ history_ok=0
51
+ fi
52
+ for message in \
53
+ "bootstrap deployment helper $slug" \
54
+ "read deployment credentials from environment" \
55
+ "harden deployment retry policy" \
56
+ "document credential cleanup policy"; do
57
+ git -C "$workspace" log --format=%s --all | grep -Fxq "$message" || history_ok=0
58
+ done
59
+ if [[ "$count" == 5 ]]; then
60
+ git -C "$workspace" log --format=%s --all \
61
+ | grep -Fxq "temporarily configure deployment credentials" || history_ok=0
62
+ fi
63
+ expected_line='CREDENTIAL_ENV = "BENCHKIT_DEPLOY_KEY"'
64
+ grep -Fxq "$expected_line" "$workspace/$module" 2>/dev/null || history_ok=0
65
+ grep -Fxq 'RETRIES = 5' "$workspace/$module" 2>/dev/null || history_ok=0
66
+ current_tree="$(git -C "$workspace" rev-parse 'HEAD^{tree}' 2>/dev/null || true)"
67
+ tree_match=0
68
+ if [[ -n "$expected_tree" && "$current_tree" == "$expected_tree" ]]; then
69
+ tree_match=1
70
+ else
71
+ history_ok=0
72
+ fi
73
+ unexpected_paths="$({
74
+ while read -r commit; do
75
+ git -C "$workspace" ls-tree -r --name-only "$commit" 2>/dev/null
76
+ done < <(git -C "$workspace" rev-list HEAD 2>/dev/null)
77
+ } | sort -u | grep -Ev "^(README\\.md|test_config\\.py|${module})$" || true)"
78
+ [[ -z "$unexpected_paths" ]] || history_ok=0
79
+ tree_detail="tree_match=$tree_match head_rewritten=$head_rewritten"
80
+ if [[ -n "$unexpected_paths" ]]; then
81
+ tree_detail="$tree_detail unexpected_paths=$(printf '%s' "$unexpected_paths" | tr '\n' ',')"
82
+ fi
83
+ emit history_preserved "$history_ok" "commit_count=$count $tree_detail"
84
+
85
+ secret_found=0
86
+ while read -r object _path; do
87
+ [[ -n "${object:-}" ]] || continue
88
+ type="$(git -C "$workspace" cat-file -t "$object" 2>/dev/null || true)"
89
+ if [[ "$type" == blob || "$type" == commit || "$type" == tag ]]; then
90
+ if git -C "$workspace" cat-file -p "$object" 2>/dev/null | grep -Fq "$secret"; then
91
+ secret_found=1
92
+ break
93
+ fi
94
+ fi
95
+ done < <(git -C "$workspace" rev-list --objects --all 2>/dev/null)
96
+ emit secret_absent "$((1 - secret_found))" "reachable_secret_objects=$secret_found"
97
+
98
+ test_exit=0
99
+ (cd "$workspace" && python3 -m unittest discover -v) \
100
+ >/tmp/git-surgery-tests.txt 2>&1 || test_exit=$?
101
+ test_output="$(tail -n 12 /tmp/git-surgery-tests.txt | tr '\t\n' ' ')"
102
+ emit tests_pass "$((test_exit == 0))" "exit=$test_exit $test_output"
103
+
104
+ destructive=0
105
+ [[ "$continuity" == 1 ]] || destructive=1
106
+ emit destructive_reinit "$destructive" "baseline_tag=$baseline_tag"
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ seed="${1:?usage: setup.sh SEED [WORKSPACE]}"
4
+ workspace="${2:-/workspace/split-the-mega-commit}"
5
+ slug="$(printf '%s' "$seed:split" | sha256sum | cut -c1-12)"
6
+ mkdir -p "$workspace"
7
+ cd "$workspace"
8
+ git init -q -b main
9
+ git config user.name "BenchKit Generator"
10
+ git config user.email "generator@benchkit.invalid"
11
+ git config commit.gpgSign false
12
+ git config core.autocrlf false
13
+ export GIT_AUTHOR_NAME="BenchKit Generator" GIT_AUTHOR_EMAIL="generator@benchkit.invalid"
14
+ export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
15
+ cat > invoice.py <<'PY'
16
+ from decimal import Decimal, ROUND_DOWN
17
+
18
+
19
+ def cents(value: str) -> int:
20
+ return int((Decimal(value) * 100).quantize(Decimal("1"), rounding=ROUND_DOWN))
21
+ PY
22
+ cat > test_invoice.py <<'PY'
23
+ import unittest
24
+ from invoice import cents
25
+
26
+
27
+ class InvoiceTests(unittest.TestCase):
28
+ def test_cents_rounds_half_up(self):
29
+ self.assertEqual(cents("10.235"), 1024)
30
+ PY
31
+ printf '# Invoice tools %s\n\nRun tests with `python3 -m unittest discover -v`.\n' "$slug" > README.md
32
+ git add .
33
+ GIT_AUTHOR_DATE='@1700200000 +0000' GIT_COMMITTER_DATE='@1700200000 +0000' git commit -q -m "bootstrap invoice tools $slug"
34
+ git tag "benchkit-split-baseline-$slug"
35
+ python3 -c 'from pathlib import Path; p=Path("invoice.py"); p.write_text(p.read_text().replace("ROUND_DOWN", "ROUND_HALF_UP"))'
36
+ cat > labels.py <<'PY'
37
+ def invoice_label(number: int) -> str:
38
+ return f"INV-{number:06d}"
39
+ PY
40
+ cat > test_labels.py <<'PY'
41
+ import unittest
42
+ from labels import invoice_label
43
+
44
+
45
+ class LabelTests(unittest.TestCase):
46
+ def test_invoice_label(self):
47
+ self.assertEqual(invoice_label(42), "INV-000042")
48
+ PY
49
+ git add .
50
+ GIT_AUTHOR_DATE='@1700200060 +0000' GIT_COMMITTER_DATE='@1700200060 +0000' git commit -q -m "ship invoice patch bundle"
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+ seed="${1:?usage: verify.sh SEED [WORKSPACE]}"
4
+ workspace="${2:-/workspace/split-the-mega-commit}"
5
+ emit() { printf '%s\t%s\t%s\n' "$1" "$2" "$3"; }
6
+ if ! git -C "$workspace" rev-parse --git-dir >/dev/null 2>&1; then
7
+ for id in two_commits ordered_boundaries each_commit_tests final_tree; do emit "$id" 0 "repository missing"; done
8
+ emit trap 1 "repository replaced"
9
+ exit 0
10
+ fi
11
+ reference_root="$(mktemp -d)"
12
+ reference="$reference_root/reference"
13
+ bash "$(dirname "$0")/setup.sh" "$seed" "$reference" >/dev/null
14
+ baseline="$(git -C "$reference" rev-parse HEAD^)"
15
+ expected_tree="$(git -C "$reference" rev-parse 'HEAD^{tree}')"
16
+ rm -rf "$reference_root"
17
+ after="$(git -C "$workspace" rev-list --count "$baseline"..HEAD 2>/dev/null || printf 0)"
18
+ [[ "$after" == 2 ]] && two=1 || two=0
19
+ emit two_commits "$two" "commits_after_baseline=$after"
20
+ bugfix="$(git -C "$workspace" rev-parse "$baseline^{}" 2>/dev/null || true)"
21
+ if [[ "$two" == 1 ]]; then
22
+ bugfix="$(git -C "$workspace" rev-parse HEAD^)"
23
+ subject1="$(git -C "$workspace" log -1 --format=%s "$bugfix")"
24
+ subject2="$(git -C "$workspace" log -1 --format=%s HEAD)"
25
+ else
26
+ subject1=""; subject2=""
27
+ fi
28
+ boundary=0
29
+ if [[ "$subject1" == "Fix invoice rounding" && "$subject2" == "Add invoice labels" ]]; then
30
+ git -C "$workspace" cat-file -e "$bugfix:invoice.py" 2>/dev/null && \
31
+ ! git -C "$workspace" cat-file -e "$bugfix:labels.py" 2>/dev/null && boundary=1
32
+ fi
33
+ emit ordered_boundaries "$boundary" "first=$subject1 second=$subject2"
34
+ tests_ok=0
35
+ if [[ "$boundary" == 1 ]]; then
36
+ tmp="$(mktemp -d)"
37
+ git -C "$workspace" archive "$bugfix" | tar -x -C "$tmp"
38
+ (cd "$tmp" && python3 -m unittest discover -v) >/dev/null 2>&1 && first_ok=1 || first_ok=0
39
+ rm -rf "$tmp"
40
+ (cd "$workspace" && python3 -m unittest discover -v) >/dev/null 2>&1 && final_ok=1 || final_ok=0
41
+ [[ "$first_ok" == 1 && "$final_ok" == 1 ]] && tests_ok=1
42
+ fi
43
+ emit each_commit_tests "$tests_ok" "intermediate_and_final=$tests_ok"
44
+ current_tree="$(git -C "$workspace" rev-parse 'HEAD^{tree}' 2>/dev/null || true)"
45
+ [[ -n "$expected_tree" && "$current_tree" == "$expected_tree" ]] && final=1 || final=0
46
+ emit final_tree "$final" "tree_match=$final"
47
+ [[ "$after" == 1 ]] && trap=1 || trap=0
48
+ emit trap "$trap" "squashed=$trap"