-module(version_bump@plugins@github).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/version_bump/plugins/github.gleam").
-export([is_prerelease/1, verify/2, plugin/0]).
-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(
" The GitHub publish plugin.\n"
"\n"
" Mirrors `@semantic-release/github`'s core responsibility: authenticate with\n"
" a GitHub token, resolve the target repository, and create a GitHub Release\n"
" for the version semantic-release just determined.\n"
"\n"
" Hooks implemented:\n"
" - verify_conditions: a `GITHUB_TOKEN`/`GH_TOKEN` and a resolvable\n"
" repository URL must be present, otherwise the pipeline aborts.\n"
" - publish: create the GitHub release and return it.\n"
" - success: log that the release was published.\n"
"\n"
" Effectful work (HTTP, environment access) is kept in the hook bodies; the\n"
" pure decision logic lives in helpers (`resolve_token`, `is_prerelease`,\n"
" `verify`) so it can be unit-tested without a network or live environment.\n"
).
-file("src/version_bump/plugins/github.gleam", 150).
?DOC(" Log that the GitHub release was published. MVP: a single log line.\n").
-spec do_success(
version_bump@config:plugin_spec(),
version_bump@context:context()
) -> {ok, nil} | {error, version_bump@error:release_error()}.
do_success(_, Context) ->
Message = case erlang:element(9, Context) of
{some, Next_release} ->
<<"Published GitHub release "/utf8,
(erlang:element(4, Next_release))/binary>>;
none ->
<<"Published GitHub release"/utf8>>
end,
version_bump@logging:success(Message),
{ok, nil}.
-file("src/version_bump/plugins/github.gleam", 226).
?DOC(
" Map a transport-level error onto the plugin's namespace so failures are\n"
" attributed to the GitHub plugin in aggregate reporting.\n"
).
-spec adapt_error(version_bump@error:release_error()) -> version_bump@error:release_error().
adapt_error(Err) ->
{plugin_error, <<"github"/utf8>>, version_bump@error:to_string(Err)}.
-file("src/version_bump/plugins/github.gleam", 166).
?DOC(
" Whether the upcoming release is a prerelease (i.e. its channel-tied version\n"
" carries a prerelease identifier such as `-beta.1`).\n"
).
-spec is_prerelease(version_bump@release:next_release()) -> boolean().
is_prerelease(Next_release) ->
gleam_stdlib:contains_string(erlang:element(2, Next_release), <<"-"/utf8>>).
-file("src/version_bump/plugins/github.gleam", 269).
-spec with_parsed_repo(
binary(),
fun(({binary(), binary()}) -> {ok, LBF} |
{error, version_bump@error:release_error()})
) -> {ok, LBF} | {error, version_bump@error:release_error()}.
with_parsed_repo(Url, Next) ->
case version_bump@github_api:parse_repo_url(Url) of
{ok, Owner_repo} ->
Next(Owner_repo);
{error, Err} ->
{error, adapt_error(Err)}
end.
-file("src/version_bump/plugins/github.gleam", 259).
-spec with_next_release(
version_bump@context:context(),
fun((version_bump@release:next_release()) -> {ok, LBA} |
{error, version_bump@error:release_error()})
) -> {ok, LBA} | {error, version_bump@error:release_error()}.
with_next_release(Context, Next) ->
case erlang:element(9, Context) of
{some, Next_release} ->
Next(Next_release);
none ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"No next release to publish to GitHub."/utf8>>}}
end.
-file("src/version_bump/plugins/github.gleam", 189).
?DOC(" The configured repository URL, treating an empty/whitespace value as absent.\n").
-spec resolve_repo_url(version_bump@config:config()) -> gleam@option:option(binary()).
resolve_repo_url(Config) ->
case erlang:element(2, Config) of
{some, Url} ->
case gleam@string:trim(Url) of
<<""/utf8>> ->
none;
Trimmed ->
{some, Trimmed}
end;
none ->
none
end.
-file("src/version_bump/plugins/github.gleam", 249).
-spec with_repo_url(
version_bump@config:config(),
fun((binary()) -> {ok, LAV} | {error, version_bump@error:release_error()})
) -> {ok, LAV} | {error, version_bump@error:release_error()}.
with_repo_url(Config, Next) ->
case resolve_repo_url(Config) of
{some, Url} ->
Next(Url);
none ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"No repository URL configured."/utf8>>}}
end.
-file("src/version_bump/plugins/github.gleam", 217).
-spec non_empty(binary()) -> gleam@option:option(binary()).
non_empty(Value) ->
case gleam@string:trim(Value) of
<<""/utf8>> ->
none;
Trimmed ->
{some, Trimmed}
end.
-file("src/version_bump/plugins/github.gleam", 210).
?DOC(" Look up a key in the live process environment via `envoy`.\n").
-spec envoy_lookup(binary()) -> gleam@option:option(binary()).
envoy_lookup(Key) ->
case envoy_ffi:get(Key) of
{ok, Value} ->
non_empty(Value);
{error, _} ->
none
end.
-file("src/version_bump/plugins/github.gleam", 202).
?DOC(
" Look up a key in the passed-in environment, treating empty/whitespace-only\n"
" values as absent.\n"
).
-spec env_lookup(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(binary()).
env_lookup(Env, Key) ->
case gleam_stdlib:map_get(Env, Key) of
{ok, Value} ->
non_empty(Value);
{error, _} ->
none
end.
-file("src/version_bump/plugins/github.gleam", 173).
?DOC(
" Resolve a GitHub token from the supplied environment, preferring\n"
" `GITHUB_TOKEN` over `GH_TOKEN`, falling back to the live process environment\n"
" when neither key is present in the passed-in dictionary.\n"
).
-spec resolve_token(gleam@dict:dict(binary(), binary())) -> gleam@option:option(binary()).
resolve_token(Env) ->
case env_lookup(Env, <<"GITHUB_TOKEN"/utf8>>) of
{some, Token} ->
{some, Token};
none ->
case env_lookup(Env, <<"GH_TOKEN"/utf8>>) of
{some, Token@1} ->
{some, Token@1};
none ->
case envoy_lookup(<<"GITHUB_TOKEN"/utf8>>) of
{some, Token@2} ->
{some, Token@2};
none ->
envoy_lookup(<<"GH_TOKEN"/utf8>>)
end
end
end.
-file("src/version_bump/plugins/github.gleam", 234).
-spec with_token(
gleam@dict:dict(binary(), binary()),
fun((binary()) -> {ok, LAQ} | {error, version_bump@error:release_error()})
) -> {ok, LAQ} | {error, version_bump@error:release_error()}.
with_token(Env, Next) ->
case resolve_token(Env) of
{some, Token} ->
Next(Token);
none ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"No GitHub token found. Set the GITHUB_TOKEN or GH_TOKEN environment "/utf8,
"variable."/utf8>>}}
end.
-file("src/version_bump/plugins/github.gleam", 137).
?DOC(
" Collect the synchronous inputs `do_publish` needs, short-circuiting with a\n"
" `PluginError` if any are missing or unparseable.\n"
).
-spec gather_publish_inputs(version_bump@context:context()) -> {ok,
{binary(), binary(), binary(), version_bump@release:next_release()}} |
{error, version_bump@error:release_error()}.
gather_publish_inputs(Context) ->
with_token(
erlang:element(3, Context),
fun(Token) ->
with_repo_url(
erlang:element(4, Context),
fun(Url) ->
with_next_release(
Context,
fun(Next_release) ->
with_parsed_repo(
Url,
fun(_use0) ->
{Owner, Repo} = _use0,
{ok, {Token, Owner, Repo, Next_release}}
end
)
end
)
end
)
end
).
-file("src/version_bump/plugins/github.gleam", 105).
?DOC(
" Create a GitHub release for the upcoming version and return it.\n"
"\n"
" The synchronous pre-flight (token, repo URL, next release, owner/repo) is\n"
" gathered first; only the HTTP create-release call is asynchronous.\n"
).
-spec do_publish(
version_bump@config:plugin_spec(),
version_bump@context:context()
) -> version_bump@task:task({ok,
gleam@option:option(version_bump@release:release())} |
{error, version_bump@error:release_error()}).
do_publish(_, Context) ->
case gather_publish_inputs(Context) of
{error, Err} ->
version_bump_task_ffi:resolve({error, Err});
{ok, {Token, Owner, Repo, Next_release}} ->
Prerelease = is_prerelease(Next_release),
Head = erlang:element(5, Next_release),
_pipe = version_bump@github_api:create_release(
Token,
Owner,
Repo,
erlang:element(4, Next_release),
erlang:element(2, Next_release),
erlang:element(7, Next_release),
Prerelease,
Head
),
version_bump_task_ffi:map(_pipe, fun(Result) -> case Result of
{ok, Release} ->
{ok, {some, Release}};
{error, Err@1} ->
{error, adapt_error(Err@1)}
end end)
end.
-file("src/version_bump/plugins/github.gleam", 66).
?DOC(
" PURE verification: given a resolved token (if any) and the config, decide\n"
" whether the GitHub plugin can run. Separated out so it can be tested without\n"
" touching the process environment.\n"
).
-spec verify(gleam@option:option(binary()), version_bump@config:config()) -> {ok,
nil} |
{error, version_bump@error:release_error()}.
verify(Token, Config) ->
case Token of
none ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"No GitHub token found. Set the GITHUB_TOKEN or GH_TOKEN environment "/utf8,
"variable."/utf8>>}};
{some, _} ->
case resolve_repo_url(Config) of
none ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"No repository URL configured. Set `repositoryUrl` so the GitHub "/utf8,
"release can be created."/utf8>>}};
{some, Url} ->
case version_bump@github_api:parse_repo_url(Url) of
{ok, _} ->
{ok, nil};
{error, _} ->
{error,
{plugin_error,
<<"github"/utf8>>,
<<"Could not parse a GitHub owner/repo from repository URL: "/utf8,
Url/binary>>}}
end
end
end.
-file("src/version_bump/plugins/github.gleam", 48).
?DOC(
" Ensure a GitHub token and a resolvable repository are available before the\n"
" pipeline does any work.\n"
).
-spec do_verify_conditions(
version_bump@config:plugin_spec(),
version_bump@context:context()
) -> {ok, nil} | {error, version_bump@error:release_error()}.
do_verify_conditions(_, Context) ->
case erlang:element(12, Context) of
true ->
{ok, nil};
false ->
Token = resolve_token(erlang:element(3, Context)),
verify(Token, erlang:element(4, Context))
end.
-file("src/version_bump/plugins/github.gleam", 35).
?DOC(" The plugin record, wiring up the GitHub hooks.\n").
-spec plugin() -> version_bump@plugin:plugin().
plugin() ->
_record = version_bump@plugin:new(<<"github"/utf8>>),
{plugin,
erlang:element(2, _record),
{some, fun do_verify_conditions/2},
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
erlang:element(8, _record),
{some, fun do_publish/2},
{some, fun do_success/2},
erlang:element(11, _record)}.