mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
feat: per-repo issue forms and prefilled bug reports instead of the support repo
This commit is contained in:
1 parent
15058119a3
commit
ac19183f40
18 files changed
+553
-43
No files matched your search
@@ -0,0 +1,108 @@
|
|||||||
|
name: Beta feedback
|
||||||
|
description: A bug or feedback on a weekly beta build
|
||||||
|
labels: [bug, beta]
|
||||||
|
type: Bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for testing a beta. Betas are weekly snapshots of active development (the `:beta` Docker tag and a rolling pre-release here), so things will break sometimes.
|
||||||
|
- type: input
|
||||||
|
id: termix-version
|
||||||
|
attributes:
|
||||||
|
label: Beta build
|
||||||
|
description: Your image tag, or the version in Settings under About.
|
||||||
|
placeholder: "e.g. 26.10.0-beta.20261005"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: regression
|
||||||
|
attributes:
|
||||||
|
label: Does this happen on the latest stable release?
|
||||||
|
description: Check stable too if you can.
|
||||||
|
options:
|
||||||
|
- "Not tested on stable"
|
||||||
|
- "Yes, it happens on stable too"
|
||||||
|
- "No, only on this beta"
|
||||||
|
- "Not applicable, this is a new beta feature"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: platform
|
||||||
|
attributes:
|
||||||
|
label: Platform
|
||||||
|
description: Where do you see the problem?
|
||||||
|
options:
|
||||||
|
- "Web browser"
|
||||||
|
- "Desktop app (Windows)"
|
||||||
|
- "Desktop app (macOS)"
|
||||||
|
- "Desktop app (Linux)"
|
||||||
|
- "Mobile app (iOS)"
|
||||||
|
- "Mobile app (Android)"
|
||||||
|
- "Other"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Server install
|
||||||
|
description: How is the Termix server installed?
|
||||||
|
options:
|
||||||
|
- "Docker"
|
||||||
|
- "Manual build"
|
||||||
|
- "Proxmox community script"
|
||||||
|
- "Desktop app (built-in server)"
|
||||||
|
- "Not sure"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: What happened?
|
||||||
|
description: Describe the bug or feedback. Screenshots help a lot.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: As few steps as you can.
|
||||||
|
placeholder: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Browser and OS, plus anything special about your setup.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: Errors from the browser console, `docker logs termix`, or the error Termix shows. This is shown as code, so no backticks needed.
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Anything else?
|
||||||
|
description: Links, config, or anything that might help.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: checks
|
||||||
|
attributes:
|
||||||
|
label: Before you submit
|
||||||
|
options:
|
||||||
|
- label: I searched open and closed issues and this is not a duplicate
|
||||||
|
required: true
|
||||||
|
- label: I am on the latest version, or I said which version I am on
|
||||||
|
required: true
|
||||||
|
- label: I understand beta builds are not stable
|
||||||
|
required: true
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
name: Bug report
|
||||||
|
description: Something in Termix itself is broken
|
||||||
|
labels: [bug]
|
||||||
|
type: Bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for the report. A maintainer reads every issue, even when there is no reply right away. We usually reply once there is a fix or a workaround.
|
||||||
|
|
||||||
|
If you came from **Report an issue** in Termix, your versions and environment are already filled in below.
|
||||||
|
|
||||||
|
Is it about a plugin (terminal, file manager, Docker, remote desktop and so on)? Report it on that plugin's repo instead. **Report an issue** on the plugin's page in Termix takes you there with your versions filled in.
|
||||||
|
- type: textarea
|
||||||
|
id: what-happened
|
||||||
|
attributes:
|
||||||
|
label: What happened?
|
||||||
|
description: Describe the bug. Screenshots help a lot.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: As few steps as you can.
|
||||||
|
placeholder: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: What did you expect?
|
||||||
|
description: What should have happened instead?
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: termix-version
|
||||||
|
attributes:
|
||||||
|
label: Termix version
|
||||||
|
description: Shown in Settings under About, or in your User Profile.
|
||||||
|
placeholder: "e.g. 26.10.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: platform
|
||||||
|
attributes:
|
||||||
|
label: Platform
|
||||||
|
description: Where do you see the problem?
|
||||||
|
options:
|
||||||
|
- "Web browser"
|
||||||
|
- "Desktop app (Windows)"
|
||||||
|
- "Desktop app (macOS)"
|
||||||
|
- "Desktop app (Linux)"
|
||||||
|
- "Mobile app (iOS)"
|
||||||
|
- "Mobile app (Android)"
|
||||||
|
- "Other"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Server install
|
||||||
|
description: How is the Termix server installed?
|
||||||
|
options:
|
||||||
|
- "Docker"
|
||||||
|
- "Manual build"
|
||||||
|
- "Proxmox community script"
|
||||||
|
- "Desktop app (built-in server)"
|
||||||
|
- "Not sure"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Browser and OS, plus anything special about your setup (reverse proxy, database, SSO).
|
||||||
|
placeholder: |
|
||||||
|
Web, Chrome on Windows. Behind nginx, Postgres.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: Errors from the browser console, `docker logs termix`, or the error Termix shows. This is shown as code, so no backticks needed.
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Anything else?
|
||||||
|
description: Links, config, or anything that might help.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: checks
|
||||||
|
attributes:
|
||||||
|
label: Before you submit
|
||||||
|
options:
|
||||||
|
- label: I searched open and closed issues and this is not a duplicate
|
||||||
|
required: true
|
||||||
|
- label: I am on the latest version, or I said which version I am on
|
||||||
|
required: true
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Problem with a plugin?
|
||||||
|
url: https://github.com/Termix-SSH/Termix#plugins
|
||||||
|
about: Each plugin has its own repo. Find it in this list, or use Report an issue on the plugin's page in Termix.
|
||||||
|
- name: Questions and chat
|
||||||
|
url: https://discord.gg/jVQGdvHDrf
|
||||||
|
about: Ask the community on Discord. Bugs and ideas belong in issues.
|
||||||
|
- name: Business or priority support
|
||||||
|
url: https://termix.site/contact
|
||||||
|
about: For teams that need a faster response. Contact us to discuss terms.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest an idea for Termix itself
|
||||||
|
labels: [feature]
|
||||||
|
type: Feature
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Is it about a plugin (terminal, file manager, Docker, remote desktop and so on)? Report it on that plugin's repo instead. **Report an issue** on the plugin's page in Termix takes you there with your versions filled in.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for the idea. Explain the problem first, then how you would solve it.
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: What problem would this solve?
|
||||||
|
description: What are you trying to do, and what gets in the way today?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: What would you like?
|
||||||
|
description: Describe how it should work.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives
|
||||||
|
description: Anything you tried or considered instead.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Anything else?
|
||||||
|
description: Screenshots, mockups or links.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -148,7 +148,7 @@ jobs:
|
|||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> This is an automated weekly beta build, snapshotted from the \`${{ needs.prep.outputs.dev_branch }}\` branch. It is not a stable release: it may contain unfinished features, regressions, or breaking changes, and this tag is overwritten every week. Do not run it in production.
|
> This is an automated weekly beta build, snapshotted from the \`${{ needs.prep.outputs.dev_branch }}\` branch. It is not a stable release: it may contain unfinished features, regressions, or breaking changes, and this tag is overwritten every week. Do not run it in production.
|
||||||
>
|
>
|
||||||
> Found a bug? [Open a Beta Feedback report](https://github.com/Termix-SSH/Support/issues/new?template=beta_feedback.yml) and mention this build: \`${{ needs.prep.outputs.beta_version }}\`.
|
> Found a bug? [Open a Beta Feedback report](https://github.com/Termix-SSH/Termix/issues/new?template=beta_feedback.yml) and mention this build: \`${{ needs.prep.outputs.beta_version }}\`.
|
||||||
|
|
||||||
**Snapshot of:** \`${{ needs.prep.outputs.dev_branch }}\` @ \`${{ needs.prep.outputs.sha }}\`
|
**Snapshot of:** \`${{ needs.prep.outputs.dev_branch }}\` @ \`${{ needs.prep.outputs.sha }}\`
|
||||||
**Docker image:** \`ghcr.io/termix-ssh/termix:beta\` / \`docker.io/termixssh/termix:beta\` (rolling), or pin to \`:beta-${{ needs.prep.outputs.beta_version }}\` for this exact build.
|
**Docker image:** \`ghcr.io/termix-ssh/termix:beta\` / \`docker.io/termixssh/termix:beta\` (rolling), or pin to \`:beta-${{ needs.prep.outputs.beta_version }}\` for this exact build.
|
||||||
|
|||||||
+1
-1
@@ -72,4 +72,4 @@ Start from the [plugin template](https://github.com/Termix-SSH/Termix-Plugin-Tem
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
To report a bug or request a feature, open a [support ticket](https://github.com/Termix-SSH/Support/issues/new/choose). Please be as detailed as possible, preferably in English. You can also ask in the [Discord](https://discord.gg/jVQGdvHDrf) server.
|
Bugs and ideas for Termix itself go in [this repo](https://github.com/Termix-SSH/Termix/issues/new/choose). Plugin problems go in that plugin's own `Plugin-<Name>` repo. Please be as detailed as possible, preferably in English. You can also ask in the [Discord](https://discord.gg/jVQGdvHDrf) server.
|
||||||
@@ -242,7 +242,11 @@ Interested in a paid placement to support development? Email [mail@termix.site](
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
To report a bug or request a feature, open a [support ticket](https://github.com/Termix-SSH/Support/issues/new/choose). You need to be logged in to GitHub. Please be as detailed as possible, preferably in English.
|
Bugs and ideas for Termix itself (login, hosts, credentials, sharing, sync, the app and desktop app) go in this repo: [report a bug](https://github.com/Termix-SSH/Termix/issues/new?template=bug_report.yml) or [request a feature](https://github.com/Termix-SSH/Termix/issues/new?template=feature_request.yml).
|
||||||
|
|
||||||
|
Every plugin has its own repo, so a problem with the terminal, file manager, Docker, remote desktop or any other plugin goes in that plugin's repo (see the table above, or use Report an issue on the plugin's page in the app, which fills in your versions for you). Not sure where it goes? Open it here and it will be moved.
|
||||||
|
|
||||||
|
You need to be logged in to GitHub. Please be as detailed as possible, preferably in English.
|
||||||
|
|
||||||
For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server. Update videos are on [YouTube](https://www.youtube.com/@TermixSSH/videos).
|
For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server. Update videos are on [YouTube](https://www.youtube.com/@TermixSSH/videos).
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ in place of the flags. Reload the page to pick up frontend changes.
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
To report a bug or request a feature, open a [support ticket](https://github.com/Termix-SSH/Support/issues/new/choose). You need to be logged in to GitHub. Please be as detailed as possible, preferably in English.
|
Bugs and ideas for the SDK or the `termix-plugin` CLI go in the [Termix repo](https://github.com/Termix-SSH/Termix/issues/new/choose). You need to be logged in to GitHub. Please be as detailed as possible, preferably in English.
|
||||||
|
|
||||||
For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server.
|
For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server.
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<projectSourceUrl>https://github.com/Termix-SSH/Termix</projectSourceUrl>
|
<projectSourceUrl>https://github.com/Termix-SSH/Termix</projectSourceUrl>
|
||||||
<docsUrl>https://docs.termix.site/install</docsUrl>
|
<docsUrl>https://docs.termix.site/install</docsUrl>
|
||||||
<bugTrackerUrl>https://github.com/Termix-SSH/Support/issues</bugTrackerUrl>
|
<bugTrackerUrl>https://github.com/Termix-SSH/Termix/issues</bugTrackerUrl>
|
||||||
<tags>docker ssh terminal telnet self-hosted rdp file-management vnc ssh-tunnel server-stats termix</tags>
|
<tags>docker ssh terminal telnet self-hosted rdp file-management vnc ssh-tunnel server-stats termix</tags>
|
||||||
<summary>Self-hosted, plugin-based server management.</summary>
|
<summary>Self-hosted, plugin-based server management.</summary>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<launchable type="desktop-id">com.karmaa.termix.desktop</launchable>
|
<launchable type="desktop-id">com.karmaa.termix.desktop</launchable>
|
||||||
|
|
||||||
<url type="homepage">https://github.com/Termix-SSH/Termix</url>
|
<url type="homepage">https://github.com/Termix-SSH/Termix</url>
|
||||||
<url type="bugtracker">https://github.com/Termix-SSH/Support/issues</url>
|
<url type="bugtracker">https://github.com/Termix-SSH/Termix/issues</url>
|
||||||
<url type="help">https://docs.termix.site</url>
|
<url type="help">https://docs.termix.site</url>
|
||||||
<url type="vcs-browser">https://github.com/Termix-SSH/Termix</url>
|
<url type="vcs-browser">https://github.com/Termix-SSH/Termix</url>
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import {
|
|||||||
StatsStrip,
|
StatsStrip,
|
||||||
type VersionStatus,
|
type VersionStatus,
|
||||||
} from "./dashboard-core-cards";
|
} from "./dashboard-core-cards";
|
||||||
|
import { reportCoreIssueUrl } from "@/lib/issue-url";
|
||||||
|
|
||||||
export { HostStatusList as HostStatusCard } from "./dashboard-core-cards";
|
export { HostStatusList as HostStatusCard } from "./dashboard-core-cards";
|
||||||
|
|
||||||
@@ -795,8 +796,8 @@ export function DashboardTab({
|
|||||||
label: t("dashboard.github"),
|
label: t("dashboard.github"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "https://github.com/Termix-SSH/Support",
|
href: reportCoreIssueUrl(import.meta.env.VITE_APP_VERSION || undefined),
|
||||||
label: t("dashboard.support"),
|
label: t("dashboard.reportBug"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "https://discord.com/invite/jVQGdvHDrf",
|
href: "https://discord.com/invite/jVQGdvHDrf",
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { isElectron } from "@/lib/electron";
|
||||||
|
|
||||||
|
export const CORE_REPO_URL = "https://github.com/Termix-SSH/Termix";
|
||||||
|
|
||||||
|
const MAX_URL_LENGTH = 7500;
|
||||||
|
|
||||||
|
/** A GitHub repo URL without .git or a trailing slash, or null if it is not one. */
|
||||||
|
export function normalizeGitHubRepo(repository?: string | null): string | null {
|
||||||
|
const repo = repository
|
||||||
|
?.trim()
|
||||||
|
.replace(/\.git$/, "")
|
||||||
|
.replace(/\/+$/, "");
|
||||||
|
if (!repo || !/^https:\/\/github\.com\/[^/]+\/[^/]+$/.test(repo)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A new issue from one of the repo's issue forms, with fields prefilled by
|
||||||
|
* their form ids. Logs get cut first when the URL would be too long.
|
||||||
|
*/
|
||||||
|
export function buildIssueUrl(
|
||||||
|
repo: string,
|
||||||
|
options: {
|
||||||
|
template: string;
|
||||||
|
title?: string;
|
||||||
|
fields?: Record<string, string | null | undefined>;
|
||||||
|
},
|
||||||
|
): string {
|
||||||
|
const fields = Object.fromEntries(
|
||||||
|
Object.entries(options.fields ?? {}).filter(
|
||||||
|
(entry): entry is [string, string] => !!entry[1]?.trim(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const build = () => {
|
||||||
|
const params = new URLSearchParams({ template: options.template });
|
||||||
|
if (options.title) params.set("title", options.title);
|
||||||
|
for (const [key, value] of Object.entries(fields)) params.set(key, value);
|
||||||
|
return `${repo}/issues/new?${params.toString()}`;
|
||||||
|
};
|
||||||
|
let url = build();
|
||||||
|
while (url.length > MAX_URL_LENGTH && fields.logs) {
|
||||||
|
const cut = Math.max(0, fields.logs.length - (url.length - MAX_URL_LENGTH));
|
||||||
|
fields.logs =
|
||||||
|
cut > 200 ? `${fields.logs.slice(0, cut - 20)}\n...(cut)` : undefined;
|
||||||
|
if (!fields.logs) delete fields.logs;
|
||||||
|
url = build();
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function osName(ua: string): string {
|
||||||
|
if (/Android/i.test(ua)) return "Android";
|
||||||
|
if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
|
||||||
|
if (/Windows/i.test(ua)) return "Windows";
|
||||||
|
if (/Mac OS X|Macintosh/i.test(ua)) return "macOS";
|
||||||
|
if (/CrOS/i.test(ua)) return "ChromeOS";
|
||||||
|
if (/Linux/i.test(ua)) return "Linux";
|
||||||
|
return "Unknown OS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function browserName(ua: string): string {
|
||||||
|
const match =
|
||||||
|
ua.match(/(Edg)\/(\d+)/) ??
|
||||||
|
ua.match(/(OPR)\/(\d+)/) ??
|
||||||
|
ua.match(/(Firefox)\/(\d+)/) ??
|
||||||
|
ua.match(/(Chrome)\/(\d+)/) ??
|
||||||
|
ua.match(/Version\/(\d+).*(Safari)/);
|
||||||
|
if (!match) return "Unknown browser";
|
||||||
|
if (match[2] === "Safari") return `Safari ${match[1]}`;
|
||||||
|
const names: Record<string, string> = { Edg: "Edge", OPR: "Opera" };
|
||||||
|
return `${names[match[1]] ?? match[1]} ${match[2]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One line on where Termix is running, for the environment field. */
|
||||||
|
export function describeEnvironment(
|
||||||
|
userAgent: string = typeof navigator === "undefined"
|
||||||
|
? ""
|
||||||
|
: navigator.userAgent,
|
||||||
|
desktop: boolean = isElectron(),
|
||||||
|
): string {
|
||||||
|
const os = osName(userAgent);
|
||||||
|
return desktop
|
||||||
|
? `Desktop app on ${os}`
|
||||||
|
: `Web, ${browserName(userAgent)} on ${os}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A bug report on the Termix core repo. */
|
||||||
|
export function reportCoreIssueUrl(
|
||||||
|
termixVersion?: string,
|
||||||
|
template: "bug_report.yml" | "beta_feedback.yml" = "bug_report.yml",
|
||||||
|
): string {
|
||||||
|
return buildIssueUrl(CORE_REPO_URL, {
|
||||||
|
template,
|
||||||
|
fields: {
|
||||||
|
"termix-version": termixVersion,
|
||||||
|
environment: describeEnvironment(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1172,7 +1172,7 @@
|
|||||||
"title": "Dashboard",
|
"title": "Dashboard",
|
||||||
"loading": "Loading dashboard...",
|
"loading": "Loading dashboard...",
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"support": "Support",
|
"reportBug": "Report a bug",
|
||||||
"discord": "Discord",
|
"discord": "Discord",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ import type {
|
|||||||
RegistryPluginEntry,
|
RegistryPluginEntry,
|
||||||
RegistryPluginVersion,
|
RegistryPluginVersion,
|
||||||
} from "@/api/plugins-api";
|
} from "@/api/plugins-api";
|
||||||
|
import {
|
||||||
|
buildIssueUrl,
|
||||||
|
describeEnvironment,
|
||||||
|
normalizeGitHubRepo,
|
||||||
|
} from "@/lib/issue-url";
|
||||||
|
|
||||||
export type PluginStatus = "running" | "disabled" | "failed" | "blocked";
|
export type PluginStatus = "running" | "disabled" | "failed" | "blocked";
|
||||||
|
|
||||||
@@ -314,28 +319,48 @@ export function formatCount(count: number): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A new GitHub issue on the plugin's own repo with what a maintainer asks
|
* A new GitHub issue on the plugin's own repo with what a maintainer asks
|
||||||
* first already filled in. Null when the repository is not on GitHub.
|
* 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.
|
||||||
*/
|
*/
|
||||||
export function reportIssueUrl(
|
export function reportIssueUrl(
|
||||||
plugin: Pick<
|
plugin: Pick<
|
||||||
PluginEntry,
|
PluginEntry,
|
||||||
"id" | "repository" | "version" | "latestVersion" | "status" | "source"
|
| "id"
|
||||||
|
| "repository"
|
||||||
|
| "version"
|
||||||
|
| "latestVersion"
|
||||||
|
| "status"
|
||||||
|
| "source"
|
||||||
|
| "lastError"
|
||||||
>,
|
>,
|
||||||
termixVersion?: string,
|
termixVersion?: string,
|
||||||
): string | null {
|
): string | null {
|
||||||
const repo = plugin.repository
|
const repo = normalizeGitHubRepo(plugin.repository);
|
||||||
?.trim()
|
if (!repo) return null;
|
||||||
.replace(/\.git$/, "")
|
const version = plugin.version ?? plugin.latestVersion ?? "not installed";
|
||||||
.replace(/\/+$/, "");
|
const state = `Plugin ${plugin.id}, ${plugin.source}, ${plugin.status ?? "not installed"}`;
|
||||||
if (!repo || !/^https:\/\/github\.com\/[^/]+\/[^/]+$/.test(repo)) {
|
if (plugin.source !== "unverified") {
|
||||||
return null;
|
return buildIssueUrl(repo, {
|
||||||
|
template: "bug_report.yml",
|
||||||
|
fields: {
|
||||||
|
"termix-version": termixVersion,
|
||||||
|
"plugin-version": version,
|
||||||
|
environment: `${describeEnvironment()}\n${state}`,
|
||||||
|
logs: plugin.lastError,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const lines = [
|
const lines = [
|
||||||
`**Plugin:** ${plugin.id}`,
|
`**Plugin:** ${plugin.id}`,
|
||||||
`**Version:** ${plugin.version ?? plugin.latestVersion ?? "not installed"}`,
|
`**Version:** ${version}`,
|
||||||
`**Source:** ${plugin.source}`,
|
`**Source:** ${plugin.source}`,
|
||||||
`**State:** ${plugin.status ?? "not installed"}`,
|
`**State:** ${plugin.status ?? "not installed"}`,
|
||||||
...(termixVersion ? [`**Termix:** ${termixVersion}`] : []),
|
...(termixVersion ? [`**Termix:** ${termixVersion}`] : []),
|
||||||
|
`**Environment:** ${describeEnvironment()}`,
|
||||||
|
...(plugin.lastError
|
||||||
|
? ["", "**Error:**", "```", plugin.lastError, "```"]
|
||||||
|
: []),
|
||||||
"",
|
"",
|
||||||
"**What happened:**",
|
"**What happened:**",
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import {
|
|||||||
CommandEmpty,
|
CommandEmpty,
|
||||||
} from "@/components/command";
|
} from "@/components/command";
|
||||||
import {
|
import {
|
||||||
|
Bug,
|
||||||
Clock,
|
Clock,
|
||||||
Folder,
|
Folder,
|
||||||
Globe,
|
Globe,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
LifeBuoy,
|
|
||||||
MessagesSquare,
|
MessagesSquare,
|
||||||
Pencil,
|
Pencil,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
type PaletteItemDef,
|
type PaletteItemDef,
|
||||||
} from "./palette-registry";
|
} from "./palette-registry";
|
||||||
import { activityTarget } from "@/lib/activity-types";
|
import { activityTarget } from "@/lib/activity-types";
|
||||||
|
import { reportCoreIssueUrl } from "@/lib/issue-url";
|
||||||
import { shell } from "@/plugin-host/shell-bridge";
|
import { shell } from "@/plugin-host/shell-bridge";
|
||||||
import { getLiveHostStatus } from "@/lib/ServerStatusContext";
|
import { getLiveHostStatus } from "@/lib/ServerStatusContext";
|
||||||
|
|
||||||
@@ -228,10 +229,10 @@ export function CommandPalette({
|
|||||||
url: "https://discord.com/invite/jVQGdvHDrf",
|
url: "https://discord.com/invite/jVQGdvHDrf",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "link-support",
|
id: "link-report-bug",
|
||||||
label: t("dashboard.support"),
|
label: t("dashboard.reportBug"),
|
||||||
icon: LifeBuoy,
|
icon: Bug,
|
||||||
url: "https://github.com/Termix-SSH/Support/issues/new",
|
url: reportCoreIssueUrl(import.meta.env.VITE_APP_VERSION || undefined),
|
||||||
},
|
},
|
||||||
].filter((link) => matches(link.label));
|
].filter((link) => matches(link.label));
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import { changeAppLanguage, normalizeLanguageCode } from "@/i18n/i18n";
|
|||||||
import { Select2 } from "@/components/select2";
|
import { Select2 } from "@/components/select2";
|
||||||
import { LANGUAGES, THEMES } from "@/settings/appearance/appearance-controls";
|
import { LANGUAGES, THEMES } from "@/settings/appearance/appearance-controls";
|
||||||
import { clearLocalAdaptivePreferences } from "@/lib/local-adaptive-preferences";
|
import { clearLocalAdaptivePreferences } from "@/lib/local-adaptive-preferences";
|
||||||
|
import { reportCoreIssueUrl } from "@/lib/issue-url";
|
||||||
import {
|
import {
|
||||||
FeatureSettingsSection,
|
FeatureSettingsSection,
|
||||||
featureSectionId,
|
featureSectionId,
|
||||||
@@ -1302,7 +1303,10 @@ export function UserProfilePanel({
|
|||||||
<span className="text-[10px] text-muted-foreground">
|
<span className="text-[10px] text-muted-foreground">
|
||||||
{t("newUi.sidebar.userProfile.betaProgramDescription")}{" "}
|
{t("newUi.sidebar.userProfile.betaProgramDescription")}{" "}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/Termix-SSH/Support/issues/new?template=beta_feedback.yml"
|
href={reportCoreIssueUrl(
|
||||||
|
version || undefined,
|
||||||
|
"beta_feedback.yml",
|
||||||
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-accent-brand hover:underline"
|
className="text-accent-brand hover:underline"
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
buildIssueUrl,
|
||||||
|
describeEnvironment,
|
||||||
|
normalizeGitHubRepo,
|
||||||
|
reportCoreIssueUrl,
|
||||||
|
} from "@/lib/issue-url";
|
||||||
|
|
||||||
|
const CHROME_WIN =
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36";
|
||||||
|
const SAFARI_MAC =
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15";
|
||||||
|
const FIREFOX_LINUX =
|
||||||
|
"Mozilla/5.0 (X11; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0";
|
||||||
|
|
||||||
|
describe("normalizeGitHubRepo", () => {
|
||||||
|
it("strips .git and trailing slashes", () => {
|
||||||
|
expect(normalizeGitHubRepo(" https://github.com/a/b.git ")).toBe(
|
||||||
|
"https://github.com/a/b",
|
||||||
|
);
|
||||||
|
expect(normalizeGitHubRepo("https://github.com/a/b/")).toBe(
|
||||||
|
"https://github.com/a/b",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects anything that is not a GitHub repo", () => {
|
||||||
|
expect(normalizeGitHubRepo("https://gitlab.com/a/b")).toBeNull();
|
||||||
|
expect(normalizeGitHubRepo("https://github.com/a")).toBeNull();
|
||||||
|
expect(normalizeGitHubRepo(undefined)).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buildIssueUrl", () => {
|
||||||
|
it("sets the template and skips empty fields", () => {
|
||||||
|
const url = new URL(
|
||||||
|
buildIssueUrl("https://github.com/a/b", {
|
||||||
|
template: "bug_report.yml",
|
||||||
|
fields: { "termix-version": "26.10.0", logs: "", other: undefined },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(url.pathname).toBe("/a/b/issues/new");
|
||||||
|
expect(url.searchParams.get("template")).toBe("bug_report.yml");
|
||||||
|
expect(url.searchParams.get("termix-version")).toBe("26.10.0");
|
||||||
|
expect(url.searchParams.has("logs")).toBe(false);
|
||||||
|
expect(url.searchParams.has("other")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("encodes values safely", () => {
|
||||||
|
const url = new URL(
|
||||||
|
buildIssueUrl("https://github.com/a/b", {
|
||||||
|
template: "bug_report.yml",
|
||||||
|
fields: { environment: "a & b\nc=d" },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(url.searchParams.get("environment")).toBe("a & b\nc=d");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("cuts long logs to keep the URL short", () => {
|
||||||
|
const url = buildIssueUrl("https://github.com/a/b", {
|
||||||
|
template: "bug_report.yml",
|
||||||
|
fields: { logs: "x".repeat(20000) },
|
||||||
|
});
|
||||||
|
expect(url.length).toBeLessThanOrEqual(7500);
|
||||||
|
expect(new URL(url).searchParams.get("logs")).toMatch(/\(cut\)$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("describeEnvironment", () => {
|
||||||
|
it("names the browser and OS on the web", () => {
|
||||||
|
expect(describeEnvironment(CHROME_WIN, false)).toBe(
|
||||||
|
"Web, Chrome 141 on Windows",
|
||||||
|
);
|
||||||
|
expect(describeEnvironment(SAFARI_MAC, false)).toBe(
|
||||||
|
"Web, Safari 18 on macOS",
|
||||||
|
);
|
||||||
|
expect(describeEnvironment(FIREFOX_LINUX, false)).toBe(
|
||||||
|
"Web, Firefox 130 on Linux",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("names the desktop app", () => {
|
||||||
|
expect(describeEnvironment(CHROME_WIN, true)).toBe(
|
||||||
|
"Desktop app on Windows",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("reportCoreIssueUrl", () => {
|
||||||
|
it("points at the core repo with the version filled in", () => {
|
||||||
|
const url = new URL(reportCoreIssueUrl("26.10.0", "beta_feedback.yml"));
|
||||||
|
expect(url.pathname).toBe("/Termix-SSH/Termix/issues/new");
|
||||||
|
expect(url.searchParams.get("template")).toBe("beta_feedback.yml");
|
||||||
|
expect(url.searchParams.get("termix-version")).toBe("26.10.0");
|
||||||
|
expect(url.searchParams.get("environment")).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -248,40 +248,51 @@ describe("source and trust", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("reportIssueUrl", () => {
|
describe("reportIssueUrl", () => {
|
||||||
it("prefills id, version, source and state on the plugin repo", () => {
|
const base = {
|
||||||
const url = reportIssueUrl(
|
id: "docker",
|
||||||
{
|
repository: "https://github.com/Termix-SSH/Plugin-Docker.git",
|
||||||
id: "docker",
|
version: "1.0.0",
|
||||||
repository: "https://github.com/Termix-SSH/Plugin-Docker.git",
|
latestVersion: "1.1.0",
|
||||||
version: "1.0.0",
|
status: "failed" as const,
|
||||||
latestVersion: "1.1.0",
|
source: "official" as const,
|
||||||
status: "failed",
|
lastError: "boom",
|
||||||
source: "official",
|
};
|
||||||
},
|
|
||||||
"26.10.0",
|
it("fills the bug form on an official plugin repo", () => {
|
||||||
)!;
|
const parsed = new URL(reportIssueUrl(base, "26.10.0")!);
|
||||||
const parsed = new URL(url);
|
|
||||||
expect(parsed.origin + parsed.pathname).toBe(
|
expect(parsed.origin + parsed.pathname).toBe(
|
||||||
"https://github.com/Termix-SSH/Plugin-Docker/issues/new",
|
"https://github.com/Termix-SSH/Plugin-Docker/issues/new",
|
||||||
);
|
);
|
||||||
|
expect(parsed.searchParams.get("template")).toBe("bug_report.yml");
|
||||||
|
expect(parsed.searchParams.get("termix-version")).toBe("26.10.0");
|
||||||
|
expect(parsed.searchParams.get("plugin-version")).toBe("1.0.0");
|
||||||
|
expect(parsed.searchParams.get("environment")).toContain(
|
||||||
|
"Plugin docker, official, failed",
|
||||||
|
);
|
||||||
|
expect(parsed.searchParams.get("logs")).toBe("boom");
|
||||||
|
expect(parsed.searchParams.has("body")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses a plain body for an unverified plugin", () => {
|
||||||
|
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] ");
|
expect(parsed.searchParams.get("title")).toBe("[docker] ");
|
||||||
const body = parsed.searchParams.get("body")!;
|
const body = parsed.searchParams.get("body")!;
|
||||||
expect(body).toContain("**Plugin:** docker");
|
expect(body).toContain("**Plugin:** docker");
|
||||||
expect(body).toContain("**Version:** 1.0.0");
|
expect(body).toContain("**Version:** 1.0.0");
|
||||||
expect(body).toContain("**Source:** official");
|
expect(body).toContain("**Source:** unverified");
|
||||||
expect(body).toContain("**State:** failed");
|
expect(body).toContain("**State:** failed");
|
||||||
expect(body).toContain("**Termix:** 26.10.0");
|
expect(body).toContain("**Termix:** 26.10.0");
|
||||||
|
expect(body).toContain("boom");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("gives nothing for a repo that is not on GitHub", () => {
|
it("gives nothing for a repo that is not on GitHub", () => {
|
||||||
expect(
|
expect(
|
||||||
reportIssueUrl({
|
reportIssueUrl({
|
||||||
id: "x",
|
...base,
|
||||||
repository: "https://gitlab.com/a/b",
|
repository: "https://gitlab.com/a/b",
|
||||||
version: null,
|
|
||||||
latestVersion: null,
|
|
||||||
status: null,
|
|
||||||
source: "official",
|
|
||||||
}),
|
}),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user