mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-11 06:11:31 +00:00
feat: request a feature link for plugins and use the bug form for unverified plugins
This commit is contained in:
1 parent
8666533162
commit
6a855c0fa8
5 files changed
+70
-45
No files matched your search
@@ -2167,6 +2167,7 @@
|
||||
"unverifiedHint": "Installed from a file. Termix did not check or sign it.",
|
||||
"reportIssue": "Report an issue",
|
||||
"reportTo": "Report to {{author}}",
|
||||
"requestFeature": "Request a feature",
|
||||
"openSettings": "Open its settings"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Database,
|
||||
Download,
|
||||
ExternalLink,
|
||||
Lightbulb,
|
||||
Pin,
|
||||
RotateCcw,
|
||||
Settings,
|
||||
@@ -35,6 +36,7 @@ import {
|
||||
formatBytes,
|
||||
orderByRisk,
|
||||
reportIssueUrl,
|
||||
requestFeatureUrl,
|
||||
type PluginEntry,
|
||||
} from "./plugin-model";
|
||||
import { openPluginSettings } from "./open-plugins";
|
||||
@@ -77,6 +79,7 @@ export function PluginDetail({
|
||||
plugin,
|
||||
import.meta.env.VITE_APP_VERSION || undefined,
|
||||
);
|
||||
const featureUrl = requestFeatureUrl(plugin);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -398,6 +401,20 @@ export function PluginDetail({
|
||||
</a>
|
||||
)}
|
||||
|
||||
{featureUrl && (
|
||||
<a
|
||||
href={featureUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={LINK_ROW}
|
||||
>
|
||||
<Lightbulb className="size-3.5 shrink-0" />
|
||||
<span className="truncate">
|
||||
{t("plugins.manager.requestFeature")}
|
||||
</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{plugin.repository && (
|
||||
<a
|
||||
href={plugin.repository}
|
||||
|
||||
@@ -325,10 +325,9 @@ export function formatCount(count: number): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* A new GitHub issue on the plugin's own repo with what a maintainer asks
|
||||
* first already filled in. Official plugins use the org bug form; a third
|
||||
* party repo may not have it, so it gets a plain prefilled body. Null when
|
||||
* the repository is not on GitHub.
|
||||
* A new bug report on the plugin's own repo with what a maintainer asks first
|
||||
* already filled in. Official plugins use the org bug form and the plugin
|
||||
* template ships the same one. Null when the repository is not on GitHub.
|
||||
*/
|
||||
export function reportIssueUrl(
|
||||
plugin: Pick<
|
||||
@@ -347,38 +346,25 @@ export function reportIssueUrl(
|
||||
if (!repo) return null;
|
||||
const version = plugin.version ?? plugin.latestVersion ?? "not installed";
|
||||
const state = `Plugin ${plugin.id}, ${plugin.source}, ${plugin.status ?? "not installed"}`;
|
||||
if (plugin.source !== "unverified") {
|
||||
return buildIssueUrl(repo, {
|
||||
template: "bug_report.yml",
|
||||
fields: {
|
||||
"termix-version": termixVersion,
|
||||
"plugin-version": version,
|
||||
environment: `${describeEnvironment()}\n${state}`,
|
||||
logs: plugin.lastError,
|
||||
},
|
||||
});
|
||||
}
|
||||
const lines = [
|
||||
`**Plugin:** ${plugin.id}`,
|
||||
`**Version:** ${version}`,
|
||||
`**Source:** ${plugin.source}`,
|
||||
`**State:** ${plugin.status ?? "not installed"}`,
|
||||
...(termixVersion ? [`**Termix:** ${termixVersion}`] : []),
|
||||
`**Environment:** ${describeEnvironment()}`,
|
||||
...(plugin.lastError
|
||||
? ["", "**Error:**", "```", plugin.lastError, "```"]
|
||||
: []),
|
||||
"",
|
||||
"**What happened:**",
|
||||
"",
|
||||
"**What you expected:**",
|
||||
"",
|
||||
];
|
||||
const params = new URLSearchParams({
|
||||
title: `[${plugin.id}] `,
|
||||
body: lines.join("\n"),
|
||||
return buildIssueUrl(repo, {
|
||||
template: "bug_report.yml",
|
||||
fields: {
|
||||
"termix-version": termixVersion,
|
||||
"plugin-version": version,
|
||||
environment: `${describeEnvironment()}
|
||||
${state}`,
|
||||
logs: plugin.lastError,
|
||||
},
|
||||
});
|
||||
return `${repo}/issues/new?${params.toString()}`;
|
||||
}
|
||||
|
||||
/** A new feature request on the plugin's own repo, or null when not on GitHub. */
|
||||
export function requestFeatureUrl(
|
||||
plugin: Pick<PluginEntry, "repository">,
|
||||
): string | null {
|
||||
const repo = normalizeGitHubRepo(plugin.repository);
|
||||
if (!repo) return null;
|
||||
return buildIssueUrl(repo, { template: "feature_request.yml" });
|
||||
}
|
||||
|
||||
/** One version in a plugin's release notes. */
|
||||
|
||||
@@ -99,6 +99,9 @@ describe("PluginsStep", () => {
|
||||
/>,
|
||||
);
|
||||
await screen.findByText("TERM");
|
||||
// findByText resolves on the DOM change, before the effect that hands
|
||||
// over the loaded apply has run. Flush it so beforeNext is never stale.
|
||||
await act(async () => {});
|
||||
}
|
||||
|
||||
it("groups plugins by category and puts consent plugins in their own section", async () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
pluginSource,
|
||||
pluginStatus,
|
||||
reportIssueUrl,
|
||||
requestFeatureUrl,
|
||||
sortPlugins,
|
||||
uploadEntry,
|
||||
youtubeEmbedUrl,
|
||||
@@ -289,19 +290,16 @@ describe("reportIssueUrl", () => {
|
||||
expect(parsed.searchParams.has("body")).toBe(false);
|
||||
});
|
||||
|
||||
it("uses a plain body for an unverified plugin", () => {
|
||||
it("fills the same bug form on an unverified plugin repo", () => {
|
||||
const parsed = new URL(
|
||||
reportIssueUrl({ ...base, source: "unverified" }, "26.10.0")!,
|
||||
);
|
||||
expect(parsed.searchParams.has("template")).toBe(false);
|
||||
expect(parsed.searchParams.get("title")).toBe("[docker] ");
|
||||
const body = parsed.searchParams.get("body")!;
|
||||
expect(body).toContain("**Plugin:** docker");
|
||||
expect(body).toContain("**Version:** 1.0.0");
|
||||
expect(body).toContain("**Source:** unverified");
|
||||
expect(body).toContain("**State:** failed");
|
||||
expect(body).toContain("**Termix:** 26.10.0");
|
||||
expect(body).toContain("boom");
|
||||
expect(parsed.searchParams.get("template")).toBe("bug_report.yml");
|
||||
expect(parsed.searchParams.get("plugin-version")).toBe("1.0.0");
|
||||
expect(parsed.searchParams.get("environment")).toContain(
|
||||
"Plugin docker, unverified, failed",
|
||||
);
|
||||
expect(parsed.searchParams.has("body")).toBe(false);
|
||||
});
|
||||
|
||||
it("gives nothing for a repo that is not on GitHub", () => {
|
||||
@@ -314,6 +312,26 @@ describe("reportIssueUrl", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("requestFeatureUrl", () => {
|
||||
it("opens the feature form on the plugin repo", () => {
|
||||
const parsed = new URL(
|
||||
requestFeatureUrl({
|
||||
repository: "https://github.com/Termix-SSH/Plugin-Docker/",
|
||||
})!,
|
||||
);
|
||||
expect(parsed.origin + parsed.pathname).toBe(
|
||||
"https://github.com/Termix-SSH/Plugin-Docker/issues/new",
|
||||
);
|
||||
expect(parsed.searchParams.get("template")).toBe("feature_request.yml");
|
||||
});
|
||||
|
||||
it("gives nothing for a repo that is not on GitHub", () => {
|
||||
expect(
|
||||
requestFeatureUrl({ repository: "https://gitlab.com/a/b" }),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("mergeReleaseNotes", () => {
|
||||
const registryVersion = (version: string, extra = {}) => ({
|
||||
version,
|
||||
|
||||
Reference in new issue
Block a user