Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 0 additions & 4 deletions extensions/mssql/src/controllers/mainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3085,9 +3085,6 @@ export default class MainController implements vscode.Disposable {
* @param projectFilePath The file path of the database project to publish.
*/
public async onPublishDatabaseProject(projectFilePath: string): Promise<void> {
const deploymentOptionsResult = await this.dacFxService.getDeploymentOptions(
DeploymentScenario.Deployment,
);
const publishProjectWebView = new PublishProjectWebViewController(
this._context,
this._vscodeWrapper,
Expand All @@ -3097,7 +3094,6 @@ export default class MainController implements vscode.Disposable {
this.sqlProjectsService,
this.dacFxService,
this.sqlPackageService,
deploymentOptionsResult.defaultDeploymentOptions,
);

publishProjectWebView.revealToForeground();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
import { SqlProjectsService } from "../services/sqlProjectsService";
import { Deferred } from "../protocol";
import { TelemetryViews, TelemetryActions } from "../sharedInterfaces/telemetry";
import { TaskExecutionMode } from "../enums";
import { TaskExecutionMode, DeploymentScenario } from "../enums";
import { hasAnyMissingRequiredValues, getErrorMessage, uuid } from "../utils/utils";
import { ConnectionCredentials } from "../models/connectionCredentials";
import { ProjectController } from "../controllers/projectController";
Expand All @@ -60,6 +60,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
> {
private _cachedDatabaseList?: { displayName: string; value: string }[];
private _cachedSelectedDatabase?: string;
private _preloadedContainerPort?: Promise<number>;
Comment thread
ssreerama marked this conversation as resolved.
private _deploymentOptionsPromise?: Promise<void>;
private _connectionUri?: string;
private _serverTypes: string = "";
private _availableConnections?: IConnectionDialogProfile[];
Expand All @@ -81,7 +83,6 @@ export class PublishProjectWebViewController extends FormWebviewController<
sqlProjectsService?: SqlProjectsService,
dacFxService?: mssql.IDacFxService,
sqlPackageService?: SqlPackageService,
deploymentOptions?: mssql.DeploymentOptions,
) {
super(
context,
Expand All @@ -101,8 +102,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
inProgress: false,
lastPublishResult: undefined,
hasFormErrors: true,
deploymentOptions: deploymentOptions,
defaultDeploymentOptions: undefined, //Clone after clearing excludeObjectTypes so reset uses the correct defaults
deploymentOptions: undefined,
defaultDeploymentOptions: undefined,
} as PublishDialogState,
{
title: Loc.Title,
Expand All @@ -122,16 +123,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
},
);

// Clear default excludeObjectTypes for publish dialog, no default exclude options should exist
if (deploymentOptions?.excludeObjectTypes !== undefined) {
deploymentOptions.excludeObjectTypes.value = [];
}

// Clone after clearing excludeObjectTypes so reset uses the correct defaults
this.state.defaultDeploymentOptions = deploymentOptions
? structuredClone(deploymentOptions)
: undefined;

// Fire port detection immediately so it runs in the background while the dialog initializes.
this._preloadedContainerPort = dockerUtils.findAvailablePort(constants.defaultPortNumber);
Comment thread
ssreerama marked this conversation as resolved.
Outdated
this._sqlProjectsService = sqlProjectsService;
this._dacFxService = dacFxService;
this._sqlPackageService = sqlPackageService;
Expand All @@ -146,6 +139,7 @@ export class PublishProjectWebViewController extends FormWebviewController<
});

this.registerRpcHandlers();
this.updateState();
Comment thread
ssreerama marked this conversation as resolved.

// Listen for new connections being added elsewhere (e.g., Object Explorer)
// Refresh the saved connections list so new connections appear in the dropdown
Expand All @@ -164,6 +158,9 @@ export class PublishProjectWebViewController extends FormWebviewController<
this.initialized.resolve();
})
.catch((err) => {
this.logger.error(
`Error initializing PublishProjectWebViewController: ${getErrorMessage(err)}`,
);
this.initialized.reject(err);
});
}
Expand Down Expand Up @@ -207,6 +204,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
databaseName: string,
upgradeExisting: boolean,
): Promise<void> {
// Ensure deployment options are loaded before executing DacFx operations.
await this._deploymentOptionsPromise;
const connectionUri = this._connectionUri || "";
const sqlCmdVariables = new Map(Object.entries(state.formState.sqlCmdVariables || {}));

Expand Down Expand Up @@ -272,6 +271,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
dacpacPath: string,
databaseName: string,
): Promise<void> {
// Ensure deployment options are loaded before executing DacFx operations.
await this._deploymentOptionsPromise;
const connectionUri = this._connectionUri || "";
const sqlCmdVariables = new Map(Object.entries(state.formState.sqlCmdVariables || {}));

Expand Down Expand Up @@ -783,6 +784,36 @@ export class PublishProjectWebViewController extends FormWebviewController<
this.state.projectFilePath = projectFilePath;
}

// Fetch deployment options in the background while other init work proceeds.
// Cache the promise so consumers can await it if they run before the fetch completes.
if (this._dacFxService) {
// getDeploymentOptions returns a Thenable; wrap in Promise.resolve() for .catch support.
this._deploymentOptionsPromise = Promise.resolve(
Comment thread
ssreerama marked this conversation as resolved.
this._dacFxService.getDeploymentOptions(DeploymentScenario.Deployment),
)
.then((result) => {
if (this.isDisposed) {
return;
}
const options = result?.defaultDeploymentOptions;
if (options) {
// Clear default excludeObjectTypes — no default exclude options for the publish dialog.
if (options.excludeObjectTypes !== undefined) {
options.excludeObjectTypes.value = [];
}
Comment thread
ssreerama marked this conversation as resolved.
this.state.deploymentOptions = options;
// Clone after clearing so reset uses the correct defaults.
this.state.defaultDeploymentOptions = structuredClone(options);
Comment thread
ssreerama marked this conversation as resolved.
this.updateState();
}
})
.catch((err) => {
this.logger.error("Failed to fetch deployment options:", err);
});
Comment thread
ssreerama marked this conversation as resolved.
// Intentionally not awaited here — callers await _deploymentOptionsPromise before using the options.
void this._deploymentOptionsPromise;
}
Comment thread
ssreerama marked this conversation as resolved.

// Get the project properties from the proj file
let projectTargetVersion: string | undefined;
try {
Expand Down Expand Up @@ -1301,6 +1332,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
}

try {
// Ensure deployment options are loaded before saving profile.
await this._deploymentOptionsPromise;
const databaseName = state.formState.databaseName || projectName;
// Connection string depends on publish target:
// - For container targets: empty string because we're provisioning a new container
Expand Down Expand Up @@ -1377,6 +1410,8 @@ export class PublishProjectWebViewController extends FormWebviewController<
// Request handler to generate sqlpackage command string
this.onRequest(GenerateSqlPackageCommandRequest.type, async (params) => {
try {
// Ensure deployment options are loaded before building the command.
await this._deploymentOptionsPromise;
const dacpacPath = this.state.projectProperties?.dacpacOutputPath;

if (!dacpacPath) {
Expand Down Expand Up @@ -1628,11 +1663,10 @@ export class PublishProjectWebViewController extends FormWebviewController<
this._connectionUri = undefined;
this._serverTypes = "";

// Auto-detect the first port available from 1433 upward so the field
// never shows a port that is already in use.
const availablePort = await dockerUtils.findAvailablePort(
constants.defaultPortNumber,
);
// Use pre-fetched port if available, otherwise fetch live.
const availablePort = this._preloadedContainerPort
? await this._preloadedContainerPort
Comment thread
ssreerama marked this conversation as resolved.
Outdated
: await dockerUtils.findAvailablePort(constants.defaultPortNumber);
Comment thread
ssreerama marked this conversation as resolved.
Outdated
this.state.formState.containerPort =
availablePort > 0 ? String(availablePort) : String(constants.defaultPortNumber);
Comment thread
ssreerama marked this conversation as resolved.
} else if (this.state.formState.publishTarget === PublishTarget.ExistingServer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ suite("PublishProjectWebViewController Tests", () => {

// Create mock for interface (IDacFxService) - only stub methods we actually use in tests
mockDacFxService = {
getDeploymentOptions: sandbox.stub().resolves({ defaultDeploymentOptions: undefined }),
getOptionsFromProfile: sandbox.stub(),
savePublishProfile: sandbox.stub(),
deployDacpac: sandbox.stub(),
Expand All @@ -104,6 +105,9 @@ suite("PublishProjectWebViewController Tests", () => {
connectionManager: mockConnectionManager,
createObjectExplorerSession: sandbox.stub().resolves(),
} as unknown as sinon.SinonStubbedInstance<MainController>;

// Stub findAvailablePort — called eagerly in the constructor to pre-fetch the port.
sandbox.stub(dockerUtils, "findAvailablePort").resolves(1433);
Comment thread
ssreerama marked this conversation as resolved.
});

teardown(() => {
Expand Down Expand Up @@ -1517,7 +1521,7 @@ suite("PublishProjectWebViewController Tests", () => {
controller.state.formState.containerPort = "1433";

// Simulate port in use: findAvailablePort returns a different port
sandbox.stub(dockerUtils, "findAvailablePort").resolves(1434);
(dockerUtils.findAvailablePort as sinon.SinonStub).resolves(1434);

// Stub parent validateForm to return no errors
sandbox
Expand Down
Loading