Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ const CLI_WINDOWS_PATHS = [

export const CLI_PATHS =
platform() === "win32" ? CLI_WINDOWS_PATHS : CLI_UNIX_PATHS;

export const LOCALSTACK_DOCKER_IMAGE_NAME = "localstack/localstack-pro";
32 changes: 30 additions & 2 deletions src/plugins/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
activateLicenseUntilValid,
} from "../utils/license.ts";
import { minDelay } from "../utils/promises.ts";
import { updateDockerImage } from "../utils/setup.ts";

export default createPlugin(
"setup",
Expand Down Expand Up @@ -60,7 +61,7 @@
},
});

window.withProgress(

Check warning on line 64 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
{
location: ProgressLocation.Notification,
title: "Setup LocalStack",
Expand Down Expand Up @@ -93,6 +94,14 @@
}
}

let imagePulled = false;
const pullImageProcess = updateDockerImage(
outputChannel,
cancellationToken,
).then(() => {
imagePulled = true;
});

/////////////////////////////////////////////////////////////////////
progress.report({
message: "Verifying authentication...",
Expand Down Expand Up @@ -195,7 +204,7 @@
"License is not valid or not assigned. Open License settings page to activate it.",
});

commands.executeCommand("localstack.openLicensePage");
await commands.executeCommand("localstack.openLicensePage");

await activateLicenseUntilValid(
outputChannel,
Expand All @@ -220,34 +229,53 @@
}),
);

commands.executeCommand("localstack.refreshStatusBar");
void commands.executeCommand("localstack.refreshStatusBar");

progress.report({
message: 'Finished configuring "localstack" AWS profiles.',
});
await minDelay(Promise.resolve());

if (!imagePulled) {
progress.report({
message: "Downloading LocalStack docker image...",
Copy link
Member

Choose a reason for hiding this comment

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

nit capitalization: @tiurin "Docker" as a name is preferably capitalized

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

thanks, will fix it in one of the upcoming releases.

});
await minDelay(pullImageProcess);
}

if (cancellationToken.isCancellationRequested) {
telemetry.track({
name: "setup_ended",
payload: {
namespace: "onboarding",
steps: [1, 2, 3],
status: "CANCELLED",
},
});
return;
}

/////////////////////////////////////////////////////////////////////
if (localStackStatusTracker.status() === "running") {
window

Check warning on line 260 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
.showInformationMessage("LocalStack is running.", {
title: "View Logs",
command: "localstack.viewLogs",
})
.then((selection) => {
if (selection) {
commands.executeCommand(selection.command);

Check warning on line 267 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
});
} else {
window

Check warning on line 271 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
.showInformationMessage("LocalStack is ready to start.", {
title: "Start LocalStack",
command: "localstack.start",
})
.then((selection) => {
if (selection) {
commands.executeCommand(selection.command);

Check warning on line 278 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
});
}
Expand All @@ -267,14 +295,14 @@
);

if (setupStatusTracker.status() === "setup_required") {
window

Check warning on line 298 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
.showInformationMessage("Setup LocalStack to get started.", {
title: "Setup",
command: "localstack.setup",
})
.then((selected) => {
if (selected) {
commands.executeCommand(selected.command, "extension_startup");

Check warning on line 305 in src/plugins/setup.ts

View workflow job for this annotation

GitHub Actions / Lint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { access } from "node:fs/promises";
import type { CancellationToken, LogOutputChannel } from "vscode";
import { workspace } from "vscode";

import { CLI_PATHS } from "../constants.ts";
import { CLI_PATHS, LOCALSTACK_DOCKER_IMAGE_NAME } from "../constants.ts";

import { exec } from "./exec.ts";
import { spawn } from "./spawn.ts";
import type { SpawnOptions } from "./spawn.ts";

const IMAGE_NAME = "localstack/localstack-pro";
const IMAGE_NAME = LOCALSTACK_DOCKER_IMAGE_NAME; // not using the import directly as the constant name should match the env var
const LOCALSTACK_LDM_PREVIEW = "1";

const findLocalStack = async (): Promise<string> => {
Expand Down
70 changes: 69 additions & 1 deletion src/utils/setup.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type { LogOutputChannel } from "vscode";
import type { CancellationToken, LogOutputChannel } from "vscode";
import * as z from "zod/v4-mini";

import { LOCALSTACK_DOCKER_IMAGE_NAME } from "../constants.ts";

import { checkIsAuthenticated } from "./authenticate.ts";
import { checkIsProfileConfigured } from "./configure-aws.ts";
import { exec } from "./exec.ts";
import { checkLocalstackInstalled } from "./install.ts";
import { checkIsLicenseValid } from "./license.ts";
import { spawn } from "./spawn.ts";

export async function checkSetupStatus(outputChannel: LogOutputChannel) {
const [isInstalled, isAuthenticated, isLicenseValid, isProfileConfigured] =
Expand All @@ -21,3 +26,66 @@ export async function checkSetupStatus(outputChannel: LogOutputChannel) {
isProfileConfigured,
};
}

export async function updateDockerImage(
outputChannel: LogOutputChannel,
cancellationToken: CancellationToken,
): Promise<void> {
const imageVersion = await getDockerImageSemverVersion(outputChannel);
if (!imageVersion) {
await pullDockerImage(outputChannel, cancellationToken);
}
}

const InspectSchema = z.array(
z.object({
Config: z.object({
Env: z.array(z.string()),
}),
}),
);

async function getDockerImageSemverVersion(
outputChannel: LogOutputChannel,
): Promise<string | undefined> {
try {
const { stdout } = await exec(
`docker inspect ${LOCALSTACK_DOCKER_IMAGE_NAME}`,
);
const data: unknown = JSON.parse(stdout);
const parsed = InspectSchema.safeParse(data);
if (!parsed.success) {
throw new Error(
`Could not parse "docker inspect" output: ${JSON.stringify(z.treeifyError(parsed.error))}`,
);
}
const env = parsed.data[0]?.Config.Env ?? [];
const imageVersion = env
.find((line) => line.startsWith("LOCALSTACK_BUILD_VERSION="))
?.slice("LOCALSTACK_BUILD_VERSION=".length);
if (!imageVersion) {
return;
}
return imageVersion;
} catch (error) {
outputChannel.error("Could not inspect LocalStack docker image");
outputChannel.error(error instanceof Error ? error : String(error));
return undefined;
}
}

async function pullDockerImage(
outputChannel: LogOutputChannel,
cancellationToken: CancellationToken,
): Promise<void> {
try {
await spawn("docker", ["pull", LOCALSTACK_DOCKER_IMAGE_NAME], {
outputChannel,
outputLabel: "docker.pull",
cancellationToken: cancellationToken,
});
} catch (error) {
outputChannel.error("Could not pull LocalStack docker image");
outputChannel.error(error instanceof Error ? error : String(error));
}
}
Loading