-module(version_bump@runner). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/version_bump/runner.gleam"). -export([run_verify_conditions/2, run_verify_release/2, run_prepare/2, run_success/2, run_fail/2, run_analyze_commits/2, run_generate_notes/2, run_publish/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Hook runners — the glue that drives a list of resolved plugins through one\n" " lifecycle hook each, applying the per-hook combination semantics the plugin\n" " contract documents.\n" "\n" " A \"resolved plugin\" is a `#(PluginSpec, Plugin)` pair: the spec carries the\n" " user's options, the plugin carries the hook implementations. Each runner\n" " folds over the list, looks up the relevant `Option(hook)` on each plugin,\n" " skips plugins that don't implement it (`None`), and combines the results:\n" "\n" " - verify_conditions / verify_release / prepare / success / fail\n" " run every implementing plugin for effect; ALL errors are collected and,\n" " if any occurred, surfaced together as an `AggregateError`.\n" " - analyze_commits highest `ReleaseType` (by `release_type_rank`) wins; a\n" " single error short-circuits.\n" " - generate_notes notes are concatenated in plugin order; a single error\n" " short-circuits.\n" " - publish every implementing plugin runs; the `Some(release)`\n" " results are collected in order; a single error short-circuits.\n" ). -file("src/version_bump/runner.gleam", 81). ?DOC( " Shared driver for the for-effect hooks. Runs every plugin that implements\n" " the selected hook, collecting every error rather than stopping at the first.\n" " Returns `Ok(Nil)` when nothing failed, otherwise an `AggregateError` of all\n" " collected failures (a single failure is still wrapped for a uniform shape).\n" ). -spec run_effect_hook( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context(), fun((version_bump@plugin:plugin()) -> gleam@option:option(fun((version_bump@config:plugin_spec(), version_bump@context:context()) -> {ok, nil} | {error, version_bump@error:release_error()}))) ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_effect_hook(Plugins, Context, Select) -> Errors = gleam@list:fold( Plugins, [], fun(Acc, Resolved) -> {Spec, Plugin} = Resolved, case Select(Plugin) of none -> Acc; {some, Hook} -> case Hook(Spec, Context) of {ok, nil} -> Acc; {error, Err} -> [Err | Acc] end end end ), case lists:reverse(Errors) of [] -> {ok, nil}; Collected -> {error, {aggregate_error, Collected}} end. -file("src/version_bump/runner.gleam", 38). ?DOC(" Run `verify_conditions` across all plugins, aggregating any failures.\n"). -spec run_verify_conditions( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_verify_conditions(Plugins, Context) -> run_effect_hook(Plugins, Context, fun(P) -> erlang:element(3, P) end). -file("src/version_bump/runner.gleam", 46). ?DOC(" Run `verify_release` across all plugins, aggregating any failures.\n"). -spec run_verify_release( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_verify_release(Plugins, Context) -> run_effect_hook(Plugins, Context, fun(P) -> erlang:element(5, P) end). -file("src/version_bump/runner.gleam", 54). ?DOC(" Run `prepare` across all plugins, aggregating any failures.\n"). -spec run_prepare( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_prepare(Plugins, Context) -> run_effect_hook(Plugins, Context, fun(P) -> erlang:element(8, P) end). -file("src/version_bump/runner.gleam", 62). ?DOC(" Run `success` across all plugins, aggregating any failures.\n"). -spec run_success( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_success(Plugins, Context) -> run_effect_hook(Plugins, Context, fun(P) -> erlang:element(10, P) end). -file("src/version_bump/runner.gleam", 70). ?DOC(" Run `fail` across all plugins, aggregating any failures.\n"). -spec run_fail( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, nil} | {error, version_bump@error:release_error()}. run_fail(Plugins, Context) -> run_effect_hook(Plugins, Context, fun(P) -> erlang:element(11, P) end). -file("src/version_bump/runner.gleam", 131). ?DOC( " Keep whichever of two optional release types ranks higher; `None` (no\n" " release) ranks below any concrete type.\n" ). -spec keep_highest( gleam@option:option(version_bump@semver:release_type()), gleam@option:option(version_bump@semver:release_type()) ) -> gleam@option:option(version_bump@semver:release_type()). keep_highest(Current, Candidate) -> case {Current, Candidate} of {none, Other} -> Other; {Other@1, none} -> Other@1; {{some, A}, {some, B}} -> case version_bump@semver:release_type_rank(B) > version_bump@semver:release_type_rank( A ) of true -> {some, B}; false -> {some, A} end end. -file("src/version_bump/runner.gleam", 111). ?DOC( " Run `analyze_commits` across all plugins and return the highest implied\n" " `ReleaseType` (by `release_type_rank`), or `None` when no plugin warrants a\n" " release. The first error short-circuits.\n" ). -spec run_analyze_commits( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, gleam@option:option(version_bump@semver:release_type())} | {error, version_bump@error:release_error()}. run_analyze_commits(Plugins, Context) -> gleam@list:try_fold( Plugins, none, fun(Acc, Resolved) -> {Spec, Plugin} = Resolved, case erlang:element(4, Plugin) of none -> {ok, Acc}; {some, Hook} -> case Hook(Spec, Context) of {ok, Candidate} -> {ok, keep_highest(Acc, Candidate)}; {error, Err} -> {error, Err} end end end ). -file("src/version_bump/runner.gleam", 175). ?DOC(" Join non-empty note sections with a blank line between them.\n"). -spec join_sections(list(binary())) -> binary(). join_sections(Sections) -> case Sections of [] -> <<""/utf8>>; [First | Rest] -> gleam@list:fold( Rest, First, fun(Acc, Section) -> <<<>/binary, Section/binary>> end ) end. -file("src/version_bump/runner.gleam", 228). ?DOC( " `result.map` written as a `use`-friendly continuation so the post-processing\n" " of a `try_fold` result reads top-to-bottom without an extra import alias.\n" ). -spec result_map({ok, LYC} | {error, LYD}, fun((LYC) -> LYG)) -> {ok, LYG} | {error, LYD}. result_map(Result, Transform) -> case Result of {ok, Value} -> {ok, Transform(Value)}; {error, Err} -> {error, Err} end. -file("src/version_bump/runner.gleam", 151). ?DOC( " Run `generate_notes` across all plugins, concatenating each plugin's notes\n" " in plugin order. Empty contributions add nothing; non-empty ones are joined\n" " with a blank line between sections. The first error short-circuits.\n" ). -spec run_generate_notes( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> {ok, binary()} | {error, version_bump@error:release_error()}. run_generate_notes(Plugins, Context) -> result_map( gleam@list:try_fold( Plugins, [], fun(Acc, Resolved) -> {Spec, Plugin} = Resolved, case erlang:element(6, Plugin) of none -> {ok, Acc}; {some, Hook} -> case Hook(Spec, Context) of {ok, Notes} -> {ok, [Notes | Acc]}; {error, Err} -> {error, Err} end end end ), fun(Sections) -> _pipe = Sections, _pipe@1 = lists:reverse(_pipe), _pipe@2 = gleam@list:filter( _pipe@1, fun(Section) -> Section /= <<""/utf8>> end ), join_sections(_pipe@2) end ). -file("src/version_bump/runner.gleam", 192). ?DOC( " Run `publish` across all plugins, collecting the `Some(release)` results in\n" " plugin order. Plugins returning `None` (not handled) contribute nothing. The\n" " first error short-circuits.\n" "\n" " `publish` is asynchronous, so the plugins are chained sequentially through a\n" " `Task`: each plugin's publish runs after the previous one resolves, and the\n" " whole sequence yields a single `Task` of the collected releases.\n" ). -spec run_publish( list({version_bump@config:plugin_spec(), version_bump@plugin:plugin()}), version_bump@context:context() ) -> version_bump@task:task({ok, list(version_bump@release:release())} | {error, version_bump@error:release_error()}). run_publish(Plugins, Context) -> Accumulated = gleam@list:fold( Plugins, version_bump_task_ffi:resolve({ok, []}), fun(Acc_task, Resolved) -> {Spec, Plugin} = Resolved, version_bump_task_ffi:await(Acc_task, fun(Acc) -> case Acc of {error, Err} -> version_bump_task_ffi:resolve({error, Err}); {ok, Releases} -> case erlang:element(9, Plugin) of none -> version_bump_task_ffi:resolve( {ok, Releases} ); {some, Hook} -> version_bump_task_ffi:map( Hook(Spec, Context), fun(Published) -> case Published of {ok, {some, Release}} -> {ok, [Release | Releases]}; {ok, none} -> {ok, Releases}; {error, Err@1} -> {error, Err@1} end end ) end end end) end ), version_bump_task_ffi:map(Accumulated, fun(Collected) -> case Collected of {ok, Releases@1} -> {ok, lists:reverse(Releases@1)}; {error, Err@2} -> {error, Err@2} end end).