neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Credo: single-clause withs become cases
4f41acc · Gabe Morell · 2026-11-04 20:38
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