Skip to content

Commit

Permalink
feat(deducer): configure local arch for pluto run on Mac (#337)
Browse files Browse the repository at this point in the history
Avoid unnecessary use of Docker for x86 pypi package downloads when executing pluto run on Mac. Previously, target architecture was set to x86 for all environments, leading to Docker usage on Mac. This change sets the target architecture to the local one during pluto run execution.
  • Loading branch information
jianzs authored Aug 29, 2024
1 parent 33bba6b commit 3db055d
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 16 deletions.
8 changes: 8 additions & 0 deletions .changeset/sour-rules-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@plutolang/pyright-deducer": patch
"@plutolang/cli": patch
---

feat(deducer): configure local arch for pluto run on Mac

Avoid unnecessary use of Docker for x86 pypi package downloads when executing pluto run on Mac. Previously, target architecture was set to x86 for all environments, leading to Docker usage on Mac. This change sets the target architecture to the local one during pluto run execution.
4 changes: 4 additions & 0 deletions apps/cli/src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { loadAndDeduce } from "./compile";
import { deployWithAdapter } from "./deploy";
import {
buildAdapterByProvisionType,
getCurrentArch,
getCurrentPlatform,
getDefaultDeducerPkg,
getDefaultEntrypoint,
loadProjectAndStack,
Expand All @@ -27,6 +29,8 @@ export async function run(entrypoint: string, options: RunOptions) {
const projectRoot = loadProjectRoot();
const { project } = loadProjectAndStack(projectRoot);
const stack = new config.Stack("local_run", PlatformType.Simulator, ProvisionType.Simulator);
stack.configs["targetArch"] = getCurrentArch();
stack.configs["targetPlatform"] = getCurrentPlatform();

// Load the environment variables from the `.env` files.
loadDotEnvs(projectRoot, stack.name, false);
Expand Down
17 changes: 17 additions & 0 deletions apps/cli/src/commands/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ import { Architecture } from "@plutolang/base/arch";
import { ExitError } from "../errors";
import { isPlutoProject, loadProject } from "../utils";

/**
* Get the architecture of the current system.
* @returns The architecture of the current system.
*/
export function getCurrentArch() {
const currentArch = process.arch === "x64" ? "x86_64" : process.arch;
return currentArch;
}

/**
* Get the platform of the current system.
* @returns The platform of the current system.
*/
export function getCurrentPlatform() {
return process.platform;
}

/**
* load the default export of the target package.
*/
Expand Down
16 changes: 16 additions & 0 deletions components/deducers/python-pyright/src/common/os-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Get the architecture of the current system.
* @returns The architecture of the current system.
*/
export function getCurrentArch() {
const currentArch = process.arch === "x64" ? "x86_64" : process.arch;
return currentArch;
}

/**
* Get the platform of the current system.
* @returns The platform of the current system.
*/
export function getCurrentPlatform() {
return process.platform;
}
16 changes: 15 additions & 1 deletion components/deducers/python-pyright/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,20 @@ export default class PyrightDeducer extends core.Deducer {
const installPkg = this.stack.configs["bundleWithDependencies"] !== false;
const runtime = await getDefaultPythonRuntime();

const targetArch = this.stack.configs["targetArch"] ?? "x86_64";
if (targetArch !== "x86_64" && this.stack.name !== "local_run") {
throw new Error(
`The non-x86_64 architecture is only supported when the 'pluto run' command is executed locally. The current stack is ${this.stack.name}.`
);
}

const targetPlatform = this.stack.configs["targetPlatform"] ?? "linux";
if (targetPlatform !== "linux" && this.stack.name !== "local_run") {
throw new Error(
`The non-linux platform is only supported iwhen the 'pluto run' command is executed locally. The current stack is ${this.stack.name}.`
);
}

await Promise.all(
closures.map((closure) =>
bundleOne(closure, this.stack.platformType, this.importFinder!, this.bundleFilename)
Expand Down Expand Up @@ -331,7 +345,7 @@ export default class PyrightDeducer extends core.Deducer {
// multiple places, including the Deducer and the infrastructure SDK. The former determines
// the Python version and architecture for bundling dependencies, while the latter sets the
// cloud runtime environment.
await bundleModules(runtime, "x86_64", modules, closure.path, destBaseDir, {
await bundleModules(runtime, targetPlatform, targetArch, modules, closure.path, destBaseDir, {
install: installPkg,
slim: true,
// By default, we'll delete the `dist-info` directory, but LangChain needs it, so we'll just
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as AwsUtils from "./aws-utils";
import * as CmdUtils from "./command-utils";
import * as MetadataUtils from "./metadata";
import { getIndexUrls, IndexUrl } from "./index-url";
import { getCurrentArch, getCurrentPlatform } from "../common/os-utils";
import { Architecture, InstalledModule, Module, ModuleType, Runtime } from "./types";

export interface BundleModulesOptions {
Expand All @@ -23,21 +24,30 @@ export interface BundleModulesOptions {

export async function bundleModules(
runtime: Runtime,
platform: typeof process.platform,
architecture: Architecture,
modules: readonly Module[],
bundleDir: string,
sitePackagesDir: string,
options: BundleModulesOptions = {}
): Promise<void> {
// When running on non-Linux platforms or packaging for cross-architecture, the Docker is
// required. If the user has explicitly disabled Docker, throw an error.
const currentArch = process.arch === "x64" ? "x86_64" : process.arch;
if (process.platform !== "linux" || currentArch !== architecture) {
if (options.dockerPip === false) {
if (getCurrentPlatform() !== platform || getCurrentArch() !== architecture) {
// In this case, the user is trying to bundle the modules for a different platform or
// architecture. We need to check if the Docker can meet the requirement.

if (platform !== "linux") {
throw new Error("Only Linux is supported for cross-platfrom.");
}

if (!Architecture.isSupported(architecture)) {
throw new Error(
"Docker is required to bundle modules on non-Linux platforms, or for cross-architecture."
`The architecture '${architecture}' is not supported for cross-architecture.`
);
}

if (options.dockerPip === false) {
throw new Error("Docker is required to bundle modules for cross-architecture.");
}
options.dockerPip = true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export type Runtime = "python3.12" | "python3.11" | "python3.10" | "python3.9" | "python3.8";
export type Architecture = "x86_64" | "arm64";

export namespace Architecture {
export function isSupported(arch: Architecture): boolean {
return arch === "x86_64" || arch === "arm64";
}
}

export enum ModuleType {
Local = "local",
Installed = "installed",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { bundleModules } from "../../module-bundler/bundle-module";
import * as CommandUtils from "../../module-bundler/command-utils";

describe("bundle with the local modules", () => {
const platform = "linux";

test("should correctly bundle with a local module", async () => {
const { tmpdir, cleanup } = getTmpDir();

await fs.writeFile(`${tmpdir}/module.py`, "def hello():\n return 'Hello, world!'\n");

const runtime = await CommandUtils.getDefaultPythonRuntime();

const architecture = "x86_64";
const modules: Module[] = [LocalModule.create("module", `${tmpdir}/module.py`)];

Expand All @@ -22,7 +25,7 @@ describe("bundle with the local modules", () => {

try {
await expect(
bundleModules(runtime, architecture, modules, bundleDir, sitePackagesDir, options)
bundleModules(runtime, platform, architecture, modules, bundleDir, sitePackagesDir, options)
).resolves.not.toThrow();

const files = await fs.readdir(bundleDir);
Expand Down Expand Up @@ -50,7 +53,7 @@ describe("bundle with the local modules", () => {

try {
await expect(
bundleModules(runtime, architecture, modules, bundleDir, sitePackagesDir, options)
bundleModules(runtime, platform, architecture, modules, bundleDir, sitePackagesDir, options)
).resolves.not.toThrow();

const files = await fs.readdir(bundleDir);
Expand All @@ -62,6 +65,8 @@ describe("bundle with the local modules", () => {
});

describe("bundle with the packages that need to install", () => {
const platform = "linux";

test("should bundle packages and remove useless files", async () => {
const { tmpdir, cleanup } = getTmpDir();

Expand All @@ -76,7 +81,15 @@ describe("bundle with the packages that need to install", () => {
const options = { slim: true };

try {
await bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options);
await bundleModules(
runtime,
platform,
architecture,
modules,
targetFolder,
targetFolder,
options
);

const files = fs.readdirSync(targetFolder);
expect(files).toContain("requirements.txt");
Expand Down Expand Up @@ -110,7 +123,7 @@ describe("bundle with the packages that need to install", () => {

try {
await expect(
bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options)
bundleModules(runtime, platform, architecture, modules, targetFolder, targetFolder, options)
).rejects.toThrow(
"Docker is required to bundle modules on non-Linux platforms, or for cross-architecture."
);
Expand Down Expand Up @@ -153,7 +166,7 @@ describe("bundle with the packages that need to install", () => {

try {
await expect(
bundleModules(runtime, architecture, modules, targetFolder, targetFolder, options)
bundleModules(runtime, platform, architecture, modules, targetFolder, targetFolder, options)
).rejects.toThrow(
`${runtime} is not installed. Please install it first, or use Docker to bundle modules instead.`
);
Expand Down
2 changes: 0 additions & 2 deletions examples/rag-qa-bot-with-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ deployUrl: https://codesandbox.io/p/devbox/pluto-doc-qa-bot-forked-8njh42

Let me introduce you to an incredibly simple way to build a document Q&A bot, which allows you to create a personalized Web Q&A bot based on your GitHub documentation repository in **just 5 minutes**.

First, let's take a look at the result. You can also experience it by opening [this link](https://xw3vdvjmyp7jig7tmrvrqbisiu0peosf.lambda-url.us-east-1.on.aws/).

<p align="center">
<img src="./assets/doc-qa-bot-web-show-en.png" alt="Demo" width="500" />
</p>
Expand Down
2 changes: 0 additions & 2 deletions examples/rag-qa-bot-with-web/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ deployUrl: https://codesandbox.io/p/devbox/pluto-doc-qa-bot-forked-8njh42

这里给大家介绍一种非常简单的构建文档问答机器人的方式,**只需要 5 分钟**就可以基于你的 GitHub 文档仓库创建一个专属的问答机器人,并且将其部署到 AWS 上(免费),让你的用户可以通过 Web 界面来提问。

首先看一下效果,你也可以打开[这个链接](https://xw3vdvjmyp7jig7tmrvrqbisiu0peosf.lambda-url.us-east-1.on.aws/)来体验。

<p align="center">
<img src="./assets/doc-qa-bot-web-show.png" alt="Demo" width="500" />
</p>
Expand Down

0 comments on commit 3db055d

Please sign in to comment.