Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import * as os from "os";
import { logger } from "../log";
import { requireNpmV7 } from "../npm";

// TODO: remove nodecg-io directory if initial installation was aborted?

export const installModule: CommandModule<unknown, { concurrency: number }> = {
command: "install",
describe: "installs nodecg-io to your local nodecg installation",
Expand Down
20 changes: 16 additions & 4 deletions src/install/production.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
buildNpmPackagePath,
downloadNpmPackage,
createNpmSymlinks,
getSubPackages,
} from "../npm";
import { directoryExists, ensureDirectory } from "../fsUtils";
import { logger } from "../log";
Expand Down Expand Up @@ -53,14 +54,25 @@ export function diffPackages(
requested: NpmPackage[],
current: NpmPackage[],
): { pkgInstall: NpmPackage[]; pkgRemove: NpmPackage[] } {
// currently installed but not requested exactly anymore
const pkgRemove = current.filter((a) => !requested.find((b) => isPackageEquals(a, b)));

// requested and not already exactly installed (e.g. version change)
const pkgInstall = requested.filter((a) => !current.find((b) => isPackageEquals(a, b)));

// Gets sub-packages of packages in pkgInstall that might not be in there.
// E.g. core got upgraded => nodecg-io-core will be removed and reinstalled
// nodecg-io-dashboard will also be removed because it is in nodecg-io-core and
// contained in the directory of the core package. This ensures that the dashboard will
// also be reinstalled, even though it got no upgrade.
const installAdditional = pkgInstall.map((pkg) => getSubPackages(requested, pkg)).flat();

return {
pkgInstall: requested.filter((a) => !current.find((b) => isPackageEquals(a, b))), // requested and not already exactly installed (e.g. version change)
pkgRemove: current.filter((a) => !requested.find((b) => isPackageEquals(a, b))), // currently installed but not requested exactly anymore
pkgRemove,
pkgInstall: [...new Set(pkgInstall.concat(installAdditional))],
};
}

// TODO: handle when e.g. core upgrades and removes nodecg-io-core directory. Need to re-download dashboard because it got deleted (or don't delete it).

/**
* Removes a list of packages from a production nodecg-io install.
* @param pkgs the packages that should be removed
Expand Down
2 changes: 1 addition & 1 deletion src/install/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ function getPackagePath(pkgName: string) {
async function getPackageVersion(pkgName: string, minorVersion: string) {
const version = await getHighestPatchVersion(pkgName, minorVersion);
// if patch part could be found out we will use .0 as it should always exist if the minor version also does.
return version ?? `${version}.0`;
return version ?? `${minorVersion}.0`;
}

function getPackageSymlinks(pkgName: string) {
Expand Down
10 changes: 10 additions & 0 deletions src/npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ export async function removeNpmPackage(pkg: NpmPackage, nodecgIODir: string): Pr
await removeDirectory(buildNpmPackagePath(pkg, nodecgIODir));
}

/**
* Returns you all packages that are in a sub-path of rootPkg.
* This is helpful if you have reinstalled rootPkg and now also need to reinstall all packages
* that were in the directory of rootPkg because they also got removed in {@link removeNpmPackage}.
* @returns the packages that are contained in rootPkg
*/
export function getSubPackages(allPackages: NpmPackage[], rootPkg: NpmPackage): NpmPackage[] {
return allPackages.filter((pkg) => pkg !== rootPkg && pkg.path.startsWith(rootPkg.path));
}

/**
* Gets version of the installed npm by running "npm --version".
* @returns the npm version or undefined if npm is not installed/not in $PATH.
Expand Down
9 changes: 8 additions & 1 deletion test/install/production.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { vol } from "memfs";
import * as path from "path";
import { corePkg, nodecgIODir, twitchChatPkg, validProdInstall } from "../testUtils";
import { corePkg, dashboardPkg, nodecgIODir, twitchChatPkg, validProdInstall } from "../testUtils";
import { diffPackages, installPackages, removePackages, validateInstall } from "../../src/install/production";
import * as installation from "../../src/installation";
import * as fsUtils from "../../src/fsUtils";
Expand Down Expand Up @@ -45,6 +45,13 @@ describe("diffPackages", () => {
expect(pkgRemove).toStrictEqual([]);
expect(pkgInstall).toStrictEqual([]);
});

test("should install dashboard (sub pkg) if upgrading core", async () => {
// dashboard not modified, but should still be installed because it is in core and core gets upgraded
const { pkgInstall, pkgRemove } = diffPackages([corePkg2, dashboardPkg], [corePkg, dashboardPkg]);
expect(pkgRemove).toStrictEqual([corePkg]);
expect(pkgInstall).toStrictEqual([corePkg2, dashboardPkg]);
});
});

describe("removePackages", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/install/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("getCompatibleVersions", () => {
});

describe("buildPackageList", () => {
const ver = "0.1.0";
const ver = "0.1";
const testSvcName = "testSvc";
const testSvcPkgName = `nodecg-io-${testSvcName}`;
const mock = jest.spyOn(npm, "getHighestPatchVersion").mockResolvedValue("0.1.1");
Expand Down
14 changes: 12 additions & 2 deletions test/npm/pkgManagement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createFsFromVolume, vol } from "memfs";
import { createNpmSymlinks, removeNpmPackage, runNpmInstall } from "../../src/npm";
import { tempDir, corePkg, fsRoot } from "../testUtils";
import { createNpmSymlinks, getSubPackages, removeNpmPackage, runNpmInstall } from "../../src/npm";
import { tempDir, corePkg, fsRoot, twitchChatPkg, dashboardPkg } from "../testUtils";
import * as fsUtils from "../../src/fsUtils";
import * as path from "path";

Expand Down Expand Up @@ -45,3 +45,13 @@ describe("removeNpmPackage", () => {
expect(spy).toHaveBeenCalled();
});
});

describe("getSubPackages", () => {
test("should return empty list if no packages are inside the passed package", async () => {
expect(getSubPackages([corePkg, twitchChatPkg], corePkg)).toStrictEqual([]);
});

test("should return dashboard to be inside core", async () => {
expect(getSubPackages([corePkg, dashboardPkg], corePkg)).toStrictEqual([dashboardPkg]);
});
});
7 changes: 6 additions & 1 deletion test/testUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as temp from "temp";
import { corePackage } from "../src/install/nodecgIOVersions";
import { corePackage, dashboardPackage, dashboardPath } from "../src/install/nodecgIOVersions";
import * as path from "path";
import { DevelopmentInstallation, ProductionInstallation } from "../src/installation";

Expand All @@ -21,6 +21,11 @@ export const twitchChatPkg = {
path: "nodecg-io-twitch-chat",
version: "0.1.0",
};
export const dashboardPkg = {
name: dashboardPackage,
path: dashboardPath,
version: "0.1.0",
};
export const nodecgExampleConfig = {
bundles: {
paths: ["some-custom-bundle-dir"],
Expand Down