-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, LMD} | {error, version_bump@error:release_error()}) ) -> {ok, LMD} | {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, LLY} | {error, version_bump@error:release_error()}) ) -> {ok, LLY} | {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, LLT} | {error, version_bump@error:release_error()}) ) -> {ok, LLT} | {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, LLO} | {error, version_bump@error:release_error()}) ) -> {ok, LLO} | {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)}.