Skip to content

Write event.json once per job instead of once per step - #4643

Open
MarshallOfSound wants to merge 1 commit into
actions:mainfrom
MarshallOfSound:write-event-payload-once
Open

Write event.json once per job instead of once per step#4643
MarshallOfSound wants to merge 1 commit into
actions:mainfrom
MarshallOfSound:write-event-payload-once

Conversation

@MarshallOfSound

@MarshallOfSound MarshallOfSound commented Aug 22, 2026

Copy link
Copy Markdown

Fixes #4641

ActionRunner.RunAsync() calls ExecutionContext.WriteWebhookPayload() for every step — run:, uses:, job hooks, and each inner step of a composite — and that does a plain File.WriteAllText(<temp>/_github_workflow/event.json, …). Harmless while steps were sequential; with background / parallel: steps it races:

github.event is delivered in the job message and the worker never modifies it (the only SetGitHubContext writers are action*, event_path, workspace, the *_urls and file commands), so the content being written is identical for every step of the job. This changes WriteWebhookPayload() to:

  • serialize and write the payload once per job (first step or job-started hook to get there), under a job-wide lock on Global since background steps reach it from multiple threads
  • keep setting github.event_path on every step's context, since background and composite steps get a ShallowCopy()
  • rewrite only if the file has gone missing, so a step that deletes it still doesn't break later steps — same self-healing property as today
  • do that (re)write via a temp file in the same directory + File.Move(overwrite: true) so a reader can never observe it half-written

I kept the write lazy rather than moving it into ExecutionContext.InitializeJob because TempDirectoryManager.InitializeTempDirectory runs after that in JobRunner and can clean _temp.

Side effect: every step start no longer does a DictionaryContextData -> JToken -> indented-string serialization plus a synchronous file write of the whole payload. For pull_request-triggered jobs that's tens of KB per step, more noticeable in jobs with lots of small composite steps than anything else, but it was pure waste.

Added L0 coverage for: write + event_path set, no rewrite on subsequent steps, rewrite after the file is removed, and concurrent callers with a reader polling the file (the reader observes 0-length reads on main, none with this change).

Every step start rewrote _github_workflow/event.json with identical
content. With background/parallel steps that write races with sibling
steps: a sharing violation on Windows, and a truncated read of
GITHUB_EVENT_PATH on macOS/Linux. Write it once per job, only rewrite if
it has gone missing, and make the write atomic via temp file + rename.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Background actions frequently fail on Windows due to locking event.json

1 participant