mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-04 17:45:24 +00:00
feat: rework default plugins and keep unchosen plugins off in the first run picker
This commit is contained in:
1 parent
31df4b12e3
commit
b9df36b09a
7 files changed
+74
-23
No files matched your search
@@ -5,7 +5,6 @@
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Host status, import and export, and credential tools now start on for new installs
|
- Host status, import and export, and credential tools now start on for new installs
|
||||||
- Service Manager is now in the standard image and starts on for new installs
|
|
||||||
- Sync now starts on in a new desktop app, so it can link to a server
|
- Sync now starts on in a new desktop app, so it can link to a server
|
||||||
- The first run plugin picker leaves plugins you did not choose off instead of removing them
|
- The first run plugin picker leaves plugins you did not choose off instead of removing them
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,11 @@
|
|||||||
"source": "tmxplug",
|
"source": "tmxplug",
|
||||||
"url": "https://github.com/Termix-SSH/Plugin-Credential-Tools/releases/download/v1.0.0/credential-tools-1.0.0.tmxplug",
|
"url": "https://github.com/Termix-SSH/Plugin-Credential-Tools/releases/download/v1.0.0/credential-tools-1.0.0.tmxplug",
|
||||||
"sha256": "413eff710b5c832a2575a890bbab1c90d28ea55f10cd7a7b94b851d51dd77dce",
|
"sha256": "413eff710b5c832a2575a890bbab1c90d28ea55f10cd7a7b94b851d51dd77dce",
|
||||||
"tier": "standard"
|
"tier": "standard",
|
||||||
|
"onboarding": {
|
||||||
|
"recommended": true,
|
||||||
|
"consent": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "databases",
|
"id": "databases",
|
||||||
@@ -128,7 +132,11 @@
|
|||||||
"source": "tmxplug",
|
"source": "tmxplug",
|
||||||
"url": "https://github.com/Termix-SSH/Plugin-Host-Status/releases/download/v1.0.0/host-status-1.0.0.tmxplug",
|
"url": "https://github.com/Termix-SSH/Plugin-Host-Status/releases/download/v1.0.0/host-status-1.0.0.tmxplug",
|
||||||
"sha256": "1ba62577dc7373a2a2787bab7efdb89b544a30a8003b1de91ce51f372fc4f170",
|
"sha256": "1ba62577dc7373a2a2787bab7efdb89b544a30a8003b1de91ce51f372fc4f170",
|
||||||
"tier": "standard"
|
"tier": "standard",
|
||||||
|
"onboarding": {
|
||||||
|
"recommended": true,
|
||||||
|
"consent": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "hosts-config",
|
"id": "hosts-config",
|
||||||
@@ -142,7 +150,11 @@
|
|||||||
"source": "tmxplug",
|
"source": "tmxplug",
|
||||||
"url": "https://github.com/Termix-SSH/Plugin-Import-Export/releases/download/v1.0.1/import-export-1.0.1.tmxplug",
|
"url": "https://github.com/Termix-SSH/Plugin-Import-Export/releases/download/v1.0.1/import-export-1.0.1.tmxplug",
|
||||||
"sha256": "d8f2f865e1e6b226f4fe38501bf69b752f47a643bed1d9d127181083e27da2f0",
|
"sha256": "d8f2f865e1e6b226f4fe38501bf69b752f47a643bed1d9d127181083e27da2f0",
|
||||||
"tier": "standard"
|
"tier": "standard",
|
||||||
|
"onboarding": {
|
||||||
|
"recommended": true,
|
||||||
|
"consent": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "kubernetes",
|
"id": "kubernetes",
|
||||||
@@ -288,9 +300,7 @@
|
|||||||
"onboarding": {
|
"onboarding": {
|
||||||
"recommended": false,
|
"recommended": false,
|
||||||
"consent": false,
|
"consent": false,
|
||||||
"enabledByEnv": [
|
"enabledByEnv": ["OIDC_CLIENT_ID"]
|
||||||
"OIDC_CLIENT_ID"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -305,7 +315,11 @@
|
|||||||
"source": "tmxplug",
|
"source": "tmxplug",
|
||||||
"url": "https://github.com/Termix-SSH/Plugin-Sync/releases/download/v1.1.0/sync-1.1.0.tmxplug",
|
"url": "https://github.com/Termix-SSH/Plugin-Sync/releases/download/v1.1.0/sync-1.1.0.tmxplug",
|
||||||
"sha256": "9023d547f0e0bcef582c6ba7b7a8ab2ef68d8d0b973dde17d9e8c84d92b6429e",
|
"sha256": "9023d547f0e0bcef582c6ba7b7a8ab2ef68d8d0b973dde17d9e8c84d92b6429e",
|
||||||
"tier": "standard"
|
"tier": "standard",
|
||||||
|
"onboarding": {
|
||||||
|
"recommended": "desktop",
|
||||||
|
"consent": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "tailscale",
|
"id": "tailscale",
|
||||||
|
|||||||
@@ -537,6 +537,38 @@ describe("default look", () => {
|
|||||||
).toMatchObject({ id: "look", defaultLook: true });
|
).toMatchObject({ id: "look", defaultLook: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("leaves out new plugins with no usable release when pinning all", () => {
|
||||||
|
const stable = {
|
||||||
|
version: "1.0.0",
|
||||||
|
url: "https://x/a.tmxplug",
|
||||||
|
sha256: SHA,
|
||||||
|
};
|
||||||
|
const index = {
|
||||||
|
plugins: [
|
||||||
|
{ id: "ready", versions: [stable] },
|
||||||
|
{
|
||||||
|
id: "beta-only",
|
||||||
|
versions: [],
|
||||||
|
prereleases: [{ ...stable, version: "1.0.0-beta.1" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "too-new",
|
||||||
|
versions: [],
|
||||||
|
gated: [{ ...stable, engine: { termix: ">=99.0.0" } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
pinsFromIndex(index, null, [], "26.11.1").map(
|
||||||
|
(pin: { id: string }) => pin.id,
|
||||||
|
),
|
||||||
|
).toEqual(["ready"]);
|
||||||
|
expect(() =>
|
||||||
|
pinsFromIndex(index, null, [{ id: "beta-only" }], "26.11.1"),
|
||||||
|
).toThrow(/beta-only/);
|
||||||
|
expect(() => pinsFromIndex(index, ["beta-only"])).toThrow(/beta-only/);
|
||||||
|
});
|
||||||
|
|
||||||
it("copies a local build's theme files", () => {
|
it("copies a local build's theme files", () => {
|
||||||
const repo = tempDir();
|
const repo = tempDir();
|
||||||
fs.mkdirSync(path.join(repo, "dist"), { recursive: true });
|
fs.mkdirSync(path.join(repo, "dist"), { recursive: true });
|
||||||
|
|||||||
@@ -278,14 +278,15 @@ function pluginsForProfile(plugins, profile) {
|
|||||||
* pin list, so fields the registry does not know (onboarding, tier) carry over;
|
* pin list, so fields the registry does not know (onboarding, tier) carry over;
|
||||||
* a plugin new to the list starts in the full tier. coreVersion, when given,
|
* a plugin new to the list starts in the full tier. coreVersion, when given,
|
||||||
* skips releases whose engine.termix range does not include it, so the image
|
* skips releases whose engine.termix range does not include it, so the image
|
||||||
* never ships a plugin its own core cannot run.
|
* never ships a plugin its own core cannot run. With ids null, a plugin new to
|
||||||
|
* the list that has no usable release (only betas, say) is left out.
|
||||||
*/
|
*/
|
||||||
function pinsFromIndex(index, ids, current = [], coreVersion = null) {
|
function pinsFromIndex(index, ids, current = [], coreVersion = null) {
|
||||||
const plugins = Array.isArray(index?.plugins) ? index.plugins : [];
|
const plugins = Array.isArray(index?.plugins) ? index.plugins : [];
|
||||||
const byId = new Map(plugins.map((plugin) => [plugin.id, plugin]));
|
const byId = new Map(plugins.map((plugin) => [plugin.id, plugin]));
|
||||||
const currentById = new Map(current.map((entry) => [entry.id, entry]));
|
const currentById = new Map(current.map((entry) => [entry.id, entry]));
|
||||||
const wanted = ids ?? plugins.map((plugin) => plugin.id);
|
const wanted = ids ?? plugins.map((plugin) => plugin.id);
|
||||||
return [...wanted].sort().map((id) => {
|
const pins = [...wanted].sort().map((id) => {
|
||||||
const listed = byId.get(id);
|
const listed = byId.get(id);
|
||||||
// gated holds releases that need a newer Termix than old builds check for.
|
// gated holds releases that need a newer Termix than old builds check for.
|
||||||
const latest = [...(listed?.versions ?? []), ...(listed?.gated ?? [])]
|
const latest = [...(listed?.versions ?? []), ...(listed?.gated ?? [])]
|
||||||
@@ -300,7 +301,10 @@ function pinsFromIndex(index, ids, current = [], coreVersion = null) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.sort((a, b) => semver.rcompare(a.version, b.version))[0];
|
.sort((a, b) => semver.rcompare(a.version, b.version))[0];
|
||||||
if (!latest) throw new Error(`${id} is not in the registry index`);
|
if (!latest) {
|
||||||
|
if (ids === null && !currentById.has(id)) return null;
|
||||||
|
throw new Error(`${id} is not in the registry index`);
|
||||||
|
}
|
||||||
const onboarding = currentById.get(id)?.onboarding;
|
const onboarding = currentById.get(id)?.onboarding;
|
||||||
const defaultLook = currentById.get(id)?.defaultLook === true;
|
const defaultLook = currentById.get(id)?.defaultLook === true;
|
||||||
const tier = currentById.get(id)?.tier ?? "full";
|
const tier = currentById.get(id)?.tier ?? "full";
|
||||||
@@ -314,6 +318,7 @@ function pinsFromIndex(index, ids, current = [], coreVersion = null) {
|
|||||||
...(defaultLook ? { defaultLook } : {}),
|
...(defaultLook ? { defaultLook } : {}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
return pins.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOCAL_FILES = [
|
const LOCAL_FILES = [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* re-released.
|
* re-released.
|
||||||
*
|
*
|
||||||
* node scripts/pin-bundled-plugins.cjs refresh the plugins already listed
|
* node scripts/pin-bundled-plugins.cjs refresh the plugins already listed
|
||||||
* node scripts/pin-bundled-plugins.cjs --all also add plugins new to the index (tier full)
|
* node scripts/pin-bundled-plugins.cjs --all also add plugins new to the index that have a stable release (tier full)
|
||||||
* --core-version X skip releases whose engine.termix excludes X
|
* --core-version X skip releases whose engine.termix excludes X
|
||||||
*
|
*
|
||||||
* The release workflow runs it with --all and the version being released.
|
* The release workflow runs it with --all and the version being released.
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ export function currentChoice(plugin: OnboardingPluginInfo): PluginChoice {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* What the picker starts on. On a fresh install recommended plugins are on
|
* What the picker starts on. On a fresh install recommended plugins are on
|
||||||
* and the rest are set to be removed; consent ones start on. Later runs
|
* and the rest stay installed but off, so a full image keeps every plugin
|
||||||
* start from how things are now.
|
* unless the admin picks Remove; consent ones start on. Later runs start from
|
||||||
|
* how things are now.
|
||||||
*/
|
*/
|
||||||
export function defaultChoices(
|
export function defaultChoices(
|
||||||
list: OnboardingPluginList,
|
list: OnboardingPluginList,
|
||||||
@@ -26,7 +27,7 @@ export function defaultChoices(
|
|||||||
return [plugin.id, currentChoice(plugin)];
|
return [plugin.id, currentChoice(plugin)];
|
||||||
}
|
}
|
||||||
if (plugin.consent) return [plugin.id, "enabled"];
|
if (plugin.consent) return [plugin.id, "enabled"];
|
||||||
return [plugin.id, plugin.recommended ? "enabled" : "remove"];
|
return [plugin.id, plugin.recommended ? "enabled" : "disabled"];
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -37,7 +38,7 @@ export function recommendedChoices(
|
|||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
list.plugins.map((plugin) => [
|
list.plugins.map((plugin) => [
|
||||||
plugin.id,
|
plugin.id,
|
||||||
plugin.recommended || plugin.consent ? "enabled" : "remove",
|
plugin.recommended || plugin.consent ? "enabled" : "disabled",
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -49,11 +49,11 @@ const LIST: OnboardingPluginList = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe("defaultChoices", () => {
|
describe("defaultChoices", () => {
|
||||||
it("keeps recommended plugins and removes the rest on a fresh install", () => {
|
it("keeps recommended plugins on and the rest off on a fresh install", () => {
|
||||||
expect(defaultChoices(LIST)).toEqual({
|
expect(defaultChoices(LIST)).toEqual({
|
||||||
term: "enabled",
|
term: "enabled",
|
||||||
base: "remove",
|
base: "disabled",
|
||||||
addon: "remove",
|
addon: "disabled",
|
||||||
stats: "enabled",
|
stats: "enabled",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -139,8 +139,8 @@ describe("PluginsStep", () => {
|
|||||||
resolved: {},
|
resolved: {},
|
||||||
adjustments: [],
|
adjustments: [],
|
||||||
enabled: [],
|
enabled: [],
|
||||||
disabled: [],
|
disabled: ["base", "addon"],
|
||||||
removed: ["base", "addon"],
|
removed: [],
|
||||||
failed: [],
|
failed: [],
|
||||||
});
|
});
|
||||||
api.getOnboardingPlugins.mockResolvedValueOnce(LIST).mockResolvedValue({
|
api.getOnboardingPlugins.mockResolvedValueOnce(LIST).mockResolvedValue({
|
||||||
@@ -155,8 +155,8 @@ describe("PluginsStep", () => {
|
|||||||
expect(ok).toBe(true);
|
expect(ok).toBe(true);
|
||||||
expect(api.applyOnboardingPlugins).toHaveBeenCalledWith({
|
expect(api.applyOnboardingPlugins).toHaveBeenCalledWith({
|
||||||
term: "enabled",
|
term: "enabled",
|
||||||
base: "remove",
|
base: "disabled",
|
||||||
addon: "remove",
|
addon: "disabled",
|
||||||
stats: "enabled",
|
stats: "enabled",
|
||||||
});
|
});
|
||||||
expect(loader.syncPlugins).toHaveBeenCalled();
|
expect(loader.syncPlugins).toHaveBeenCalled();
|
||||||
|
|||||||
Reference in new issue
Block a user