Credo: single-clause withs become cases

4f41acc · Gabe Morell · 2026-11-04 20:38

22 files +133 -111

Files changed

modified lib/git_gud/git/cli.ex
+11 −9
@@ -47,15 +47,17 @@ defmodule GitGud.Git.Cli do
47 47 defp parse_ref_line(line) do
48 48 case String.split(line, "\0") do
49 49 [name, target_hex, peeled_hex, type] ->
50 − with {:ok, target} <- Git.from_hex(target_hex) do
51 − %{
52 − name: name,
53 − target_sha: target,
54 − peeled_sha: hex_to_sha_or_nil(peeled_hex),
55 − ref_type: ref_type(name, type)
56 − }
57 − else
58 − _ -> nil
50 + case Git.from_hex(target_hex) do
51 + {:ok, target} ->
52 + %{
53 + name: name,
54 + target_sha: target,
55 + peeled_sha: hex_to_sha_or_nil(peeled_hex),
56 + ref_type: ref_type(name, type)
57 + }
58 +
59 + _ ->
60 + nil
59 61 end
60 62
61 63 _ ->
modified lib/git_gud/packages/formats/fdroid/apk.ex
+6 −4
@@ -162,10 +162,12 @@ defmodule GitGud.Packages.Formats.Fdroid.Apk do
162 162 defp image_extension(_), do: :none
163 163
164 164 defp resources(body) do
165 − with {:ok, bytes} <- Zip.read(body, @resources) do
166 − Arsc.parse(bytes)
167 − else
168 − _ -> :none
165 + case Zip.read(body, @resources) do
166 + {:ok, bytes} ->
167 + Arsc.parse(bytes)
168 +
169 + _ ->
170 + :none
169 171 end
170 172 end
171 173
modified lib/git_gud/workflows/runners.ex
+8 −6
@@ -320,7 +320,7 @@ defmodule GitGud.Workflows.Runners do
320 320 _ -> ["self-hosted", "ubuntu-latest"]
321 321 end
322 322
323 − label_lines = labels |> Enum.map(&" - #{&1}") |> Enum.join("\n")
323 + label_lines = Enum.map_join(labels, "\n", &" - #{&1}")
324 324
325 325 """
326 326 # forgejo-runner config — runs jobs in rootless Podman.
@@ -369,11 +369,13 @@ defmodule GitGud.Workflows.Runners do
369 369 `create_offline/2`.
370 370 """
371 371 def find_by_token(token) when is_binary(token) do
372 − with {:ok, raw} <- Base.decode16(token, case: :mixed) do
373 − hash = :crypto.hash(:sha256, raw)
374 − Repo.get_by(Runner, token_hash: hash)
375 − else
376 − _ -> nil
372 + case Base.decode16(token, case: :mixed) do
373 + {:ok, raw} ->
374 + hash = :crypto.hash(:sha256, raw)
375 + Repo.get_by(Runner, token_hash: hash)
376 +
377 + _ ->
378 + nil
377 379 end
378 380 end
379 381
modified lib/git_gud_web/controllers/api/mobile/repository_controller.ex
+19 −17
@@ -24,25 +24,27 @@ defmodule GitGudWeb.Api.Mobile.RepositoryController do
24 24 alias GitGudWeb.Api.Mobile.JSON
25 25
26 26 def index(conn, _params) do
27 − with {:ok, user} <- Auth.require_scope(conn, "repo:read") do
28 − repos =
29 − user
30 − |> Repositories.relevant_repo_ids_for_user()
31 − |> load_repositories()
27 + case Auth.require_scope(conn, "repo:read") do
28 + {:ok, user} ->
29 + repos =
30 + user
31 + |> Repositories.relevant_repo_ids_for_user()
32 + |> load_repositories()
32 33
33 − # One query for every row's CI badge rather than one per row.
34 − ci_by_repo = Workflows.latest_run_status_by_repo(Enum.map(repos, & &1.id))
34 + # One query for every row's CI badge rather than one per row.
35 + ci_by_repo = Workflows.latest_run_status_by_repo(Enum.map(repos, & &1.id))
35 36
36 − json(conn, %{
37 − "namespaces" => namespaces(user),
38 − "repositories" =>
39 − Enum.map(
40 − repos,
41 − &JSON.repository(&1, viewer: user, ci_status: Map.get(ci_by_repo, &1.id))
42 − )
43 − })
44 − else
45 − {:error, conn} -> conn
37 + json(conn, %{
38 + "namespaces" => namespaces(user),
39 + "repositories" =>
40 + Enum.map(
41 + repos,
42 + &JSON.repository(&1, viewer: user, ci_status: Map.get(ci_by_repo, &1.id))
43 + )
44 + })
45 +
46 + {:error, conn} ->
47 + conn
46 48 end
47 49 end
48 50
modified lib/git_gud_web/controllers/packages_webhook_controller.ex
+15 −14
@@ -38,24 +38,25 @@ defmodule GitGudWeb.PackagesWebhookController do
38 38 require Logger
39 39
40 40 def notify(conn, params) do
41 − with :ok <- authenticate(conn) do
42 − ce_type = cloudevent_type(conn)
41 + case authenticate(conn) do
42 + :ok ->
43 + ce_type = cloudevent_type(conn)
43 44
44 − Logger.debug(fn ->
45 − "zot webhook: ce-type=#{inspect(ce_type)} name=#{inspect(params["name"])} ref=#{inspect(params["reference"])}"
46 − end)
45 + Logger.debug(fn ->
46 + "zot webhook: ce-type=#{inspect(ce_type)} name=#{inspect(params["name"])} ref=#{inspect(params["reference"])}"
47 + end)
47 48
48 − case classify(ce_type, params) do
49 − {:push, payload} ->
50 − handle_push(conn, payload)
49 + case classify(ce_type, params) do
50 + {:push, payload} ->
51 + handle_push(conn, payload)
51 52
52 − {:delete, payload} ->
53 − handle_delete(conn, payload)
53 + {:delete, payload} ->
54 + handle_delete(conn, payload)
55 +
56 + :ignored ->
57 + send_resp(conn, 204, "")
58 + end
54 59
55 − :ignored ->
56 − send_resp(conn, 204, "")
57 − end
58 − else
59 60 {:error, :unauthorized} ->
60 61 conn |> put_status(:unauthorized) |> json(%{"error" => "unauthorized"})
61 62 end
modified lib/git_gud_web/controllers/storage_controller.ex
+14 −13
@@ -48,19 +48,20 @@ defmodule GitGudWeb.StorageController do
48 48 key = Enum.join(key_parts, "/")
49 49 token = conn.query_params["token"] || ""
50 50
51 − with {:ok, %{bucket: ^bucket, key: ^key}} <- Disk.verify_token(token, :put),
52 − path = Disk.object_path(bucket, key) do
53 − File.mkdir_p!(Path.dirname(path))
54 −
55 − case stream_body_to_file(conn, path) do
56 − {:ok, conn} ->
57 − send_resp(conn, 200, "")
58 −
59 − {:error, _, conn} ->
60 − _ = File.rm(path)
61 − send_resp(conn, 500, "write_failed")
62 − end
63 − else
51 + case Disk.verify_token(token, :put) do
52 + {:ok, %{bucket: ^bucket, key: ^key}} ->
53 + path = Disk.object_path(bucket, key)
54 + File.mkdir_p!(Path.dirname(path))
55 +
56 + case stream_body_to_file(conn, path) do
57 + {:ok, conn} ->
58 + send_resp(conn, 200, "")
59 +
60 + {:error, _, conn} ->
61 + _ = File.rm(path)
62 + send_resp(conn, 500, "write_failed")
63 + end
64 +
64 65 {:error, :expired} ->
65 66 send_resp(conn, 410, "expired token")
66 67
modified lib/git_gud_web/controllers/webauthn_controller.ex
+29 −23
@@ -99,15 +99,17 @@ defmodule GitGudWeb.WebauthnController do
99 99 # ── authentication (mid-2FA) ────────────────────────────────────────
100 100
101 101 def auth_start(conn, _params) do
102 − with %{} = user <- pending_two_factor_user(conn) do
103 − challenge = Webauthn.authentication_challenge(user)
104 − options = Webauthn.serialize_authentication_options(challenge)
102 + case pending_two_factor_user(conn) do
103 + %{} = user ->
104 + challenge = Webauthn.authentication_challenge(user)
105 + options = Webauthn.serialize_authentication_options(challenge)
105 106
106 − conn
107 − |> put_session(:webauthn_auth_challenge, serialize_challenge(challenge))
108 − |> json(%{"publicKey" => options})
109 − else
110 − _ -> conn |> put_status(:unauthorized) |> json(%{"error" => "no_pending_2fa"})
107 + conn
108 + |> put_session(:webauthn_auth_challenge, serialize_challenge(challenge))
109 + |> json(%{"publicKey" => options})
110 +
111 + _ ->
112 + conn |> put_status(:unauthorized) |> json(%{"error" => "no_pending_2fa"})
111 113 end
112 114 end
113 115
@@ -141,15 +143,17 @@ defmodule GitGudWeb.WebauthnController do
141 143 # from the session token rather than a pending-2FA stash.
142 144
143 145 def sudo_start(conn, _params) do
144 − with %{} = user <- sudo_candidate(conn) do
145 − challenge = Webauthn.authentication_challenge(user)
146 − options = Webauthn.serialize_authentication_options(challenge)
146 + case sudo_candidate(conn) do
147 + %{} = user ->
148 + challenge = Webauthn.authentication_challenge(user)
149 + options = Webauthn.serialize_authentication_options(challenge)
147 150
148 − conn
149 − |> put_session(:webauthn_sudo_challenge, serialize_challenge(challenge))
150 − |> json(%{"publicKey" => options})
151 − else
152 − _ -> conn |> put_status(:unauthorized) |> json(%{"error" => "not_available"})
151 + conn
152 + |> put_session(:webauthn_sudo_challenge, serialize_challenge(challenge))
153 + |> json(%{"publicKey" => options})
154 +
155 + _ ->
156 + conn |> put_status(:unauthorized) |> json(%{"error" => "not_available"})
153 157 end
154 158 end
155 159
@@ -211,13 +215,15 @@ defmodule GitGudWeb.WebauthnController do
211 215 defp load_session_challenge(conn, key) do
212 216 case get_session(conn, key) do
213 217 bin when is_binary(bin) and bin != "" ->
214 − with {:ok, decoded} <- Base.decode64(bin) do
215 − # Trust boundary — we serialized the struct ourselves and
216 − # never echo this back to the user. binary_to_term/2 with
217 − # :safe gives belt-and-braces protection.
218 − :erlang.binary_to_term(decoded, [:safe])
219 − else
220 − _ -> nil
218 + case Base.decode64(bin) do
219 + {:ok, decoded} ->
220 + # Trust boundary — we serialized the struct ourselves and
221 + # never echo this back to the user. binary_to_term/2 with
222 + # :safe gives belt-and-braces protection.
223 + :erlang.binary_to_term(decoded, [:safe])
224 +
225 + _ ->
226 + nil
221 227 end
222 228
223 229 _ ->
modified lib/git_gud_web/live/moderation_actions.ex
+20 −16
@@ -81,28 +81,32 @@ defmodule GitGudWeb.ModerationActions do
81 81 defp still_moderator?(_socket), do: false
82 82
83 83 defp do_dismiss_report(socket, id, reload) do
84 − with {:ok, report} <- scoped_report(socket, id) do
85 − {:ok, _} =
86 − audited(socket, "report.dismissed", report, fn ->
87 − Reports.dismiss(report, socket.assigns.moderator)
88 − end)
84 + case scoped_report(socket, id) do
85 + {:ok, report} ->
86 + {:ok, _} =
87 + audited(socket, "report.dismissed", report, fn ->
88 + Reports.dismiss(report, socket.assigns.moderator)
89 + end)
89 90
90 − socket |> reload.() |> put_flash(:info, "Report dismissed.")
91 − else
92 − _ -> put_flash(socket, :error, "That report isn't yours to act on.")
91 + socket |> reload.() |> put_flash(:info, "Report dismissed.")
92 +
93 + _ ->
94 + put_flash(socket, :error, "That report isn't yours to act on.")
93 95 end
94 96 end
95 97
96 98 defp do_action_report(socket, id, reload) do
97 − with {:ok, report} <- scoped_report(socket, id) do
98 − {:ok, _} =
99 − audited(socket, "report.actioned", report, fn ->
100 − Reports.action(report, socket.assigns.moderator)
101 − end)
99 + case scoped_report(socket, id) do
100 + {:ok, report} ->
101 + {:ok, _} =
102 + audited(socket, "report.actioned", report, fn ->
103 + Reports.action(report, socket.assigns.moderator)
104 + end)
102 105
103 − socket |> reload.() |> put_flash(:info, "Report marked handled.")
104 − else
105 − _ -> put_flash(socket, :error, "That report isn't yours to act on.")
106 + socket |> reload.() |> put_flash(:info, "Report marked handled.")
107 +
108 + _ ->
109 + put_flash(socket, :error, "That report isn't yours to act on.")
106 110 end
107 111 end
108 112
modified lib/git_gud_web/plugs/client_ip.ex
+11 −9
@@ -116,16 +116,18 @@ defmodule GitGudWeb.Plugs.ClientIp do
116 116 [addr] -> {addr, :all}
117 117 end
118 118
119 − with {:ok, ip} <- :inet.parse_strict_address(String.to_charlist(addr)) do
120 − size = tuple_size(ip) * if(tuple_size(ip) == 4, do: 8, else: 16)
119 + case :inet.parse_strict_address(String.to_charlist(addr)) do
120 + {:ok, ip} ->
121 + size = tuple_size(ip) * if(tuple_size(ip) == 4, do: 8, else: 16)
121 122
122 − case bits do
123 − :all -> {ip, size}
124 − {n, ""} when n in 0..size//1 -> {ip, n}
125 − _ -> nil
126 − end
127 − else
128 − _ -> nil
123 + case bits do
124 + :all -> {ip, size}
125 + {n, ""} when n in 0..size//1 -> {ip, n}
126 + _ -> nil
127 + end
128 +
129 + _ ->
130 + nil
129 131 end
130 132 end
131 133

Parents: 6107e04