Skip to content

Commit

Permalink
Add compose service startup CodeLenses, update `vscode-docker-registr…
Browse files Browse the repository at this point in the history
…ies` (#4432)
  • Loading branch information
bwateratmsft authored Dec 18, 2024
1 parent 3303668 commit 62dcfc5
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 27 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3008,13 +3008,13 @@
"@azure/arm-authorization": "^9.0.0",
"@azure/arm-containerregistry": "^10.1.0",
"@azure/storage-blob": "^12.14.0",
"@microsoft/compose-language-service": "^0.2.0",
"@microsoft/compose-language-service": "^0.3.0",
"@microsoft/vscode-azext-azureappservice": "~3.3.0",
"@microsoft/vscode-azext-azureauth": "^2.4.1",
"@microsoft/vscode-azext-azureutils": "^3.0.1",
"@microsoft/vscode-azext-utils": "^2.5.1",
"@microsoft/vscode-container-client": "^0.1.2",
"@microsoft/vscode-docker-registries": "^0.1.12",
"@microsoft/vscode-docker-registries": "^0.1.13",
"dayjs": "^1.11.7",
"dockerfile-language-server-nodejs": "^0.13.0",
"fs-extra": "^11.1.1",
Expand Down
25 changes: 16 additions & 9 deletions src/commands/compose/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import { quickPickWorkspaceFolder } from '../../utils/quickPickWorkspaceFolder';
import { selectComposeCommand } from '../selectCommandTemplate';
import { getComposeProfileList, getComposeProfilesOrServices, getComposeServiceList } from './getComposeSubsetList';

async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSubset')[], message: string, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise<void> {
async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSubset')[], message: string, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[], preselectedServices?: string[], preselectedProfiles?: string[]): Promise<void> {
if (!vscode.workspace.isTrusted) {
throw new UserCancelledError('enforceTrust');
}

if (typeof dockerComposeFileUri === 'string') {
dockerComposeFileUri = vscode.Uri.parse(dockerComposeFileUri);
}

// If a file is chosen, get its workspace folder, otherwise, require the user to choose
// If a file is chosen that is not in a workspace, it will automatically fall back to quickPickWorkspaceFolder
const folder: vscode.WorkspaceFolder = (dockerComposeFileUri ? vscode.workspace.getWorkspaceFolder(dockerComposeFileUri) : undefined) ||
Expand Down Expand Up @@ -59,7 +63,7 @@ async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSu
);

// Add the service list if needed
terminalCommand.command = await addServicesOrProfilesIfNeeded(context, folder, terminalCommand.command);
terminalCommand.command = await addServicesOrProfilesIfNeeded(context, folder, terminalCommand.command, preselectedServices, preselectedProfiles);

const client = await ext.orchestratorManager.getClient();
const taskCRF = new TaskCommandRunnerFactory({
Expand All @@ -72,12 +76,14 @@ async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSu
}
}

export async function composeUp(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise<void> {
// The parameters of this function should not be changed without updating the compose language service which uses this command
export async function composeUp(context: IActionContext, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[]): Promise<void> {
return await compose(context, ['up'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris);
}

export async function composeUpSubset(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise<void> {
return await compose(context, ['upSubset'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris);
// The parameters of this function should not be changed without updating the compose language service which uses this command
export async function composeUpSubset(context: IActionContext, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[], preselectedServices?: string[], preselectedProfiles?: string[]): Promise<void> {
return await compose(context, ['upSubset'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris, preselectedServices, preselectedProfiles);
}

export async function composeDown(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise<void> {
Expand All @@ -90,18 +96,19 @@ export async function composeRestart(context: IActionContext, dockerComposeFileU

const serviceListPlaceholder = /\${serviceList}/i;
const profileListPlaceholder = /\${profileList}/i;
async function addServicesOrProfilesIfNeeded(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, command: string): Promise<string> {
async function addServicesOrProfilesIfNeeded(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, command: string, preselectedServices: string[], preselectedProfiles: string[]): Promise<string> {
const commandWithoutPlaceholders = command.replace(serviceListPlaceholder, '').replace(profileListPlaceholder, '');

if (serviceListPlaceholder.test(command) && profileListPlaceholder.test(command)) {
// If both are present, need to ask
const { services, profiles } = await getComposeProfilesOrServices(context, workspaceFolder, commandWithoutPlaceholders);
const { services, profiles } = await getComposeProfilesOrServices(context, workspaceFolder, commandWithoutPlaceholders, preselectedServices, preselectedProfiles);
return command
.replace(serviceListPlaceholder, services)
.replace(profileListPlaceholder, profiles);
} else if (serviceListPlaceholder.test(command)) {
return command.replace(serviceListPlaceholder, await getComposeServiceList(context, workspaceFolder, commandWithoutPlaceholders));
return command.replace(serviceListPlaceholder, await getComposeServiceList(context, workspaceFolder, commandWithoutPlaceholders, preselectedServices));
} else if (profileListPlaceholder.test(command)) {
return command.replace(profileListPlaceholder, await getComposeProfileList(context, workspaceFolder, commandWithoutPlaceholders));
return command.replace(profileListPlaceholder, await getComposeProfileList(context, workspaceFolder, commandWithoutPlaceholders, preselectedProfiles));
} else {
return command;
}
Expand Down
24 changes: 16 additions & 8 deletions src/commands/compose/getComposeSubsetList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@ const composeCommandReplaceRegex = /(\b(up|down)\b).*$/i;

type SubsetType = 'services' | 'profiles';

export async function getComposeProfilesOrServices(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string): Promise<{ services: string | undefined, profiles: string | undefined }> {
export async function getComposeProfilesOrServices(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, preselectedServices?: string[], preselectedProfiles?: string[]): Promise<{ services: string | undefined, profiles: string | undefined }> {
const profiles = await getServiceSubsets(workspaceFolder, composeCommand, 'profiles');

if (preselectedServices?.length && preselectedProfiles?.length) {
throw new Error(vscode.l10n.t('Cannot specify both services and profiles to start. Please choose one or the other.'));
}

// If there any profiles, we need to ask the user whether they want profiles or services, since they are mutually exclusive to use
// Otherwise, if there are no profiles, we'll automatically assume services
let useProfiles = false;
if (profiles?.length) {
if (preselectedProfiles?.length) {
useProfiles = true;
} else if (preselectedServices?.length) {
useProfiles = false;
} else if (profiles?.length) {
const profilesOrServices: IAzureQuickPickItem<SubsetType>[] = [
{
label: vscode.l10n.t('Services'),
Expand All @@ -35,12 +43,12 @@ export async function getComposeProfilesOrServices(context: IActionContext, work
}

return {
profiles: useProfiles ? await getComposeProfileList(context, workspaceFolder, composeCommand, profiles) : '',
services: !useProfiles ? await getComposeServiceList(context, workspaceFolder, composeCommand) : '',
profiles: useProfiles ? await getComposeProfileList(context, workspaceFolder, composeCommand, profiles, preselectedProfiles) : '',
services: !useProfiles ? await getComposeServiceList(context, workspaceFolder, composeCommand, preselectedServices) : '',
};
}

export async function getComposeProfileList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, prefetchedProfiles?: string[]): Promise<string> {
export async function getComposeProfileList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, prefetchedProfiles?: string[], preselectedProfiles?: string[]): Promise<string> {
const profiles = prefetchedProfiles ?? await getServiceSubsets(workspaceFolder, composeCommand, 'profiles');

if (!profiles?.length) {
Expand All @@ -51,15 +59,15 @@ export async function getComposeProfileList(context: IActionContext, workspaceFo
// Fetch the previously chosen profiles list. By default, all will be selected.
const workspaceProfileListKey = `vscode-docker.composeProfiles.${workspaceFolder.name}`;
const previousChoices = ext.context.workspaceState.get<string[]>(workspaceProfileListKey, profiles);
const result = await pickSubsets(context, 'profiles', profiles, previousChoices);
const result = preselectedProfiles?.length ? preselectedProfiles : await pickSubsets(context, 'profiles', profiles, previousChoices);

// Update the cache
await ext.context.workspaceState.update(workspaceProfileListKey, result);

return result.map(p => `--profile ${p}`).join(' ');
}

export async function getComposeServiceList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string): Promise<string> {
export async function getComposeServiceList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, preselectedServices?: string[]): Promise<string> {
const services = await getServiceSubsets(workspaceFolder, composeCommand, 'services');

if (!services?.length) {
Expand All @@ -70,7 +78,7 @@ export async function getComposeServiceList(context: IActionContext, workspaceFo
// Fetch the previously chosen services list. By default, all will be selected.
const workspaceServiceListKey = `vscode-docker.composeServices.${workspaceFolder.name}`;
const previousChoices = ext.context.workspaceState.get<string[]>(workspaceServiceListKey, services);
const result = await pickSubsets(context, 'services', services, previousChoices);
const result = preselectedServices?.length ? preselectedServices : await pickSubsets(context, 'services', services, previousChoices);

// Update the cache
await ext.context.workspaceState.update(workspaceServiceListKey, result);
Expand Down
1 change: 1 addition & 0 deletions src/utils/AlternateYamlLanguageServiceClientFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class AlternateYamlLanguageServiceClientFeature implements StaticFeature,
advancedCompletions: false, // YAML extension does not have advanced completions for compose docs
hover: false, // YAML extension provides hover, but the compose spec lacks descriptions -- https://github.com/compose-spec/compose-spec/issues/138
imageLinks: false, // YAML extension does not have image hyperlinks for compose docs
serviceStartupCodeLens: false, // YAML extension does not have service startup code lens for compose docs
formatting: true,
};

Expand Down

0 comments on commit 62dcfc5

Please sign in to comment.