Skip to content

Commit a929a53

Browse files
chore: Update texts in apify create flow (#938)
1 parent 3a9820f commit a929a53

File tree

3 files changed

+112
-37
lines changed

3 files changed

+112
-37
lines changed

src/commands/create.ts

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ import {
1616
PYTHON_VENV_PATH,
1717
SUPPORTED_NODEJS_VERSION,
1818
} from '../lib/consts.js';
19-
import { enhanceReadmeWithLocalSuffix, ensureValidActorName, getTemplateDefinition } from '../lib/create-utils.js';
19+
import {
20+
enhanceReadmeWithLocalSuffix,
21+
ensureValidActorName,
22+
formatCreateSuccessMessage,
23+
getTemplateDefinition,
24+
} from '../lib/create-utils.js';
2025
import { execWithLog } from '../lib/exec.js';
2126
import { updateLocalJson } from '../lib/files.js';
2227
import { usePythonRuntime } from '../lib/hooks/runtimes/python.js';
28+
import { getInstallCommandSuggestion } from '../lib/hooks/runtimes/utils.js';
2329
import { ProjectLanguage, useCwdProject } from '../lib/hooks/useCwdProject.js';
2430
import { createPrefilledInputFileFromInputSchema } from '../lib/input_schema.js';
25-
import { error, info, success, warning } from '../lib/outputs.js';
31+
import { error, info, simpleLog, success, warning } from '../lib/outputs.js';
2632
import {
2733
downloadAndUnzip,
2834
getJsonFileContent,
@@ -329,29 +335,28 @@ export class CreateCommand extends ApifyCommand<typeof CreateCommand> {
329335
}
330336
}
331337

332-
if (dependenciesInstalled) {
333-
success({ message: `Actor '${actorName}' was created. To run it, run "cd ${actorName}" and "apify run".` });
334-
info({ message: 'To run your code in the cloud, run "apify push" and deploy your code to Apify Console.' });
335-
if (messages?.postCreate) {
336-
info({ message: messages?.postCreate });
337-
}
338-
} else {
339-
success({
340-
message: `Actor '${actorName}' was created. Please install its dependencies to be able to run it using "apify run".`,
341-
});
342-
}
338+
// Suggest install command if dependencies were not installed
339+
const installCommandSuggestion = !dependenciesInstalled
340+
? await getInstallCommandSuggestion(actFolderDir)
341+
: null;
342+
343+
// Success message with extra empty line
344+
simpleLog({ message: '' });
345+
success({
346+
message: formatCreateSuccessMessage({
347+
actorName,
348+
dependenciesInstalled,
349+
postCreate: messages?.postCreate ?? null,
350+
gitRepositoryInitialized: !skipGitInit && !cwdHasGit && gitInitResult.success,
351+
installCommandSuggestion,
352+
}),
353+
});
343354

344-
// Report git initialization result after actor creation success
345-
if (!skipGitInit && !cwdHasGit) {
346-
if (gitInitResult.success) {
347-
info({
348-
message: `Git repository initialized in '${actorName}'. You can now commit and push your Actor to Git.`,
349-
});
350-
} else {
351-
// Git init is not critical, so we just warn if it fails
352-
warning({ message: `Failed to initialize git repository: ${gitInitResult.error!.message}` });
353-
warning({ message: 'You can manually run "git init" in the Actor directory if needed.' });
354-
}
355+
// Report git initialization result only if it failed (success already included in success message)
356+
if (!skipGitInit && !cwdHasGit && !gitInitResult.success) {
357+
// Git init is not critical, so we just warn if it fails
358+
warning({ message: `Failed to initialize git repository: ${gitInitResult.error!.message}` });
359+
warning({ message: 'You can manually run "git init" in the Actor directory if needed.' });
355360
}
356361
}
357362
}

src/lib/create-utils.ts

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { createWriteStream } from 'node:fs';
22
import { pipeline } from 'node:stream/promises';
33

44
import { Separator } from '@inquirer/core';
5-
import chalk from 'chalk';
65

76
import type { Manifest, Template } from '@apify/actor-templates';
87

@@ -66,6 +65,37 @@ export async function enhanceReadmeWithLocalSuffix(readmePath: string, manifestP
6665
}
6766
}
6867

68+
export function formatCreateSuccessMessage(params: {
69+
actorName: string;
70+
dependenciesInstalled: boolean;
71+
postCreate?: string | null;
72+
gitRepositoryInitialized?: boolean;
73+
installCommandSuggestion?: string | null;
74+
}) {
75+
const { actorName, dependenciesInstalled, postCreate, gitRepositoryInitialized, installCommandSuggestion } = params;
76+
77+
let message = `✅ Actor '${actorName}' created successfully!`;
78+
79+
if (dependenciesInstalled) {
80+
message += `\n\nNext steps:\n\ncd '${actorName}'\napify run`;
81+
} else {
82+
const installLine = installCommandSuggestion || 'install dependencies with your package manager';
83+
message += `\n\nNext steps:\n\ncd '${actorName}'\n${installLine}\napify run`;
84+
}
85+
86+
message += `\n\n💡 Tip: Use 'apify push' to deploy your Actor to the Apify platform\n📖 Docs: https://docs.apify.com/platform/actors/development`;
87+
88+
if (gitRepositoryInitialized) {
89+
message += `\n🌱 Git repository initialized in '${actorName}'. You can now commit and push your Actor to Git.`;
90+
}
91+
92+
if (postCreate) {
93+
message += `\n\n${postCreate}`;
94+
}
95+
96+
return message;
97+
}
98+
6999
/**
70100
* Inquirer does not have a native way to "go back" between prompts.
71101
*/
@@ -75,7 +105,7 @@ async function executePrompts(manifest: Manifest) {
75105
while (true) {
76106
const templateDefinition = await promptTemplateDefinition(manifest, programmingLanguage);
77107
if (templateDefinition) {
78-
const shouldInstall = await promptTemplateInstallation(templateDefinition);
108+
const shouldInstall = await promptTemplateInstallation();
79109
if (shouldInstall) {
80110
return templateDefinition;
81111
}
@@ -134,8 +164,7 @@ async function promptTemplateDefinition(manifest: Manifest, programmingLanguage:
134164
];
135165

136166
const templateDefinition = await useSelectFromList({
137-
message:
138-
'Choose a template for your new Actor. Detailed information about the template will be shown in the next step.',
167+
message: 'Choose a template for your new Actor. You can check more information at https://apify.com/templates.',
139168
default: choices[0],
140169
choices,
141170
loop: false,
@@ -145,20 +174,15 @@ async function promptTemplateDefinition(manifest: Manifest, programmingLanguage:
145174
return templateDefinition;
146175
}
147176

148-
async function promptTemplateInstallation(templateDefinition: Template) {
177+
async function promptTemplateInstallation() {
149178
const choices: ChoicesType<boolean> = [
150-
{ name: `Install template`, value: true },
179+
{ name: `Install dependencies`, value: true },
151180
new Separator(),
152181
{ name: 'Go back', value: false },
153182
];
154183

155-
const label = chalk.underline(templateDefinition.label);
156-
const description = chalk.dim(templateDefinition.description);
157-
const suffix = `\n ${label}:\n ${description}`;
158-
const message = `Do you want to install the following template?${suffix}`;
159-
160184
const answer = await useSelectFromList<boolean>({
161-
message,
185+
message: 'Almost done! Last step is to install dependencies.',
162186
default: choices[0],
163187
choices,
164188
loop: false,

src/lib/hooks/runtimes/utils.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { isAbsolute } from 'node:path';
1+
import { stat } from 'node:fs/promises';
2+
import { isAbsolute, join } from 'node:path';
3+
4+
import { ProjectLanguage, useCwdProject } from '../useCwdProject.js';
25

36
export function normalizeExecutablePath(path: string): string;
47
export function normalizeExecutablePath(path: string | null): string | null;
@@ -19,3 +22,46 @@ export function normalizeExecutablePath(path: string | null) {
1922

2023
return path;
2124
}
25+
26+
export async function getInstallCommandSuggestion(actFolderDir: string) {
27+
let installCommandSuggestion: string | null = null;
28+
29+
const projectInfo = await useCwdProject({ cwd: actFolderDir });
30+
await projectInfo.inspectAsync(async (project) => {
31+
if (project.type === ProjectLanguage.JavaScript) {
32+
const [hasYarnLock, hasPnpmLock, hasBunLockb, hasBunLock] = await Promise.all([
33+
stat(join(actFolderDir, 'yarn.lock'))
34+
.then(() => true)
35+
.catch(() => false),
36+
stat(join(actFolderDir, 'pnpm-lock.yaml'))
37+
.then(() => true)
38+
.catch(() => false),
39+
stat(join(actFolderDir, 'bun.lockb'))
40+
.then(() => true)
41+
.catch(() => false),
42+
stat(join(actFolderDir, 'bun.lock'))
43+
.then(() => true)
44+
.catch(() => false),
45+
]);
46+
47+
const hasAnyBunLock = hasBunLockb || hasBunLock;
48+
if (hasYarnLock) {
49+
installCommandSuggestion = 'yarn install';
50+
} else if (hasPnpmLock) {
51+
installCommandSuggestion = 'pnpm install';
52+
} else if (hasAnyBunLock) {
53+
installCommandSuggestion = 'bun install';
54+
} else if (project.runtime?.pmName === 'bun') {
55+
installCommandSuggestion = 'bun install';
56+
} else if (project.runtime?.pmName === 'deno') {
57+
installCommandSuggestion = 'deno install --node-modules-dir';
58+
} else {
59+
installCommandSuggestion = 'npm install';
60+
}
61+
} else if (project.type === ProjectLanguage.Python || project.type === ProjectLanguage.Scrapy) {
62+
installCommandSuggestion = 'python -m pip install -r requirements.txt';
63+
}
64+
});
65+
66+
return installCommandSuggestion;
67+
}

0 commit comments

Comments
 (0)