In a fastlane Fastfile, when do before_all, before_each, after_all and error each run?
answer
- once per run versus once per lane
- success path and failure path differ
- after_all is skipped when a lane raises
- error receives the lane and exception
- no combined finally hook exists
basics
~20 sbefore_all runs once before the first lane of a run and before_each before every lane in it. after_each and after_all run only on success, after_all once at the end. error is the single hook that fires when a lane or action raises.
solid answer
~50 sThe Fastfile has five hooks and the pairs are not synonyms. `before_all do |lane, options|` runs **once**, before the first lane of the invocation; `before_each` runs **before every lane** that executes in that run, so a lane calling another lane triggers it more than once. On the success path, `after_each` runs after each lane and `after_all do |lane|` runs once at the end. On the failure path, none of the `after_*` hooks run: the first action that raises aborts the lane and `error do |lane, exception|` fires instead, receiving the lane name and the exception, after which the process still exits non-zero. There is no hook that runs on both paths, which is why anything that must always happen — clearing the orchard spray-diary app's staging directory on a shared runner — is called from both `after_all` and `error`.
code
ruby · 19 linesbefore_all do |lane, options|
ENV["SPRAY_DIARY_RUN_ID"] = Time.now.to_i.to_s
end
before_each do |lane, options|
puts "spray-diary: entering lane #{lane}"
end
after_each do |lane|
puts "spray-diary: lane #{lane} finished cleanly"
end
after_all do |lane|
puts "spray-diary: run started by #{lane} succeeded"
end
error do |lane, exception|
puts "spray-diary: lane #{lane} failed - #{exception.message}"
endgo deeper
Be ready to name the five hooks and say, for each, whether it runs once per run or once per lane. The single fact to hold onto is that after_all does not run when a lane fails.
Explain the two paths precisely: which hooks fire on success, which on failure, what parameters each receives, and why there is no combined hook to lean on for cleanup.
Show how you use the hooks operationally — failure reporting that names the lane and exception, cleanup that shared runners actually need, and defensive success reporting that cannot turn a green release red.
Own the convention across many repositories: one agreed hook shape for reporting and cleanup keeps release failures diagnosable at a glance, and you should argue where that belongs versus the pipeline's own notification layer.
## The five hooks A Fastfile can declare hooks that wrap lane execution. They belong to the same short DSL as `lane` and `platform`, and their differences get asked about constantly because two pairs look interchangeable and are not. | hook | when it runs | how many times per invocation | on a failed run | |---|---|---|---| | `before_all` | before the first lane of the run | once | it has already run, then `error` follows | | `before_each` | before every lane that executes | once per lane executed | for lanes reached before the failure | | `after_each` | after each lane that finished cleanly | once per successful lane | no | | `after_all` | after the run finishes successfully | once | **no — this is the trap** | | `error` | when a lane or one of its actions raises | once | yes, this is the failure hook | ## before_all versus before_each `before_all do |lane, options|` receives the lane the run was started with and its parameters, and it runs **exactly once** no matter how many lanes execute. It is the place for setup that is true of the whole invocation: asserting that the environment variables the run needs are present, checking the working directory, stamping a run identifier. `before_each` runs before **each** lane. If the orchard spray-diary app's `release` lane calls a private lane that stages artifacts, `before_all` fires once while `before_each` fires for each lane that executes. Choosing the wrong one produces two classic bugs: - Expensive one-time setup written in `before_each` repeats, slows the run, and can conflict with itself. - Per-lane preconditions written in `before_all` are checked once and then silently not enforced for the lanes that follow. ## The success path When every action in every lane completes, `after_each` runs after each lane and `after_all do |lane|` runs once at the end, receiving the name of the lane the run started with. This is where success reporting belongs: the notification that the spray-diary beta build reached Apple's TestFlight, the summary line, the tidy-up of a staging directory. What makes `after_all` dangerous is the honest reading of its name. It is not a `finally`. It runs **after all lanes succeeded**, not after all lanes finished. ## The failure path The first action that raises aborts its lane. The remaining actions never run, any lane that called it aborts too, and fastlane invokes `error do |lane, exception|`: - `lane` is the lane that was executing, so a shared handler can branch on it. - `exception` is the error object, so the handler can report a real message instead of a generic failure. - Nothing in `after_all` or `after_each` runs for that invocation. - The `error` block **does not rescue** the failure: it runs, and then the process still exits non-zero, which is how the CI step learns the release failed. That last point is worth stating explicitly in an interview, because candidates often assume a declared `error` hook swallows the exception and turns the run green. ## Where to put what 1. **`before_all`** — assertions about the whole run: required environment present, correct working directory, a run identifier for correlating logs. 2. **`before_each`** — preconditions that must hold for every lane individually, kept cheap because they repeat. 3. **`after_all`** — success-only work: notifications that say the release actually shipped, promotion of a build, final summaries. 4. **`error`** — failure reporting and cleanup: a message naming the lane and the exception, and removal of whatever the run left on the machine. 5. **Both `after_all` and `error`** — anything that must happen regardless of outcome. Because no combined hook exists, factor that work into one private lane and call it from both, rather than maintaining two copies that drift. ## Gotchas worth naming - A shared runner keeps whatever a failed lane left behind, so cleanup living only in `after_all` never runs on exactly the runs where it matters most. - Hooks are declared once for the file, and the `lane` parameter they receive is how one handler serves the spray-diary app's Apple and Google Play lanes without two copies. - Work in `after_all` that can itself raise turns a successful release into a failed run, so keep reporting there defensive. - Hooks are not a substitute for lane structure: if two lanes need the same three actions, extract a private lane rather than smuggling the work into `before_each`.
- You need cleanup that runs whether the release succeeds or fails. How do you write it?No hook covers both paths, so call the same work from `after_all` and from `error`. Put the steps in one private lane and have both hooks invoke it, which keeps a single definition instead of two copies that drift. Keep that cleanup defensive: an exception raised inside `after_all` turns a successful release into a failed run.
- Does declaring an error hook stop the fastlane process from failing?No. The `error` block is a reporting and cleanup opportunity, not a rescue. It runs with the lane name and the exception, and afterwards the process still exits non-zero, so the CI step still fails. If a failure should be tolerated, that decision belongs in the lane logic or the pipeline, not in the hook.
before_all is the one pre-flight check a crew runs at the start of the day, while before_each is the walk-around a pilot repeats before every single leg.
saying these in an interview costs you the question
- Says before_all runs before every lane
- Expects after_all cleanup to run when the lane fails
- Thinks a declared error hook makes the run exit zero
- Cannot name which hook receives the exception object
- Puts expensive one-time setup in before_each