9797 printf 'source_commit=%s\n' "$FASTLANES_SOURCE_COMMIT"
9898 printf 'requested_numeric_rows=%s\n' '${{ inputs.numeric_rows }}'
9999 printf 'requested_timeout_seconds=%s\n' '${{ inputs.timeout_seconds }}'
100+ printf 'host_python='; python3 --version
100101 printf 'python='; nix develop --command python --version
101102 if test -x native/fastlanes/.venv/bin/python; then
102103 printf 'fastlanes_python='; native/fastlanes/.venv/bin/python --version
@@ -116,20 +117,27 @@ jobs:
116117 CASE_TIMEOUT_SECONDS : ${{ inputs.timeout_seconds }}
117118 run : |
118119 set -euo pipefail
120+ status=0
119121 case "$CASE_TIMEOUT_SECONDS" in
120122 ''|*[!0-9]*)
121- echo "timeout_seconds must be an integer" >&2
122- exit 2
123+ printf '%s\n' "timeout_seconds must be an integer" \
124+ > "$RUN_DIR/measure-error.txt"
125+ status=2
126+ ;;
127+ *)
128+ if test "$CASE_TIMEOUT_SECONDS" -lt 1 || \
129+ test "$CASE_TIMEOUT_SECONDS" -gt 120; then
130+ printf '%s\n' "timeout_seconds must be between 1 and 120" \
131+ > "$RUN_DIR/measure-error.txt"
132+ status=2
133+ fi
123134 ;;
124135 esac
125- if test "$CASE_TIMEOUT_SECONDS" -lt 1 || \
126- test "$CASE_TIMEOUT_SECONDS" -gt 120; then
127- echo "timeout_seconds must be between 1 and 120" >&2
128- exit 2
129- fi
130- set +e
131- PYTHONPATH="$GITHUB_WORKSPACE/src" \
132- native/fastlanes/.venv/bin/python - "$RUN_DIR/results.json" <<'PY'
136+ if test "$status" -eq 0; then
137+ set +e
138+ PYTHONPATH="$GITHUB_WORKSPACE/src" \
139+ native/fastlanes/.venv/bin/python - "$RUN_DIR/results.json" \
140+ > "$RUN_DIR/measure.log" 2>&1 <<'PY'
133141 import json
134142 import os
135143 import sys
@@ -153,8 +161,9 @@ jobs:
153161 }
154162 output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
155163 PY
156- status=$?
157- set -e
164+ status=$?
165+ set -e
166+ fi
158167 printf '%s\n' "$status" > "$RUN_DIR/measure-status.txt"
159168 exit "$status"
160169
@@ -165,16 +174,25 @@ jobs:
165174 if test -s "$RUN_DIR/results.json"; then
166175 exit 0
167176 fi
168- nix develop --command python - "$RUN_DIR/results.json" \
177+ python3 - "$RUN_DIR/results.json" \
169178 '${{ steps.source.outcome }}' '${{ steps.build.outcome }}' \
170- '${{ steps.measure.outcome }}' <<'PY'
179+ '${{ steps.measure.outcome }}' "$RUN_DIR/measure-error.txt" \
180+ "$RUN_DIR/measure.log" <<'PY'
171181 import json
172182 import sys
173183 from pathlib import Path
174184
175185 output = Path(sys.argv[1])
176- source, build, measure = sys.argv[2:]
186+ source, build, measure = sys.argv[2:5]
187+ error_path = Path(sys.argv[5])
188+ log_path = Path(sys.argv[6])
177189 available = source == "success" and build == "success"
190+ if error_path.is_file():
191+ error = error_path.read_text().strip()
192+ elif log_path.is_file():
193+ error = log_path.read_text(errors="replace").strip()[-1000:]
194+ else:
195+ error = "none"
178196 output.write_text(
179197 json.dumps(
180198 {
@@ -185,7 +203,7 @@ jobs:
185203 "source_commit": "f0edc1020a538f1f8098640fce8347c9ac247a0d",
186204 "status": "UNSUPPORTED" if not available else "FAILED",
187205 "failure_reason": (
188- f"source={source}; build={build}; measure={measure}"
206+ f"source={source}; build={build}; measure={measure}; error={error} "
189207 ),
190208 },
191209 indent=2,
@@ -195,11 +213,52 @@ jobs:
195213 )
196214 PY
197215
216+ - name : Bind case inputs and artifacts to hashes
217+ if : always()
218+ run : |
219+ set -euo pipefail
220+ if test ! -s "$RUN_DIR/results.json" || \
221+ test ! -d "$RUN_DIR/claims/fastlanes-official"; then
222+ exit 0
223+ fi
224+ python3 - "$RUN_DIR/results.json" \
225+ "$RUN_DIR/claims/fastlanes-official" <<'PY'
226+ import hashlib
227+ import json
228+ import sys
229+ from pathlib import Path
230+
231+ results_path = Path(sys.argv[1])
232+ root = Path(sys.argv[2])
233+
234+ def digest(path: Path) -> str | None:
235+ if not path.is_file():
236+ return None
237+ value = hashlib.sha256()
238+ with path.open("rb") as handle:
239+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
240+ value.update(chunk)
241+ return value.hexdigest()
242+
243+ case_hashes = {}
244+ for case_dir in sorted(path for path in root.iterdir() if path.is_dir()):
245+ case_hashes[case_dir.name] = {
246+ "input/data.csv": digest(case_dir / "input/data.csv"),
247+ "input/schema.json": digest(case_dir / "input/schema.json"),
248+ "data.fls": digest(case_dir / "data.fls"),
249+ "decoded.csv": digest(case_dir / "decoded.csv"),
250+ }
251+ payload = json.loads(results_path.read_text())
252+ payload["case_hash_algorithm"] = "sha256"
253+ payload["case_hashes"] = case_hashes
254+ results_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
255+ PY
256+
198257 - name : Write concise evidence report
199258 if : always()
200259 run : |
201260 set -euo pipefail
202- nix develop --command python - "$RUN_DIR/results.json" "$RUN_DIR/report.md" <<'PY'
261+ python3 - "$RUN_DIR/results.json" "$RUN_DIR/report.md" <<'PY'
203262 import json
204263 import sys
205264 from pathlib import Path
@@ -249,7 +308,7 @@ jobs:
249308 test '${{ steps.checkout.outcome }}' = success
250309 test '${{ steps.source.outcome }}' = success
251310 test '${{ steps.build.outcome }}' = success
252- nix develop --command python - "$RUN_DIR/results.json" <<'PY'
311+ python3 - "$RUN_DIR/results.json" <<'PY'
253312 import json
254313 import sys
255314 from pathlib import Path
0 commit comments