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 ability to create environment from lockfile #395

Closed
wants to merge 10 commits into from
Closed
1 change: 1 addition & 0 deletions src/common/models/CondaSpecification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export type CondaSpecification = {
dependencies: (string | CondaSpecificationPip)[];
variables: Record<string, string>;
prefix?: string | null;
lockfile?: any | null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to rely on any particular format here since it might change upstream.

};
13 changes: 10 additions & 3 deletions src/features/channels/channelsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ export const channelsSlice = createSlice({
{
payload: {
data: {
specification: {
spec: { channels }
}
specification: { spec }
}
}
}
) => {
// channels can be undefined if a lockfile specification is provided,
// try getting channels from metadata in that case
const channels =
spec?.channels ??
spec?.lockfile?.metadata?.channels?.map(
(channel: any) => channel?.url
) ??
[];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and similar code for deps is what populates GUI channels and dependencies lists on env save.


state.channels = channels;
}
);
Expand Down
28 changes: 21 additions & 7 deletions src/features/environmentCreate/components/EnvironmentCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,28 @@ export const EnvironmentCreate = ({ environmentNotification }: IEnvCreate) => {
dispatch(descriptionChanged(value));
}, 300);

const createEnvironment = async (code: ICreateEnvironmentArgs) => {
const createEnvironment = async (
code: ICreateEnvironmentArgs,
is_lockfile: boolean
) => {
const namespace = newEnvironment?.namespace;
const environmentInfo = {
namespace,
specification: `${stringify(
code
)}\ndescription: '${description}'\nname: ${name}\nprefix: null`
};
let environmentInfo;
if (is_lockfile) {
environmentInfo = {
namespace,
specification: stringify(code),
environment_name: name,
environment_description: description,
is_lockfile: true
};
} else {
environmentInfo = {
namespace,
specification: `${stringify(
code
)}\ndescription: '${description}'\nname: ${name}\nprefix: null`
};
}

try {
const { data } = await createOrUpdate(environmentInfo).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,34 @@ import {
environmentCreateStateCleared
} from "../../environmentCreateSlice";
import { getStylesForStyleType } from "../../../../utils/helpers";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";

export const SpecificationCreate = ({ onCreateEnvironment }: any) => {
const dispatch = useAppDispatch();
const { channels, requestedPackages, environmentVariables } = useAppSelector(
state => state.environmentCreate
);
const [show, setShow] = useState(false);
const [specificationType, setSpecificationType] =
React.useState("specification");
const [editorContent, setEditorContent] = useState<{
channels: string[];
dependencies: string[];
variables: Record<string, string>;
}>({ channels: [], dependencies: [], variables: {} });
const [editorContentLockfile, setEditorContentLockfile] = useState<any>({});

const buttonStyles = getStylesForStyleType(
{ padding: "5px 60px" },
{ padding: "5px 48px" }
);

const onUpdateSpecificationType = (event: SelectChangeEvent) => {
setSpecificationType(event.target.value as string);
};

const onUpdateChannels = useCallback((channels: string[]) => {
dispatch(channelsChanged(channels));
}, []);
Expand Down Expand Up @@ -71,15 +81,22 @@ export const SpecificationCreate = ({ onCreateEnvironment }: any) => {
setEditorContent(code);
};

const onUpdateEditorLockfile = (lockfile: any) => {
setEditorContentLockfile(lockfile);
};

const onToggleEditorView = (value: boolean) => {
if (show) {
dispatch(
editorCodeUpdated({
channels: editorContent.channels,
dependencies: editorContent.dependencies,
variables: editorContent.variables
})
);
if (specificationType === "specification") {
dispatch(
editorCodeUpdated({
channels: editorContent.channels,
dependencies: editorContent.dependencies,
variables: editorContent.variables
})
);
}
// Do nothing when specificationType === lockfile
} else {
setEditorContent({
dependencies: requestedPackages,
Expand All @@ -103,16 +120,36 @@ export const SpecificationCreate = ({ onCreateEnvironment }: any) => {
return stringify({ channels, dependencies, variables });
};

const formatCodeLockfile = () => {
return stringify({
version: 1,
metadata: {},
package: []
});
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just to hint to the user which lockfile format is expected. Similar idea to formatCode above.


const handleSubmit = () => {
const code = show
? editorContent
: {
dependencies: requestedPackages,
variables: environmentVariables,
channels
};

onCreateEnvironment(code);
let code;
let is_lockfile;

if (show) {
if (specificationType === "specification") {
code = editorContent;
is_lockfile = false;
} else {
code = editorContentLockfile;
is_lockfile = true;
}
} else {
code = {
dependencies: requestedPackages,
variables: environmentVariables,
channels
};
is_lockfile = false;
}

onCreateEnvironment(code, is_lockfile);
};

useEffect(() => {
Expand All @@ -129,18 +166,41 @@ export const SpecificationCreate = ({ onCreateEnvironment }: any) => {
>
<Box>
{show ? (
<CodeEditor
code={formatCode(channels, requestedPackages, environmentVariables)}
onChangeEditor={onUpdateEditor}
/>
<>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
value={specificationType}
onChange={onUpdateSpecificationType}
displayEmpty
>
<MenuItem value="specification">specification</MenuItem>
<MenuItem value="lockfile">unified lockfile</MenuItem>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MenuItem values here match those we show in the admin interface (see the companion conda-store PR).

</Select>
</FormControl>
{specificationType === "specification" ? (
<CodeEditor
code={formatCode(
channels,
requestedPackages,
environmentVariables
)}
onChangeEditor={onUpdateEditor}
/>
) : (
<CodeEditor
code={formatCodeLockfile()}
onChangeEditor={onUpdateEditorLockfile}
/>
)}
</>
) : (
<>
<Box sx={{ marginBottom: "30px" }}>
<CreateEnvironmentPackages
requestedPackages={requestedPackages}
/>
</Box>
<Box sx={{ margiBottom: "30px" }}>
<Box sx={{ marginBottom: "30px" }}>
peytondmurray marked this conversation as resolved.
Show resolved Hide resolved
<ChannelsEdit
channelsList={channels}
updateChannels={onUpdateChannels}
Expand Down
28 changes: 21 additions & 7 deletions src/features/environmentDetails/components/EnvironmentDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,33 @@ export const EnvironmentDetails = ({
}
}, [currentBuild]);

const updateEnvironment = async (code: IUpdateEnvironmentArgs) => {
const updateEnvironment = async (
code: IUpdateEnvironmentArgs,
is_lockfile: boolean
) => {
if (!selectedEnvironment) {
return;
}

const namespace = selectedEnvironment.namespace.name;
const environment = selectedEnvironment.name;
const environmentInfo = {
specification: `${stringify(
code
)}\ndescription: ${description}\nname: ${environment}\nprefix: null`,
namespace
};
let environmentInfo;
if (is_lockfile) {
environmentInfo = {
namespace,
specification: stringify(code),
environment_name: environment,
environment_description: description,
is_lockfile: true
};
} else {
environmentInfo = {
specification: `${stringify(
code
)}\ndescription: ${description}\nname: ${environment}\nprefix: null`,
namespace
};
}

try {
const { data } = await createOrUpdate(environmentInfo).unwrap();
Expand Down
Loading
Loading