/repos is no longer a route, so specs fell back to the inbox and logged a
routing error. The inbox opens the first-launch guide, so tests now start
with it dismissed. The router now logs the URL it couldn't resolve
instead of just "URL".
svelte-check already type-checks what tsc did, clippy already runs cargo
check, and radicle-tauri has no doc tests. Linting all targets also
covers test code, which cargo test builds but never lints.
Runs the same steps as the workflows and logs each step's output, exit
code and duration, so a failed run can be read back without the terminal.
The output ends with a summary of each step.
The dev server's unbundled modules made each page load take seconds, which
is most of the time a test spends; E2E_DEV_SERVER=1 keeps the dev server.
Each run writes a JSON report, and CI uses two workers now that the
dropped keep-alive connections behind the flakiness are fixed.
The page always talked to the shared palm backend, so tests couldn't set
up peers of their own. Peers now have no passphrase by default, so tests
don't need an ssh-agent and test keys stay out of the developer's agent.
Fixtures wait for replication instead of relying on gossip timing, and a
request cancelled by a reload no longer counts as a page error.
Several commands had no route or returned stubs, so e2e tests silently got
null or empty data. A missing route now fails loudly, the profile loads on
first use so onboarding works, and node status is polled over HTTP. A
parity test keeps the routes from drifting from the Tauri handlers again;
it also caught revisions_by_patch being registered twice.
Parallel e2e tests hit the repo's sigrefs lock when creating COBs
concurrently, which the Tauri avoids by running these commands on
the main thread serially.
A single catalog in lib/shortcuts defines every shortcut's keys and
description. Components bind behaviour to it by id for as long as they
are mounted, and one listener applies the shared rules for text fields,
modals, priority and cycling. The shortcuts modal lists the catalog.
A repository can sit in storage without being seeded, which `rad ls`
hides and `rad ls --all` shows as "local". A new Settings preference
picks the `Show` passed to `list_repos_summary`, defaulting to seeded.
Unseeding only deletes the policy row, so a node defaulting to `allow`
keeps seeding: `unseed` now reports `DefaultPolicySeeds` instead of
claiming success, and both callers refresh in a `finally` so a failed
`clean` no longer leaves a stale row behind.
Show the email alongside each name. Co-authors are read from the commit
message trailers and listed as their own section. The trigger avatars
now stack and fan apart on hover, and cover everyone credited on the
commit rather than just the author and committer. Timestamps now show
both committed and authored times in the tooltip.
A desktop launch inherits a `PATH` that holds none of the user's own
install locations, so `Command::new("git")` finds nothing usable, or on
macOS finds Apple's shim and only prompts to install the developer
tools.
Resolve git to an absolute path instead, searching `PATH` first and the
usual install locations after, and cache the result for the process.
`rad` is looked for the same way, which also fixes the CLI check for a
Homebrew or Nix install.
`list_commits` walked back from the revision head and stopped at the
first commit equal to `base`. A merge commit reaches `base` through one
of its parents, so the walk truncated there and dropped everything the
other parent contributed: a revision whose head merges the target branch
in looked like a single-commit revision.
Add it to the patch list byline and the patch page metadata, shown only
when it differs from the default branch. The merge activity item now
names the real target instead of always naming the default branch.
Co-Authored-By: Brandon Oxendine <brandon.oxendine@gmail.com>
Serve `targetBranch` so the UI can tell where a patch is headed. Editing
a title no longer sends its own target, which reset branch-targeted
patches to the default branch.
- Add a sticky row over both columns, its left half the comment
column's own header
- Move collapse-all and the diff settings into it, out of the topbar
- Switch the column between the review's comments and a file tree
- Toggle between every file in the revision and only the commented ones
- Put the column away entirely, giving the diff the full width
- Keep a pixel under the bar so the first file keeps its top edge
`max-height: 100%` resolved against an auto-height box, so it was
dropped and the commit list and comment column grew past the port
instead of scrolling inside it.
The comment records only that it is resolved, so the name comes from the
resolve operation in the patch's log. Adds the action to timeline
threads too, and shapes the badge like a review's verdict.
The tree-sitter highlighter goes with it: the hunk types name its
`Paint` spans and it implements `ToPretty` for them, so neither side
can be removed on its own.
`revision_by_patch_and_id` and `review_by_patch_and_revision_and_id`
were registered as Tauri commands but never invoked: the patch view
resolves a revision or review from the revisions it already holds. Drop
the commands, their registrations and the `revision_by_id` / `review_by_id`
port methods, which had no other callers.
`fileDiffName` in `diffText` has had no callers since it was added.
radicle-surf 0.29 switched from radicle-git-ext to radicle-oid for
its Oid type, making radicle_surf::Oid and radicle::git::Oid the
same type. The crate::oid::{from_surf, into_surf} helpers were
therefore no-op round-trips through git2::Oid.
Exclude target/, node_modules/, releases/ and .git/ from the
build context. Dockerfile.release COPYs nothing, so podman was
tarring the entire 32G target/ dir into the build context,
exhausting the machine's temp disk with "no space left on
device".
PatchTeaser requests diff_stats per row, and each call re-opened the
whole repo through libgit2-surf, so a list of patches serialized into
tens of seconds on a large repo. Shell out to `git diff --numstat`,
which opens and diffs in one fast pass, falling back to the surf diff
when git is unavailable.
repo_blob and repo_readme resolved the blob through radicle-surf, which
also walks history to find the last commit that touched the path.
libgit2 ignores the commit-graph and diffs every commit, so opening a
file last changed long ago took a long time and varied by how far back
the file changed, not by its size.
Resolve the content with a direct tree lookup and the last commit via
`git rev-list -1`, falling back to the libgit2 walk when git is
unavailable.
libgit2's revwalk ignores the pack bitmap and commit-graph, so counting
walks every commit. Shell out to `git rev-list --count --use-bitmap-index`,
which uses those indexes, and fall back to the revwalk if git is
missing, too old, or errors, so the result is unchanged, just faster on
maintained storage.
The file tree was the one list left un-virtualized, so wide directories
mounted every entry and re-rendered all rows on selection. Render
entries through VirtualList like the other lists.
Navigating away from a list and back via history (the sidebar
back/forward buttons) returns to the same scroll position with the
previously loaded pages intact, instead of refetching only the first
page at the top.
A reusable windowing wrapper that mounts virtua's Virtualizer against
the existing OverlayScrollbars viewport, so the app-wide custom
scrollbar is preserved.
list_issues previously read through heartwood's id-ordered issue cache,
forcing it to load and sort every issue in memory on each page. Mirror
the patch path instead: a domain/issue Service backed by an IssueStorage
adapter that queries the issues table directly.
The issues list shipped each issue's full root comment (body) over IPC,
but the teaser never renders it, only the detail view does. Make
Issue.body optional and add an Issue::summary constructor that skips it;
list_issues now builds summaries while issue_by_id keeps the full body.
The patches list paged through everything without scrolling: ScrollArea
fired its load-more check on every `updated` DOM event, and the list's
min-height made the bottom threshold always true, so each appended page
triggered the next. Move it to InfiniteScrollSentinel like the commits
list.
The settings footer showed "Up to date" whenever no new version was
known — including when the update checker was disabled or the version
fetch had silently failed. Derive an explicit upToDate state that is
only true when a check succeeded and found no newer version, so a
failed or disabled check no longer claims the app is current.
The version check fetched latest.json without any cache directive.
The server sends no Cache-Control header, so WebKit applied heuristic
caching (RFC 9111 4.2.2): the response is considered fresh for 10% of
the time since Last-Modified, which can be hours to days between
releases. Apps checking for updates right after a release were served
a stale latest.json from disk cache and concluded they were up to
date. Dev builds use a separate cache, which is why the bug only
appeared in release builds.
Fetch with cache: "no-store" so every check hits the server.
On Linux, Tauri derives the deb/rpm package name from productName with
no way to set them independently. We override productName to
radicle-desktop so the package is named correctly, which would otherwise
also force the launcher entry to read radicle-desktop.
Add a custom .desktop template that hardcodes Name=Radicle while keeping
the package name unchanged.
Make sure docker and podman are up and running as they're necessary for
the arch packaging and artifact building.
This also shows what commands would have been run when a phase fails.