Skip to content

Commit 843360e

Browse files
docs: rewrite README, add CLAUDE.md, refresh CONTRIBUTING (#1079)
## Summary - Rewrites `README.md` around a scannable structure (one-line description, features, 3-step quick start, per-platform install, commands table, docs links, telemetry, contributing, support, license) aimed at both users and AI agents. Moves the `.actor/actor.json` deep dive and env-vars reference out to the platform docs. - Adds a root `CLAUDE.md` with project overview, key directories, common commands, a **pre-push checklist** (`yarn lint` → `yarn format` → `yarn build` → `yarn test:local`), test hook patterns, and release flow. - Refreshes `CONTRIBUTING.md`: adds prerequisites, local setup, code style tooling (Biome / ESLint / Prettier), test categories table, PR guidelines, and rewrites the Publish section to match the actual automated release flow (no more manual `package.json` bump). Closes #1067
1 parent 5555faf commit 843360e

3 files changed

Lines changed: 216 additions & 306 deletions

File tree

CLAUDE.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# CLAUDE.md
2+
3+
Guidance for Claude Code working in this repository. Humans should read [README.md](./README.md) and [CONTRIBUTING.md](./CONTRIBUTING.md) first.
4+
5+
## Entry points
6+
7+
- `apify` CLI — `src/entrypoints/apify.ts`
8+
- `actor` CLI — `src/entrypoints/actor.ts`
9+
10+
## Before you push
11+
12+
Run `yarn lint && yarn format && yarn build && yarn test:local` before pushing. Run `yarn test:api` too if you changed anything that touches the Apify API.
13+
14+
If you modified a command's flags, args, description, or added/removed a command, also run `yarn update-docs` and commit the regenerated `docs/` output.
15+
16+
## Code conventions
17+
18+
- Package manager: **Yarn 4** (via Corepack). Do not use npm.
19+
- Use `.js` import specifiers for local files (e.g. `import { foo } from './foo.js'`). The `.ts` source resolves at build time.
20+
- Commands extend `ApifyCommand` from `src/lib/command-framework/apify-command.ts`. Follow the pattern of existing commands: `static override name`, `static override description`, `static override flags/args`, and an `async run()` method.
21+
- New commands must be registered in `src/commands/_register.ts` (or the parent `_index.ts` for subcommands).
22+
- Do not add docstrings, comments, or type annotations to code you did not change. Keep diffs tight.
23+
24+
## Testing
25+
26+
- Tests use **Vitest**. See [CONTRIBUTING.md](./CONTRIBUTING.md#writing-tests) for `useAuthSetup` and `useTempPath` hook usage.
27+
- API tests must include `[api]` in the test name and live in `test/api/`.
28+
- Always `import process from 'node:process'` in command/lib code — never use `globalThis.process`. This is required for test cwd mocks to work.
29+
30+
## Things to avoid
31+
32+
- Do not use `--no-verify` to skip git hooks. Fix the underlying issue.
33+
- Do not edit `docs/` by hand — it is generated by `yarn update-docs`.

CONTRIBUTING.md

Lines changed: 109 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,168 @@
1-
# Contributing to apify-cli
1+
# Contributing to Apify CLI
22

3-
## Dev mode
3+
Thanks for your interest in contributing! This guide covers local setup, code style, tests, and the PR process. For a high-level tour of the repo aimed at AI coding assistants, see [CLAUDE.md](./CLAUDE.md).
44

5-
You can run `yarn dev:apify` to run the CLI in development mode. This will use the local version of the CLI instead of the one installed globally.
5+
## Prerequisites
66

7-
## Tests
7+
- **Node.js** 22 or higher
8+
- **Yarn 4** (enabled via Corepack — do not install Yarn globally with npm)
9+
- **Bun** ≥ 1.2.5 (optional; only needed if you want to build the standalone bundles locally)
10+
- An [Apify account](https://console.apify.com/) and an API token if you want to run the API test suite
811

9-
Tests are implemented using the [Vitest](https://vitest.dev/) framework.
10-
You need to have Apify account to test all apify-cli features.
12+
Enable Corepack once per machine:
1113

12-
Then you can run tests with commands in repository root directory:
14+
```bash
15+
corepack enable
16+
```
1317

14-
1. Install all dependencies:
15-
`yarn`
18+
## Local setup
1619

17-
2. Run tests using credentials of the 'apify-test' user:
18-
`TEST_USER_TOKEN=<apifyUserApiToken> yarn test:all`
20+
```bash
21+
git clone https://github.com/apify/apify-cli.git
22+
cd apify-cli
23+
yarn
24+
yarn build
25+
```
1926

20-
## Publish new version
27+
Run the CLI straight from source in dev mode (no global install required):
2128

22-
Only users with access to [apify-cli package](https://www.npmjs.com/package/apify-cli) can publish new version.
29+
```bash
30+
yarn dev:apify --version
31+
yarn dev:apify create my-actor
32+
```
2333

24-
Release of new versions is managed by GitHub Actions. On pushes to the master branch, prerelease versions are automatically produced. Latest releases are triggered manually through the GitHub release tool. After creating a release there, Actions will automatically produce the latest version of the package.
34+
`yarn dev:actor` does the same for the in-Actor `actor` binary.
2535

26-
1. Manually increment version in `package.json`
36+
## Code style
2737

28-
2. GitHub Actions build is triggered by a push to `master` (typically a merge of a PR).
38+
The repo uses three tools, wired together via a pre-commit hook (`husky` + `lint-staged`):
2939

30-
3. To trigger the latest release, go to the GitHub release tool (select `releases` under `<> Code`). There, draft a new release, fill the form and hit `Publish release`. Actions will automatically release the latest version of the package.
40+
- **Biome** — formats JavaScript / TypeScript. Config: `biome.json`.
41+
- **ESLint** — lints JavaScript / TypeScript using `@apify/eslint-config`. Config: `eslint.config.mjs`.
42+
- **Prettier** — formats Markdown and YAML. Config: inherited defaults.
3143

32-
## Writing tests
44+
Run checks manually:
3345

34-
In `test/__setup__/hooks` we have a collection of hooks that you can use while writing tests to set up the testing environment to be usable when running tests in parallel (especially useful for tests that require authenticating into an Apify account).
46+
```bash
47+
yarn lint # ESLint
48+
yarn format # Biome + Prettier (check only)
49+
yarn lint:fix # Auto-fix ESLint issues
50+
yarn format:fix # Auto-format with Biome + Prettier
51+
```
3552

36-
### `useAuthSetup`
53+
The pre-commit hook runs these on staged files automatically. Do not bypass it with `--no-verify` — if a hook fails, fix the underlying issue and create a new commit.
3754

38-
Use this hook at the start of your test file to mark the entire suite as require-ing a separated authentication setup. By default, this will recreate the authentication setup per test in your suite, but you can disable that by passing in `{ perTest: false }` in the call to `useAuthSetup`.`
55+
## Before opening a PR
3956

40-
If you're writing a test case or file that relies on API interactions, make sure it either goes in the `test/api` folder, and that it has a `[api]` reference in the name of the test case (usually at the start).
41-
If your test file also mixes local tests, always add `[api]` for test cases that need the API. They will automatically be skipped for local tests.
57+
Run the full local gauntlet before pushing:
4258

43-
#### Example usage
59+
1. `yarn lint`
60+
2. `yarn format`
61+
3. `yarn build`
62+
4. `yarn test:local` (and `yarn test:api` if relevant — see below)
4463

45-
```typescript
46-
import { useAuthSetup } from "./__setup__/hooks";
64+
If you added, removed, or changed a command's signature, regenerate the reference docs:
4765

48-
useAuthSetup();
49-
// Alternatively, if this suite requires the authentication to persist across all tests
50-
useAuthSetup({ perTest: false });
66+
```bash
67+
yarn update-docs
5168
```
5269

53-
### `useTempPath`
70+
Commit the updated `docs/` output alongside your change.
5471

55-
This hook should always be used when working with commands that alter the file system. This hook:
72+
## Tests
5673

57-
- creates your temporary directory with the name you provided
58-
- provides calls for before and after all tests to setup and clean up the temporary directory
59-
- supports mocking the process cwd to the temporary directory so you can run commands and test their behavior.
74+
Tests use [Vitest](https://vitest.dev/). They fall into four categories:
6075

61-
**Important note about the cwd mocking**: when you use this hook and tell it to mock the cwd, you need to ensure these following things **always** happen:
76+
| Script | What it runs | When to run |
77+
| -------------------- | ------------------------------------------------------------------------ | -------------------------------------------------- |
78+
| `yarn test:local` | Everything except tests named `[api]` and everything outside `test/api/` | Always, on every change |
79+
| `yarn test:api` | Only tests tagged `[api]` — hits the real Apify API | When you touch API-facing code |
80+
| `yarn test:python` | Tests tagged `[python]` — exercises Python Actor templates | When you touch Python template / integration code |
81+
| `yarn test:cucumber` | Cucumber features in `features/` | When you touch anything covered by a `.feature.md` |
82+
| `yarn test:all` | `test:local` then `test:api` | Full pre-release check |
6283

63-
- You import `process` from `node:process` in your command or file you want to test with the mocked cwd. You do not use `globalThis.process` at all!
64-
- You import the files that may rely on the mocked cwd AFTER you call `useTempPath` in your test file, by using `await import()` instead of `import x from '..';`
84+
API tests need a token:
6585

66-
It also comes with several options:
86+
```bash
87+
TEST_USER_TOKEN=<your-apify-token> yarn test:api
88+
```
6789

68-
- `create`: defaulted to `true`, it decides if the temporary directory should be created or not in the beforeAll hook.
69-
- `remove`: defaulted to `true`, it decides if the temporary directory should be removed or not in the afterAll hook.
70-
- `cwd`: defaulted to `false`, it decides if the process.cwd should be mocked to the temporary directory or not.
71-
- `cwdParent`: defaulted to `false`, it decides whether the initial value of the mocked process.cwd will point to the parent directory of the temporary directory or the actual temporary directory.
90+
Use a dedicated test account — API tests create and destroy Actors, datasets, and other resources.
7291

73-
This hook also returns several values in an object:
92+
## Writing tests
7493

75-
- `tmpPath`: the full path to the temporary directory that was requested
76-
- `joinPath`: a utility function similar to `path.join` that lets you work with paths in the temporary directory
77-
- `beforeAllCalls`: a function you should manually call in your `beforeAll` hook to set up the temporary directory as well as the used cwd mock
78-
- `afterAllCalls`: a function you should manually call in your `afterAll` hook to clean up the temporary directory
79-
- `toggleCwdBetweenFullAndParentPath`: a function you can call to toggle the cwd mock between the full path to the temporary directory and the parent directory of the temporary directory
94+
Shared helpers live in `test/__setup__/hooks/`. Two of them show up in most new tests:
8095

81-
#### Example usage (creates an actor, then pushes it, then calls it)
96+
### `useAuthSetup`
8297

83-
> This example assumes you've also handled logging in.
98+
Isolates Apify authentication state per suite (or per test). Use it in any file that logs in, pushes, pulls, or otherwise touches `~/.apify`. By default it recreates the auth setup per test; pass `{ perTest: false }` to share one setup across the suite.
8499

85100
```typescript
86-
import { useTempPath } from "./__setup__/hooks";
87-
import { writeFile } from "node:fs/promises";
101+
import { useAuthSetup } from "./__setup__/hooks";
88102

89-
const ACTOR_NAME = "owo";
103+
useAuthSetup();
104+
// or: useAuthSetup({ perTest: false });
105+
```
106+
107+
API-dependent test cases must have `[api]` in the test name and live in `test/api/`. Files outside `test/api/` may mix local and `[api]` tests — the `test:local` script skips the `[api]` ones by name.
108+
109+
### `useTempPath`
110+
111+
Creates (and cleans up) a temporary directory, and optionally mocks `process.cwd()` so commands run as if executed there.
112+
113+
```typescript
114+
import { useTempPath } from "./__setup__/hooks";
90115

91116
const {
92117
beforeAllCalls,
93118
afterAllCalls,
94119
joinPath,
95120
toggleCwdBetweenFullAndParentPath,
96-
} = useTempPath(ACTOR_NAME, {
121+
} = useTempPath("my-actor", {
97122
cwd: true,
98123
cwdParent: true,
99124
create: true,
100125
remove: true,
101126
});
102127

103128
const { CreateCommand } = await import("../../src/commands/create.js");
104-
const { PushCommand } = await import("../../src/commands/push.js");
105-
const { CallCommand } = await import("../../src/commands/call.js");
129+
```
106130

107-
beforeAll(async () => {
108-
await beforeAllCalls();
131+
Options:
109132

110-
await CreateCommand.run(
111-
[ACTOR_NAME, "--template", "project_empty", "--skip-dependency-install"],
112-
import.meta.url,
113-
);
133+
- `create` (default `true`) — create the temp directory in `beforeAll`.
134+
- `remove` (default `true`) — remove it in `afterAll`.
135+
- `cwd` (default `false`) — mock `process.cwd()` to point at the temp directory.
136+
- `cwdParent` (default `false`) — start the mocked cwd at the parent of the temp directory.
114137

115-
const code = `
116-
import { Actor } from 'apify';
138+
Returned helpers:
117139

118-
Actor.main(async () => {
119-
await Actor.setValue('OUTPUT', 'Hello world!');
120-
console.log('Done!');
121-
});`;
140+
- `tmpPath` — absolute path of the temp directory.
141+
- `joinPath(...segments)``path.join` rooted at `tmpPath`.
142+
- `beforeAllCalls`, `afterAllCalls` — call these from your `beforeAll` / `afterAll`.
143+
- `toggleCwdBetweenFullAndParentPath()` — flip the mocked cwd between the temp dir and its parent.
122144

123-
await writeFile(joinPath("main.js"), code);
145+
**Important when using `cwd: true`:** in the code you are testing, always `import process from 'node:process'` (never `globalThis.process`), and dynamically `await import(...)` anything that reads cwd **after** `useTempPath` runs.
124146

125-
toggleCwdBetweenFullAndParentPath();
147+
### Running individual commands
126148

127-
await PushCommand.run(["--no-prompt"], import.meta.url);
128-
});
149+
Use `testRunCommand` from the command framework to invoke CLI commands in tests — it bypasses the entry-point wrapper and gives you the parsed context directly.
129150

130-
afterAll(async () => {
131-
await afterAllCalls();
132-
});
133-
```
151+
## Pull requests
152+
153+
- Branch from `master`. Name branches descriptively (e.g. `fix/push-handles-empty-dir`, `feat/actors-search`).
154+
- Write [Conventional Commit](https://www.conventionalcommits.org/) messages: `feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`. The changelog generator (`git-cliff`) groups entries by prefix, so this matters.
155+
- Keep PRs focused. Unrelated cleanup is easier to review in a separate PR.
156+
- Include tests when you fix a bug or add behavior.
157+
- Update `docs/` via `yarn update-docs` if you changed any command.
158+
159+
Open PRs against `master`. Code owners are configured in [`.github/CODEOWNERS`](./.github/CODEOWNERS) and will be requested automatically.
160+
161+
## Releases
162+
163+
Releases are fully automated via GitHub Actions — **do not bump the version in `package.json` manually**.
134164

135-
### Running commands
165+
- **Pre-releases (beta):** every push to `master` triggers `.github/workflows/pre_release.yaml`, which computes the next beta version, updates `CHANGELOG.md`, and publishes to npm under the `beta` tag.
166+
- **Stable releases:** trigger the **Create a release** workflow (`.github/workflows/release.yaml`) manually from the GitHub Actions UI. It computes the next version (auto / patch / minor / major / custom), updates `CHANGELOG.md`, builds standalone bundles for Linux / macOS / Windows (x64 + ARM64), creates a GitHub release with the bundles attached, publishes to npm under `latest`, and opens a PR against the Homebrew formula.
136167

137-
Running commands in tests can be done by calling `testRunCommand` (imported from the command framework)
168+
Only users with publish access to the [`apify-cli` npm package](https://www.npmjs.com/package/apify-cli) can trigger the stable release workflow.

0 commit comments

Comments
 (0)