diff --git a/src/commands/init.ts b/src/commands/init.ts index 87f02ceb0..23e99cadd 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -1,4 +1,5 @@ -import { basename } from 'node:path'; +import { existsSync } from 'node:fs'; +import { basename, join } from 'node:path'; import process from 'node:process'; import { ApifyCommand } from '../lib/command-framework/apify-command.js'; @@ -163,6 +164,20 @@ export class InitCommand extends ApifyCommand { await setLocalConfig(configToWrite, cwd); } + const isPythonWithoutDockerfile = + project.type === ProjectLanguage.Python && + !this.flags.dockerfile && + !actorConfig.unwrapOr(undefined)?.config.dockerfile && + !existsSync(join(cwd, '.actor', 'Dockerfile')) && + !existsSync(join(cwd, 'Dockerfile')); + + if (isPythonWithoutDockerfile) { + warning({ + message: + 'No Dockerfile found. Without one, the Apify platform builds the Actor with a Node.js base image. Add one at .actor/Dockerfile (e.g. FROM apify/actor-python) or pass --dockerfile.', + }); + } + await setLocalEnv(cwd); // Create prefilled INPUT.json file from the input schema prefills diff --git a/test/local/commands/init.test.ts b/test/local/commands/init.test.ts index 782ef874e..cac0d7e48 100644 --- a/test/local/commands/init.test.ts +++ b/test/local/commands/init.test.ts @@ -6,7 +6,9 @@ import { KEY_VALUE_STORE_KEYS } from '@apify/consts'; import { testRunCommand } from '../../../src/lib/command-framework/apify-command.js'; import { EMPTY_LOCAL_CONFIG, LOCAL_CONFIG_PATH } from '../../../src/lib/consts.js'; import { getLocalKeyValueStorePath } from '../../../src/lib/utils.js'; +import { useConsoleSpy } from '../../__setup__/hooks/useConsoleSpy.js'; import { useTempPath } from '../../__setup__/hooks/useTempPath.js'; +import { resetCwdCaches } from '../../__setup__/reset-cwd-caches.js'; const actName = 'init-my-actor'; const { beforeAllCalls, afterAllCalls, joinPath } = useTempPath(actName, { @@ -16,11 +18,14 @@ const { beforeAllCalls, afterAllCalls, joinPath } = useTempPath(actName, { cwdParent: false, }); +const { logMessages } = useConsoleSpy(); + const { InitCommand } = await import('../../../src/commands/init.js'); describe('apify init', () => { beforeEach(async () => { await beforeAllCalls(); + resetCwdCaches(); }); afterEach(async () => { @@ -120,4 +125,25 @@ describe('apify init', () => { expect(config).toStrictEqual(Object.assign(EMPTY_LOCAL_CONFIG, { name: actName })); expect(config.dockerfile).toBeUndefined(); }); + + it('warns when a Python project has no Dockerfile', async () => { + await mkdir(joinPath('my_actor'), { recursive: true }); + writeFileSync(joinPath('my_actor', '__init__.py'), ''); + writeFileSync(joinPath('my_actor', '__main__.py'), ''); + + await testRunCommand(InitCommand, { args_actorName: actName, flags_yes: true }); + + expect(logMessages.error.join('\n')).toMatch(/No Dockerfile found/); + }); + + it('does not warn when a Python project has a Dockerfile', async () => { + await mkdir(joinPath('my_actor'), { recursive: true }); + writeFileSync(joinPath('my_actor', '__init__.py'), ''); + writeFileSync(joinPath('my_actor', '__main__.py'), ''); + writeFileSync(joinPath('Dockerfile'), 'FROM apify/actor-python'); + + await testRunCommand(InitCommand, { args_actorName: actName, flags_yes: true }); + + expect(logMessages.error.join('\n')).not.toMatch(/No Dockerfile found/); + }); });