-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) ->
<<<<Acc/binary, "\n\n"/utf8>>/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).