apify-test-tools 0.9.1-beta.9 → 0.10.1-beta.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.
Files changed (88) hide show
  1. package/.github/actions/checkout-restore-dependencies/action.yaml +2 -28
  2. package/.github/workflows/_move_major_tag.yaml +40 -6
  3. package/.github/workflows/manual_release_stable.yaml +1 -0
  4. package/.github/workflows/public_platform-tests.yaml +0 -12
  5. package/.github/workflows/public_pr-build-test.yaml +2 -24
  6. package/.github/workflows/public_push-build-latest.yaml +3 -14
  7. package/.github/workflows/public_review.yaml +0 -7
  8. package/.github/workflows-package-version +1 -1
  9. package/CHANGELOG.md +12 -2
  10. package/CONTRIBUTING.md +2 -4
  11. package/README.md +147 -36
  12. package/bin/build-from-local.ts +15 -11
  13. package/bin/build.ts +127 -67
  14. package/bin/git.ts +140 -0
  15. package/bin/main.ts +128 -91
  16. package/bin/slack.ts +5 -2
  17. package/bin/types.ts +0 -73
  18. package/bin/utils/config/parser.ts +6 -5
  19. package/bin/utils/config/structures/base.ts +5 -1
  20. package/bin/utils/config/structures/grouped.ts +40 -0
  21. package/bin/utils/config/structures/legacy.ts +3 -1
  22. package/bin/utils.ts +7 -15
  23. package/dist/bin/build-from-local.d.ts +2 -2
  24. package/dist/bin/build-from-local.d.ts.map +1 -1
  25. package/dist/bin/build-from-local.js +11 -6
  26. package/dist/bin/build-from-local.js.map +1 -1
  27. package/dist/bin/build.d.ts +26 -4
  28. package/dist/bin/build.d.ts.map +1 -1
  29. package/dist/bin/build.js +90 -47
  30. package/dist/bin/build.js.map +1 -1
  31. package/dist/bin/git.d.ts +40 -0
  32. package/dist/bin/git.d.ts.map +1 -1
  33. package/dist/bin/git.js +118 -0
  34. package/dist/bin/git.js.map +1 -1
  35. package/dist/bin/main.d.ts +1 -20
  36. package/dist/bin/main.d.ts.map +1 -1
  37. package/dist/bin/main.js +65 -62
  38. package/dist/bin/main.js.map +1 -1
  39. package/dist/bin/slack.d.ts.map +1 -1
  40. package/dist/bin/slack.js +4 -2
  41. package/dist/bin/slack.js.map +1 -1
  42. package/dist/bin/types.d.ts +0 -64
  43. package/dist/bin/types.d.ts.map +1 -1
  44. package/dist/bin/utils/config/parser.d.ts.map +1 -1
  45. package/dist/bin/utils/config/parser.js +6 -5
  46. package/dist/bin/utils/config/parser.js.map +1 -1
  47. package/dist/bin/utils/config/structures/base.d.ts +3 -2
  48. package/dist/bin/utils/config/structures/base.d.ts.map +1 -1
  49. package/dist/bin/utils/config/structures/base.js +1 -0
  50. package/dist/bin/utils/config/structures/base.js.map +1 -1
  51. package/dist/bin/utils/config/structures/grouped.d.ts +6 -0
  52. package/dist/bin/utils/config/structures/grouped.d.ts.map +1 -0
  53. package/dist/bin/utils/config/structures/grouped.js +29 -0
  54. package/dist/bin/utils/config/structures/grouped.js.map +1 -0
  55. package/dist/bin/utils/config/structures/legacy.d.ts +1 -1
  56. package/dist/bin/utils/config/structures/legacy.d.ts.map +1 -1
  57. package/dist/bin/utils/config/structures/legacy.js +1 -1
  58. package/dist/bin/utils/config/structures/legacy.js.map +1 -1
  59. package/dist/bin/utils.d.ts +5 -2
  60. package/dist/bin/utils.d.ts.map +1 -1
  61. package/dist/bin/utils.js +6 -12
  62. package/dist/bin/utils.js.map +1 -1
  63. package/dist/test/unit/bin/build.test.d.ts +2 -0
  64. package/dist/test/unit/bin/build.test.d.ts.map +1 -0
  65. package/dist/test/unit/bin/build.test.js +46 -0
  66. package/dist/test/unit/bin/build.test.js.map +1 -0
  67. package/dist/test/unit/bin/git.test.js +104 -1
  68. package/dist/test/unit/bin/git.test.js.map +1 -1
  69. package/dist/test/unit/bin/utils/config/load-config.test.js +1 -0
  70. package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
  71. package/dist/test/unit/bin/utils/config/parser.test.js +23 -5
  72. package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
  73. package/dist/test/unit/bin/utils/config/structures/grouped.test.d.ts +2 -0
  74. package/dist/test/unit/bin/utils/config/structures/grouped.test.d.ts.map +1 -0
  75. package/dist/test/unit/bin/utils/config/structures/grouped.test.js +75 -0
  76. package/dist/test/unit/bin/utils/config/structures/grouped.test.js.map +1 -0
  77. package/dist/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +4 -2
  79. package/test/unit/bin/build.test.ts +68 -0
  80. package/test/unit/bin/git.test.ts +122 -0
  81. package/test/unit/bin/utils/config/load-config.test.ts +1 -0
  82. package/test/unit/bin/utils/config/parser.test.ts +26 -5
  83. package/test/unit/bin/utils/config/structures/grouped.test.ts +90 -0
  84. package/bin/github.ts +0 -69
  85. package/dist/bin/github.d.ts +0 -11
  86. package/dist/bin/github.d.ts.map +0 -1
  87. package/dist/bin/github.js +0 -59
  88. package/dist/bin/github.js.map +0 -1
@@ -1,13 +1,6 @@
1
1
  name: Checkout and restore dependencies
2
2
  description: Checkout and restore dependencies
3
3
  inputs:
4
- working-directory:
5
- description: Working directory
6
- required: false
7
- default: .
8
- additional-working-directory:
9
- description: Additional working directory
10
- required: false
11
4
  npm-token:
12
5
  description: >-
13
6
  Token for installing private npm packages. Exported as both NPM_TOKEN (what a
@@ -60,30 +53,12 @@ runs:
60
53
  id: check-dependencies-cache
61
54
  uses: actions/cache@v5
62
55
  with:
63
- path: ${{ inputs.working-directory }}/node_modules
64
- key: modules-npm-${{ inputs.working-directory }}-${{ hashFiles(format('{0}/package-lock.json', inputs.working-directory)) }}
65
-
66
- - name: Cache additional dependencies npm
67
- id: check-additional-dependencies-cache
68
- if: inputs.additional-working-directory != ''
69
- uses: actions/cache@v5
70
- with:
71
- path: ${{ inputs.additional-working-directory }}/node_modules
72
- key: modules-npm-${{ inputs.additional-working-directory }}-${{ hashFiles(format('{0}/package-lock.json', inputs.additional-working-directory)) }}
56
+ path: node_modules
57
+ key: modules-npm-${{ hashFiles('package-lock.json') }}
73
58
 
74
59
  - name: install npm dependencies
75
60
  if: steps.check-dependencies-cache.outputs.cache-hit != 'true'
76
61
  shell: bash
77
- working-directory: ${{ inputs.working-directory }}
78
- env:
79
- NPM_TOKEN: ${{ inputs.npm-token }}
80
- NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
81
- run: npm ci
82
-
83
- - name: install additional npm dependencies
84
- if: inputs.additional-working-directory != '' && steps.check-additional-dependencies-cache.outputs.cache-hit != 'true'
85
- shell: bash
86
- working-directory: ${{ inputs.additional-working-directory }}
87
62
  env:
88
63
  NPM_TOKEN: ${{ inputs.npm-token }}
89
64
  NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
@@ -96,7 +71,6 @@ runs:
96
71
  # Needs the npm token too: a repo whose .npmrc routes all traffic through a private
97
72
  # registry has to authenticate even for public packages.
98
73
  shell: bash
99
- working-directory: ${{ inputs.working-directory }}
100
74
  env:
101
75
  NPM_TOKEN: ${{ inputs.npm-token }}
102
76
  NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
@@ -16,6 +16,13 @@ on:
16
16
  description: Commit to move the tag to. Defaults to the commit that triggered the caller.
17
17
  type: string
18
18
  default: ''
19
+ wait_for_publish:
20
+ description: >-
21
+ Set by a release that has just published the pinned version. npm accepts a publish
22
+ before the version is readable, so the check waits instead of treating the gap as
23
+ a held tag, and fails if the version never appears.
24
+ type: boolean
25
+ default: false
19
26
 
20
27
  permissions:
21
28
  contents: read
@@ -58,23 +65,50 @@ jobs:
58
65
 
59
66
  - name: Check the required package version is published
60
67
  id: check
68
+ env:
69
+ # npm accepts a publish and processes it asynchronously ("Your package is being
70
+ # processed and may take a few minutes to become available"). Observed at ~19
71
+ # minutes for a provenance-signed release, so the release path waits well past
72
+ # npm's own estimate rather than shipping a tag consumers cannot install against.
73
+ WAIT_SECONDS: ${{ inputs.wait_for_publish && 2400 || 0 }}
61
74
  run: |
62
75
  pinned_version=$(cat .github/workflows-package-version)
63
76
  echo "pinned_version=$pinned_version" >> "$GITHUB_OUTPUT"
64
77
 
65
- # The publish that satisfies a pin bump can still be in flight when this runs, so
66
- # give it a short grace period before deciding the tag has to stay put.
67
- for attempt in 1 2 3 4 5 6; do
68
- if npm view "apify-test-tools@$pinned_version" version >/dev/null 2>&1; then
78
+ deadline=$(( $(date +%s) + WAIT_SECONDS ))
79
+ while :; do
80
+ # --prefer-online so a 404 cached from an earlier attempt cannot outlive the
81
+ # publish it was polling for.
82
+ if output=$(npm view "apify-test-tools@$pinned_version" version --prefer-online 2>&1); then
69
83
  echo "apify-test-tools@$pinned_version is on npm"
70
84
  echo "published=true" >> "$GITHUB_OUTPUT"
71
85
  exit 0
72
86
  fi
73
- echo "apify-test-tools@$pinned_version is not on npm yet (attempt $attempt/6)"
74
- if [ "$attempt" -lt 6 ]; then sleep 20; fi
87
+
88
+ # Anything that is not "this version does not exist" is a registry, network or
89
+ # auth problem. Sitting in the retry loop would hide it behind a held tag.
90
+ if ! grep -q 'code E404' <<< "$output"; then
91
+ echo "::error::npm view failed for a reason other than the version being absent."
92
+ echo "$output"
93
+ exit 1
94
+ fi
95
+
96
+ if [ "$(date +%s)" -ge "$deadline" ]; then
97
+ break
98
+ fi
99
+ echo "apify-test-tools@$pinned_version is not on npm yet, retrying in 30s"
100
+ sleep 30
75
101
  done
102
+
76
103
  echo "published=false" >> "$GITHUB_OUTPUT"
77
104
 
105
+ # A release waited for its own publish and never saw it. That is a broken release,
106
+ # not a held tag, and it has to be red so it can be re-run once npm catches up.
107
+ if [ "$WAIT_SECONDS" -gt 0 ]; then
108
+ echo "::error::apify-test-tools@$pinned_version was published by this run but is still not readable after $((WAIT_SECONDS / 60))m. Re-run this job once it appears on npm."
109
+ exit 1
110
+ fi
111
+
78
112
  - name: Move tag
79
113
  if: steps.check.outputs.published == 'true'
80
114
  env:
@@ -91,3 +91,4 @@ jobs:
91
91
  uses: ./.github/workflows/_move_major_tag.yaml
92
92
  with:
93
93
  ref: ${{ needs.release_metadata.outputs.changelog_commitish }}
94
+ wait_for_publish: true
@@ -13,13 +13,6 @@ on:
13
13
  type: string
14
14
  default: ''
15
15
  required: false
16
- working-directory:
17
- type: string
18
- default: .
19
- required: false
20
- additional-working-directory:
21
- type: string
22
- required: false
23
16
  # Defaults to #notif-<repo-name> when empty, matching the previous behavior.
24
17
  report-slack-channel:
25
18
  type: string
@@ -31,16 +24,11 @@ jobs:
31
24
  name: Scheduled Platform tests
32
25
  if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
33
26
  runs-on: ubuntu-latest
34
- defaults:
35
- run:
36
- working-directory: ${{ inputs.working-directory }}
37
27
  steps:
38
28
  - name: Setup repository and dependencies
39
29
  # For testing, you have to temporarily change the branch
40
30
  uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@workflows-v0
41
31
  with:
42
- working-directory: ${{ inputs.working-directory }}
43
- additional-working-directory: ${{ inputs.additional-working-directory }}
44
32
  npm-token: ${{ secrets.NPM_TOKEN }}
45
33
 
46
34
  - name: Test
@@ -3,13 +3,6 @@ name: Test
3
3
  on:
4
4
  workflow_call:
5
5
  inputs:
6
- working-directory:
7
- type: string
8
- default: .
9
- required: false
10
- additional-working-directory:
11
- type: string
12
- required: false
13
6
  skip-platform-tests:
14
7
  type: boolean
15
8
  default: false
@@ -28,17 +21,12 @@ jobs:
28
21
  platformTest:
29
22
  if: ${{ !inputs.skip-platform-tests }}
30
23
  runs-on: ubuntu-latest
31
- defaults:
32
- run:
33
- working-directory: ${{ inputs.working-directory }}
34
24
  steps:
35
25
  - name: Setup repository and dependencies
36
26
  id: setup
37
27
  # For testing, you have to temporarily change the branch
38
28
  uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@workflows-v0
39
29
  with:
40
- working-directory: ${{ inputs.working-directory }}
41
- additional-working-directory: ${{ inputs.additional-working-directory }}
42
30
  # Some repos use private npm packages, they need to pass NPM_TOKEN secret to this workflow
43
31
  npm-token: ${{ secrets.NPM_TOKEN }}
44
32
 
@@ -87,13 +75,9 @@ jobs:
87
75
  # environment variables rather than being interpolated into it.
88
76
  HEAD_REF: ${{ github.head_ref }}
89
77
  BASE_REF: ${{ github.base_ref }}
90
- WORKING_DIRECTORY: ${{ inputs.working-directory }}
91
78
  run: |
92
- work_dir="$WORKING_DIRECTORY"
93
- [ "$work_dir" = "." ] && work_dir=""
94
- prefix="${work_dir:+$work_dir/}test/platform/"
95
- changed=$(git diff --name-only --diff-filter=d "origin/$BASE_REF...HEAD" -- "$prefix" \
96
- | sed "s|^$prefix|./test/platform/|" | tr '\n' ' ')
79
+ changed=$(git diff --name-only --diff-filter=d "origin/$BASE_REF...HEAD" -- test/platform/ \
80
+ | sed "s|^|./|" | tr '\n' ' ')
97
81
  branch_actors=$(npx apify-test-tools get-affected-actors \
98
82
  --source-branch "origin/$HEAD_REF" \
99
83
  --target-branch "origin/$BASE_REF")
@@ -135,17 +119,11 @@ jobs:
135
119
  unitTest:
136
120
  # TODO: only run on changes to code
137
121
  runs-on: ubuntu-latest
138
- defaults:
139
- run:
140
- working-directory: ${{ inputs.working-directory }}
141
-
142
122
  steps:
143
123
  - name: Setup repository and dependencies
144
124
  # For testing, you have to temporarily change the branch
145
125
  uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@workflows-v0
146
126
  with:
147
- working-directory: ${{ inputs.working-directory }}
148
- additional-working-directory: ${{ inputs.additional-working-directory }}
149
127
  npm-token: ${{ secrets.NPM_TOKEN }}
150
128
 
151
129
  - name: TypeScript
@@ -3,13 +3,6 @@ name: Build latest and report to slack
3
3
  on:
4
4
  workflow_call:
5
5
  inputs:
6
- working-directory:
7
- type: string
8
- default: .
9
- required: false
10
- additional-working-directory:
11
- type: string
12
- required: false
13
6
  # Defaults to #notif-<repo-name> when empty, matching the previous behavior.
14
7
  report-slack-channel:
15
8
  type: string
@@ -24,20 +17,16 @@ jobs:
24
17
  !contains(github.event.head_commit.message, '[skip platform-test]')
25
18
 
26
19
  runs-on: ubuntu-latest
27
- defaults:
28
- run:
29
- working-directory: ${{ inputs.working-directory }}
30
-
31
20
  steps:
32
21
  - name: Setup repository and dependencies
33
22
  id: setup
34
23
  # For testing, you have to temporarily change the branch
35
24
  uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@workflows-v0
36
25
  with:
37
- working-directory: ${{ inputs.working-directory }}
38
- additional-working-directory: ${{ inputs.additional-working-directory }}
39
26
  npm-token: ${{ secrets.NPM_TOKEN }}
40
27
 
28
+ # --base-commit is the last commit that is already released: the push's "before" commit.
29
+ # Everything after it up to HEAD gets released. See the "Releasing Actors" section of the README.
41
30
  # run-with-apify-tokens.mjs reads the token names from in apify-test-tools.config.json
42
31
  # and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
43
32
  # The library never sees secrets it doesn't need
@@ -47,7 +36,7 @@ jobs:
47
36
  SLACK_TOKEN_RELEASES_BOT: ${{ secrets.SLACK_TOKEN_RELEASES_BOT }}
48
37
  run: |
49
38
  node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
50
- npx apify-test-tools release --push-event-path ${{ github.event_path }} --release-slack-channel "#delivery-public-actors" --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" --use-docker-cache
39
+ npx apify-test-tools release --base-commit ${{ github.event.before }} --release-slack-channel "#delivery-public-actors" --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" --use-docker-cache
51
40
 
52
41
  - name: Delete old builds
53
42
  env:
@@ -26,13 +26,6 @@ on:
26
26
  type: string
27
27
  description: Model to use for Claude Code (best, sonnet, opus, haiku, sonnet[1m], opus[1m], opusplan)
28
28
  required: false
29
- working-directory:
30
- type: string
31
- default: .
32
- required: false
33
- additional-working-directory:
34
- type: string
35
- required: false
36
29
  secrets:
37
30
  ANTHROPIC_API_KEY:
38
31
  required: true
@@ -1 +1 @@
1
- 0.9.0
1
+ 0.10.0
package/CHANGELOG.md CHANGED
@@ -3,23 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
5
  <!-- git-cliff-unreleased-start -->
6
- ## 0.9.1 - **not yet released**
6
+ ## 0.10.1 - **not yet released**
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ - **internal-workflows:** Wait out npm&#x27;s async publish before holding the major tag ([#160](https://github.com/apify/apify-test-tools/pull/160)) ([78434f0](https://github.com/apify/apify-test-tools/commit/78434f03d288ecbd383d55d56a1610f06ded298f)) by [@metalwarrior665](https://github.com/metalwarrior665)
11
+
12
+
13
+ <!-- git-cliff-unreleased-end -->
14
+ ## [0.10.0](https://github.com/apify/apify-test-tools/releases/tag/v0.10.0) (2026-09-30)
7
15
 
8
16
  ### 🚀 Features
9
17
 
10
18
  - **config/modes:** Lay groundwork for multiple configuration structures ([#142](https://github.com/apify/apify-test-tools/pull/142)) ([7e18638](https://github.com/apify/apify-test-tools/commit/7e18638a8d3523154dbe819312c2b16b54c548ff)) by [@JuanGalilea](https://github.com/JuanGalilea)
11
19
  - **workflows:** Host the reusable GitHub workflows in this repo ([#120](https://github.com/apify/apify-test-tools/pull/120)) ([10f089d](https://github.com/apify/apify-test-tools/commit/10f089d85146ceb7cdae964cfa1fc2577df43a37)) by [@metalwarrior665](https://github.com/metalwarrior665)
20
+ - **mode:** Add grouped config mode ([#143](https://github.com/apify/apify-test-tools/pull/143)) ([9d0b93e](https://github.com/apify/apify-test-tools/commit/9d0b93e194a2cd810a31bcef4ca9457fd9ebfb9b)) by [@JuanGalilea](https://github.com/JuanGalilea)
12
21
 
13
22
  ### 🐛 Bug Fixes
14
23
 
15
24
  - Apply default actor timeout to runs ([#134](https://github.com/apify/apify-test-tools/pull/134)) ([82f5f7d](https://github.com/apify/apify-test-tools/commit/82f5f7d4de990db7ad8edf940c19ffd6b20bad13)) by [@Patai5](https://github.com/Patai5)
25
+ - Copy cli to zip to KV store local builds ([#151](https://github.com/apify/apify-test-tools/pull/151)) ([ecdda58](https://github.com/apify/apify-test-tools/commit/ecdda58b68a8c7173a023e3a92689bc4fa0ce53c)) by [@gytelio](https://github.com/gytelio), closes [#140](https://github.com/apify/apify-test-tools/issues/140)
16
26
 
17
27
  ### 🚜 Refactor
18
28
 
19
29
  - **workflows:** [**breaking**] Rename the floating tag to workflows-v0 ([#146](https://github.com/apify/apify-test-tools/pull/146)) ([c48fb22](https://github.com/apify/apify-test-tools/commit/c48fb223c885c4e5de7e5f64d8c14f6d03fcc7de)) by [@metalwarrior665](https://github.com/metalwarrior665)
30
+ - **bin:** [**breaking**] Remove GitHub event dependency from release command ([#153](https://github.com/apify/apify-test-tools/pull/153)) ([e6953aa](https://github.com/apify/apify-test-tools/commit/e6953aa095d1a92638c6d72441e1b312d9f835c3)) by [@metalwarrior665](https://github.com/metalwarrior665), closes [#131](https://github.com/apify/apify-test-tools/issues/131)
20
31
 
21
32
 
22
- <!-- git-cliff-unreleased-end -->
23
33
  ## [0.9.0](https://github.com/apify/apify-test-tools/releases/tag/v0.9.0) (2026-08-11)
24
34
 
25
35
  ### 🚀 Features
package/CONTRIBUTING.md CHANGED
@@ -10,7 +10,6 @@ The package consists of three parts:
10
10
 
11
11
  - `bin/build.ts` actor building
12
12
  - `bin/git.ts` git wrappers for getting relevant git commits and changed files
13
- - `bin/github.ts` github push event parsing
14
13
  - `bin/main.ts` entrypoing
15
14
  - `bin/slack.ts` sending notifications to slack
16
15
  - `bin/test-report.ts` processing vitest's test reports
@@ -40,12 +39,11 @@ git clone git@github.com:apify-store/testing-repo-for-github-actions.git
40
39
 
41
40
  #### Working on the CLI
42
41
 
43
- To work on the library, you just need to define `GITHUB_WORKSPACE` to tell the cli where you repo is located:
42
+ To work on the library, point the cli to your repo with `--workspace` (it defaults to the current directory):
44
43
 
45
44
  ```sh
46
- export GITHUB_WORKSPACE=../path/to/testing-repo-for-github-actions # path to the repo
47
45
  npx tsx bin/main.ts --help
48
- npx tsx bin/main.ts get-commits --target-branch master --source-branch feat/testing-feature-branch
46
+ npx tsx bin/main.ts --workspace ../path/to/testing-repo-for-github-actions get-commits --target-branch master --source-branch feat/testing-feature-branch
49
47
  ```
50
48
 
51
49
  #### Working on the library
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. This file tells the tool which actors live in the repo, how to identify them, and which token to use.
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
- ```json
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,6 +75,82 @@ 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
@@ -172,6 +226,10 @@ jobs:
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
@@ -537,7 +595,7 @@ The main local flow is:
537
595
  4. Build Actors on Apify (with your new code)
538
596
  5. Run tests against those builds. You can change tests and run on the same builds.
539
597
 
540
- `cd` into the actor repository you want to work with (or use `--workspace`).
598
+ Run every command from the root of the actor repository you want to work with (or point `--workspace` at it).
541
599
 
542
600
  #### 4. Build affected Actors
543
601
 
@@ -547,7 +605,6 @@ Requires `APIFY_TOKEN_<USERNAME>` for all Apify users that own your Actors (e.g.
547
605
 
548
606
  ```bash
549
607
  APIFY_TOKEN_JOHN_DOE=<token> \
550
- GITHUB_WORKSPACE=. \
551
608
  npx apify-test-tools build \
552
609
  --target-branch origin/master \
553
610
  --source-branch origin/my-dummy-branch \
@@ -562,11 +619,10 @@ Remove `--dry-run` to actually trigger builds and update the branch names/ The c
562
619
 
563
620
  #### Build from local source (no push needed)
564
621
 
565
- 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 uploaded as `SOURCE_FILES`), skipping steps 1-4 above.
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.
566
623
 
567
624
  ```bash
568
625
  APIFY_TOKEN_JOHN_DOE=<token> \
569
- GITHUB_WORKSPACE=. \
570
626
  npx apify-test-tools build-from-local --actors john.doe/my-actor
571
627
  ```
572
628
 
@@ -575,8 +631,7 @@ Pass a hardcoded actor name via `--actors` to build only that Actor (comma-separ
575
631
  ```bash
576
632
  # Build from local source and capture output
577
633
  BUILDS=$(APIFY_TOKEN_JOHN_DOE=apify_api_xxx \
578
- GITHUB_WORKSPACE=. \
579
- npx apify-test-tools build-from-local --actors apify/my-actor)
634
+ npx apify-test-tools build-from-local --actors apify/my-actor)
580
635
  ```
581
636
 
582
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.
@@ -598,8 +653,7 @@ TESTER_APIFY_TOKEN=<token> \
598
653
  ```bash
599
654
  # Build and capture output
600
655
  BUILDS=$(APIFY_TOKEN_JOHN_DOE=apify_api_xxx \
601
- GITHUB_WORKSPACE=. \
602
- npx apify-test-tools build \
656
+ npx apify-test-tools build \
603
657
  --target-branch origin/master \
604
658
  --source-branch origin/my-dummy-branch)
605
659
 
@@ -609,10 +663,67 @@ TESTER_APIFY_TOKEN=apify_api_yyy \
609
663
  npx vitest --run --maxConcurrency 20 --fileParallelism=true --maxWorkers 100 test/platform
610
664
  ```
611
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
+
612
723
  #### Dev mode
613
724
 
614
725
  For development on `apify-test-tools` itself, use `tsx` directly:
615
726
 
616
727
  ```bash
617
- GITHUB_WORKSPACE=local-clone tsx bin/main.ts get-actor-configs
728
+ tsx bin/main.ts --workspace local-clone get-actor-configs
618
729
  ```