Compare commits

...
Author SHA1 Message Date
Maycon Santos 9fab9d7a83 Cover RPM-based distros and desktop app dependencies in the Linux tab (#733)
* Cover RPM-based distros and desktop app dependencies in the Linux tab

The manual-install section of the add-peer modal only covered Ubuntu via
APT. Replace the single Ubuntu accordion with a distro selector covering
Debian/Ubuntu (APT), Fedora (DNF), RHEL/AlmaLinux/Rocky (DNF), and Amazon
Linux (YUM).

The desktop app links GTK 4.10+ and WebKitGTK 6.0 but the released
netbird-ui packages do not declare them, so each desktop-app install line
names the packages explicitly and a callout states the minimum release
(Ubuntu 24.04, Debian 13, Fedora 43, RHEL 10 with EPEL). Distros without
the required stack get CLI-only instructions.

* Add e2e coverage for the Linux distro selector
2026-07-28 03:03:46 +09:00
2 changed files with 237 additions and 30 deletions

No files matched your search

+103
View File
@@ -0,0 +1,103 @@
/**
* Linux tab of the setup modal — distro selector spec.
*
* The manual-install accordion offers per-distro instructions via a
* SelectDropdown (data-testid="linux-distro-select"). This validates that
* switching distros swaps the repository and install commands, that
* CLI-only distros render no desktop-app line, and that the copy button
* emits commands without comment lines. Runs against /install, which
* renders the setup modal without authentication.
*/
import { expect, test } from "@playwright/test";
async function openLinuxManualInstall(page: import("@playwright/test").Page) {
await page.goto("/install");
await page.getByTestId("setup-netbird-modal").waitFor();
await page.getByRole("tab", { name: /linux/i }).click();
await page.getByText("Install manually with a package manager").click();
await page.getByTestId("linux-distro-select").waitFor();
}
async function selectDistro(
page: import("@playwright/test").Page,
name: string,
) {
await page.getByTestId("linux-distro-select").click();
await page.getByRole("option", { name }).click();
}
test.describe("Setup modal Linux distro instructions", () => {
test("defaults to APT with repository, install and run steps", async ({
page,
}) => {
await openLinuxManualInstall(page);
const modal = page.getByTestId("setup-netbird-modal");
await expect(modal.getByTestId("linux-distro-select")).toContainText(
"Debian / Ubuntu (APT)",
);
await expect(modal).toContainText(
"https://pkgs.netbird.io/debian stable main",
);
await expect(modal).toContainText("sudo apt-get install netbird");
await expect(modal).toContainText(
"sudo apt-get install netbird-ui libgtk-4-1 libwebkitgtk-6.0-4 xdg-utils",
);
// The run step stays present alongside the distro-specific steps
await expect(modal.getByText("netbird up")).toHaveCount(2);
});
test("switches repository and install commands per distro", async ({
page,
}) => {
await openLinuxManualInstall(page);
const modal = page.getByTestId("setup-netbird-modal");
await selectDistro(page, "Fedora (DNF)");
await expect(modal).toContainText("/etc/yum.repos.d/netbird.repo");
await expect(modal).toContainText("sudo dnf install netbird");
await expect(modal).toContainText(
"sudo dnf install netbird-ui gtk4 webkitgtk6.0 xdg-utils",
);
await expect(modal).not.toContainText("apt-get");
await expect(modal).not.toContainText("epel-release");
await selectDistro(page, "RHEL / AlmaLinux / Rocky (DNF)");
await expect(modal).toContainText("sudo dnf install epel-release -y");
await expect(modal).toContainText(
"sudo dnf install netbird-ui gtk4 webkitgtk6.0 xdg-utils",
);
});
test("offers CLI only on Amazon Linux", async ({ page }) => {
await openLinuxManualInstall(page);
const modal = page.getByTestId("setup-netbird-modal");
await selectDistro(page, "Amazon Linux (YUM)");
await expect(modal).toContainText("sudo yum install netbird");
await expect(modal).not.toContainText("netbird-ui");
await expect(modal).not.toContainText("for CLI only");
await expect(modal).toContainText("only the CLI is available");
});
test("copies install commands without comment lines", async ({
page,
context,
}) => {
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await openLinuxManualInstall(page);
const modal = page.getByTestId("setup-netbird-modal");
// Copy buttons in DOM order: install script, run command, repository,
// install step, run command (accordion).
await modal.getByTestId("copy-to-clipboard").nth(3).click();
const copied = await page.evaluate(() => navigator.clipboard.readText());
expect(copied, "clipboard should hold runnable commands only").toBe(
[
"sudo apt-get update",
"sudo apt-get install netbird",
"sudo apt-get install netbird-ui libgtk-4-1 libwebkitgtk-6.0-4 xdg-utils",
].join("\n"),
);
});
});
+134 -30
View File
@@ -4,13 +4,14 @@ import {
AccordionItem,
AccordionTrigger,
} from "@components/Accordion";
import { Callout } from "@components/Callout";
import Code from "@components/Code";
import { SelectDropdown } from "@components/select/SelectDropdown";
import Separator from "@components/Separator";
import Steps from "@components/Steps";
import TabsContentPadding, { TabsContent } from "@components/Tabs";
import { IconBrandUbuntu } from "@tabler/icons-react";
import { TerminalSquareIcon } from "lucide-react";
import React from "react";
import { PackageIcon, TerminalSquareIcon } from "lucide-react";
import React, { useState } from "react";
import { OperatingSystem } from "@/interfaces/OperatingSystem";
import {
NetBirdUpCommand,
@@ -25,6 +26,84 @@ type Props = {
hostname?: string;
};
type Distro = {
label: string;
value: string;
/** Commands that register the NetBird package repository. */
repository: string[];
/** Commands that run before the install lines, after the repository is added. */
beforeInstall?: string[];
cli: string;
/**
* Desktop app install lines. Omitted when the distribution cannot run it,
* in which case only the CLI is offered.
*/
desktopApp?: string[];
note: string;
};
const YUM_REPOSITORY = [
"sudo tee /etc/yum.repos.d/netbird.repo <<EOF",
"[netbird]",
"name=netbird",
"baseurl=https://pkgs.netbird.io/yum/",
"enabled=1",
"gpgcheck=1",
"gpgkey=https://pkgs.netbird.io/yum/repodata/repomd.xml.key",
"repo_gpgcheck=1",
"EOF",
];
// The desktop app links GTK 4.10+ and WebKitGTK 6.0, and the released
// packages do not declare those as dependencies, so each install line names
// them explicitly. Distributions below that floor get the CLI only.
const DISTROS: Distro[] = [
{
label: "Debian / Ubuntu (APT)",
value: "apt",
repository: [
"sudo apt-get update",
"sudo apt-get install ca-certificates curl gnupg -y",
"curl -sSL https://pkgs.netbird.io/debian/public.key | sudo gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg",
`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`,
],
beforeInstall: ["sudo apt-get update"],
cli: "sudo apt-get install netbird",
desktopApp: [
"sudo apt-get install netbird-ui libgtk-4-1 libwebkitgtk-6.0-4 xdg-utils",
],
note: "The desktop app needs Ubuntu 24.04 or Debian 13 and newer. On earlier releases install the CLI only.",
},
{
label: "Fedora (DNF)",
value: "fedora",
repository: YUM_REPOSITORY,
cli: "sudo dnf install netbird",
desktopApp: ["sudo dnf install netbird-ui gtk4 webkitgtk6.0 xdg-utils"],
note: "The desktop app needs Fedora 43 and newer. On earlier releases install the CLI only.",
},
{
label: "RHEL / AlmaLinux / Rocky (DNF)",
value: "rhel",
repository: YUM_REPOSITORY,
cli: "sudo dnf install netbird",
// WebKitGTK 6.0 ships in EPEL rather than the base repositories, so the
// desktop app path enables it first. CLI-only users do not need it.
desktopApp: [
"sudo dnf install epel-release -y",
"sudo dnf install netbird-ui gtk4 webkitgtk6.0 xdg-utils",
],
note: "The desktop app needs version 10 or newer with EPEL enabled, which provides WebKitGTK 6.0. On version 9 install the CLI only.",
},
{
label: "Amazon Linux (YUM)",
value: "amazon",
repository: YUM_REPOSITORY,
cli: "sudo yum install netbird",
note: "Amazon Linux does not ship GTK 4 or WebKitGTK 6.0, so only the CLI is available.",
},
];
export default function LinuxTab({
setupKey,
setupKeyContent,
@@ -32,8 +111,20 @@ export default function LinuxTab({
showSetupKeyInfo = false,
hostname,
}: Readonly<Props>) {
const [distroValue, setDistroValue] = useState(DISTROS[0].value);
const distro =
DISTROS.find((option) => option.value === distroValue) ?? DISTROS[0];
const runStep = setupKeyContent ? 3 : 2;
const usingSetupKey = !!setupKey || !!setupKeyPlaceholder;
const hasDesktopApp = !!distro.desktopApp?.length;
const installLines = [
...(distro.beforeInstall ?? []),
distro.cli,
...(distro.desktopApp ?? []),
];
return (
<TabsContent value={String(OperatingSystem.LINUX)}>
<TabsContentPadding>
@@ -66,43 +157,56 @@ export default function LinuxTab({
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>
<IconBrandUbuntu size={16} />
Install manually on Ubuntu
<PackageIcon size={16} />
Install manually with a package manager
</AccordionTrigger>
<AccordionContent>
<div className={"mt-1"}>
<SelectDropdown
value={distroValue}
className={"w-[280px]"}
onChange={setDistroValue}
placeholder={"Select distribution"}
options={DISTROS.map(({ label, value }) => ({
label,
value,
}))}
data-testid={"linux-distro-select"}
/>
</div>
<Steps>
<Steps.Step step={1}>
<p>Add our repository</p>
<Code>
<Code.Line>sudo apt-get update</Code.Line>
<Code.Line>
sudo apt install ca-certificates curl gnupg -y
</Code.Line>
<Code.Line>
curl -sSL https://pkgs.netbird.io/debian/public.key | sudo
gpg --dearmor --output
/usr/share/keyrings/netbird-archive-keyring.gpg
</Code.Line>
<Code.Line>
{`echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' | sudo tee /etc/apt/sources.list.d/netbird.list`}
</Code.Line>
<Code codeToCopy={distro.repository.join("\n")}>
{distro.repository.map((line) => (
<Code.Line key={line}>{line}</Code.Line>
))}
</Code>
</Steps.Step>
<Steps.Step step={2}>
<p>Install NetBird</p>
<Code
codeToCopy={[
`sudo apt-get update`,
`sudo apt-get install netbird`,
`sudo apt-get install netbird-ui`,
].join("\n")}
>
<Code.Line>sudo apt-get update</Code.Line>
<Code.Comment># for CLI only</Code.Comment>
<Code.Line>sudo apt-get install netbird</Code.Line>
<Code.Comment># for GUI package</Code.Comment>
<Code.Line>sudo apt-get install netbird-ui</Code.Line>
<Code codeToCopy={installLines.join("\n")}>
{distro.beforeInstall?.map((line) => (
<Code.Line key={line}>{line}</Code.Line>
))}
{hasDesktopApp && (
<Code.Comment># for CLI only</Code.Comment>
)}
<Code.Line>{distro.cli}</Code.Line>
{hasDesktopApp && (
<>
<Code.Comment># for the desktop app</Code.Comment>
{distro.desktopApp?.map((line) => (
<Code.Line key={line}>{line}</Code.Line>
))}
</>
)}
</Code>
<Callout variant={"info"} className={"mt-1"}>
{distro.note}
{hasDesktopApp &&
" Desktop app packages are available for x86_64 only."}
</Callout>
</Steps.Step>
<Steps.Step step={3} line={false}>
<p>