Skip to content

chore: remove duplicated code and add make dupes - #2267

Merged
geodro merged 9 commits into
mainfrom
chore/dupes-target
Oct 21, 2026
Merged

geodro merged 9 commits into
mainfrom
chore/dupes-target

Conversation

@geodro

@geodro geodro commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

This adds make dupes, a pinned jscpd run over internal/ and cmd/ that opens an HTML report of copy-pasted blocks, and uses it to fold the copies that change together into shared code. Clones go from 289 to 153, and the ones of 15 lines or more from 93 to 21.

One copy was hiding a bug. Adding, renaming or removing a domain was written out seven times across the CLI, the dashboard and the MCP server, and the copies had drifted: the dashboard's add and both MCP tools never moved a group's secondaries onto the main's new primary, and MCP reloaded nginx without waiting for the old workers to finish. All of them now go through grouping.ApplyDomainChange.

On the web UI side, the fourteen save, restore and reset confirmations of the nginx, php.ini, tuning and env editors share one modal shell, and the four editor tabs share a ConfigFile state class and the same toolbar. That means the service config tab now uses the nginx editor's toolbar, so its file path text is a shade brighter, with identical strings in every language. ConfigFile keeps an action that lands late on the file it was opened for, so switching PHP versions mid save or restore neither leaves the editor stuck busy nor overwrites the newly shown file's edits. Backup reads, JSON action calls and NDJSON streams in the stores each go through one helper, and smaller pieces moved into shared components.

In Go, the dashboard's toggle, SSE and backup handlers, the LAN IP probe, the status page CSS, the MCP site lookup and a handful of service and CLI paths share one implementation each, and code the darwin and linux files both carried now lives in unix-tagged files so a Windows implementation won't collide.

What's left is deliberate: HTTP and MCP handler preambles whose validation order the tests pin, the tray menu, the vite templates, the CLI's VPN-aware LAN probe and the watcher's dial-only one, and component prop wiring.

Closes #2266

Adding, renaming or removing a site's domain lived in seven hand-copied blocks across the CLI, the dashboard and the MCP server, and the copies had drifted apart. Adding a domain from the dashboard or through MCP never cascaded a group's secondaries onto the main's new primary, and MCP reloaded nginx without waiting for the old workers to let go, so a domain it reported as added could still answer 404 for a moment.

grouping.ApplyDomainChange now does the whole follow-through once the registry holds the new list: vhost, certificate SANs, container hosts, a settled nginx reload, the .env and its worktrees, and the group cascade. Only a vhost failure stops it; everything after comes back as a warning each surface reports in its own way.
…ations

The nginx, php.ini, service tuning and env editors each carried their own copy of the same save, restore and reset confirmation, fourteen modals in all, and four editor tabs repeated the same loading, backup listing, refresh and restore logic line for line.

The confirmations now sit on ConfigConfirmModal, which owns the busy state, the error and the post-success refresh, with BackupDiff and BackupCheckbox for the pieces they show. The editor state moved into a ConfigFile class the four tabs share, and the service tuning tab now uses the same ConfigToolbar as the others instead of its own copy, which leaves three of its translation keys unused.
Smaller copies spread across the dashboard get one home each. The lenses share row expansion and the local time format, the disk and stats stores share their visibility-aware poll, and three stores share one POST action helper. The domain and group modals run their actions through the same SiteAction, the PHP install and rebuild streams read their build log the same way, and the snapshot expiry label lives next to the Snapshot type both snapshot views use.

On the markup side, the worker gear button, the reload watcher install modal and the type-the-name confirmation field each become a component instead of two copies.
A pass over the copy-paste report, taking only the copies that change together.

The dashboard's toggle and settings handlers share their POST, authority and body decoding, and its event streams open the same way through openEventStream. The LAN IP probe that dns, podman, the TUI and the dashboard each carried lives once in internal/lanip; the CLI's VPN-aware probe and the watcher's dial-only one stay as they are on purpose. The paused, waking and not-found pages build their CSS from shared constants and render byte for byte as before.

Smaller pieces: lerd env provisions a wired service from one function on both of its paths, presets materialise a version in one place, the share commands read nginx's ports through config.NginxPorts, the worktree worker filters share one walk, and the service migrate and update paths share their image swap and pin save. Tuning saves go through cfgedit's staged write, and the fnm and mise managers share how they append env.
Three darwin and linux pairs carried the same code twice. The installer's tool download now lives in install_binaries.go, the link change watchers share their read loop and poll in linkchanges_unix.go with only the socket setup and macOS's message filter left per platform, and the macOS log stream reads its two commands through one helper. The new files are tagged unix, so a Windows implementation of the same functions does not collide with them.

The Linux installer now reports a failed phpantom_lsp download through feedback.WarnOn like macOS does, rather than its own hand-formatted line.
make dupes runs a pinned jscpd over internal/ and cmd/ for Go, TypeScript and Svelte and opens an HTML report of duplicated blocks, the same view PhpStorm's Duplicated Code inspection gives, with a JSON copy beside it for tooling. Tests, the demo build and generated i18n are left out. It is a tool to run before and after extracting a helper, not a CI gate.
@geodro
geodro requested a review from a team as a code owner October 21, 2026 08:27
@geodro geodro added the no-ui-change Touches UI files without a visible change; skips the screenshot check label Oct 21, 2026
@greptile-apps

greptile-apps Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous restore issue is fixed and no new actionable issues were found.

Summary

Adds make dupes and shares repeated code across Go operations and the dashboard.

  • Contributors can run one command to open a report of repeated code.
  • CLI, dashboard, and MCP domain changes use one shared update path.
  • Four configuration editors share file state, toolbar, and confirmation windows.
  • Shared web helpers and components handle repeated requests, streams, and controls.
  • CLI and TUI paths use one shared LAN IP lookup.
  • Code shared by macOS and Linux now lives in Unix-tagged files.
  • Dashboard and MCP paths share repeated handler and event-stream code.
  • Repeated CLI, service, and system routines now use shared implementations.

Reviews (3) · Last reviewed commit: "fix: bind a config restore to its file b..." · Reviewed by Greptile

Comment thread internal/ui/web/src/lib/configFile.svelte.ts
Comment thread internal/ui/web/src/lib/configFile.svelte.ts Outdated
… for

Switching PHP versions while a refresh of the previous one was still out left the editor's busy flag set, so the version now shown offered no Save, Reset or Revert until the pane was reopened. And a save, reset or restore confirmed after the switch refreshed the file now shown instead of the one it acted on, replacing that file's unsaved edits.

A new load now takes the busy flag over, and the editors hand their confirmations ConfigFile.refreshLater, which refreshes only while the same file is still shown.
A second pass over the copy-paste report, through the clones of ten to fourteen lines, again taking only the ones that change together.

Every config editor's backup reads go through one pair of helpers on each side: the dashboard serves a backup's bytes from writeBackupContent, and the stores list and read backups through fetchBackupList and fetchBackupText. The stores' JSON action calls go through postAction and sendAction, and the three hand-rolled NDJSON readers use readNDJSON. The dashboard's domain handlers share their finishing step, the MCP tools that only take a site share one lookup, tuning checks whether a service can be tuned in one place, conf.d vhosts are written by one function, and lerd db builds its container credentials once.
The restore built its refresh callback only after the backup read came back, so switching PHP versions while that read was out tied the callback to the version shown by then, and confirming the restore replaced that version's unsaved edits. The callback is now taken before the read starts.
@geodro
geodro merged commit 1d3adb5 into main Oct 21, 2026
12 checks passed
@geodro
geodro deleted the chore/dupes-target branch October 21, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-ui-change Touches UI files without a visible change; skips the screenshot check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Find and remove copy-pasted code, and add a duplication report

1 participant