apify-test-tools 0.9.1-beta.8 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/checkout-restore-dependencies/action.yaml +3 -29
- package/.github/scripts/check-major-tag-refs.mjs +1 -1
- package/.github/workflows/_move_major_tag.yaml +11 -3
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +2 -2
- package/.github/workflows/public_platform-tests.yaml +1 -13
- package/.github/workflows/public_pr-build-test.yaml +4 -26
- package/.github/workflows/public_push-build-latest.yaml +4 -15
- package/.github/workflows/public_review.yaml +1 -8
- package/.github/workflows-package-version +1 -1
- package/CHANGELOG.md +8 -3
- package/CONTRIBUTING.md +11 -7
- package/README.md +169 -56
- package/bin/build-from-local.ts +15 -11
- package/bin/build.ts +127 -67
- package/bin/git.ts +140 -0
- package/bin/main.ts +128 -91
- package/bin/slack.ts +5 -2
- package/bin/types.ts +0 -73
- package/bin/utils/config/parser.ts +6 -5
- package/bin/utils/config/structures/base.ts +5 -1
- package/bin/utils/config/structures/grouped.ts +40 -0
- package/bin/utils/config/structures/legacy.ts +3 -1
- package/bin/utils.ts +7 -15
- package/dist/bin/build-from-local.d.ts +2 -2
- package/dist/bin/build-from-local.d.ts.map +1 -1
- package/dist/bin/build-from-local.js +11 -6
- package/dist/bin/build-from-local.js.map +1 -1
- package/dist/bin/build.d.ts +26 -4
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +90 -47
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/git.d.ts +40 -0
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +118 -0
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.d.ts +1 -20
- package/dist/bin/main.d.ts.map +1 -1
- package/dist/bin/main.js +65 -62
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +4 -2
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/types.d.ts +0 -64
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/bin/utils/config/parser.d.ts.map +1 -1
- package/dist/bin/utils/config/parser.js +6 -5
- package/dist/bin/utils/config/parser.js.map +1 -1
- package/dist/bin/utils/config/structures/base.d.ts +3 -2
- package/dist/bin/utils/config/structures/base.d.ts.map +1 -1
- package/dist/bin/utils/config/structures/base.js +1 -0
- package/dist/bin/utils/config/structures/base.js.map +1 -1
- package/dist/bin/utils/config/structures/grouped.d.ts +6 -0
- package/dist/bin/utils/config/structures/grouped.d.ts.map +1 -0
- package/dist/bin/utils/config/structures/grouped.js +29 -0
- package/dist/bin/utils/config/structures/grouped.js.map +1 -0
- package/dist/bin/utils/config/structures/legacy.d.ts +1 -1
- package/dist/bin/utils/config/structures/legacy.d.ts.map +1 -1
- package/dist/bin/utils/config/structures/legacy.js +1 -1
- package/dist/bin/utils/config/structures/legacy.js.map +1 -1
- package/dist/bin/utils.d.ts +5 -2
- package/dist/bin/utils.d.ts.map +1 -1
- package/dist/bin/utils.js +6 -12
- package/dist/bin/utils.js.map +1 -1
- package/dist/test/unit/bin/build.test.d.ts +2 -0
- package/dist/test/unit/bin/build.test.d.ts.map +1 -0
- package/dist/test/unit/bin/build.test.js +46 -0
- package/dist/test/unit/bin/build.test.js.map +1 -0
- package/dist/test/unit/bin/git.test.js +104 -1
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/utils/config/load-config.test.js +1 -0
- package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
- package/dist/test/unit/bin/utils/config/parser.test.js +23 -5
- package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
- package/dist/test/unit/bin/utils/config/structures/grouped.test.d.ts +2 -0
- package/dist/test/unit/bin/utils/config/structures/grouped.test.d.ts.map +1 -0
- package/dist/test/unit/bin/utils/config/structures/grouped.test.js +75 -0
- package/dist/test/unit/bin/utils/config/structures/grouped.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +65 -63
- package/test/unit/bin/build.test.ts +68 -0
- package/test/unit/bin/git.test.ts +122 -0
- package/test/unit/bin/utils/config/load-config.test.ts +1 -0
- package/test/unit/bin/utils/config/parser.test.ts +26 -5
- package/test/unit/bin/utils/config/structures/grouped.test.ts +90 -0
- package/bin/github.ts +0 -69
- package/dist/bin/github.d.ts +0 -11
- package/dist/bin/github.d.ts.map +0 -1
- package/dist/bin/github.js +0 -59
- package/dist/bin/github.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,37 +12,15 @@ npm i -D apify-test-tools
|
|
|
12
12
|
|
|
13
13
|
- Requires `vitest` version `3.2.0` or later (uses [annotate](https://vitest.dev/guide/test-context.html#annotate))
|
|
14
14
|
- Make sure `target` and `module` in your `tsconfig.json`'s `compilerOptions` are set to `ES2022`
|
|
15
|
+
- Every Actor must already have at least one build under its default build tag (usually `latest`), built
|
|
16
|
+
from a Git repository version whose URL points to this repository. Build it once manually on the
|
|
17
|
+
platform before CI takes over. See [Repository check](#repository-check).
|
|
15
18
|
|
|
16
19
|
### 2. Create the config file
|
|
17
20
|
|
|
18
|
-
Every repo that uses `apify-test-tools` must have an `apify-test-tools.config.json` file at the root.
|
|
21
|
+
Every repo that uses `apify-test-tools` must have an `apify-test-tools.config.json` file at the root. It lists the actors in the repo, their folders, their full names on the platform, and which env var holds the token to use for each. A top-level `mode` field picks the file's layout; leave it out to use the default `"legacy"` layout.
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
"actors": [
|
|
23
|
-
{
|
|
24
|
-
"folder": "actors/web-scraper",
|
|
25
|
-
"actorFullName": "myteam/web-scraper",
|
|
26
|
-
"tokenEnvVar": "APIFY_TOKEN_MYTEAM"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"folder": "actors/email-sender",
|
|
30
|
-
"actorFullName": "myteam/email-sender",
|
|
31
|
-
"tokenEnvVar": "APIFY_TOKEN_MYTEAM",
|
|
32
|
-
"overrideActorContext": ["actors/email-sender", "packages/shared"]
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Each entry has:
|
|
39
|
-
|
|
40
|
-
| Field | Required | Description |
|
|
41
|
-
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
42
|
-
| `folder` | yes | Relative path from repo root to the actor's own project directory — the folder that directly contains `.actor/actor.json` (i.e. `<folder>/.actor/actor.json`), the actor's README/CHANGELOG, and its source. Use `"."` for a single-actor repo where `.actor/` is at the root. |
|
|
43
|
-
| `actorFullName` | yes | Full actor identifier in `owner/name` format (e.g. `"apify/web-scraper"`). This is the source of truth for the actor name — the `name` field in `actor.json` is not used. |
|
|
44
|
-
| `tokenEnvVar` | yes | Name of the environment variable holding the Apify API token for this actor. No fallback — if the env var is not set at build time, the build fails. |
|
|
45
|
-
| `overrideActorContext` | no | Array of paths (relative to repo root) that define which files are relevant to this actor. When set, replaces the `dockerContextDir` from `actor.json` for change detection. Useful when an actor depends on shared packages outside its Docker build context. Entries must not be prefixes of one another (e.g. `["", "code"]` or `["actors", "actors/foo"]` are rejected). The actor's own `folder` is always part of its context — if none of the listed entries reach it, it's added automatically. |
|
|
23
|
+
See [Config file](#config-file) for the available modes and every field.
|
|
46
24
|
|
|
47
25
|
### 3. Set up actor folders
|
|
48
26
|
|
|
@@ -97,11 +75,87 @@ mkdir -p test/platform/core
|
|
|
97
75
|
|
|
98
76
|
See the [GitHub workflows](#github-worklows) section below.
|
|
99
77
|
|
|
78
|
+
## Config file
|
|
79
|
+
|
|
80
|
+
`apify-test-tools.config.json` lives at the repo root and tells the tool which actors live in the repo, how to identify them, and which token to use.
|
|
81
|
+
|
|
82
|
+
### Actor fields
|
|
83
|
+
|
|
84
|
+
Every actor in the config is described by these fields, whatever the mode:
|
|
85
|
+
|
|
86
|
+
| Field | Required | Description |
|
|
87
|
+
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
88
|
+
| `folder` | yes | Relative path from repo root to the actor's own project directory — the folder that directly contains `.actor/actor.json` (i.e. `<folder>/.actor/actor.json`), the actor's README/CHANGELOG, and its source. Use `"."` for a single-actor repo where `.actor/` is at the root. |
|
|
89
|
+
| `actorFullName` | yes | Full actor identifier in `owner/name` format (e.g. `"apify/web-scraper"`). This is the source of truth for the actor name — the `name` field in `actor.json` is not used. |
|
|
90
|
+
| `tokenEnvVar` | yes | Name of the environment variable holding the Apify API token for this actor. In [grouped mode](#grouped-mode) it's inherited from the group unless the actor sets its own. No fallback — if the env var is not set at build time, the build fails. |
|
|
91
|
+
| `overrideActorContext` | no | Array of paths (relative to repo root) that define which files are relevant to this actor. When set, replaces the `dockerContextDir` from `actor.json` for change detection. Useful when an actor depends on shared packages outside its Docker build context. Entries must not be prefixes of one another (e.g. `["", "code"]` or `["actors", "actors/foo"]` are rejected). The actor's own `folder` is always part of its context — if none of the listed entries reach it, it's added automatically. |
|
|
92
|
+
|
|
93
|
+
### Modes
|
|
94
|
+
|
|
95
|
+
The file's layout is picked by the top-level `mode` field. Each mode is a different way of writing the same thing: every mode resolves to a list of actors with the fields above, and the same rules apply to all of them — no two actors may share a `folder`, and no two entries may point at the same `actorFullName`. If `mode` is left out, [`"legacy"`](#legacy-mode) is used, so existing config files keep working unchanged.
|
|
96
|
+
|
|
97
|
+
### Legacy mode
|
|
98
|
+
|
|
99
|
+
The default. A flat `actors` array where every entry carries all of its settings. Good for repos with a few actors, or where every actor has different settings.
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mode": "legacy",
|
|
104
|
+
"actors": [
|
|
105
|
+
{
|
|
106
|
+
"folder": "actors/web-scraper",
|
|
107
|
+
"actorFullName": "myteam/web-scraper",
|
|
108
|
+
"tokenEnvVar": "APIFY_TOKEN_MYTEAM"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"folder": "actors/email-sender",
|
|
112
|
+
"actorFullName": "myteam/email-sender",
|
|
113
|
+
"tokenEnvVar": "APIFY_TOKEN_MYTEAM",
|
|
114
|
+
"overrideActorContext": ["actors/email-sender", "packages/shared"]
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`actors` must hold at least one entry. Every entry takes all the [actor fields](#actor-fields) directly, and `tokenEnvVar` is required on each one.
|
|
121
|
+
|
|
122
|
+
### Grouped mode
|
|
123
|
+
|
|
124
|
+
Actors are organized into `groups`, and each group sets `tokenEnvVar` and `overrideActorContext` once for all of its actors. Good for repos where many actors share the same token or context.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"mode": "grouped",
|
|
129
|
+
"groups": {
|
|
130
|
+
"myteam": {
|
|
131
|
+
"tokenEnvVar": "APIFY_TOKEN_MYTEAM",
|
|
132
|
+
"overrideActorContext": ["packages/shared"],
|
|
133
|
+
"actors": [
|
|
134
|
+
{ "folder": "actors/web-scraper", "actorFullName": "myteam/web-scraper" },
|
|
135
|
+
{
|
|
136
|
+
"folder": "actors/email-sender",
|
|
137
|
+
"actorFullName": "myteam/email-sender",
|
|
138
|
+
"overrideActorContext": ["actors/email-sender", "packages/mailer"]
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"partner": {
|
|
143
|
+
"tokenEnvVar": "APIFY_TOKEN_PARTNER",
|
|
144
|
+
"actors": [{ "folder": "actors/partner-crawler", "actorFullName": "partner/crawler" }]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`groups` is an object with at least one group. The group key (`myteam`, `partner` above) is only a label for readability; it has no effect on the result, and it does not need to match any folder or username. Each group needs a `tokenEnvVar` and at least one actor, and can optionally set `overrideActorContext`.
|
|
151
|
+
|
|
152
|
+
An actor inside a group can set its own `tokenEnvVar` or `overrideActorContext`, and that value replaces the group's for that actor only. Values are replaced, not merged: in the example above, `myteam/email-sender` gets `["actors/email-sender", "packages/mailer"]`, not `packages/shared` as well.
|
|
153
|
+
|
|
100
154
|
## Github worklows
|
|
101
155
|
|
|
102
156
|
The reusable workflows live in this repo, alongside the package they call. They are the
|
|
103
157
|
`public_`-prefixed files in `.github/workflows`; everything else there is this repo's own CI.
|
|
104
|
-
Reference them at the `@v0` major tag, never at `@master` — see
|
|
158
|
+
Reference them at the `@workflows-v0` major tag, never at `@master` — see
|
|
105
159
|
[Versioning and releases](#versioning-and-releases).
|
|
106
160
|
|
|
107
161
|
There should be 4 GH workflow files in `.github/workflows`, plus an optional fifth for Claude reviews.
|
|
@@ -119,7 +173,7 @@ on:
|
|
|
119
173
|
|
|
120
174
|
jobs:
|
|
121
175
|
platformTestsCore:
|
|
122
|
-
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@v0
|
|
176
|
+
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
|
|
123
177
|
with:
|
|
124
178
|
subtest: core
|
|
125
179
|
secrets: inherit
|
|
@@ -138,7 +192,7 @@ on:
|
|
|
138
192
|
|
|
139
193
|
jobs:
|
|
140
194
|
platformTestsDaily:
|
|
141
|
-
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@v0
|
|
195
|
+
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
|
|
142
196
|
secrets: inherit
|
|
143
197
|
```
|
|
144
198
|
|
|
@@ -153,7 +207,7 @@ on:
|
|
|
153
207
|
|
|
154
208
|
jobs:
|
|
155
209
|
buildDevelAndTest:
|
|
156
|
-
uses: apify/apify-test-tools/.github/workflows/public_pr-build-test.yaml@v0
|
|
210
|
+
uses: apify/apify-test-tools/.github/workflows/public_pr-build-test.yaml@workflows-v0
|
|
157
211
|
secrets: inherit
|
|
158
212
|
```
|
|
159
213
|
|
|
@@ -168,10 +222,14 @@ on:
|
|
|
168
222
|
|
|
169
223
|
jobs:
|
|
170
224
|
buildLatest:
|
|
171
|
-
uses: apify/apify-test-tools/.github/workflows/public_push-build-latest.yaml@v0
|
|
225
|
+
uses: apify/apify-test-tools/.github/workflows/public_push-build-latest.yaml@workflows-v0
|
|
172
226
|
secrets: inherit
|
|
173
227
|
```
|
|
174
228
|
|
|
229
|
+
This workflow runs `apify-test-tools release --base-commit ${{ github.event.before }}`. See
|
|
230
|
+
[Releasing Actors](#releasing-actors-release) for how the released range is determined and what to
|
|
231
|
+
pass if you call the CLI from somewhere else.
|
|
232
|
+
|
|
175
233
|
### `claude-review.yaml`
|
|
176
234
|
|
|
177
235
|
Optional. Reviews a PR against the shared guidelines when you add the trigger label, and again on
|
|
@@ -186,13 +244,13 @@ on:
|
|
|
186
244
|
|
|
187
245
|
jobs:
|
|
188
246
|
review:
|
|
189
|
-
uses: apify/apify-test-tools/.github/workflows/public_review.yaml@v0
|
|
247
|
+
uses: apify/apify-test-tools/.github/workflows/public_review.yaml@workflows-v0
|
|
190
248
|
secrets: inherit
|
|
191
249
|
```
|
|
192
250
|
|
|
193
251
|
The review instructions live in `.github/review-prompt.md` in this repo and are fetched at run time,
|
|
194
252
|
because a reusable workflow doesn't get its own repo checked out. `prompt-ref` selects which ref to
|
|
195
|
-
fetch them from and defaults to `v0`, so the instructions match the workflow you're calling — point
|
|
253
|
+
fetch them from and defaults to `workflows-v0`, so the instructions match the workflow you're calling — point
|
|
196
254
|
it at a branch only to test a prompt change.
|
|
197
255
|
|
|
198
256
|
### Secrets
|
|
@@ -256,7 +314,7 @@ decide what a consumer repo actually runs:
|
|
|
256
314
|
|
|
257
315
|
| Pointer | What it selects | Moves when |
|
|
258
316
|
| ----------------------------------- | ---------------------------------------------- | -------------------------------------------------------- |
|
|
259
|
-
| the `@v0` tag in `uses:`
|
|
317
|
+
| the `@workflows-v0` tag in `uses:` | which workflows run | a master push, once the version below is published |
|
|
260
318
|
| `.github/workflows-package-version` | which `apify-test-tools` the workflows install | a stable release writes it; you may set it ahead of time |
|
|
261
319
|
|
|
262
320
|
The setup action installs that **exact** version — not a range. A tag is therefore a complete
|
|
@@ -270,31 +328,33 @@ action, which is how branch testing works.
|
|
|
270
328
|
|
|
271
329
|
Nothing is coupled that doesn't need to be:
|
|
272
330
|
|
|
273
|
-
- **Workflow-only change** — merge it. `v0` moves, it goes live, no release needed.
|
|
331
|
+
- **Workflow-only change** — merge it. `workflows-v0` moves, it goes live, no release needed.
|
|
274
332
|
- **Package-only change** — merge it, then cut a release when you want it out. The workflows are
|
|
275
333
|
unchanged, so consumers see nothing until the release lands.
|
|
276
334
|
- **A workflow that calls a new CLI feature** — the one case that can break consumers, and the only
|
|
277
335
|
one with any ceremony. Put the package change, the workflow change, and the new pin in one PR. On
|
|
278
|
-
merge the tag is **held**: the CI job reports that the pinned version isn't on npm and leaves `v0`
|
|
336
|
+
merge the tag is **held**: the CI job reports that the pinned version isn't on npm and leaves `workflows-v0`
|
|
279
337
|
where it is, so consumers keep running the previous workflows. Cut a stable release, and the tag
|
|
280
338
|
moves on its own. Run **Move major version tag** if you don't want to wait for the next master
|
|
281
339
|
push.
|
|
282
340
|
|
|
283
|
-
`v0` moving on every master push means `@v0` is as live as `@master` was —
|
|
284
|
-
just a gate on the package version. What the tag buys you is a
|
|
285
|
-
so repos migrate one at a time instead of all at once,
|
|
286
|
-
an earlier commit. Bump `MAJOR_TAG` in
|
|
287
|
-
major; the old tag then freezes where it is
|
|
341
|
+
`workflows-v0` moving on every master push means `@workflows-v0` is as live as `@master` was —
|
|
342
|
+
there's no staging step, just a gate on the package version. What the tag buys you is a
|
|
343
|
+
`workflows-v1` for breaking workflow changes, so repos migrate one at a time instead of all at once,
|
|
344
|
+
and a way to roll back by pointing the tag at an earlier commit. Bump `MAJOR_TAG` in
|
|
345
|
+
`.github/workflows/_move_major_tag.yaml` to cut the next major; the old tag then freezes where it is
|
|
346
|
+
and keeps working.
|
|
288
347
|
|
|
289
|
-
Freezing is real, which is the whole reason the version is pinned rather than a floor. A frozen `v0`
|
|
348
|
+
Freezing is real, which is the whole reason the version is pinned rather than a floor. A frozen `workflows-v0`
|
|
290
349
|
points at a commit whose pinned version never changes, so it keeps installing the library it was
|
|
291
350
|
tested against no matter how far the package moves on. Had the workflows installed `>=<floor>`, a
|
|
292
|
-
frozen `v0` would still resolve to whatever is newest — and since the release that enables
|
|
293
|
-
usually the same release that breaks `v0`, the migration window would
|
|
351
|
+
frozen `workflows-v0` would still resolve to whatever is newest — and since the release that enables
|
|
352
|
+
`workflows-v1` is usually the same release that breaks `workflows-v0`, the migration window would
|
|
353
|
+
have been zero.
|
|
294
354
|
|
|
295
355
|
The tag tracks the **workflows'** contract, not the npm package version. They move independently on
|
|
296
|
-
purpose, so `@v0` is expected to stay `@v0` after the package reaches 1.0 —
|
|
297
|
-
breaks its callers, not when the package does. Because `uses:` cannot take an expression, every ref
|
|
356
|
+
purpose, so `@workflows-v0` is expected to stay `@workflows-v0` after the package reaches 1.0 —
|
|
357
|
+
bump it when a workflow breaks its callers, not when the package does. Because `uses:` cannot take an expression, every ref
|
|
298
358
|
into this repo repeats the tag literally; `check-major-tag-refs.mjs` fails the build if `MAJOR_TAG`
|
|
299
359
|
and those refs disagree, which is the mistake that would otherwise ship silently during a bump.
|
|
300
360
|
|
|
@@ -535,7 +595,7 @@ The main local flow is:
|
|
|
535
595
|
4. Build Actors on Apify (with your new code)
|
|
536
596
|
5. Run tests against those builds. You can change tests and run on the same builds.
|
|
537
597
|
|
|
538
|
-
|
|
598
|
+
Run every command from the root of the actor repository you want to work with (or point `--workspace` at it).
|
|
539
599
|
|
|
540
600
|
#### 4. Build affected Actors
|
|
541
601
|
|
|
@@ -545,7 +605,6 @@ Requires `APIFY_TOKEN_<USERNAME>` for all Apify users that own your Actors (e.g.
|
|
|
545
605
|
|
|
546
606
|
```bash
|
|
547
607
|
APIFY_TOKEN_JOHN_DOE=<token> \
|
|
548
|
-
GITHUB_WORKSPACE=. \
|
|
549
608
|
npx apify-test-tools build \
|
|
550
609
|
--target-branch origin/master \
|
|
551
610
|
--source-branch origin/my-dummy-branch \
|
|
@@ -560,11 +619,10 @@ Remove `--dry-run` to actually trigger builds and update the branch names/ The c
|
|
|
560
619
|
|
|
561
620
|
#### Build from local source (no push needed)
|
|
562
621
|
|
|
563
|
-
If you don't want to push a dummy branch just to test a change and wait for all the tests to finish, `build-from-local` builds Actors directly from your local files (zipped and
|
|
622
|
+
If you don't want to push a dummy branch just to test a change and wait for all the tests to finish, `build-from-local` builds Actors directly from your local files (zipped into the Actor's `actor-<id>-source` key-value store and built as `TARBALL`, like `apify push`), skipping steps 1-4 above.
|
|
564
623
|
|
|
565
624
|
```bash
|
|
566
625
|
APIFY_TOKEN_JOHN_DOE=<token> \
|
|
567
|
-
GITHUB_WORKSPACE=. \
|
|
568
626
|
npx apify-test-tools build-from-local --actors john.doe/my-actor
|
|
569
627
|
```
|
|
570
628
|
|
|
@@ -573,8 +631,7 @@ Pass a hardcoded actor name via `--actors` to build only that Actor (comma-separ
|
|
|
573
631
|
```bash
|
|
574
632
|
# Build from local source and capture output
|
|
575
633
|
BUILDS=$(APIFY_TOKEN_JOHN_DOE=apify_api_xxx \
|
|
576
|
-
|
|
577
|
-
npx apify-test-tools build-from-local --actors apify/my-actor)
|
|
634
|
+
npx apify-test-tools build-from-local --actors apify/my-actor)
|
|
578
635
|
```
|
|
579
636
|
|
|
580
637
|
Since you already scoped the build to just the Actor(s) you care about, point vitest at a specific test file (or a `-t` name filter) instead of the whole `test/platform` directory — you get feedback on that one test without waiting for the full suite to run.
|
|
@@ -596,8 +653,7 @@ TESTER_APIFY_TOKEN=<token> \
|
|
|
596
653
|
```bash
|
|
597
654
|
# Build and capture output
|
|
598
655
|
BUILDS=$(APIFY_TOKEN_JOHN_DOE=apify_api_xxx \
|
|
599
|
-
|
|
600
|
-
npx apify-test-tools build \
|
|
656
|
+
npx apify-test-tools build \
|
|
601
657
|
--target-branch origin/master \
|
|
602
658
|
--source-branch origin/my-dummy-branch)
|
|
603
659
|
|
|
@@ -607,10 +663,67 @@ TESTER_APIFY_TOKEN=apify_api_yyy \
|
|
|
607
663
|
npx vitest --run --maxConcurrency 20 --fileParallelism=true --maxWorkers 100 test/platform
|
|
608
664
|
```
|
|
609
665
|
|
|
666
|
+
### Releasing Actors (`release`)
|
|
667
|
+
|
|
668
|
+
`release` builds the `latest` (default) version of every Actor changed since the last release and
|
|
669
|
+
posts release notes to Slack. It reads everything else from git: the branch from the checked-out
|
|
670
|
+
`HEAD`, the commits and changed files from the range `<base-commit>..HEAD`, and the repository from
|
|
671
|
+
the `origin` remote.
|
|
672
|
+
|
|
673
|
+
```bash
|
|
674
|
+
npx apify-test-tools release --base-commit <sha> --release-slack-channel "#releases" --dry-run
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### `--base-commit`
|
|
678
|
+
|
|
679
|
+
The last commit that is **already released**. Everything after it, up to `HEAD`, is released. It is
|
|
680
|
+
required: git alone can't tell which commits a push brought, and guessing wrong silently leaves Actors
|
|
681
|
+
on old code.
|
|
682
|
+
|
|
683
|
+
The changed files come from diffing `<base-commit>` and `HEAD` directly, so this works for every
|
|
684
|
+
merge strategy as long as the base commit is the branch tip from **before** the push:
|
|
685
|
+
|
|
686
|
+
| Merge strategy | What lands on the branch | What to pass |
|
|
687
|
+
| ------------------------------ | --------------------------------- | ---------------------------------------------------------------------- |
|
|
688
|
+
| Squash and merge | 1 new commit | the push's "before" commit (`HEAD~1` also works) |
|
|
689
|
+
| Merge commit | the PR's commits + 1 merge commit | the push's "before" commit (`HEAD^1`, the first parent, also works) |
|
|
690
|
+
| Rebase and merge / direct push | N new commits | the push's "before" commit (`HEAD~1` would miss the first N-1 commits) |
|
|
691
|
+
|
|
692
|
+
On GitHub, the push's "before" commit is `${{ github.event.before }}`, which the reusable workflow
|
|
693
|
+
passes for you. On other CIs use the equivalent, e.g. `$CI_COMMIT_BEFORE_SHA` on GitLab. The checkout
|
|
694
|
+
needs the full history (`fetch-depth: 0` in `actions/checkout`) so the base commit is available locally.
|
|
695
|
+
|
|
696
|
+
`release` fails instead of guessing when:
|
|
697
|
+
|
|
698
|
+
- the base commit is all zeros (GitHub sends this for a push that created the branch)
|
|
699
|
+
- the base commit isn't in the local history (shallow checkout) or isn't an ancestor of `HEAD`
|
|
700
|
+
(the branch was force-pushed)
|
|
701
|
+
|
|
702
|
+
In those cases, rerun `release` with a `--base-commit` you pick yourself: an ancestor of `HEAD` from
|
|
703
|
+
before the changes you want to release (for a shallow checkout, fetch the full history instead).
|
|
704
|
+
`--actors` only narrows which of the changed Actors get released; it never forces a build, so on its
|
|
705
|
+
own it doesn't get past these errors.
|
|
706
|
+
If `HEAD` is the base commit, there is nothing to release and the command exits successfully.
|
|
707
|
+
|
|
708
|
+
A failed release isn't retried by the next push: that push's base commit is the failed one's head, so
|
|
709
|
+
its changes are skipped. Re-run the failed workflow run, or release the Actors manually.
|
|
710
|
+
|
|
711
|
+
#### Repository check
|
|
712
|
+
|
|
713
|
+
`release` and `build` point each Actor's version at the `origin` remote. Because a build overwrites the
|
|
714
|
+
version's Git URL, building from a fork or a mirror would repoint the Actor. So, before building, the
|
|
715
|
+
remote is compared with the Git URL of the Actor's default version (the one its default build tag
|
|
716
|
+
points to), and the command fails if they differ or if the default version isn't built from a Git
|
|
717
|
+
repository. The URL form doesn't matter (`https://github.com/org/repo` equals
|
|
718
|
+
`git@github.com:org/repo.git`).
|
|
719
|
+
|
|
720
|
+
To build from a different repository on purpose, e.g. when moving an Actor, pass `--repo-url`. That
|
|
721
|
+
skips the check.
|
|
722
|
+
|
|
610
723
|
#### Dev mode
|
|
611
724
|
|
|
612
725
|
For development on `apify-test-tools` itself, use `tsx` directly:
|
|
613
726
|
|
|
614
727
|
```bash
|
|
615
|
-
|
|
728
|
+
tsx bin/main.ts --workspace local-clone get-actor-configs
|
|
616
729
|
```
|
package/bin/build-from-local.ts
CHANGED
|
@@ -2,14 +2,16 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import AdmZip from 'adm-zip';
|
|
6
|
+
import type * as ApifyClientTypes from 'apify-client';
|
|
6
7
|
import { ActorSourceType } from 'apify-client';
|
|
7
8
|
|
|
8
9
|
import { dryRunBuildData, LOCAL_SOURCE_VERSION_NUMBER, runAndSummarizeBuilds } from './build.js';
|
|
9
10
|
import { buildDockerIgnoreMatcher } from './dockerignore.js';
|
|
10
11
|
import { isPathWithinScope } from './path-utils.js';
|
|
11
12
|
import type { ActorConfig, BuildData } from './types.js';
|
|
12
|
-
import {
|
|
13
|
+
import type { SourceFile } from './utils.js';
|
|
14
|
+
import { getGitignoredPaths, isOutsideDir, listRepoFilePaths, readSourceFile } from './utils.js';
|
|
13
15
|
|
|
14
16
|
// JUST IN CASE. File patterns that commonly hold credentials — never ship these into a build, regardless
|
|
15
17
|
// of sourceType or of whether the repo's .gitignore happens to list them. Everything else that should be
|
|
@@ -18,7 +20,7 @@ import { getGitignoredPaths, isOutsideDir, listRepoFilePaths, toActorVersionSour
|
|
|
18
20
|
const SKIP_FILE_PATTERNS = [/^\.env(\..+)?$/, /\.pem$/, /\.key$/, /\.pfx$/, /\.p12$/];
|
|
19
21
|
const isSecretFile = (fileName: string): boolean => SKIP_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
|
|
20
22
|
|
|
21
|
-
export const collectSourceFiles = async (actorName: string, actorDir: string): Promise<
|
|
23
|
+
export const collectSourceFiles = async (actorName: string, actorDir: string): Promise<SourceFile[]> => {
|
|
22
24
|
const repoRoot = process.cwd();
|
|
23
25
|
const absActorDir = path.resolve(actorDir);
|
|
24
26
|
|
|
@@ -35,9 +37,7 @@ export const collectSourceFiles = async (actorName: string, actorDir: string): P
|
|
|
35
37
|
const keptFilePaths = collectNonIgnoredFiles(dockerContextDirAbs, repoRoot);
|
|
36
38
|
|
|
37
39
|
if (!isMonorepoActor) {
|
|
38
|
-
return Promise.all(
|
|
39
|
-
keptFilePaths.map(async (filePath) => toActorVersionSourceFile(filePath, dockerContextDirAbs)),
|
|
40
|
-
);
|
|
40
|
+
return Promise.all(keptFilePaths.map(async (filePath) => readSourceFile(filePath, dockerContextDirAbs)));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const { tempDir, filePaths } = await flattenMonorepoContext(
|
|
@@ -48,7 +48,7 @@ export const collectSourceFiles = async (actorName: string, actorDir: string): P
|
|
|
48
48
|
keptFilePaths,
|
|
49
49
|
);
|
|
50
50
|
try {
|
|
51
|
-
return await Promise.all(filePaths.map(async (filePath) =>
|
|
51
|
+
return await Promise.all(filePaths.map(async (filePath) => readSourceFile(filePath, tempDir)));
|
|
52
52
|
} finally {
|
|
53
53
|
// Only the flattened copy is temporary — never delete the actor's own directory.
|
|
54
54
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
@@ -202,11 +202,15 @@ export const runBuildsFromLocal = async ({
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
return runAndSummarizeBuilds(actorConfigs, 'LOCAL BUILDS', async (actorConfig, builder) => {
|
|
205
|
-
|
|
206
|
-
const
|
|
205
|
+
// Zipped and uploaded like `apify push` does, since inline SOURCE_FILES are capped at ~9 MB.
|
|
206
|
+
const zip = new AdmZip();
|
|
207
|
+
for (const { name, content } of await collectSourceFiles(actorConfig.actorFullName, actorConfig.folder)) {
|
|
208
|
+
zip.addFile(name, content);
|
|
209
|
+
}
|
|
210
|
+
const actorVersion: ApifyClientTypes.ActorVersion = {
|
|
207
211
|
versionNumber: LOCAL_SOURCE_VERSION_NUMBER,
|
|
208
|
-
|
|
209
|
-
sourceType: ActorSourceType.
|
|
212
|
+
tarballUrl: await builder.uploadSourceZip(LOCAL_SOURCE_VERSION_NUMBER, zip.toBuffer()),
|
|
213
|
+
sourceType: ActorSourceType.Tarball,
|
|
210
214
|
};
|
|
211
215
|
return builder.createVersionAndBuild(LOCAL_SOURCE_VERSION_NUMBER, actorVersion, false);
|
|
212
216
|
});
|