apify-test-tools 0.10.2-beta.4 → 0.10.2-beta.5
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/cli/apify-test-tools.mjs +125 -127
- package/.github/scripts/run-with-apify-tokens.mjs +1 -1
- package/CHANGELOG.md +5 -0
- package/CONTRIBUTING.md +4 -1
- package/README.md +69 -71
- package/bin/affected-actors.ts +132 -91
- package/bin/build.ts +0 -2
- package/bin/diff-changes.ts +7 -6
- package/bin/diff-json-schema.ts +5 -6
- package/bin/git.ts +21 -55
- package/bin/main.ts +7 -97
- package/bin/slack.ts +14 -27
- package/bin/types.ts +6 -0
- package/dist/bin/affected-actors.d.ts +23 -22
- package/dist/bin/affected-actors.d.ts.map +1 -1
- package/dist/bin/affected-actors.js +108 -60
- package/dist/bin/affected-actors.js.map +1 -1
- package/dist/bin/build.d.ts +0 -1
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +0 -2
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +4 -3
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +4 -4
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/diff-json-schema.d.ts +2 -2
- package/dist/bin/diff-json-schema.d.ts.map +1 -1
- package/dist/bin/diff-json-schema.js +1 -4
- package/dist/bin/diff-json-schema.js.map +1 -1
- package/dist/bin/git.d.ts +6 -9
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +20 -48
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +7 -77
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts +2 -7
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +12 -18
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/types.d.ts +5 -0
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/test/bundle/cli-bundle.test.js +14 -16
- package/dist/test/bundle/cli-bundle.test.js.map +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js +22 -63
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
- package/dist/test/e2e/git-build-release.e2e.test.d.ts +2 -0
- package/dist/test/e2e/git-build-release.e2e.test.d.ts.map +1 -0
- package/dist/test/e2e/git-build-release.e2e.test.js +87 -0
- package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -0
- package/dist/test/e2e/helpers.d.ts +37 -0
- package/dist/test/e2e/helpers.d.ts.map +1 -0
- package/dist/test/e2e/helpers.js +57 -0
- package/dist/test/e2e/helpers.js.map +1 -0
- package/dist/test/unit/bin/affected-actors-cli.test.d.ts +2 -0
- package/dist/test/unit/bin/affected-actors-cli.test.d.ts.map +1 -0
- package/dist/test/unit/bin/affected-actors-cli.test.js +161 -0
- package/dist/test/unit/bin/affected-actors-cli.test.js.map +1 -0
- package/dist/test/unit/bin/affected-actors.test.js +65 -78
- package/dist/test/unit/bin/affected-actors.test.js.map +1 -1
- package/dist/test/unit/bin/build-cli.test.js +5 -13
- package/dist/test/unit/bin/build-cli.test.js.map +1 -1
- package/dist/test/unit/bin/compiled-cli.d.ts +6 -0
- package/dist/test/unit/bin/compiled-cli.d.ts.map +1 -0
- package/dist/test/unit/bin/compiled-cli.js +16 -0
- package/dist/test/unit/bin/compiled-cli.js.map +1 -0
- package/dist/test/unit/bin/diff-changes.test.js +55 -55
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/diff.test.js +25 -28
- package/dist/test/unit/bin/diff.test.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +29 -97
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/slack.test.js +6 -27
- package/dist/test/unit/bin/slack.test.js.map +1 -1
- package/dist/test/unit/should-built-and-test.test.js +18 -21
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/test/bundle/cli-bundle.test.ts +14 -24
- package/test/e2e/cli-bundle.e2e.test.ts +39 -84
- package/test/e2e/git-build-release.e2e.test.ts +113 -0
- package/test/e2e/helpers.ts +82 -0
- package/test/unit/bin/affected-actors-cli.test.ts +193 -0
- package/test/unit/bin/affected-actors.test.ts +83 -85
- package/test/unit/bin/build-cli.test.ts +6 -12
- package/test/unit/bin/compiled-cli.ts +17 -0
- package/test/unit/bin/diff-changes.test.ts +55 -55
- package/test/unit/bin/diff.test.ts +25 -28
- package/test/unit/bin/git.test.ts +37 -120
- package/test/unit/bin/slack.test.ts +6 -30
- package/test/unit/should-built-and-test.test.ts +19 -22
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Runs apify-test-tools and passes it only secrets needed for the Actors, no other secrets are leaked.
|
|
3
3
|
//
|
|
4
|
-
// node .github/scripts/run-with-apify-tokens.mjs apify-test-tools build
|
|
4
|
+
// jq ... | node .github/scripts/run-with-apify-tokens.mjs apify-test-tools build --version-number 0.99
|
|
5
5
|
//
|
|
6
6
|
// The step passes the whole secrets map as ALL_SECRETS. The token names come from `tokenEnvVar` in
|
|
7
7
|
// the repo's apify-test-tools.config.json, which is the same file apify-test-tools itself reads, so
|
package/CHANGELOG.md
CHANGED
|
@@ -10,12 +10,17 @@ All notable changes to this project will be documented in this file.
|
|
|
10
10
|
- **ci:** Ship the workflows' CLI as a bundle in the action instead of installing it ([#200](https://github.com/apify/apify-test-tools/pull/200)) ([a0ef975](https://github.com/apify/apify-test-tools/commit/a0ef9756123a30a8210590b080a2a289c6a9c17c)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
11
11
|
- [**breaking**] Revamp get-affected-actors to be a core logic command ([#190](https://github.com/apify/apify-test-tools/pull/190)) ([ea5f244](https://github.com/apify/apify-test-tools/commit/ea5f244cfd42bf3fa9ffd38cd2ad499dde2e83f8)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
12
12
|
- [**breaking**] Add logic-less build command and use command chaining in workflows ([#193](https://github.com/apify/apify-test-tools/pull/193)) ([e5b0b86](https://github.com/apify/apify-test-tools/commit/e5b0b86ee06fca51d4be0ddc81b0a4a469d31603)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
13
|
+
- **get-affected-actors:** Three-dot diff, narrowed per Actor with --since ([#204](https://github.com/apify/apify-test-tools/pull/204)) ([fa9a0e7](https://github.com/apify/apify-test-tools/commit/fa9a0e7518764dd4722ec9ecbf67b2a26a5a062e)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
13
14
|
|
|
14
15
|
### 🐛 Bug Fixes
|
|
15
16
|
|
|
16
17
|
- **toBeObject:** Reject null ([#191](https://github.com/apify/apify-test-tools/pull/191)) ([6bdae49](https://github.com/apify/apify-test-tools/commit/6bdae4905756e0ae8dd6146942e6fc04dbead48e)) by [@JuanGalilea](https://github.com/JuanGalilea), closes [#88](https://github.com/apify/apify-test-tools/issues/88)
|
|
17
18
|
- Build and test on test-only changes even when .dockerignore lists the tests ([#177](https://github.com/apify/apify-test-tools/pull/177)) ([5cb1b5a](https://github.com/apify/apify-test-tools/commit/5cb1b5ab5f6f43ec279f9624b402e73d55a5974a)) by [@artogahr](https://github.com/artogahr), closes [#145](https://github.com/apify/apify-test-tools/issues/145)
|
|
18
19
|
|
|
20
|
+
### 🚜 Refactor
|
|
21
|
+
|
|
22
|
+
- [**breaking**] Remove build-for-test and release composite commands ([#203](https://github.com/apify/apify-test-tools/pull/203)) ([85efc54](https://github.com/apify/apify-test-tools/commit/85efc54697e98208c827c4f450906e00eebcd9ca)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
23
|
+
|
|
19
24
|
|
|
20
25
|
<!-- git-cliff-unreleased-end -->
|
|
21
26
|
## [0.10.1](https://github.com/apify/apify-test-tools/releases/tag/v0.10.1) (2026-10-13)
|
package/CONTRIBUTING.md
CHANGED
|
@@ -94,9 +94,12 @@ npm release relate — the short version:
|
|
|
94
94
|
- the CLI runs from `.github/cli/apify-test-tools.mjs`, a bundle the pre-commit hook rebuilds with
|
|
95
95
|
`npm run bundle`. If you commit with `--no-verify`, run it yourself: the `CLI bundle` check fails
|
|
96
96
|
on a stale bundle, and a stale bundle would hold the tag.
|
|
97
|
-
- `npm test` covers the bundle without network (`test/bundle/`). `npm run test:e2e` runs it against
|
|
97
|
+
- `npm test` covers the bundle without network (`test/bundle/`). `npm run test:e2e` runs it against
|
|
98
98
|
dummy Actors in an isolated sandbox account (`test/e2e/`) and skips itself unless
|
|
99
99
|
`E2E_TEST_APIFY_TOKEN_DBYPODOIHUZABNQTR` is set. CI passes that secret only to the `E2E tests` job.
|
|
100
|
+
The git `build` and release pipeline tests clone
|
|
101
|
+
[testing-repo-for-github-actions](https://github.com/apify-store/testing-repo-for-github-actions) and
|
|
102
|
+
use its `e2e/base` and `e2e/feature` branches as fixtures, so never rebase or delete those.
|
|
100
103
|
|
|
101
104
|
`npm run lint` and `actionlint` (via the `Code checks` workflow) both gate master, so run them before
|
|
102
105
|
pushing workflow changes.
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ When a PR is opened or code is pushed, the tool determines which actors need to
|
|
|
57
57
|
2. **CHANGELOG classification** — a `CHANGELOG.md` file is always `cosmetic` (only triggers a release build, not tests), for every actor, regardless of context or folder. (See [issue #106](https://github.com/apify/apify-test-tools/issues/106).)
|
|
58
58
|
3. **Context matching** — the file must fall within one of the actor's context paths (`dockerContextDir` from `actor.json` by default, or `overrideActorContext` from config if set). Files outside every context path are skipped.
|
|
59
59
|
4. **Hardcoded ignore list, context-aware** — the file path is first "hoisted" relative to the context path it matched (e.g. a standalone actor's own `.eslintrc` is checked as just `.eslintrc`, not the full repo-root-relative path), then checked against repo-level dev file patterns (`.vscode/`, `.gitignore`, `.husky/`, `.eslintrc`, `eslint.config.mjs`, `.prettierrc`, `.editorconfig`). There's no hardcoded special-casing for legacy `code/`/`shared/` layouts — repos that need those directories treated as top-level must list them explicitly in `overrideActorContext`.
|
|
60
|
-
5. **`.dockerignore` filtering** — if a `.dockerignore` exists at the root of the actor's `dockerContextDir`, matching files are ignored. Patterns are resolved relative to `dockerContextDir`, matching Docker's own behavior. Test files are the exception: `
|
|
60
|
+
5. **`.dockerignore` filtering** — if a `.dockerignore` exists at the root of the actor's `dockerContextDir`, matching files are ignored. Patterns are resolved relative to `dockerContextDir`, matching Docker's own behavior. Test files are the exception: `get-affected-actors --test-files-glob <glob>` (relative to the repo root) skips this check for matching files, so a changed test triggers a build, and with it its tests, even when `.dockerignore` lists it. The PR workflow passes `test/platform/**`. Context matching still applies, so tests outside an actor's `dockerContextDir` (e.g. a root `test/` with standalone actors) need to be listed in its `overrideActorContext`; `get-affected-actors` logs a warning for changed test files outside every actor's context.
|
|
61
61
|
6. **README classification** — a `README.md` file is `cosmetic` (only triggers a release build, not tests) if it lives inside the actor's own `folder`; otherwise it's ignored entirely, since it isn't documentation for this actor.
|
|
62
62
|
7. **Cosmetic JSON classification** — `.json` files inside the actor's own `.actor/` directory with only cosmetic schema changes (whitespace, key ordering) only trigger a release build.
|
|
63
63
|
8. **Functional** — everything else triggers both build and tests.
|
|
@@ -226,9 +226,8 @@ jobs:
|
|
|
226
226
|
secrets: inherit
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
This workflow
|
|
230
|
-
|
|
231
|
-
on every changed Actor, then `notify-release`. See [Releasing Actors](#releasing-actors-release) for how the
|
|
229
|
+
This workflow runs one step per command: `get-affected-actors --since ${{ github.event.before }}`, then
|
|
230
|
+
`build --version-number default` on every changed Actor, then `notify-release`. See [Releasing Actors](#releasing-actors) for how the
|
|
232
231
|
released range is determined and what to pass if you call the CLI from somewhere else.
|
|
233
232
|
|
|
234
233
|
### `claude-review.yaml`
|
|
@@ -274,11 +273,13 @@ secrets to pass on:
|
|
|
274
273
|
|
|
275
274
|
```yaml
|
|
276
275
|
- name: Build
|
|
276
|
+
shell: bash
|
|
277
277
|
env:
|
|
278
278
|
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
279
279
|
run: |
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
jq '{ actors: .functionallyChangedActors }' affected.json \
|
|
281
|
+
| node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
|
|
282
|
+
npx apify-test-tools build --version-number 0.99
|
|
282
283
|
```
|
|
283
284
|
|
|
284
285
|
The wrapper passes only the tokens the config declares and drops `ALL_SECRETS`, so neither `npx` nor
|
|
@@ -569,12 +570,12 @@ The package includes a CLI binary used by CI workflows to build Actors, detect c
|
|
|
569
570
|
|
|
570
571
|
### Dry runs (`--dry-run`)
|
|
571
572
|
|
|
572
|
-
Every command that changes something (`build`, `build-
|
|
573
|
+
Every command that changes something (`build`, `build-from-local`, `notify-release`, `delete-old-builds`, `report-tests`) takes `--dry-run`. A dry run:
|
|
573
574
|
|
|
574
575
|
- **Changes nothing.** No versions or builds are created, nothing is uploaded or deleted, and no Slack message is posted.
|
|
575
576
|
- **Does every read and check the real run does**, so it fails wherever the real run would: change detection, the Apify token and Actor lookup, the [repository check](#repository-check), collecting and zipping local sources, and the Slack token (verified with Slack's read-only `auth.test`).
|
|
576
577
|
- **Needs the same tokens as the real run.** Read-only tokens are enough, since nothing is written. A dry run can't tell whether a token is also allowed to write, because the Apify API has no dry-run mode of its own.
|
|
577
|
-
- **Prints a plan, not results.** Logs start with `[DRY RUN] Would …`. `build
|
|
578
|
+
- **Prints a plan, not results.** Logs start with `[DRY RUN] Would …`. `build` and `build-from-local` write `{ "dryRun": true, "builds": [...] }` to stdout instead of the build array, so the plan can't be passed to tests as `ACTOR_BUILDS` by mistake.
|
|
578
579
|
|
|
579
580
|
### Running locally
|
|
580
581
|
|
|
@@ -599,16 +600,15 @@ If you want to test vs existing src code, you can skip this and instead construc
|
|
|
599
600
|
Requires `APIFY_TOKEN_<USERNAME>` for all Apify users that own your Actors (e.g. `apify`, `compass`, `lukaskrivka` users). The username is derived from the actor name — uppercased with non-word chars replaced by `_` (e.g. Actor `john.doe/my-actor` needs `APIFY_TOKEN_JOHN_DOE`).
|
|
600
601
|
|
|
601
602
|
```bash
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
--dry-run
|
|
603
|
+
npx apify-test-tools get-affected-actors --base origin/master \
|
|
604
|
+
| jq '{ actors: .functionallyChangedActors }' \
|
|
605
|
+
| APIFY_TOKEN_JOHN_DOE=<token> npx apify-test-tools build --version-number 0.99 --dry-run
|
|
606
606
|
```
|
|
607
607
|
|
|
608
|
-
Run it with your dummy branch checked out: it builds the Actors that branch changed, from the branch
|
|
609
|
-
the remote. With `--dry-run`, it prints the plan (the version and Git URL
|
|
610
|
-
`--dry-run` to actually trigger builds and update the
|
|
611
|
-
objects to stdout:
|
|
608
|
+
Run it with your dummy branch checked out: it builds the Actors that branch changed functionally, from the branch
|
|
609
|
+
as it is on the remote, into the test version `0.99`. With `--dry-run`, it prints the plan (the version and Git URL
|
|
610
|
+
each Actor would be built from). Remove `--dry-run` to actually trigger builds, and update `--base` to the branch
|
|
611
|
+
you compare against. `build` outputs a JSON array of build objects to stdout:
|
|
612
612
|
|
|
613
613
|
```json
|
|
614
614
|
[{ "buildId": "...", "actorRawId": "...", "buildNumber": "...", "actorFullName": "john.doe/my-actor" }]
|
|
@@ -647,14 +647,13 @@ TESTER_APIFY_TOKEN=<token> \
|
|
|
647
647
|
|
|
648
648
|
#### Full example
|
|
649
649
|
|
|
650
|
-
Tests only run for Actors in `ACTOR_BUILDS`, so a custom workflow has to build (and pass) the Actors even when only tests changed. Pass `--test-files-glob` so test changes count even when `.dockerignore` lists them.
|
|
650
|
+
Tests only run for Actors in `ACTOR_BUILDS`, so a custom workflow has to build (and pass) the Actors even when only tests changed. Pass `--test-files-glob` to `get-affected-actors` so test changes count even when `.dockerignore` lists them.
|
|
651
651
|
|
|
652
652
|
```bash
|
|
653
653
|
# Build and capture output
|
|
654
|
-
BUILDS=$(
|
|
655
|
-
|
|
656
|
-
--
|
|
657
|
-
--test-files-glob 'test/platform/**')
|
|
654
|
+
BUILDS=$(npx apify-test-tools get-affected-actors --base origin/master --test-files-glob 'test/platform/**' \
|
|
655
|
+
| jq '{ actors: .functionallyChangedActors }' \
|
|
656
|
+
| APIFY_TOKEN_JOHN_DOE=apify_api_xxx npx apify-test-tools build --version-number 0.99)
|
|
658
657
|
|
|
659
658
|
# Run tests with the builds
|
|
660
659
|
ACTOR_BUILDS="$BUILDS" \
|
|
@@ -669,7 +668,7 @@ TESTER_APIFY_TOKEN=apify_api_yyy \
|
|
|
669
668
|
The range always ends at the checked-out `HEAD`, so check out the branch you want to inspect first.
|
|
670
669
|
|
|
671
670
|
```bash
|
|
672
|
-
# A PR: the branch's own
|
|
671
|
+
# A PR: the branch's own changes, narrowed to the Actors that changed since its last validated commit
|
|
673
672
|
npx apify-test-tools get-affected-actors --base origin/master --since <sha>
|
|
674
673
|
|
|
675
674
|
# A release: everything after the last released commit
|
|
@@ -678,23 +677,31 @@ npx apify-test-tools get-affected-actors --since <sha>
|
|
|
678
677
|
|
|
679
678
|
| Flag | Default | Meaning |
|
|
680
679
|
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
681
|
-
| `--base` | | Only
|
|
682
|
-
| `--since` | | Only
|
|
680
|
+
| `--base` | | Only what `HEAD` changes compared to this branch counts, like `git diff <base>...HEAD` |
|
|
681
|
+
| `--since` | | Only changes after this commit count (see below) |
|
|
683
682
|
| `--test-files-glob` | | Test files (glob from the repo root) that count as changes even when `.dockerignore` lists them, see [Change detection](#change-detection) |
|
|
684
683
|
| `--actors` | all | Only report these Actors |
|
|
685
684
|
| `--ignore` | none | Never report these Actors |
|
|
686
685
|
|
|
687
686
|
Pass `--base`, `--since`, or both:
|
|
688
687
|
|
|
689
|
-
- **`--base` only** inspects the
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
- **`--base` and `--since`** inspects
|
|
693
|
-
|
|
688
|
+
- **`--base` only** inspects what the branch changes, the same files GitHub's "Files changed" tab shows: the diff
|
|
689
|
+
from where the branch forked from `--base`, or the last commit of `--base` it merged in. What it merged in from
|
|
690
|
+
`--base` is already validated there, so it doesn't count.
|
|
691
|
+
- **`--base` and `--since`** also inspects what changed after the branch's last validated commit, and reports only
|
|
692
|
+
the Actors that changed in both. An Actor in only one of them is already validated, by this branch or on `--base`.
|
|
693
|
+
When both changed an Actor, the changes can interact, so it is reported, but as functional only when both changes
|
|
694
|
+
are functional. A validated functional change followed by README edits is cosmetic, and so is a functional
|
|
695
|
+
change merged in from `--base` that meets only cosmetic changes of the branch. `--since` doesn't have to be on
|
|
696
|
+
the branch, so a rebase only re-checks the Actors that `--base` changed too. If `--since` is not in the local
|
|
697
|
+
history (a force-push), or it is `HEAD` (a rerun), it is ignored and the whole branch is checked.
|
|
694
698
|
- **`--since` only** inspects everything after it up to `HEAD`. It must be an ancestor of `HEAD` and fails
|
|
695
|
-
otherwise,
|
|
699
|
+
otherwise, see [Releasing Actors](#releasing-actors). If `HEAD` is `--since`, nothing is
|
|
696
700
|
reported.
|
|
697
701
|
|
|
702
|
+
`commits`, `changedFiles` and `changelog` describe the whole range, `--base...HEAD` or `--since..HEAD`: `--since`
|
|
703
|
+
together with `--base` only narrows the Actor lists.
|
|
704
|
+
|
|
698
705
|
The output is a JSON document:
|
|
699
706
|
|
|
700
707
|
```json
|
|
@@ -711,25 +718,23 @@ The output is a JSON document:
|
|
|
711
718
|
|
|
712
719
|
Each changed Actor is in exactly one of the lists. `functionallyChangedActors` have at least one functional change, so
|
|
713
720
|
they need a new build and tests. `cosmeticallyChangedActors` only have cosmetic changes (see
|
|
714
|
-
[Change detection](#change-detection)), so they need a new build only when released:
|
|
715
|
-
`functionallyChangedActors`,
|
|
716
|
-
|
|
717
|
-
`build-for-test` and `release` use the same detection internally.
|
|
721
|
+
[Change detection](#change-detection)), so they need a new build only when released: the PR workflow builds
|
|
722
|
+
`functionallyChangedActors`, the release workflow builds both.
|
|
718
723
|
|
|
719
724
|
### Building Actors (`build`)
|
|
720
725
|
|
|
721
726
|
`build` builds exactly the Actors it is given on stdin, from the checked-out branch as it is on the remote, and
|
|
722
|
-
prints the builds as the same JSON array `build-
|
|
727
|
+
prints the builds as the same JSON array `build-from-local` prints. A detached HEAD is an
|
|
723
728
|
error, since there is no branch to build from. It doesn't look at git history: which Actors to build is
|
|
724
729
|
up to you, usually picked from the output of [`get-affected-actors`](#finding-affected-actors-get-affected-actors).
|
|
725
730
|
|
|
726
731
|
```bash
|
|
727
|
-
#
|
|
732
|
+
# A PR: the Actors a branch changed functionally, built into the test version
|
|
728
733
|
npx apify-test-tools get-affected-actors --base origin/master \
|
|
729
734
|
| jq '{ actors: .functionallyChangedActors }' \
|
|
730
735
|
| npx apify-test-tools build --version-number 0.99
|
|
731
736
|
|
|
732
|
-
#
|
|
737
|
+
# A release: every Actor changed since the last release, into the version users run
|
|
733
738
|
npx apify-test-tools get-affected-actors --since <sha> \
|
|
734
739
|
| jq '{ actors: (.functionallyChangedActors + .cosmeticallyChangedActors) }' \
|
|
735
740
|
| npx apify-test-tools build --version-number default
|
|
@@ -751,39 +756,33 @@ Actions, give such a step `shell: bash` so the pipe also fails on its own (the d
|
|
|
751
756
|
so building the default version moves its tag (usually `latest`) to the new build, and building a version
|
|
752
757
|
without a tag leaves every tag where it is. Builds always use the Docker cache.
|
|
753
758
|
|
|
754
|
-
|
|
755
|
-
`build-for-test` takes `--target-branch`, `--base-commit` and `--test-files-glob`, inspects and builds the
|
|
756
|
-
checked-out branch, and builds into version `0.99`. `release` takes `--base-commit`, and announces the release on Slack after building, the same
|
|
757
|
-
way `notify-release` does.
|
|
759
|
+
### Releasing Actors
|
|
758
760
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
`release` builds the `latest` (default) version of every Actor changed since the last release and
|
|
762
|
-
posts release notes to Slack. It reads everything else from git: the branch from the checked-out
|
|
763
|
-
`HEAD`, the commits and changed files from the range `<base-commit>..HEAD`, and the repository from
|
|
764
|
-
the `origin` remote.
|
|
761
|
+
A release is three commands: find what changed since the last released commit, build those Actors into
|
|
762
|
+
their default version (the one users run), and announce it.
|
|
765
763
|
|
|
766
764
|
```bash
|
|
767
|
-
npx apify-test-tools
|
|
768
|
-
```
|
|
765
|
+
npx apify-test-tools get-affected-actors --since <sha> > affected.json
|
|
769
766
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
`--report-slack-channel`, and posts nothing when there are no new commits. It needs `SLACK_TOKEN_RELEASES_BOT`.
|
|
767
|
+
jq '{ actors: (.functionallyChangedActors + .cosmeticallyChangedActors) }' affected.json \
|
|
768
|
+
| npx apify-test-tools build --version-number default --dry-run
|
|
773
769
|
|
|
774
|
-
|
|
775
|
-
npx apify-test-tools get-affected-actors --since <sha> \
|
|
776
|
-
| npx apify-test-tools notify-release --release-slack-channel "#releases" --dry-run
|
|
770
|
+
npx apify-test-tools notify-release --release-slack-channel "#releases" --dry-run < affected.json
|
|
777
771
|
```
|
|
778
772
|
|
|
779
|
-
|
|
773
|
+
`notify-release` reads the output of `get-affected-actors` on stdin. It posts the changelog additions to
|
|
774
|
+
`--release-slack-channel` and the commits and changed files to `--report-slack-channel`, and posts nothing when
|
|
775
|
+
there are no new commits. It names the release after the `origin` remote (or `--repo-url`) and the author of the
|
|
776
|
+
newest commit, and needs `SLACK_TOKEN_RELEASES_BOT`.
|
|
777
|
+
|
|
778
|
+
#### The last released commit (`--since`)
|
|
780
779
|
|
|
781
|
-
The last commit that is **already released**. Everything after it, up to `HEAD`, is released.
|
|
782
|
-
|
|
783
|
-
|
|
780
|
+
The last commit that is **already released**. Everything after it, up to `HEAD`, is released. Git alone
|
|
781
|
+
can't tell which commits a push brought, and guessing wrong silently leaves Actors on old code, so it has
|
|
782
|
+
to be passed.
|
|
784
783
|
|
|
785
|
-
The changed files come from diffing
|
|
786
|
-
|
|
784
|
+
The changed files come from diffing `--since` and `HEAD` directly, so this works for every merge strategy as
|
|
785
|
+
long as `--since` is the branch tip from **before** the push:
|
|
787
786
|
|
|
788
787
|
| Merge strategy | What lands on the branch | What to pass |
|
|
789
788
|
| ------------------------------ | --------------------------------- | ---------------------------------------------------------------------- |
|
|
@@ -793,26 +792,25 @@ merge strategy as long as the base commit is the branch tip from **before** the
|
|
|
793
792
|
|
|
794
793
|
On GitHub, the push's "before" commit is `${{ github.event.before }}`, which the reusable workflow
|
|
795
794
|
passes for you. On other CIs use the equivalent, e.g. `$CI_COMMIT_BEFORE_SHA` on GitLab. The checkout
|
|
796
|
-
needs the full history (`fetch-depth: 0` in `actions/checkout`) so the
|
|
795
|
+
needs the full history (`fetch-depth: 0` in `actions/checkout`) so the commit is available locally.
|
|
797
796
|
|
|
798
|
-
`
|
|
797
|
+
`get-affected-actors --since` without `--base` fails instead of guessing when:
|
|
799
798
|
|
|
800
|
-
- the
|
|
801
|
-
- the
|
|
799
|
+
- the commit is all zeros (GitHub sends this for a push that created the branch)
|
|
800
|
+
- the commit isn't in the local history (shallow checkout) or isn't an ancestor of `HEAD`
|
|
802
801
|
(the branch was force-pushed)
|
|
803
802
|
|
|
804
|
-
In those cases, rerun
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
If `HEAD` is the base commit, there is nothing to release and the command exits successfully.
|
|
803
|
+
In those cases, rerun it with a `--since` you pick yourself: an ancestor of `HEAD` from before the changes
|
|
804
|
+
you want to release (for a shallow checkout, fetch the full history instead). `--actors` only narrows which
|
|
805
|
+
of the changed Actors are reported; it never forces a build, so on its own it doesn't get past these errors.
|
|
806
|
+
If `HEAD` is `--since`, nothing is reported, so nothing is built or announced.
|
|
809
807
|
|
|
810
808
|
A failed release isn't retried by the next push: that push's base commit is the failed one's head, so
|
|
811
809
|
its changes are skipped. Re-run the failed workflow run, or release the Actors manually.
|
|
812
810
|
|
|
813
811
|
#### Repository check
|
|
814
812
|
|
|
815
|
-
`
|
|
813
|
+
`build` points each Actor's version at the `origin` remote. Because a build overwrites the
|
|
816
814
|
version's Git URL, building from a fork or a mirror would repoint the Actor. So, before building, the
|
|
817
815
|
remote is compared with the Git URL of the Actor's default version (the one its default build tag
|
|
818
816
|
points to), and the command fails if they differ or if the default version isn't built from a Git
|
package/bin/affected-actors.ts
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { type ChangedActors, getChangedActors } from './diff-changes.js';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
findCommitSha,
|
|
4
4
|
getChangedFilesBetween,
|
|
5
5
|
getChangelogAdditions,
|
|
6
|
-
getCommits,
|
|
7
6
|
getCommitsBetween,
|
|
8
|
-
|
|
7
|
+
getMergeBase,
|
|
9
8
|
resolveCommitSha,
|
|
10
9
|
resolveReleaseBaseCommit,
|
|
11
10
|
} from './git.js';
|
|
12
11
|
import { logger } from './logger.js';
|
|
13
|
-
import type { ActorConfig, Commit } from './types.js';
|
|
12
|
+
import type { ActorConfig, Commit, GitRange } from './types.js';
|
|
14
13
|
import { readConfigFile } from './utils/config/load-config.js';
|
|
15
14
|
|
|
16
15
|
// The range always ends at the checked-out commit: check out what you want to inspect
|
|
17
16
|
const HEAD = 'HEAD';
|
|
18
17
|
|
|
19
18
|
interface AffectedActorsOptions {
|
|
20
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Base usually points to the master branch
|
|
21
|
+
* Only what HEAD changes compared to base counts, like `git diff base...HEAD` or GitHub's "Files changed" tab:
|
|
22
|
+
* the branch's own changes, not what it merged in from base.
|
|
23
|
+
*/
|
|
21
24
|
base?: string;
|
|
22
25
|
/**
|
|
23
|
-
*
|
|
24
|
-
* ignored when it isn't
|
|
26
|
+
* With `base` it's the last validated commit of the branch: Actors that didn't change after it are left out.
|
|
27
|
+
* It's ignored when it is HEAD (a rerun) or isn't in the local history. Without `base` everything after it
|
|
28
|
+
* counts, and it must be an ancestor of HEAD (e.g. the last release).
|
|
25
29
|
*/
|
|
26
30
|
since?: string;
|
|
27
31
|
/** Glob relative to the repo root. Matching files count as changes even when .dockerignore lists them. */
|
|
@@ -30,77 +34,113 @@ interface AffectedActorsOptions {
|
|
|
30
34
|
ignore: string[];
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
/** The JSON document `get-affected-actors` prints. Versioned, since other commands and workflows consume it. */
|
|
33
38
|
interface AffectedActors {
|
|
34
|
-
|
|
39
|
+
version: 1;
|
|
40
|
+
head: string;
|
|
41
|
+
/** The commits, changed files and changelog describe the whole range, `--since` with `--base` only narrows the Actors. */
|
|
35
42
|
commits: Commit[];
|
|
36
43
|
changedFiles: string[];
|
|
37
44
|
changelog: string | null;
|
|
38
|
-
/**
|
|
39
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Actors with at least one functional change. They need a new build and tests. Only the names: the config file
|
|
47
|
+
* stays the source of truth for the rest.
|
|
48
|
+
*/
|
|
49
|
+
functionallyChangedActors: { actorFullName: string }[];
|
|
40
50
|
/** Actors whose changes are all cosmetic (README, CHANGELOG, cosmetic .actor JSON). They only need a release build. */
|
|
41
|
-
cosmeticallyChangedActors:
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface CommitRange {
|
|
45
|
-
commits: Commit[];
|
|
46
|
-
fromRef: string;
|
|
47
|
-
toRef: string;
|
|
51
|
+
cosmeticallyChangedActors: { actorFullName: string }[];
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
const getBranchRange = async (base: string, since: string | undefined): Promise<CommitRange> => {
|
|
52
|
-
const commits = await getCommits({ sourceBranch: HEAD, targetBranch: base, baseCommit: since });
|
|
53
|
-
return { commits, fromRef: `${commits[0].sha}~`, toRef: commits[commits.length - 1].sha };
|
|
54
|
-
};
|
|
54
|
+
const names = (configs: ActorConfig[]) => configs.map(({ actorFullName }) => ({ actorFullName }));
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
const
|
|
58
|
-
const sinceSha = await
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
/** The branch's last validated commit, unless it can't narrow anything down. */
|
|
57
|
+
const findValidatedSha = async (since: string, headSha: string): Promise<string | undefined> => {
|
|
58
|
+
const sinceSha = await findCommitSha(since);
|
|
59
|
+
if (!sinceSha) {
|
|
60
|
+
logger.info(
|
|
61
|
+
`Last validated commit ${since} is not in the local history (force-push?), checking the whole branch`,
|
|
62
|
+
);
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
if (sinceSha === headSha) {
|
|
66
|
+
logger.info('Last validated commit is HEAD, so this is a rerun: checking the whole branch from scratch');
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return sinceSha;
|
|
61
70
|
};
|
|
62
71
|
|
|
63
72
|
/**
|
|
64
|
-
* This is
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
73
|
+
* This is Juan's magical algorithm where we need just 2 git diffs to determine functional changes on Actors.
|
|
74
|
+
*
|
|
75
|
+
* Naively, we could just check branch HEAD changes vs base or parent of first commit and be done but
|
|
76
|
+
* we do this jiu-jitsu code to apply 2 optimizations:
|
|
77
|
+
* 1. On our PR branch, we track last validated commit (since) that was tested and so we only need to test new commits.
|
|
78
|
+
* 2. We also merge changes from the base branch (usually master) and we can consider those already validated as well.
|
|
79
|
+
*
|
|
80
|
+
* But there is annoying problem: even if both our branch and base are validated, they could have novel interactions and
|
|
81
|
+
* we need to retest those.
|
|
82
|
+
*
|
|
83
|
+
* So we look at:
|
|
84
|
+
* 1. The changes introduced by our branch since the last validated commit (`since..HEAD`).
|
|
85
|
+
* 2. The changes introduced by the branch compared to the base branch (`base...HEAD`). The important part is that this
|
|
86
|
+
* diff does check the whole branch, discarding the last validated commit (since) so it checks those novel interactions problem.
|
|
87
|
+
*
|
|
88
|
+
* The magic is that both need to contain changes for an Actor to be retested:
|
|
89
|
+
* 1. If we have changes only in since..HEAD (and not in base...HEAD), it means those changes came from merging the base alone
|
|
90
|
+
* which is already validated and there were actually no real changes introduced by our branch alone.
|
|
91
|
+
* 2. If we have changes only in base...HEAD (and not in since..HEAD), it means there are no new changes on our branch
|
|
92
|
+
* since it was last validated. base...HEAD has changes that we already validated.
|
|
93
|
+
*
|
|
94
|
+
* This enables to simply do those 2 diffs for each Actor's context separately and only Actors that have changes
|
|
95
|
+
* in both diffs need to be retested.
|
|
70
96
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
97
|
+
* THIS ASSUMES BRANCH IS UP TO DATE WITH BASE WHEN IT MERGES, which we currently require. Only then is HEAD what lands on
|
|
98
|
+
* base, and base's commits validated. Otherwise what lands is HEAD combined with base's newer commits, which nobody
|
|
99
|
+
* tested, however the Actors are picked.
|
|
100
|
+
*
|
|
101
|
+
* In each example the branch's functional change to Actor A was validated at `since`, then one more push follows:
|
|
102
|
+
*
|
|
103
|
+
* Example scenarios:
|
|
104
|
+
* - Our own branch only: a commit changes A's README and B's code.
|
|
105
|
+
* `since..HEAD`: A cosmetic, B functional. `base...HEAD`: A and B functional.
|
|
106
|
+
* → A cosmetic, B functional: only B is tested, A's code is still the validated one.
|
|
107
|
+
* - Cosmetic merge: the branch merges base, which only changed READMEs and the CHANGELOG.
|
|
108
|
+
* `since..HEAD`: every Actor cosmetic (CHANGELOG counts for all). `base...HEAD`: A functional, since the merged
|
|
109
|
+
* files now match base and drop out of it.
|
|
110
|
+
* → A cosmetic, the other Actors left out: nothing is tested.
|
|
111
|
+
* - Functional merge: the branch merges base, which changed code of A and of C.
|
|
112
|
+
* `since..HEAD`: A and C functional. `base...HEAD`: A functional.
|
|
113
|
+
* → A functional, C left out: A is tested, since base's change can interact with the branch's, while C only has
|
|
114
|
+
* base's change, which base already validated.
|
|
75
115
|
*/
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
116
|
+
const intersectChangedInBranchVsSince = (branch: ChangedActors, sinceValidated: ChangedActors): ChangedActors => {
|
|
117
|
+
const narrowed: ChangedActors = { functional: [], cosmetic: [] };
|
|
118
|
+
// All checks are done for each Actor separately, that's cleanest and most accurate
|
|
119
|
+
for (const actor of [...branch.functional, ...branch.cosmetic]) {
|
|
120
|
+
// base...HEAD
|
|
121
|
+
const changedFunctionallyOnBranch = branch.functional.includes(actor);
|
|
122
|
+
// since..HEAD
|
|
123
|
+
const changedFunctionallySince = sinceValidated.functional.includes(actor);
|
|
124
|
+
|
|
125
|
+
// changedSince is used for release. Release cares also about cosmetic.
|
|
126
|
+
const changedSince = changedFunctionallySince || sinceValidated.cosmetic.includes(actor);
|
|
127
|
+
|
|
128
|
+
// Nothing changed since the last validation, so the branch's change to this Actor is already validated
|
|
129
|
+
if (!changedSince) continue;
|
|
82
130
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
testFilesGlob,
|
|
92
|
-
});
|
|
93
|
-
logger.info(
|
|
94
|
-
own.functional.length > 0
|
|
95
|
-
? `[MERGE-FROM-TARGET-OPTIMIZATION]: Branch has ${own.functional.length} functional changes, cannot optimize, we continue with full check`
|
|
96
|
-
: '[MERGE-FROM-TARGET-OPTIMIZATION]: Branch itself has no functional changes, skipping tests',
|
|
97
|
-
);
|
|
98
|
-
return own;
|
|
131
|
+
// As we said, only intersection counts as functional, otherwise it's cosmetic
|
|
132
|
+
if (changedFunctionallyOnBranch && changedFunctionallySince) {
|
|
133
|
+
narrowed.functional.push(actor);
|
|
134
|
+
} else {
|
|
135
|
+
narrowed.cosmetic.push(actor);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return narrowed;
|
|
99
139
|
};
|
|
100
140
|
|
|
101
141
|
/**
|
|
102
|
-
* Finds the Actors whose context changed in the given range. `base` selects the branch's own
|
|
103
|
-
* (a PR), `since` the
|
|
142
|
+
* Finds the Actors whose context changed in the given range. `base` selects the branch's own changes
|
|
143
|
+
* (a PR), `since` the changes after a known commit (a release, or the last validated commit of a PR).
|
|
104
144
|
*/
|
|
105
145
|
export const getAffectedActors = async ({
|
|
106
146
|
base,
|
|
@@ -115,43 +155,44 @@ export const getAffectedActors = async ({
|
|
|
115
155
|
throw new Error('Pass --base, --since, or both to select the commits to inspect.');
|
|
116
156
|
}
|
|
117
157
|
|
|
118
|
-
const headSha = await resolveCommitSha(HEAD);
|
|
119
|
-
const { commits, fromRef, toRef } = base ? await getBranchRange(base, since) : await getRangeSince(headSha, since!);
|
|
120
158
|
const actorConfigs = await readConfigFile({ actors, ignore });
|
|
159
|
+
const getChangedInRange = async (range: GitRange) => {
|
|
160
|
+
const changedFiles = await getChangedFilesBetween(range.fromRef, range.toRef);
|
|
161
|
+
return {
|
|
162
|
+
changedFiles,
|
|
163
|
+
changedActors: await getChangedActors({
|
|
164
|
+
filepathsChanged: changedFiles,
|
|
165
|
+
actorConfigs,
|
|
166
|
+
range,
|
|
167
|
+
testFilesGlob,
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
};
|
|
121
171
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
headSha,
|
|
130
|
-
commits,
|
|
131
|
-
changedFiles,
|
|
132
|
-
changelog: await getChangelogAdditions(fromRef, toRef, changedFiles),
|
|
133
|
-
functionallyChangedActors: own && own.functional.length === 0 ? [] : changed.functional,
|
|
134
|
-
cosmeticallyChangedActors: own && own.functional.length + own.cosmetic.length === 0 ? [] : changed.cosmetic,
|
|
172
|
+
const headSha = await resolveCommitSha(HEAD);
|
|
173
|
+
const rangeVsBase: GitRange = {
|
|
174
|
+
// merge base = PR, release base = push to master (release)
|
|
175
|
+
fromRef: base ? await getMergeBase(base, HEAD) : await resolveReleaseBaseCommit(since!),
|
|
176
|
+
toRef: headSha,
|
|
135
177
|
};
|
|
136
|
-
};
|
|
137
178
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
headSha
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
179
|
+
// Falls back to rangeVsBase.fromRef when there's no (usable) last validated commit — same as
|
|
180
|
+
// intersecting the branch's changes with themselves, which is a no-op (see this function's docs).
|
|
181
|
+
const validatedSha = base && since ? await findValidatedSha(since, headSha) : undefined;
|
|
182
|
+
const sinceFromRef = validatedSha ?? rangeVsBase.fromRef;
|
|
183
|
+
|
|
184
|
+
const { changedFiles, changedActors: changedVsBase } = await getChangedInRange(rangeVsBase);
|
|
185
|
+
const { changedActors: changedSinceActors } = await getChangedInRange({ fromRef: sinceFromRef, toRef: headSha });
|
|
186
|
+
// See this function's docs to understand the algorithm: only intersection counts as functional, otherwise it's cosmetic.
|
|
187
|
+
const changedActors = intersectChangedInBranchVsSince(changedVsBase, changedSinceActors);
|
|
188
|
+
|
|
148
189
|
return {
|
|
149
190
|
version: 1,
|
|
150
191
|
head: headSha,
|
|
151
|
-
commits,
|
|
192
|
+
commits: await getCommitsBetween(rangeVsBase.fromRef, rangeVsBase.toRef),
|
|
152
193
|
changedFiles,
|
|
153
|
-
changelog,
|
|
154
|
-
functionallyChangedActors: names(
|
|
155
|
-
cosmeticallyChangedActors: names(
|
|
194
|
+
changelog: await getChangelogAdditions(rangeVsBase.fromRef, rangeVsBase.toRef, changedFiles),
|
|
195
|
+
functionallyChangedActors: names(changedActors.functional),
|
|
196
|
+
cosmeticallyChangedActors: names(changedActors.cosmetic),
|
|
156
197
|
};
|
|
157
198
|
};
|
package/bin/build.ts
CHANGED
|
@@ -14,8 +14,6 @@ type BuildPrActorOptions = {
|
|
|
14
14
|
|
|
15
15
|
// Fixed version number used to build from local source files, since there is no real version to track.
|
|
16
16
|
export const LOCAL_SOURCE_VERSION_NUMBER = '0.98';
|
|
17
|
-
// Version that build-for-test builds, so tests never touch the version users run
|
|
18
|
-
export const TEST_VERSION_NUMBER = '0.99';
|
|
19
17
|
// Stands for each Actor's default version: the one its default build tag points to
|
|
20
18
|
export const DEFAULT_VERSION_KEYWORD = 'default';
|
|
21
19
|
|