Skip to content
Open
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
34 changes: 34 additions & 0 deletions src/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { join } from 'node:path';
import process from 'node:process';

import { gte, minVersion } from 'semver';
import which from 'which';

import { fetchManifest, manifestUrl } from '@apify/actor-templates';

Expand Down Expand Up @@ -197,6 +198,39 @@ export class CreateCommand extends ApifyCommand<typeof CreateCommand> {
await cwdProjectResult.inspectAsync(async (project) => {
const minimumSupportedNodeVersion = minVersion(SUPPORTED_NODEJS_VERSION);

// uv-managed Python projects (recognized by a committed `uv.lock`) manage their own virtual
// environment, dependencies, and Python version. Install them with `uv sync` instead of the
// pip + requirements.txt flow. uv provides the Python pinned in `.python-version` on its own,
// so this runs even when no system Python is detected.
const isPythonProject = project.type === ProjectLanguage.Python || project.type === ProjectLanguage.Scrapy;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question: does it make sense to introduce new parameters in project level so we do not need to check for uv.lock? Or make uv part of project.packageManager?

const hasUvLock = await stat(join(actFolderDir, 'uv.lock'))
.then(() => true)
.catch(() => false);
Comment on lines +206 to +208

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: now we run it also for javascript projects, maybe we can move it to isPythonProject code block?


if (isPythonProject && hasUvLock) {
const uvPath = await which('uv', { nothrow: true });

if (!uvPath) {
warning({
message:
'This Actor uses uv to manage its dependencies, but the uv executable was not found. ' +
'Install uv (https://docs.astral.sh/uv/getting-started/installation/), then run "uv sync" in the Actor directory.',
});
return;
Comment on lines +216 to +219

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: we should check for uvPath also in getInstallCommandSuggestion otherwise we recommend pip install after this warning (or after --skip-dependency-install)

something like this maybe 🔽 ?

} else if (project.type === ProjectLanguage.Python || project.type === ProjectLanguage.Scrapy) {
    const hasUvLock = await stat(join(actFolderDir, 'uv.lock')).then(() => true).catch(() => false);
    installCommandSuggestion = hasUvLock ? 'uv sync' : 'python -m pip install -r requirements.txt';
}

}

info({ message: 'Installing dependencies with "uv sync"...' });

await execWithLog({
cmd: uvPath,
args: ['sync'],
opts: { cwd: actFolderDir },
});
Comment on lines +224 to +228

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question: "pip way" now ignores skipOptionalDeps flag, is this the same for uv? does uv have something for optional deps?


dependenciesInstalled = true;
return;
}

if (!project.runtime) {
switch (project.type) {
case ProjectLanguage.JavaScript: {
Expand Down
Loading