neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
Packages: retention is enforced by default
5ade69f · gmorell · 2026-11-03 19:53
Message
{commit_body(@commit)}
Files changed
modified
config/config.exs
+3
−2
@@ -65,8 +65,9 @@ config :git_gud, Oban,
| 65 | 65 | # Re-sync repositories that opted into a scheduled upstream |
| 66 | 66 | # fetch. Anonymous — a private upstream is synced by hand. |
| 67 | 67 | {"@hourly", GitGud.Repositories.Workers.UpstreamSync}, |
| 68 | − # Retention pass over the package registries. Reports rather | |
| 69 | − # than deletes until `packages.retention_enforced` is on. | |
| 68 | + # Retention pass over the package registries. Deletes while the | |
| 69 | + # `packages.retention_enforced` setting is on (the default); | |
| 70 | + # reports what it would delete while an admin has it off. | |
| 70 | 71 | {"@daily", GitGud.Packages.Workers.Prune}, |
| 71 | 72 | # Prune `actions/cache` entries by age + per-repo size cap. |
| 72 | 73 | {"@hourly", GitGud.Workflows.Workers.CacheEviction}, |
modified
lib/git_gud/packages/retention.ex
+3
−4
@@ -33,10 +33,9 @@ defmodule GitGud.Packages.Retention do
| 33 | 33 | ## Reporting vs enforcing |
| 34 | 34 | |
| 35 | 35 | `plan/1` computes what would go; `apply/2` is what deletes. They are |
| 36 | − separate because the instance-wide default would prune a lot of | |
| 37 | − history the first time it ran on an instance that predates it, and | |
| 38 | − "here is what this would do" is the only honest way to ask an admin | |
| 39 | − to turn it on. `packages.retention_enforced` is off until they do. | |
| 36 | + separate so an admin can see "here is what this would do" before it | |
| 37 | + does it: `packages.retention_enforced` is on by default, and turning | |
| 38 | + it off leaves the nightly pass reporting the plan without acting. | |
| 40 | 39 | """ |
| 41 | 40 | |
| 42 | 41 | import Ecto.Query, warn: false |
modified
lib/git_gud/packages/workers/prune.ex
+4
−4
@@ -2,10 +2,10 @@ defmodule GitGud.Packages.Workers.Prune do
| 2 | 2 | @moduledoc """ |
| 3 | 3 | The nightly retention pass. |
| 4 | 4 | |
| 5 | − Does nothing at all until an admin turns `packages.retention_enforced` | |
| 6 | − on — it still runs, and still logs what it *would* free, which is the | |
| 7 | − number the admin view shows and the reason the job exists before | |
| 8 | − anyone has decided to enable it. | |
| 5 | + Deletes what the retention policy lets go, while | |
| 6 | + `packages.retention_enforced` is on (the default). With it off it | |
| 7 | + still runs, and logs what it *would* free — the number the admin view | |
| 8 | + shows. | |
| 9 | 9 | |
| 10 | 10 | Scheduled from `config/config.exs`. Daily rather than hourly: a |
| 11 | 11 | registry's disk problem develops over weeks, and a nightly job is |
modified
lib/git_gud/settings.ex
+5
−6
@@ -14,11 +14,10 @@ defmodule GitGud.Settings do
| 14 | 14 | ## The keys |
| 15 | 15 | |
| 16 | 16 | * `packages.retention_enforced` — whether the pruner deletes |
| 17 | − anything at all. **Off by default**, and deliberately: an | |
| 18 | − instance that upgrades into this slice has years of versions that | |
| 19 | − the default policy would prune on its first pass. Reporting mode | |
| 20 | − computes the same plan and shows it, so the decision to start | |
| 21 | − deleting is one an admin makes after looking at the number. | |
| 17 | + anything at all. **On by default**: CI publishes on every push, | |
| 18 | + so a registry left alone is a disk that fills. Turn it off for | |
| 19 | + reporting mode, which computes the same plan and shows it without | |
| 20 | + deleting — the admin packages page has the switch and the number. | |
| 22 | 21 | |
| 23 | 22 | * `packages.retention_mode` / `packages.retention_keep` / |
| 24 | 23 | `packages.retention_bytes` — the instance policy, and the ceiling |
@@ -37,7 +36,7 @@ defmodule GitGud.Settings do
| 37 | 36 | alias GitGud.Settings.InstanceSetting |
| 38 | 37 | |
| 39 | 38 | @keys %{ |
| 40 | − "packages.retention_enforced" => %{type: :boolean, default: false}, | |
| 39 | + "packages.retention_enforced" => %{type: :boolean, default: true}, | |
| 41 | 40 | "packages.retention_mode" => %{type: :string, default: "keep_last", in: ~w(keep_last quota)}, |
| 42 | 41 | "packages.retention_keep" => %{type: :integer, default: 24, min: 1, max: 10_000}, |
| 43 | 42 | "packages.retention_bytes" => %{type: :integer, default: nil, min: 0, nullable: true}, |
modified
test/git_gud/packages/pruner_test.exs
+5
−5
@@ -21,6 +21,11 @@ defmodule GitGud.Packages.PrunerTest do
| 21 | 21 | end |
| 22 | 22 | |
| 23 | 23 | describe "prune/2 while enforcement is off" do |
| 24 | + setup do | |
| 25 | + {:ok, _} = Settings.put("packages.retention_enforced", false) | |
| 26 | + :ok | |
| 27 | + end | |
| 28 | + | |
| 24 | 29 | test "reports what it would do and touches nothing", %{repo: repo} do |
| 25 | 30 | package = package_with_versions(repo, 30) |
| 26 | 31 |
@@ -42,11 +47,6 @@ defmodule GitGud.Packages.PrunerTest do
| 42 | 47 | end |
| 43 | 48 | |
| 44 | 49 | describe "prune/2 with enforcement on" do |
| 45 | − setup do | |
| 46 | − {:ok, _} = Settings.put("packages.retention_enforced", true) | |
| 47 | − :ok | |
| 48 | − end | |
| 49 | − | |
| 50 | 50 | test "deletes the oldest versions and their bytes", %{repo: repo} do |
| 51 | 51 | package = package_with_versions(repo, 26) |
| 52 | 52 |
modified
test/git_gud/packages/retention_test.exs
+4
−4
@@ -200,13 +200,13 @@ defmodule GitGud.Packages.RetentionTest do
| 200 | 200 | end |
| 201 | 201 | |
| 202 | 202 | describe "enforcement" do |
| 203 | − test "is off until an admin turns it on" do | |
| 204 | − refute Retention.enforced?() | |
| 203 | + test "is on until an admin turns it off" do | |
| 204 | + assert Retention.enforced?() | |
| 205 | 205 | |
| 206 | 206 | user = user_fixture() |
| 207 | − {:ok, _} = Settings.put("packages.retention_enforced", true, user) | |
| 207 | + {:ok, _} = Settings.put("packages.retention_enforced", false, user) | |
| 208 | 208 | |
| 209 | − assert Retention.enforced?() | |
| 209 | + refute Retention.enforced?() | |
| 210 | 210 | end |
| 211 | 211 | end |
| 212 | 212 |
modified
test/git_gud_web/live/admin_packages_test.exs
+7
−4
@@ -1,7 +1,8 @@
| 1 | 1 | defmodule GitGudWeb.AdminPackagesTest do |
| 2 | 2 | @moduledoc """ |
| 3 | − The admin view over registry disk, and the levers on it. The levers | |
| 4 | − are the point: retention is off until someone here turns it on. | |
| 3 | + The admin view over registry disk, and the levers on it. Retention is | |
| 4 | + on by default; with it off the page reports what it would free, and | |
| 5 | + only the button deletes. | |
| 5 | 6 | """ |
| 6 | 7 | use GitGudWeb.ConnCase, async: false |
| 7 | 8 |
@@ -24,6 +25,7 @@ defmodule GitGudWeb.AdminPackagesTest do
| 24 | 25 | end |
| 25 | 26 | |
| 26 | 27 | test "shows what retention would free without deleting anything", %{conn: conn, admin: admin} do |
| 28 | + {:ok, _} = Settings.put("packages.retention_enforced", false) | |
| 27 | 29 | {:ok, lv, _html} = conn |> log_in_user(admin) |> live(~p"/admin/packages") |
| 28 | 30 | |
| 29 | 31 | html = render_async(lv) |
@@ -41,7 +43,7 @@ defmodule GitGudWeb.AdminPackagesTest do
| 41 | 43 | lv |
| 42 | 44 | |> form("form[phx-submit=save]", %{ |
| 43 | 45 | "settings" => %{ |
| 44 | − "packages.retention_enforced" => "true", | |
| 46 | + "packages.retention_enforced" => "false", | |
| 45 | 47 | "packages.retention_mode" => "keep_last", |
| 46 | 48 | "packages.retention_keep" => "10", |
| 47 | 49 | "packages.retention_bytes" => "", |
@@ -50,7 +52,7 @@ defmodule GitGudWeb.AdminPackagesTest do
| 50 | 52 | }) |
| 51 | 53 | |> render_submit() |
| 52 | 54 | |
| 53 | − assert Settings.get("packages.retention_enforced") == true | |
| 55 | + assert Settings.get("packages.retention_enforced") == false | |
| 54 | 56 | assert Settings.get("packages.retention_keep") == 10 |
| 55 | 57 | assert Settings.get("packages.max_pins") == 5 |
| 56 | 58 | # Blank means "no ceiling", not zero — zero would prune every |
@@ -97,6 +99,7 @@ defmodule GitGudWeb.AdminPackagesTest do
| 97 | 99 | end |
| 98 | 100 | |
| 99 | 101 | test "prune now deletes, and only on the button", %{conn: conn, admin: admin} do |
| 102 | + {:ok, _} = Settings.put("packages.retention_enforced", false) | |
| 100 | 103 | {:ok, lv, _html} = conn |> log_in_user(admin) |> live(~p"/admin/packages") |
| 101 | 104 | render_async(lv) |
| 102 | 105 |
Parents: 4241ea4