Skip to content

Commit 8a9c59b

Browse files
committed
feat: ask about overriding requirements.txt first
1 parent fe5819a commit 8a9c59b

File tree

2 files changed

+97
-26
lines changed

2 files changed

+97
-26
lines changed

src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -461,29 +461,27 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
461461
// Prepare init notebook execution for when kernel starts
462462
// This MUST complete before marking controller as preferred to avoid race conditions
463463
const projectId = notebook.metadata?.deepnoteProjectId;
464-
if (projectId) {
465-
// Get the project and create requirements.txt before kernel starts
466-
const project = this.notebookManager.getOriginalProject(projectId) as
467-
| DeepnoteProject
468-
| undefined;
469-
if (project) {
470-
// Create requirements.txt first (needs to be ready for init notebook)
471-
progress.report({ message: 'Creating requirements.txt...' });
472-
await this.requirementsHelper.createRequirementsFile(project, progressToken);
473-
logger.info(`Created requirements.txt for project ${projectId}`);
474-
475-
// Check if project has an init notebook
476-
if (
477-
project.project.initNotebookId &&
478-
!this.notebookManager.hasInitNotebookBeenRun(projectId)
479-
) {
480-
// Store for execution when kernel actually starts
481-
// Kernels are created lazily when cells execute, so we can't run init notebook now
482-
this.projectsPendingInitNotebook.set(projectId, { notebook, project });
483-
logger.info(
484-
`Init notebook will run automatically when kernel starts for project ${projectId}`
485-
);
486-
}
464+
const project = projectId
465+
? (this.notebookManager.getOriginalProject(projectId) as DeepnoteProject | undefined)
466+
: undefined;
467+
468+
if (project) {
469+
// Create requirements.txt first (needs to be ready for init notebook)
470+
progress.report({ message: 'Creating requirements.txt...' });
471+
await this.requirementsHelper.createRequirementsFile(project, progressToken);
472+
logger.info(`Created requirements.txt for project ${projectId}`);
473+
474+
// Check if project has an init notebook that hasn't been run yet
475+
if (
476+
project.project.initNotebookId &&
477+
!this.notebookManager.hasInitNotebookBeenRun(projectId!)
478+
) {
479+
// Store for execution when kernel actually starts
480+
// Kernels are created lazily when cells execute, so we can't run init notebook now
481+
this.projectsPendingInitNotebook.set(projectId!, { notebook, project });
482+
logger.info(
483+
`Init notebook will run automatically when kernel starts for project ${projectId}`
484+
);
487485
}
488486
}
489487

src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
import { inject, injectable } from 'inversify';
2-
import { workspace, CancellationToken } from 'vscode';
2+
import { workspace, CancellationToken, window } from 'vscode';
33
import * as fs from 'fs';
44
import * as path from '../../platform/vscode-path/path';
55
import type { DeepnoteProject } from './deepnoteTypes';
66
import { ILogger } from '../../platform/logging/types';
7+
import { IPersistentStateFactory } from '../../platform/common/types';
8+
9+
const DONT_ASK_OVERWRITE_REQUIREMENTS_KEY = 'DEEPNOTE_DONT_ASK_OVERWRITE_REQUIREMENTS';
710

811
/**
912
* Helper class for creating requirements.txt files from Deepnote project settings.
1013
*/
1114
@injectable()
1215
export class DeepnoteRequirementsHelper {
13-
constructor(@inject(ILogger) private readonly logger: ILogger) {}
16+
constructor(
17+
@inject(ILogger) private readonly logger: ILogger,
18+
@inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory
19+
) {}
1420

1521
/**
1622
* Extracts requirements from project settings and creates a local requirements.txt file.
@@ -45,9 +51,76 @@ export class DeepnoteRequirementsHelper {
4551
const workspaceRoot = workspaceFolders[0].uri.fsPath;
4652
const requirementsPath = path.join(workspaceRoot, 'requirements.txt');
4753

48-
// Convert requirements array to text format
54+
// Convert requirements array to text format first
4955
const requirementsText = requirements.join('\n') + '\n';
5056

57+
// Check if requirements.txt already exists
58+
const fileExists = await fs.promises
59+
.access(requirementsPath)
60+
.then(() => true)
61+
.catch(() => false);
62+
63+
if (fileExists) {
64+
// Read existing file contents and compare
65+
const existingContent = await fs.promises.readFile(requirementsPath, 'utf8');
66+
67+
if (existingContent === requirementsText) {
68+
this.logger.info('requirements.txt already has the correct content, skipping update');
69+
return;
70+
}
71+
72+
// File exists but content is different, check if we should prompt user
73+
const dontAskState = this.persistentStateFactory.createGlobalPersistentState<boolean>(
74+
DONT_ASK_OVERWRITE_REQUIREMENTS_KEY,
75+
false // default: ask user
76+
);
77+
78+
if (!dontAskState.value) {
79+
// User hasn't chosen "Don't Ask Again", so prompt them
80+
const yes = 'Yes';
81+
const no = 'No';
82+
const dontAskAgain = "Don't Ask Again";
83+
84+
const response = await window.showWarningMessage(
85+
`A requirements.txt file already exists in this workspace. Do you want to override it with requirements from your Deepnote project?`,
86+
{ modal: true },
87+
yes,
88+
no,
89+
dontAskAgain
90+
);
91+
92+
// Check cancellation after showing the prompt
93+
if (token.isCancellationRequested) {
94+
return;
95+
}
96+
97+
switch (response) {
98+
case yes:
99+
// User wants to override, continue with writing
100+
this.logger.info('User chose to override requirements.txt');
101+
break;
102+
case no:
103+
// User doesn't want to override
104+
this.logger.info('User chose not to override requirements.txt');
105+
return;
106+
case dontAskAgain:
107+
// User chose "Don't Ask Again", save preference and override this time
108+
await dontAskState.updateValue(true);
109+
this.logger.info('User chose "Don\'t Ask Again" for requirements.txt override');
110+
break;
111+
default:
112+
// User dismissed the prompt (clicked X)
113+
this.logger.info('User dismissed requirements.txt override prompt');
114+
return;
115+
}
116+
} else {
117+
// User previously selected "Don't Ask Again", automatically override
118+
this.logger.info(
119+
'Automatically overriding requirements.txt (user previously selected "Don\'t Ask Again")'
120+
);
121+
}
122+
}
123+
51124
// Write the requirements.txt file
52125
await fs.promises.writeFile(requirementsPath, requirementsText, 'utf8');
53126

0 commit comments

Comments
 (0)