diff --git a/CHANGELOG.md b/CHANGELOG.md index 6594eaf..d622e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Versioning. ### Added +- Mirrored verified `nddev_tool_cache_event` records into the GitHub runner + diagnostic directory so teardown bundles retain job-level cache evidence. - Added bounded extraction of `nddev_tool_cache_event` records from verified runner diagnostic artifacts, with malformed job text counted but non-blocking. - Added the portable scheduler-recovery decision core: exact stuck-dispatch diff --git a/actions/tool-cache/tool-cache.sh b/actions/tool-cache/tool-cache.sh index 53de28c..a16d12a 100755 --- a/actions/tool-cache/tool-cache.sh +++ b/actions/tool-cache/tool-cache.sh @@ -132,5 +132,17 @@ test ! -L "$output" install -m 0600 "$candidate" "$output" duration_ms=$(( $(date +%s%3N) - started )) printf 'source=%s\nbytes=%s\nduration_ms=%s\n' "$source" "$bytes" "$duration_ms" >>"$GITHUB_OUTPUT" -printf 'nddev_tool_cache_event={"source":"%s","cache_result":"%s","sha256":"%s","bytes":%s,"duration_ms":%s}\n' \ +printf -v event 'nddev_tool_cache_event={"source":"%s","cache_result":"%s","sha256":"%s","bytes":%s,"duration_ms":%s}' \ "$source" "$cache_result" "$expected_sha256" "$bytes" "$duration_ms" +printf '%s\n' "$event" + +runner_work=$(dirname -- "$RUNNER_TEMP") +runner_root=$(dirname -- "$runner_work") +diagnostic_directory=$runner_root/_diag +diagnostic_file=$diagnostic_directory/nddev-tool-cache-events.log +if [[ "$(basename -- "$RUNNER_TEMP")" == _temp && "$(basename -- "$runner_work")" == _work && + -d "$diagnostic_directory" && ! -L "$diagnostic_directory" && -w "$diagnostic_directory" && + ( ! -e "$diagnostic_file" || ( -f "$diagnostic_file" && ! -L "$diagnostic_file" && + "$(stat --format='%s' -- "$diagnostic_file")" -le 1048576 ) ) ]]; then + printf '%s\n' "$event" >>"$diagnostic_file" +fi diff --git a/internal/repositorycontract/tool_cache_action_test.go b/internal/repositorycontract/tool_cache_action_test.go index 3a30aa5..d05cde6 100644 --- a/internal/repositorycontract/tool_cache_action_test.go +++ b/internal/repositorycontract/tool_cache_action_test.go @@ -78,13 +78,16 @@ type toolCacheResult struct { outputs string stdout string invocations string + diagnostics string } func runToolCache(t *testing.T, cacheMode string) toolCacheResult { t.Helper() root := toolCacheRepositoryRoot(t) directory := t.TempDir() - runnerTemp := filepath.Join(directory, "runner-temp") + runnerRoot := filepath.Join(directory, "actions-runner") + runnerTemp := filepath.Join(runnerRoot, "_work", "_temp") + diagnosticDirectory := filepath.Join(runnerRoot, "_diag") bin := filepath.Join(directory, "bin") if err := os.MkdirAll(runnerTemp, 0o700); err != nil { t.Fatal(err) @@ -92,6 +95,9 @@ func runToolCache(t *testing.T, cacheMode string) toolCacheResult { if err := os.MkdirAll(bin, 0o700); err != nil { t.Fatal(err) } + if err := os.MkdirAll(diagnosticDirectory, 0o700); err != nil { + t.Fatal(err) + } fixture := []byte("immutable verified tool artifact\n") fixturePath := filepath.Join(directory, "fixture") if err := os.WriteFile(fixturePath, fixture, 0o600); err != nil { @@ -186,7 +192,16 @@ fi if err != nil { t.Fatal(err) } - return toolCacheResult{outputs: string(outputs), stdout: string(combined), invocations: string(calls)} + diagnostics, err := os.ReadFile(filepath.Join(diagnosticDirectory, "nddev-tool-cache-events.log")) + if err != nil { + t.Fatal(err) + } + if string(diagnostics) != string(combined) { + t.Fatalf("diagnostic event differs from stdout: diagnostics=%q stdout=%q", diagnostics, combined) + } + return toolCacheResult{ + outputs: string(outputs), stdout: string(combined), invocations: string(calls), diagnostics: string(diagnostics), + } } func toolCacheRepositoryRoot(t *testing.T) string {