Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Manual/Copy" install mode to dependency manager #2462

Merged
merged 11 commits into from
Jan 21, 2024
2 changes: 1 addition & 1 deletion src/common/pip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const runPip = async (
});
};

const getFindLinks = (dependencies: readonly PyPiPackage[]): string[] => {
export const getFindLinks = (dependencies: readonly PyPiPackage[]): string[] => {
const links = new Set<string>();
for (const p of dependencies) {
if (p.findLink) {
Expand Down
123 changes: 89 additions & 34 deletions src/renderer/contexts/DependencyContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ import {
ModalHeader,
ModalOverlay,
Progress,
Select,
Spacer,
Spinner,
Switch,
Tag,
Text,
Textarea,
Tooltip,
VStack,
useDisclosure,
useToast,
} from '@chakra-ui/react';
import { clipboard } from 'electron';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { BsQuestionCircle, BsTerminalFill } from 'react-icons/bs';
import { HiOutlineRefresh } from 'react-icons/hi';
Expand All @@ -46,7 +48,7 @@ import {
Version,
} from '../../common/common-types';
import { log } from '../../common/log';
import { OnStdio, runPipInstall, runPipUninstall } from '../../common/pip';
import { OnStdio, getFindLinks, runPipInstall, runPipUninstall } from '../../common/pip';
import { noop } from '../../common/util';
import { versionGt } from '../../common/version';
import { Markdown } from '../components/Markdown';
Expand All @@ -56,6 +58,12 @@ import { BackendContext } from './BackendContext';
import { GlobalContext } from './GlobalNodeState';
import { SettingsContext } from './SettingsContext';

const installModes = {
NORMAL: 'Normal Install',
DIRECT_PIP: 'Direct Pip',
MANUAL_COPY: 'Manual/Copy',
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
};

export interface DependencyContextValue {
openDependencyManager: () => void;
availableUpdates: number;
Expand Down Expand Up @@ -382,7 +390,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
const [isSystemPython] = useIsSystemPython;

const [isConsoleOpen, setIsConsoleOpen] = useState(false);
const [usePipDirectly, setUsePipDirectly] = useState(false);
const [installMode, setInstallMode] = useState(installModes.NORMAL);

const { data: installedPyPi, refetch: refetchInstalledPyPi } = useQuery({
queryKey: 'dependencies',
Expand Down Expand Up @@ -415,6 +423,8 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
);
const onStdio: OnStdio = { onStderr: appendToOutput, onStdout: appendToOutput };

const toast = useToast();

const changePackages = (supplier: () => Promise<void>) => {
if (isRunningShell) throw new Error('Cannot run two pip commands at once');

Expand Down Expand Up @@ -444,25 +454,65 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
});
};

const installPackage = (p: Package) => {
setInstallingPackage(p);
const copyCommandToClipboard = (command: string) => {
clipboard.writeText(command);
toast({
title: 'Command copied to clipboard.',
description:
'Open up anm external terminal, paste the command, and run it. When it is done running, manually restart chaiNNer.',
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
status: 'success',
duration: 9000,
isClosable: true,
});
};

const installPackage = (pkg: Package) => {
if (installMode === installModes.MANUAL_COPY) {
const deps = pkg.dependencies.map((p) => `${p.pypiName}==${p.version}`);
const findLinks = getFindLinks(pkg.dependencies).flatMap((l) => [
'--extra-index-url',
l,
]);
const args = [
pythonInfo.python,
'-m',
'pip',
'install',
'--upgrade',
...deps,
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
...findLinks,
'--no-cache-dir',
'--disable-pip-version-check',
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
];
const cmd = args.join(' ');
copyCommandToClipboard(cmd);
return;
}
setInstallingPackage(pkg);
changePackages(() =>
runPipInstall(
pythonInfo,
p.dependencies,
usePipDirectly ? undefined : setProgress,
pkg.dependencies,
installMode === installModes.NORMAL ? setProgress : undefined,
onStdio
)
);
};

const uninstallPackage = (p: Package) => {
setUninstallingPackage(p);
const uninstallPackage = (pkg: Package) => {
if (installMode === installModes.MANUAL_COPY) {
const deps = pkg.dependencies.map((p) => p.pypiName);
const args = [pythonInfo.python, '-m', 'pip', 'uninstall', ...deps];
const cmd = args.join(' ');
copyCommandToClipboard(cmd);
return;
}
setUninstallingPackage(pkg);
changePackages(() =>
runPipUninstall(
pythonInfo,
p.dependencies,
usePipDirectly ? undefined : setProgress,
pkg.dependencies,
installMode === installModes.NORMAL ? setProgress : undefined,
onStdio
)
);
Expand Down Expand Up @@ -531,29 +581,34 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
Python ({pythonInfo.version}) [
{isSystemPython ? 'System' : 'Integrated'}]
</Text>
<HStack>
<HStack gap={2}>
<HStack>
<Switch
isChecked={usePipDirectly}
isDisabled={isRunningShell}
onChange={() => {
setUsePipDirectly(!usePipDirectly);
}}
/>
<Text>Use Pip Directly</Text>
<Tooltip
hasArrow
borderRadius={8}
label="Disable progress bars and use pip to directly download and install the packages. Use this setting if you are having issues installing normally."
maxW="auto"
openDelay={500}
px={2}
py={0}
>
<Center>
<Icon as={BsQuestionCircle} />
</Center>
</Tooltip>
<HStack>
<Select
isDisabled={isRunningShell}
size="md"
value={installMode}
onChange={(e) => {
setInstallMode(e.target.value);
}}
>
<option>{installModes.NORMAL}</option>
<option>{installModes.DIRECT_PIP}</option>
<option>{installModes.MANUAL_COPY}</option>
</Select>
<Tooltip
hasArrow
borderRadius={8}
label="Change the dependency install mode. Normal Install is recommended, but if you are having issues, you can try using the Direct Pip mode (which will not show progress), or Manual/Copy mode, which will copy the correct install command to your clipboard for you to run in your own terminal (restart chaiNNer manually after)."
joeyballentine marked this conversation as resolved.
Show resolved Hide resolved
openDelay={500}
px={2}
py={0}
>
<Center>
<Icon as={BsQuestionCircle} />
</Center>
</Tooltip>
</HStack>
</HStack>
<Button
aria-label={isConsoleOpen ? 'Hide Console' : 'View Console'}
Expand Down Expand Up @@ -615,7 +670,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
key={p.id}
p={p}
progress={
!usePipDirectly &&
installMode === installModes.NORMAL &&
isRunningShell &&
(installingPackage || uninstallingPackage)
?.id === p.id
Expand Down