-module(version_bump@env_ci).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/version_bump/env_ci.gleam").
-export([detect/1]).
-export_type([ci_env/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(
" CI environment detection from environment variables. This is a small, pure\n"
" reimplementation of the parts of the `env-ci` package that the release\n"
" pipeline relies on: figuring out whether we're running in CI, which provider,\n"
" the branch and commit under test, and whether the build is for a pull/merge\n"
" request.\n"
"\n"
" Everything here is a pure function of an environment dictionary so it can be\n"
" unit-tested without touching the real process environment. Callers wire up\n"
" the live environment (e.g. via `envoy`) and pass it in.\n"
).
-type ci_env() :: {ci_env,
boolean(),
binary(),
gleam@option:option(binary()),
gleam@option:option(binary()),
boolean()}.
-file("src/version_bump/env_ci.gleam", 144).
?DOC(" Look up a key, treating empty/whitespace-only values as absent.\n").
-spec get(gleam@dict:dict(binary(), binary()), binary()) -> gleam@option:option(binary()).
get(Env, Key) ->
case gleam_stdlib:map_get(Env, Key) of
{ok, Value} ->
case gleam@string:trim(Value) of
<<""/utf8>> ->
none;
Trimmed ->
{some, Trimmed}
end;
{error, _} ->
none
end.
-file("src/version_bump/env_ci.gleam", 165).
?DOC(
" Whether an optional value is a \"truthy\" flag. CI providers conventionally set\n"
" these to \"true\" or \"1\", but any present non-empty value counts as enabled.\n"
).
-spec is_truthy(gleam@option:option(binary())) -> boolean().
is_truthy(Value) ->
case Value of
{some, V} ->
case string:lowercase(V) of
<<"false"/utf8>> ->
false;
<<"0"/utf8>> ->
false;
<<"no"/utf8>> ->
false;
<<"off"/utf8>> ->
false;
_ ->
true
end;
none ->
false
end.
-file("src/version_bump/env_ci.gleam", 129).
?DOC(
" Generic fallback for any CI service that only sets `CI=true`. No branch or\n"
" commit information is assumed, and it is never treated as a PR build.\n"
).
-spec detect_generic(gleam@dict:dict(binary(), binary())) -> gleam@option:option(ci_env()).
detect_generic(Env) ->
case is_truthy(get(Env, <<"CI"/utf8>>)) of
false ->
none;
true ->
{some, {ci_env, true, <<"generic"/utf8>>, none, none, false}}
end.
-file("src/version_bump/env_ci.gleam", 180).
?DOC(" Return the first present value from a list of candidates.\n").
-spec first_present(list(gleam@option:option(binary()))) -> gleam@option:option(binary()).
first_present(Candidates) ->
case Candidates of
[] ->
none;
[{some, Value} | _] ->
{some, Value};
[none | Rest] ->
first_present(Rest)
end.
-file("src/version_bump/env_ci.gleam", 156).
?DOC(" Whether an optional value carries a non-empty string.\n").
-spec is_present(gleam@option:option(binary())) -> boolean().
is_present(Value) ->
case Value of
{some, _} ->
true;
none ->
false
end.
-file("src/version_bump/env_ci.gleam", 101).
?DOC(
" GitLab CI: identified by `GITLAB_CI=true`.\n"
"\n"
" A merge-request pipeline is detected via `CI_MERGE_REQUEST_ID` /\n"
" `CI_MERGE_REQUEST_IID`, in which case the source branch is\n"
" `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME`; otherwise the branch is\n"
" `CI_COMMIT_REF_NAME`. The commit is `CI_COMMIT_SHA`.\n"
).
-spec detect_gitlab(gleam@dict:dict(binary(), binary())) -> gleam@option:option(ci_env()).
detect_gitlab(Env) ->
case is_truthy(get(Env, <<"GITLAB_CI"/utf8>>)) of
false ->
none;
true ->
Is_pr = is_present(get(Env, <<"CI_MERGE_REQUEST_ID"/utf8>>)) orelse is_present(
get(Env, <<"CI_MERGE_REQUEST_IID"/utf8>>)
),
Branch = case Is_pr of
true ->
first_present(
[get(
Env,
<<"CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"/utf8>>
),
get(Env, <<"CI_COMMIT_REF_NAME"/utf8>>)]
);
false ->
get(Env, <<"CI_COMMIT_REF_NAME"/utf8>>)
end,
{some,
{ci_env,
true,
<<"gitlab"/utf8>>,
Branch,
get(Env, <<"CI_COMMIT_SHA"/utf8>>),
Is_pr}}
end.
-file("src/version_bump/env_ci.gleam", 190).
?DOC(
" Strip a leading `refs/heads/` prefix from a git ref, leaving other refs and\n"
" plain branch names untouched.\n"
).
-spec strip_ref(gleam@option:option(binary())) -> gleam@option:option(binary()).
strip_ref(Value) ->
case Value of
{some, Ref} ->
case gleam_stdlib:string_starts_with(Ref, <<"refs/heads/"/utf8>>) of
true ->
{some,
gleam@string:replace(
Ref,
<<"refs/heads/"/utf8>>,
<<""/utf8>>
)};
false ->
{some, Ref}
end;
none ->
none
end.
-file("src/version_bump/env_ci.gleam", 66).
?DOC(
" GitHub Actions: identified by `GITHUB_ACTIONS=true`.\n"
"\n"
" On a pull-request build the source branch lives in `GITHUB_HEAD_REF`; on a\n"
" push build the branch is in `GITHUB_REF_NAME` (falling back to stripping the\n"
" `refs/heads/` prefix off `GITHUB_REF`). The commit is `GITHUB_SHA`, and a PR\n"
" build is signalled by `GITHUB_EVENT_NAME` being `pull_request` or\n"
" `pull_request_target`.\n"
).
-spec detect_github(gleam@dict:dict(binary(), binary())) -> gleam@option:option(ci_env()).
detect_github(Env) ->
case is_truthy(get(Env, <<"GITHUB_ACTIONS"/utf8>>)) of
false ->
none;
true ->
Event = get(Env, <<"GITHUB_EVENT_NAME"/utf8>>),
Is_pr = case Event of
{some, <<"pull_request"/utf8>>} ->
true;
{some, <<"pull_request_target"/utf8>>} ->
true;
_ ->
false
end,
Branch = case Is_pr of
true ->
first_present([get(Env, <<"GITHUB_HEAD_REF"/utf8>>)]);
false ->
first_present(
[get(Env, <<"GITHUB_REF_NAME"/utf8>>),
strip_ref(get(Env, <<"GITHUB_REF"/utf8>>))]
)
end,
{some,
{ci_env,
true,
<<"github"/utf8>>,
Branch,
get(Env, <<"GITHUB_SHA"/utf8>>),
Is_pr}}
end.
-file("src/version_bump/env_ci.gleam", 37).
?DOC(
" Detect the CI environment from the given environment variables.\n"
"\n"
" Providers are checked from most-specific to least-specific so that a generic\n"
" `CI=true` only wins when no known provider matched. Returns a `CiEnv` with\n"
" `is_ci: False` and an empty provider when nothing CI-like is present.\n"
).
-spec detect(gleam@dict:dict(binary(), binary())) -> ci_env().
detect(Env) ->
case detect_github(Env) of
{some, Result} ->
Result;
none ->
case detect_gitlab(Env) of
{some, Result@1} ->
Result@1;
none ->
case detect_generic(Env) of
{some, Result@2} ->
Result@2;
none ->
{ci_env, false, <<""/utf8>>, none, none, false}
end
end
end.