Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
590921e
docs: add cleanup projections implementation plan
mikhail-angelov Oct 19, 2026
a9bbb5d
feat(remove): move items to the Finder trash on macOS via /usr/bin/trash
mikhail-angelov Oct 19, 2026
bf24281
feat(webui): add cleanup projections to the treemap
mikhail-angelov Oct 19, 2026
9710443
feat(webui): delete items optimistically
mikhail-angelov Oct 19, 2026
73539e6
feat(webui): keep the treemap still after deletions and on hover in p…
mikhail-angelov Oct 19, 2026
67a7890
feat(webui): show the space freed by deletions in the header
mikhail-angelov Oct 19, 2026
690b94a
fix(cleanup): match path rules under the virtual root; cover more cac…
mikhail-angelov Oct 19, 2026
1238c19
fix(cleanup): items inside a matched directory inherit its category
mikhail-angelov Oct 19, 2026
5c9bd04
feat(webui): make the legend's category highlight stand out
mikhail-angelov Oct 19, 2026
aaf9081
docs: drop the implementation plan
mikhail-angelov Oct 19, 2026
62505ec
revert: move items to the Finder trash on macOS via /usr/bin/trash
dundee Oct 19, 2026
9987db8
fix(cleanup): upgrade only git-ignored build output to safe
dundee Oct 19, 2026
0abccf8
fix(cleanup): split a matched directory around exclusions and more sp…
dundee Oct 19, 2026
aeefca1
fix(cleanup): classify siblings again after a marker or .gitignore is…
dundee Oct 19, 2026
9098f50
fix(webui): do not cache a classification that overlapped a deletion
dundee Oct 19, 2026
adc2cff
refactor(webui): name the category tree after its projection
dundee Oct 19, 2026
00eec8a
refactor(webui): share the response for a missing classification
dundee Oct 19, 2026
17e9bc6
refactor(webui): pick size or usage with one helper
dundee Oct 19, 2026
8128d4b
feat(webui): classify cleanup candidates in the background
dundee Oct 19, 2026
b57d427
feat(webui): keep browsing while deletions run
dundee Oct 19, 2026
933e56e
fix(webui): report a trash command that left the item in place
dundee Oct 19, 2026
b7bc277
docs: describe cleanup units, split directories and classification in…
dundee Oct 19, 2026
421aa77
docs: describe the cleanup config section in configuration.md
dundee Oct 19, 2026
13087d0
chore(webui): rebuild assets
dundee Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,36 @@ What a bar is measured against. Two modes, applying to every bar alike:

Rows the user has ignored are excluded from the denominator and draw an empty
bar.

## Projection

A way of coloring (and, for one projection, regrouping) the web UI treemap
other than by location. Area always stays the item's size; a projection only
changes what the color says.

## Cleanup candidate

An item that is likely safe to remove to free space, because a tool can
regenerate or re-download it. A directory that is a candidate is a unit: the
items inside it show its category, but they are not candidates of their own,
its space is counted once, and the "By category" projection lists only the
unit. A matched directory that holds an excluded path, or a path a more
specific rule matches, is split instead: its children become the units, so
deleting any candidate never takes an excluded item with it.

## Classification

Finding the cleanup candidates of the scanned tree and the space they take
per directory. It runs in the background once a scan ends or the tree
changes; the cleanup projections fill in when it is done.

## Category

What kind of cleanup candidate an item is: build artifact, package cache, app
cache, SDK or toolchain, ML model, installer or archive, or git-ignored.

## Confidence

How safe removing a cleanup candidate is: **safe** (regenerated on demand) or
**review** (probably unneeded, but may hold the only copy of something or need
its own tool to be removed cleanly).
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ make install-dev-dependencies
## Project layout

- `cmd/gdu` — CLI entrypoint and flag wiring.
- `pkg/` — reusable libraries: `analyze`, `device`, `fs`, `path`, `remove`,
`timefilter`, `annex`.
- `pkg/` — reusable libraries: `analyze`, `cleanup`, `device`, `fs`, `path`,
`remove`, `timefilter`, `annex`.
- `stdout/`, `report/`, `tui/` — output backends (non-interactive stdout, JSON
export/import, and the interactive terminal UI).
- `webui/` — the optional React web UI (compiled output is committed).
Expand Down
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,55 @@ its own reveal and delete buttons that act on that item; deletion asks for
confirmation with the same dialog. The deletion dialog can suppress further
confirmations for the current browser session, for both the treemap and the
table, so with "permanent" chosen a single click on a row's delete button
deletes immediately. The `--no-delete` flag and
deletes immediately. Deletion runs in the background: the item is drawn
hatched right away, more items can be deleted meanwhile, and a failed deletion
puts the item back with the reason. A deleted item stays in place,
cross-hatched, so the layout does not jump; the directory is reloaded when you
open another one. You can keep browsing while deletions run; an item that is
being deleted cannot be opened. The `--no-delete` flag and
filtered-scan deletion restrictions apply to the web UI as well as the
terminal UI. Reveal remains available with `--no-delete` because it does not
modify scanned data. To deliberately allow deletion while filters are active,
set `GDU_ALLOW_DELETE_WITH_FILTER=1`; the displayed tree may then omit files
that are still present on disk.

### Cleanup projections

The treemap's projection selector (or `P`) recolors it to help free space
without touching anything important. Area always stays size.

- **Location** - the default coloring by top-level directory.
- **Cleanup** - everything grey except cleanup candidates: strong orange for
*safe* ones (regenerated by their tool, e.g. `node_modules` next to
`package.json`, `target` next to `Cargo.toml`, `__pycache__`, the Go, Gradle,
Cargo, npm, uv and Homebrew caches, app caches)
and muted gold for ones to *review first* (downloaded ML models, installed
toolchains such as `~/.rustup` or `~/.nvm`, installers,
virtualenvs, `dist`/`build`, git-ignored files). A directory is tinted by the
share of candidates inside it, including files too small to draw.
- **Age** - colored by the latest change inside an item.
- **By category** - candidates within the current directory regrouped by
category, so cache space scattered over many small directories shows up as
one block.

The legend shows how much each category takes within the current directory;
click a category to single it out: its items are drawn in the category's color
and outlined, everything else fades (`Esc` shows all again). Hovering a candidate
shows what it is and how to remove it cleanly (e.g. `ollama rm` for Ollama
models). Build-like directories only count next to a project marker, and
anything ignored by a repository's `.gitignore` is flagged for review (ignored
`dist`/`build` output counts as safe). Candidates are found in the background
after a scan, while the header shows "classifying…". They are only
highlighted: deleting stays a deliberate action.

Paths can be excluded and rules added in the `cleanup` section of the config
file, see [configuration.md](configuration.md#cleanupexclude):

```yaml
cleanup:
exclude: [~/work/current-project]
```

By default the server binds to `localhost` on a random free port. Use
`--web-listen` (or the `web.listen` config option) to pin a fixed address.

Expand Down
7 changes: 7 additions & 0 deletions cmd/gdu/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/dundee/gdu/v5/build"
"github.com/dundee/gdu/v5/internal/common"
"github.com/dundee/gdu/v5/pkg/analyze"
"github.com/dundee/gdu/v5/pkg/cleanup"
"github.com/dundee/gdu/v5/pkg/device"
gfs "github.com/dundee/gdu/v5/pkg/fs"
"github.com/dundee/gdu/v5/pkg/timefilter"
Expand Down Expand Up @@ -119,6 +120,9 @@ type Flags struct {
BrowseParentDirs bool `yaml:"browse-parent-dirs"`
Web bool `yaml:"-"`
WebConfig WebConfig `yaml:"web"`
// Cleanup holds user rules and exclusions for the web UI's cleanup
// projections.
Cleanup cleanup.Config `yaml:"cleanup"`
}

// WebConfig defines the web UI options that can be set from the config file.
Expand Down Expand Up @@ -496,6 +500,9 @@ func (a *App) createUI(outputAttributes gfs.JSONAttributes) (UI, error) {
if a.Flags.TrashCommand != "" {
webUI.SetTrashCommand(a.Flags.TrashCommand)
}
if err := webUI.SetCleanupConfig(a.Flags.Cleanup); err != nil {
return nil, fmt.Errorf("cleanup config: %w", err)
}
ui = webUI
case a.Flags.OutputFile != "":
var output io.Writer
Expand Down
41 changes: 41 additions & 0 deletions cmd/gdu/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (
"time"

log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"

"github.com/dundee/gdu/v5/internal/common"
"github.com/dundee/gdu/v5/internal/testapp"
"github.com/dundee/gdu/v5/internal/testdev"
"github.com/dundee/gdu/v5/internal/testdir"
"github.com/dundee/gdu/v5/pkg/cleanup"
"github.com/dundee/gdu/v5/pkg/device"
gfs "github.com/dundee/gdu/v5/pkg/fs"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -872,6 +874,45 @@ func TestWebFlagCreatesWebUIAndHonorsNoDelete(t *testing.T) {
assert.ErrorContains(t, err, "binding")
}

func TestWebFlagRejectsInvalidCleanupRule(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

_, err := runApp(
&Flags{
LogFile: "/dev/null",
Web: true,
Cleanup: cleanup.Config{Rules: []cleanup.Rule{{Category: "junk", Dir: "x"}}},
},
[]string{"test_dir"},
false,
testdev.DevicesInfoGetterMock{},
)

assert.ErrorContains(t, err, `cleanup config: cleanup rule 1: unknown category "junk"`)
}

func TestCleanupConfigFromYAML(t *testing.T) {
var f Flags
err := yaml.Unmarshal([]byte(`
cleanup:
exclude: [~/work]
rules:
- category: build-artifact
confidence: safe
dir: .gradle
markers: [build.gradle]
`), &f)
assert.NoError(t, err)
assert.Equal(t, cleanup.Config{
Exclude: []string{"~/work"},
Rules: []cleanup.Rule{{
Category: cleanup.BuildArtifact, Confidence: cleanup.Safe,
Dir: ".gradle", Markers: []string{"build.gradle"},
}},
}, f.Cleanup)
}

func TestListDevices(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()
Expand Down
44 changes: 43 additions & 1 deletion configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Further details:
* When the command fails, its exit status and standard error output are shown in an error dialog and the item stays in the listing.
* When the command succeeds but the item is still present on disk, the item stays in the listing as well. Only the selected item is refreshed, so an item moved elsewhere by the command is not picked up at its new location.
* `no-delete` disables the `D` key regardless of this option.
* Also used by the web UI's "Move to Trash" action, under the same contract.
* Also used by the web UI's "Move to Trash" action, under the same contract. The web UI reports an item the command left in place as an error.
* Not supported on Windows, which has no POSIX shell to evaluate the command in.

#### `browse-parent-dirs`
Expand All @@ -232,6 +232,48 @@ Whether the web UI opens in the default browser on start. Enabled by default. Th

Override the command used to open the browser (the URL is appended as the final argument), e.g. `firefox --new-window`. When empty (the default), the operating system's default handler is used.

#### `cleanup.exclude`

Paths the web UI's cleanup projections never mark as candidates, nor anything below them. A leading `~/` stands for the home directory.

A matched directory that holds an excluded path is not offered as a whole: its other children are marked instead, so deleting a highlighted candidate never takes an excluded item with it. Excluding a path does not protect it from a deliberate deletion.

```yaml
cleanup:
exclude:
- ~/work/current-project
```

#### `cleanup.rules`

Extra rules for the web UI's cleanup projections, checked before the built-in ones. The first rule that matches an item decides its category. Each rule has:

* `category` - required, one of `build-artifact`, `package-cache`, `app-cache`, `toolchain`, `ml-model`, `installer` and `git-ignored`.
* `confidence` - `safe` (regenerated on demand) or `review` (probably unneeded, but check first). Defaults to `review`.
* Exactly one of:
* `dir` - a glob matched against the name of directories, e.g. `.gradle`.
* `file` - a glob matched against the name of files, e.g. `*.gguf`.
* `path` - a glob matched against the absolute path of any item, e.g. `~/.cache/my-tool`. A leading `~/` stands for the home directory, and `*` does not match `/`.
* `under` - only match items below this directory, e.g. `~/Downloads`.
* `markers` - only match when the item has a sibling with one of these names, e.g. `package.json` for `node_modules`. Markers may be globs, e.g. `*.xcodeproj`.
* `hint` - shown with the candidate, e.g. how to remove it cleanly.

A matched directory is a unit: the items inside it show its category but are not candidates of their own. When an excluded path or the path of a more specific `path` rule lies below it, it is split instead and its children become units, so the more specific rule wins where it matches.

Items no rule matches but a repository's `.gitignore` ignores are `git-ignored` candidates to review.

```yaml
cleanup:
rules:
- category: build-artifact
confidence: safe
dir: .gradle
markers: [build.gradle, build.gradle.kts]
hint: Rebuilt by Gradle
- category: ml-model
path: ~/models/*
```


#### `style.selected-row.text-color`

Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/ebitengine/purego v0.10.2
github.com/fatih/color v1.19.0
github.com/gdamore/tcell/v2 v2.13.10
github.com/go-git/go-git/v5 v5.19.3
github.com/h2non/filetype v1.1.3
github.com/maruel/natural v1.3.0
github.com/mattn/go-isatty v0.0.24
Expand All @@ -27,11 +28,14 @@ require (
github.com/dgraph-io/ristretto/v2 v2.3.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.9.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/flatbuffers v25.9.23+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/klauspost/compress v1.18.1 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
Expand All @@ -44,8 +48,10 @@ require (
go.opentelemetry.io/otel/metric v1.41.0 // indirect
go.opentelemetry.io/otel/trace v1.41.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/term v0.43.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/term v0.45.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.75.7 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.12.1 // indirect
Expand Down
16 changes: 14 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uh
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.13.10 h1:Afs3JKt83HnhuUKdZ3MnxUgOqQRWftj5JyDqv1LLynA=
github.com/gdamore/tcell/v2 v2.13.10/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.9.2 h1:OXFSRyz4g20upsGDJgQG9Bak1l/ZEv8GHVYB52O71sE=
github.com/go-git/go-billy/v5 v5.9.2/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
github.com/go-git/go-git/v5 v5.19.3 h1:qHttbZ+Am7wEjdTpR1XMQ4rl42FK9SIXzZ4o9x7s6M4=
github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW61gcOyhl4=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand All @@ -36,6 +42,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
Expand Down Expand Up @@ -96,6 +104,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -112,8 +122,8 @@ golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
Expand All @@ -133,6 +143,8 @@ google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
Expand Down
Loading
Loading