apify-test-tools 0.10.2-beta.12 → 0.10.2-beta.14

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 (120) hide show
  1. package/.github/cli/apify-test-tools.mjs +123 -128
  2. package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +1 -1
  3. package/.github/workflows/public_platform-tests.yaml +36 -22
  4. package/.github/workflows/public_pr-build-test.yaml +7 -6
  5. package/.github/workflows/public_push-build-latest.yaml +6 -4
  6. package/.prettierignore +1 -1
  7. package/CHANGELOG.md +3 -0
  8. package/CONTRIBUTING.md +8 -6
  9. package/README.md +10 -11
  10. package/bin/actor-ids.ts +38 -0
  11. package/bin/build.ts +1 -1
  12. package/bin/git.ts +20 -92
  13. package/bin/main.ts +87 -73
  14. package/bin/notify.ts +198 -0
  15. package/bin/run-tests.ts +141 -0
  16. package/bin/slack.ts +16 -87
  17. package/bin/stdin-input.ts +98 -7
  18. package/bin/test-report.ts +141 -140
  19. package/bin/types.ts +0 -9
  20. package/dist/bin/actor-ids.d.ts +16 -0
  21. package/dist/bin/actor-ids.d.ts.map +1 -0
  22. package/dist/bin/actor-ids.js +24 -0
  23. package/dist/bin/actor-ids.js.map +1 -0
  24. package/dist/bin/build.js +1 -1
  25. package/dist/bin/build.js.map +1 -1
  26. package/dist/bin/git.d.ts +6 -16
  27. package/dist/bin/git.d.ts.map +1 -1
  28. package/dist/bin/git.js +15 -75
  29. package/dist/bin/git.js.map +1 -1
  30. package/dist/bin/main.js +80 -51
  31. package/dist/bin/main.js.map +1 -1
  32. package/dist/bin/notify.d.ts +35 -0
  33. package/dist/bin/notify.d.ts.map +1 -0
  34. package/dist/bin/notify.js +131 -0
  35. package/dist/bin/notify.js.map +1 -0
  36. package/dist/bin/run-tests.d.ts +21 -0
  37. package/dist/bin/run-tests.d.ts.map +1 -0
  38. package/dist/bin/run-tests.js +116 -0
  39. package/dist/bin/run-tests.js.map +1 -0
  40. package/dist/bin/slack.d.ts +2 -17
  41. package/dist/bin/slack.d.ts.map +1 -1
  42. package/dist/bin/slack.js +17 -58
  43. package/dist/bin/slack.js.map +1 -1
  44. package/dist/bin/stdin-input.d.ts +71 -9
  45. package/dist/bin/stdin-input.d.ts.map +1 -1
  46. package/dist/bin/stdin-input.js +76 -6
  47. package/dist/bin/stdin-input.js.map +1 -1
  48. package/dist/bin/test-report.d.ts +69 -8
  49. package/dist/bin/test-report.d.ts.map +1 -1
  50. package/dist/bin/test-report.js +76 -67
  51. package/dist/bin/test-report.js.map +1 -1
  52. package/dist/bin/types.d.ts +0 -8
  53. package/dist/bin/types.d.ts.map +1 -1
  54. package/dist/lib/lib.d.ts +0 -3
  55. package/dist/lib/lib.d.ts.map +1 -1
  56. package/dist/lib/lib.js +5 -10
  57. package/dist/lib/lib.js.map +1 -1
  58. package/dist/test/bundle/cli-bundle.test.js +40 -48
  59. package/dist/test/bundle/cli-bundle.test.js.map +1 -1
  60. package/dist/test/e2e/cli-bundle.e2e.test.js +15 -16
  61. package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
  62. package/dist/test/e2e/git-build-release.e2e.test.js +2 -2
  63. package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -1
  64. package/dist/test/unit/bin/actor-ids.test.d.ts +2 -0
  65. package/dist/test/unit/bin/actor-ids.test.d.ts.map +1 -0
  66. package/dist/test/unit/bin/actor-ids.test.js +32 -0
  67. package/dist/test/unit/bin/actor-ids.test.js.map +1 -0
  68. package/dist/test/unit/bin/build-cli.test.js +8 -3
  69. package/dist/test/unit/bin/build-cli.test.js.map +1 -1
  70. package/dist/test/unit/bin/compiled-cli.d.ts +1 -1
  71. package/dist/test/unit/bin/compiled-cli.d.ts.map +1 -1
  72. package/dist/test/unit/bin/compiled-cli.js +12 -5
  73. package/dist/test/unit/bin/compiled-cli.js.map +1 -1
  74. package/dist/test/unit/bin/diff-changes.test.js +1 -1
  75. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  76. package/dist/test/unit/bin/git.test.js +4 -142
  77. package/dist/test/unit/bin/git.test.js.map +1 -1
  78. package/dist/test/unit/bin/notify.test.d.ts +2 -0
  79. package/dist/test/unit/bin/notify.test.d.ts.map +1 -0
  80. package/dist/test/unit/bin/notify.test.js +222 -0
  81. package/dist/test/unit/bin/notify.test.js.map +1 -0
  82. package/dist/test/unit/bin/stdin-input.test.js +51 -9
  83. package/dist/test/unit/bin/stdin-input.test.js.map +1 -1
  84. package/dist/test/unit/bin/test-cli.test.d.ts +2 -0
  85. package/dist/test/unit/bin/test-cli.test.d.ts.map +1 -0
  86. package/dist/test/unit/bin/test-cli.test.js +115 -0
  87. package/dist/test/unit/bin/test-cli.test.js.map +1 -0
  88. package/dist/test/unit/bin/test-report.test.d.ts +2 -0
  89. package/dist/test/unit/bin/test-report.test.d.ts.map +1 -0
  90. package/dist/test/unit/bin/test-report.test.js +100 -0
  91. package/dist/test/unit/bin/test-report.test.js.map +1 -0
  92. package/dist/tsconfig.tsbuildinfo +1 -1
  93. package/docs/cli.md +131 -53
  94. package/docs/lib.md +21 -16
  95. package/docs/workflows.md +24 -23
  96. package/eslint.config.mjs +1 -1
  97. package/lib/lib.ts +5 -10
  98. package/package.json +1 -1
  99. package/test/bundle/cli-bundle.test.ts +47 -60
  100. package/test/e2e/cli-bundle.e2e.test.ts +18 -25
  101. package/test/e2e/git-build-release.e2e.test.ts +2 -2
  102. package/test/unit/bin/actor-ids.test.ts +43 -0
  103. package/test/unit/bin/build-cli.test.ts +9 -3
  104. package/test/unit/bin/compiled-cli.ts +11 -5
  105. package/test/unit/bin/diff-changes.test.ts +1 -1
  106. package/test/unit/bin/git.test.ts +3 -197
  107. package/test/unit/bin/notify.test.ts +299 -0
  108. package/test/unit/bin/stdin-input.test.ts +68 -9
  109. package/test/unit/bin/test-cli.test.ts +137 -0
  110. package/test/unit/bin/test-report.test.ts +118 -0
  111. package/dist/test/unit/bin/logging.test.d.ts +0 -2
  112. package/dist/test/unit/bin/logging.test.d.ts.map +0 -1
  113. package/dist/test/unit/bin/logging.test.js +0 -43
  114. package/dist/test/unit/bin/logging.test.js.map +0 -1
  115. package/dist/test/unit/bin/slack.test.d.ts +0 -2
  116. package/dist/test/unit/bin/slack.test.d.ts.map +0 -1
  117. package/dist/test/unit/bin/slack.test.js +0 -71
  118. package/dist/test/unit/bin/slack.test.js.map +0 -1
  119. package/test/unit/bin/logging.test.ts +0 -52
  120. package/test/unit/bin/slack.test.ts +0 -84
@@ -25,7 +25,7 @@ name: 'Platform tests: Claude failure investigation and fix'
25
25
  # subtest: core
26
26
  # secrets:
27
27
  # TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
28
- # SLACK_TOKEN_TESTS_BOT: ${{ secrets.SLACK_TOKEN_TESTS_BOT }}
28
+ # SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
29
29
  #
30
30
  # handle-failure:
31
31
  # needs: platformTestsCore
@@ -13,7 +13,7 @@ on:
13
13
  type: string
14
14
  default: ''
15
15
  required: false
16
- # Defaults to #notif-<repo-name> when empty, matching the previous behavior.
16
+ # Defaults to #notif-<repo-name> when empty.
17
17
  report-slack-channel:
18
18
  type: string
19
19
  default: ''
@@ -26,7 +26,7 @@ on:
26
26
  TESTER_APIFY_TOKEN:
27
27
  required: true
28
28
  description: Apify token the platform tests run Actors with.
29
- SLACK_TOKEN_TESTS_BOT:
29
+ SLACK_BOT_TOKEN:
30
30
  required: false
31
31
  description: Slack token for reporting test results.
32
32
 
@@ -55,32 +55,46 @@ jobs:
55
55
  exit 1
56
56
  fi
57
57
 
58
+ # Runs the repo's test code and its whole import graph, so it gets the tester token only.
59
+ # Fails on failing tests, but writes the report anyway for the notification below.
60
+ # Every Actor in the config, on its default build: the one users run
58
61
  - name: Test
62
+ id: test
63
+ # bash rather than the default shell, for pipefail
64
+ shell: bash
59
65
  env:
60
66
  TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
61
- SLACK_TOKEN_TESTS_BOT: ${{ secrets.SLACK_TOKEN_TESTS_BOT }}
62
67
  run: |
63
- set +e
64
- export RUN_ALL_PLATFORM_TESTS=1
65
- npx vitest ./test/platform/${{ inputs.test-files-glob || inputs.subtest }} \
66
- --run \
67
- --reporter=default \
68
- --reporter=json \
69
- --outputFile=./test-output.json \
70
- --maxConcurrency 20 \
71
- --fileParallelism=true \
72
- --maxWorkers 100
73
- return_code=$?
74
- apify-test-tools report-tests \
75
- --report-file ./test-output.json \
76
- --workflow-name "${{ github.workflow }}" \
77
- --job-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
78
- --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}"
79
- exit $return_code
68
+ apify-test-tools get-actor-configs \
69
+ | apify-test-tools test ./test/platform/${{ inputs.test-files-glob || inputs.subtest }} \
70
+ --reporter github-actions --vitest-output-file ./test-output.json > test-report.json
80
71
 
81
- - name: Upload Vitest JSON artifact
72
+ # Posts only when the tests failed, but checks the Slack token on every run. Runs whenever the tests
73
+ # did, also after a failure, but not when the token check stopped the job before them.
74
+ - name: Notify
75
+ if: ${{ !cancelled() && steps.test.outcome != 'skipped' }}
76
+ env:
77
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
78
+ # Passed through the environment, so a quote in the workflow name can't break the command
79
+ WORKFLOW_NAME: ${{ github.workflow }}
80
+ JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
81
+ run: |
82
+ # Empty when `test` itself failed before running vitest, its error is in the Test step
83
+ if [ ! -s test-report.json ]; then
84
+ echo "No test report, so there is nothing to notify about"
85
+ exit 0
86
+ fi
87
+ apify-test-tools notify \
88
+ --slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" \
89
+ --title "$WORKFLOW_NAME" \
90
+ --job-url "$JOB_URL" \
91
+ < test-report.json
92
+
93
+ - name: Upload test reports
82
94
  uses: actions/upload-artifact@v6
83
95
  if: always()
84
96
  with:
85
97
  name: vitest-results
86
- path: ./test-output.json
98
+ path: |
99
+ ./test-output.json
100
+ ./test-report.json
@@ -100,7 +100,6 @@ jobs:
100
100
  # and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
101
101
  # The library never sees secrets it doesn't need
102
102
  - name: Build
103
- id: build
104
103
  # bash rather than the default shell, for pipefail
105
104
  shell: bash
106
105
  env:
@@ -108,17 +107,19 @@ jobs:
108
107
  SCRIPTS_PATH: ${{ steps.setup.outputs.scripts-path }}
109
108
  REQUIRE_ENV_VARS_IN_SYNC: ${{ inputs.require-env-vars-in-sync }}
110
109
  run: |
111
- actor_builds=$(jq '{ actors: .functionallyChangedActors }' affected.json \
110
+ jq '{ actors: .functionallyChangedActors }' affected.json \
112
111
  | node "$SCRIPTS_PATH/run-with-apify-tokens.mjs" apify-test-tools build --version-number 0.99 \
113
- --require-env-vars-in-sync="$REQUIRE_ENV_VARS_IN_SYNC")
114
- echo "actor_builds=$actor_builds" | tee -a "$GITHUB_OUTPUT"
112
+ --require-env-vars-in-sync="$REQUIRE_ENV_VARS_IN_SYNC" \
113
+ | tee builds.json
115
114
 
116
115
  # Runs the repo's test code and its whole import graph, so it gets the tester token only.
117
116
  - name: Test
118
117
  env:
119
- ACTOR_BUILDS: ${{ steps.build.outputs.actor_builds }}
120
118
  TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
121
- run: npx vitest ./test/platform/${{ inputs.test-files-glob }} --run --maxConcurrency 20 --fileParallelism=true --maxWorkers 100
119
+ # The github-actions reporter annotates the PR with the failing tests
120
+ run: |
121
+ apify-test-tools test ./test/platform/${{ inputs.test-files-glob }} --reporter github-actions \
122
+ < builds.json > test-report.json
122
123
 
123
124
  # NOTE: This is an optimization that if we did functional changes and later only cosmetic changes (e.g. dev readme), we will compare changes files only for the last commit. This must run after tests because we want to cache the latest commit only if the tests are successful
124
125
  - name: Store last validated commit to cache
@@ -3,7 +3,7 @@ name: Build latest and report to slack
3
3
  on:
4
4
  workflow_call:
5
5
  inputs:
6
- # Defaults to #notif-<repo-name> when empty, matching the previous behavior.
6
+ # Defaults to #notif-<repo-name> when empty.
7
7
  report-slack-channel:
8
8
  type: string
9
9
  default: ''
@@ -15,7 +15,7 @@ on:
15
15
  NPM_TOKEN:
16
16
  required: false
17
17
  description: Read-only token for installing private npm packages.
18
- SLACK_TOKEN_RELEASES_BOT:
18
+ SLACK_BOT_TOKEN:
19
19
  required: false
20
20
  description: Slack token for announcing releases.
21
21
  CONFIG_SECRETS:
@@ -68,9 +68,11 @@ jobs:
68
68
  # Skips the announcement when the push brought no new commits
69
69
  - name: Announce the release on Slack
70
70
  env:
71
- SLACK_TOKEN_RELEASES_BOT: ${{ secrets.SLACK_TOKEN_RELEASES_BOT }}
71
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
72
72
  run: |
73
- apify-test-tools notify-release --release-slack-channel "#delivery-public-actors" --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" < affected.json
73
+ apify-test-tools notify \
74
+ --slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" \
75
+ < affected.json
74
76
 
75
77
  - name: Delete old builds
76
78
  env:
package/.prettierignore CHANGED
@@ -1,5 +1,5 @@
1
1
  CHANGELOG.md
2
-
2
+ .cli-test-*
3
3
  # Operating instructions read by Claude, not prose for humans. Prettier collapses the nested bullet
4
4
  # list under "do not visit, fetch, infer, or evaluate the following external links" into one run-on
5
5
  # line, which changes what the model is told. Keeping it byte-identical also makes re-syncing it
package/CHANGELOG.md CHANGED
@@ -23,10 +23,13 @@ All notable changes to this project will be documented in this file.
23
23
  - 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)
24
24
  - **git:** List both paths of moved files when detecting changes ([#215](https://github.com/apify/apify-test-tools/pull/215)) ([44ba565](https://github.com/apify/apify-test-tools/commit/44ba565242c1ac3daea281a648acb28712bb4363)) by [@metalwarrior665](https://github.com/metalwarrior665)
25
25
  - **workflows:** Load setup action and review prompt from the called commit ([#226](https://github.com/apify/apify-test-tools/pull/226)) ([7524711](https://github.com/apify/apify-test-tools/commit/752471135d4d147f6f6492a26c08af388d7ccf72)) by [@metalwarrior665](https://github.com/metalwarrior665)
26
+ - **test:** Clean up compiled CLI dir when build fails ([#224](https://github.com/apify/apify-test-tools/pull/224)) ([f9f2f5c](https://github.com/apify/apify-test-tools/commit/f9f2f5c64cfe936ee0b72a0fbe7408282c68fbac)) by [@JuanGalilea](https://github.com/JuanGalilea)
27
+ - **cli-test:** Look up actor raw IDs for tests that reference them ([#234](https://github.com/apify/apify-test-tools/pull/234)) ([315baf4](https://github.com/apify/apify-test-tools/commit/315baf495dae984cbcd7056a101935536d7b0011)) by [@metalwarrior665](https://github.com/metalwarrior665)
26
28
 
27
29
  ### 🚜 Refactor
28
30
 
29
31
  - [**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)
32
+ - [**breaking**] Remove legacy get-commits, get-latest-commit and get-changed-files ([#228](https://github.com/apify/apify-test-tools/pull/228)) ([d254a6d](https://github.com/apify/apify-test-tools/commit/d254a6dd14ac9cb027a26ec73ca20a6ee10460ec)) by [@metalwarrior665](https://github.com/metalwarrior665)
30
33
 
31
34
 
32
35
  <!-- git-cliff-unreleased-end -->
package/CONTRIBUTING.md CHANGED
@@ -11,8 +11,10 @@ The package consists of three parts:
11
11
  - `bin/build.ts` actor building
12
12
  - `bin/git.ts` git wrappers for getting relevant git commits and changed files
13
13
  - `bin/main.ts` entrypoing
14
- - `bin/slack.ts` sending notifications to slack
15
- - `bin/test-report.ts` processing vitest's test reports
14
+ - `bin/run-tests.ts` running the repository's vitest for the `test` command
15
+ - `bin/test-report.ts` turning vitest's JSON report into the report `test` prints
16
+ - `bin/notify.ts` the `notify` command: what it says about test runs and releases, and its targets (Slack for now)
17
+ - `bin/slack.ts` the Slack client
16
18
 
17
19
  CLI command results use `writeJson` from `bin/output.ts` to write newline-terminated JSON to stdout.
18
20
  Use the CLI `logger` from `bin/logger.ts` for diagnostics; every level writes to stderr.
@@ -53,7 +55,7 @@ To work on the library, run the cli from the root of your testing repo:
53
55
  ```sh
54
56
  cd /path/to/testing-repo-for-github-actions
55
57
  npx tsx /path/to/apify-test-tools/bin/main.ts --help
56
- npx tsx /path/to/apify-test-tools/bin/main.ts get-commits --target-branch master --source-branch feat/testing-feature-branch
58
+ npx tsx /path/to/apify-test-tools/bin/main.ts get-affected-actors --base master
57
59
  ```
58
60
 
59
61
  Or clone a test repo into `local-clone/` inside this repo and run `npm run start:dev -- <command>`.
@@ -84,7 +86,7 @@ renaming one is a breaking change that needs a major tag bump, not a tidy-up.
84
86
  The tag is not semver, so `git-cliff` would abort on it. It is skipped only because
85
87
  `apify/actions`' cliff.toml sets `tag_pattern = "v[0-9]+\."` — a release failing with
86
88
  `Semver error: unexpected character` means that pattern changed, not that anything here is wrong.
87
- The action's "Nothing to release" guard resolves to this tag and no longer fires, so an empty stable
89
+ The action's "Nothing to release" guard resolves to this tag and never fires, so an empty stable
88
90
  release fails at `npm publish` instead of erroring cleanly.
89
91
 
90
92
  Consumers pin `@workflows-v0`, not `@master`. See [Versioning and releases](#versioning-and-releases) below.
@@ -130,7 +132,7 @@ that commit. The consumer's own `node_modules` is left alone, so their tests kee
130
132
  `apify-test-tools`.
131
133
 
132
134
  The CLI and the lib in one job can be different versions now, so the formats between them are a contract: the
133
- `ACTOR_BUILDS` JSON that `build` writes and the lib reads, and the vitest JSON report `report-tests` reads. Add
135
+ `ACTOR_BUILDS` JSON that `build` writes and the lib reads, and the vitest JSON report (with the run each test started on its `meta`) that `test` reads. Add
134
136
  fields freely; renaming or removing one needs a new major tag.
135
137
 
136
138
  `workflows-v0` moving on every master push means `@workflows-v0` is as live as `@master` was — there's no staging
@@ -195,7 +197,7 @@ again. The caller's workspace holds the caller's repo, not this one.
195
197
  Two tidier-looking alternatives don't work, so don't reach for them:
196
198
 
197
199
  - **Exporting to `$GITHUB_ENV`** would let the steps call the CLI directly with no wrapper, but `$GITHUB_ENV`
198
- applies to every later step in the job. In `pr-build-test` the vitest step runs after the build, so it would
200
+ applies to every later step in the job. In `pr-build-test` the test step runs after the build, so it would
199
201
  inherit Actor tokens it has no use for.
200
202
  - **Returning the tokens as a step output** would be scoped correctly, but the runner refuses to set an output whose
201
203
  value contains a registered secret. It logs `Skip output <name> since it may contain secret` and leaves the output
package/README.md CHANGED
@@ -248,7 +248,7 @@ jobs:
248
248
  buildLatest:
249
249
  uses: apify/apify-test-tools/.github/workflows/public_push-build-latest.yaml@workflows-v0
250
250
  secrets:
251
- SLACK_TOKEN_RELEASES_BOT: ${{ secrets.SLACK_TOKEN_RELEASES_BOT }}
251
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
252
252
  # One entry per secret apify-test-tools.config.json names, today each `tokenEnvVar`
253
253
  CONFIG_SECRETS: |
254
254
  {
@@ -275,7 +275,7 @@ jobs:
275
275
  uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@workflows-v0
276
276
  secrets:
277
277
  TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
278
- SLACK_TOKEN_TESTS_BOT: ${{ secrets.SLACK_TOKEN_TESTS_BOT }}
278
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
279
279
  ```
280
280
 
281
281
  and a few critical tests, kept in e.g. `test/platform/hourly`, every hour:
@@ -296,7 +296,7 @@ jobs:
296
296
  test-files-glob: hourly
297
297
  secrets:
298
298
  TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
299
- SLACK_TOKEN_TESTS_BOT: ${{ secrets.SLACK_TOKEN_TESTS_BOT }}
299
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
300
300
  ```
301
301
 
302
302
  ### Secrets
@@ -304,13 +304,12 @@ jobs:
304
304
  Set these as repository or organization secrets and pass them in each job's `secrets:` block, as in the examples
305
305
  above. The Actor tokens go packed in one JSON object, `CONFIG_SECRETS`, because their names differ per repo:
306
306
 
307
- | Secret | Needed for |
308
- | ------------------------------------------------------------------------- | ---------------------------------------------------------------- |
309
- | the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | building Actors and deleting their old builds |
310
- | `TESTER_APIFY_TOKEN` | running the platform tests |
311
- | `SLACK_TOKEN_RELEASES_BOT` | announcing releases on Slack |
312
- | `SLACK_TOKEN_TESTS_BOT` | reporting scheduled test results to Slack |
313
- | `NPM_TOKEN` | optional, installing private npm packages. Use a read-only token |
307
+ | Secret | Needed for |
308
+ | ------------------------------------------------------------------------- | ----------------------------------------------------------------- |
309
+ | the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | building Actors and deleting their old builds |
310
+ | `TESTER_APIFY_TOKEN` | running the platform tests |
311
+ | `SLACK_BOT_TOKEN` | announcing releases and reporting scheduled test results to Slack |
312
+ | `NPM_TOKEN` | optional, installing private npm packages. Use a read-only token |
314
313
 
315
314
  Repos in the `apify` org can pass `secrets: inherit` instead. Each secret only reaches the step that needs it. See
316
315
  [Secrets](docs/workflows.md#secrets) for both, and for passing `CONFIG_SECRETS` without listing the tokens.
@@ -342,7 +341,7 @@ Platform tests are skipped unless `TESTER_APIFY_TOKEN` is set, so your regular u
342
341
  To run them locally against the Actors' current default builds:
343
342
 
344
343
  ```bash
345
- RUN_ALL_PLATFORM_TESTS=1 TESTER_APIFY_TOKEN=<token> npx vitest --run test/platform
344
+ npx apify-test-tools get-actor-configs | TESTER_APIFY_TOKEN=<token> npx apify-test-tools test
346
345
  ```
347
346
 
348
347
  The [Vitest library API](docs/lib.md) covers everything `toFinishWith` checks, the other matchers, standby Actors,
@@ -0,0 +1,38 @@
1
+ import { ApifyClient } from 'apify-client';
2
+
3
+ import type { TestTarget } from './stdin-input.js';
4
+
5
+ type ActorGetter = Pick<ApifyClient, 'actor'>;
6
+
7
+ /** A test target with both IDs of its Actor */
8
+ export type ResolvedTestTarget = TestTarget & { actorFullName: string; actorRawId: string };
9
+
10
+ /**
11
+ * Adds the ID each target is missing: the raw ID to those listed by full name, e.g. by `get-actor-configs`, and the
12
+ * full name to those listed by raw ID. The lib matches a test to its Actor by the ID the test names, so a target
13
+ * without either ID would skip the tests that name its Actor by the other one.
14
+ */
15
+ export const withActorIds = async (
16
+ targets: TestTarget[],
17
+ client: ActorGetter = new ApifyClient({ token: process.env.TESTER_APIFY_TOKEN }),
18
+ ): Promise<ResolvedTestTarget[]> =>
19
+ Promise.all(
20
+ targets.map(async (target) => {
21
+ const { actorFullName, actorRawId } = target;
22
+ if (actorFullName && actorRawId) return { ...target, actorFullName, actorRawId };
23
+ // The parser lets no target through without either ID
24
+ const actorId = (actorFullName ?? actorRawId)!;
25
+ const actor = await client.actor(actorId).get();
26
+ if (!actor) {
27
+ throw new Error(
28
+ `Actor ${actorId} not found, or TESTER_APIFY_TOKEN can't access it. ` +
29
+ 'Both its full name and its raw ID are needed to run the tests that name it by either.',
30
+ );
31
+ }
32
+ return {
33
+ ...target,
34
+ actorFullName: actorFullName ?? `${actor.username}/${actor.name}`,
35
+ actorRawId: actorRawId ?? actor.id,
36
+ };
37
+ }),
38
+ );
package/bin/build.ts CHANGED
@@ -243,7 +243,7 @@ export class ApifyBuilder {
243
243
 
244
244
  const actorInfo = await this.getActorInfo();
245
245
 
246
- // 'devel' used to be hardcoded for testing version 0.99, once we get rid of this tag everywhere, we can remove this code
246
+ // A build tagged 'devel' is kept for DEFAULT_DAYS_BACK_DEVEL only. Remove this once no Actor has the tag
247
247
  const taggedDevelBuildNumber: string | undefined = actorInfo.taggedBuilds!.devel?.buildNumber;
248
248
 
249
249
  const allTags = Object.keys(actorInfo.taggedBuilds ?? {});
package/bin/git.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { logger } from './logger.js';
2
- import type { Commit, Config } from './types.js';
2
+ import type { Commit } from './types.js';
3
3
  import { GitCommandError, runGitCommand } from './utils.js';
4
4
 
5
5
  export const GIT_FORMAT_SEPARATOR = '»¦«';
@@ -9,19 +9,6 @@ const GIT_LOG_FORMAT = ['%H', '%aN<%aE>', '%aD', '%s'].join(GIT_FORMAT_SEPARATOR
9
9
  // so the Actor whose context it left wouldn't count as changed. --no-renames lists it as deleted and added instead.
10
10
  const LIST_CHANGED_PATHS = ['--name-only', '--no-renames'];
11
11
 
12
- /**
13
- * Gets the list of changed files between the given commits (inclusive), so it diffs from the parent of the oldest.
14
- */
15
- export const getChangedFiles = async (commits: Commit[]) => {
16
- // getCommits never returns an empty list (it throws on an empty range, and the rerun check returns all
17
- // commits when the base commit is the branch HEAD), so this signals a programmer error
18
- if (commits.length === 0) {
19
- throw new Error('Cannot get changed files: the commit list is empty. This should never happen.');
20
- }
21
-
22
- return getChangedFilesBetween(`${commits[0].sha}~`, commits[commits.length - 1].sha);
23
- };
24
-
25
12
  /**
26
13
  * Files that differ between two commits, i.e. changed by the commits in the range (fromRef, toRef]. fromRef's own
27
14
  * changes are not included: to include them, pass its parent, `${sha}~` (as getChangedFiles does).
@@ -56,29 +43,6 @@ export const findCommitSha = async (ref: string): Promise<string | undefined> =>
56
43
  throw error;
57
44
  });
58
45
 
59
- const SHA_REGEX = /^[0-9a-f]{40}$/i;
60
-
61
- /**
62
- *
63
- * @param shaOrCommit Supports both a SHA string or a Commit object in JSON format. Can be empty.
64
- * @returns The SHA string if valid, otherwise throws an error.
65
- */
66
- export const parseBaseCommit = (shaOrCommit: string | undefined): string | undefined => {
67
- if (!shaOrCommit) return undefined;
68
- let sha: string;
69
- if (shaOrCommit.startsWith('{')) {
70
- sha = (JSON.parse(shaOrCommit) as Commit).sha;
71
- } else {
72
- sha = shaOrCommit;
73
- }
74
- if (!SHA_REGEX.test(sha)) {
75
- throw new Error(
76
- `Invalid base commit SHA: "${sha}". It should be a 40-character hexadecimal string, instead got input: "${shaOrCommit}".`,
77
- );
78
- }
79
- return sha;
80
- };
81
-
82
46
  /**
83
47
  * Commits reachable from toRef but not from fromRef, oldest first: the range (fromRef, toRef], so fromRef itself is
84
48
  * not included. To include it, pass its parent, `${sha}~`.
@@ -93,49 +57,6 @@ export const getCommitsBetween = async (fromRef: string, toRef: string): Promise
93
57
  return commits;
94
58
  };
95
59
 
96
- /**
97
- * Gets the commits between sourceBranch and targetBranch (exclusive).
98
- * - If baseCommit is provided, only returns commits after the baseCommit.
99
- */
100
- export const getCommits = async ({
101
- sourceBranch,
102
- targetBranch,
103
- baseCommit,
104
- }: Pick<Config, 'sourceBranch' | 'targetBranch' | 'baseCommit'>): Promise<Commit[]> => {
105
- const baseCommitSha = parseBaseCommit(baseCommit);
106
- const commits = await getCommitsBetween(targetBranch, sourceBranch);
107
- if (commits.length === 0) {
108
- throw new Error(`${sourceBranch} has no commits that are not on ${targetBranch}.`);
109
- }
110
-
111
- // The last validated (base) commit being the branch HEAD means nothing new was pushed since the last
112
- // validation — the dev reran the workflow (or force-pushed to the same state) to trigger a clean test
113
- const headSha = commits[commits.length - 1]?.sha;
114
- if (baseCommitSha !== undefined && baseCommitSha === headSha) {
115
- logger.info(
116
- `Detected rerun with the same commit that we already validated. This usually means the user wants to rerun the Action from scratch, ignoring last validated commit ${baseCommitSha} and returning all commits`,
117
- );
118
- logger.info(`Commits being returned: ${commits.map((c) => c.sha).join(', ')}`);
119
- return commits;
120
- }
121
-
122
- const baseCommitIndex = commits.findIndex((commit) => commit.sha === baseCommitSha);
123
-
124
- const hasBaseCommit = baseCommitIndex !== -1;
125
- if (hasBaseCommit) {
126
- const commitsUpToBaseCommit = commits.slice(baseCommitIndex + 1);
127
- logger.info(
128
- `Found base commit ${baseCommitSha} at index ${baseCommitIndex}, returning ${commitsUpToBaseCommit.length} commits after it`,
129
- );
130
- logger.info(`Commits being returned: ${commitsUpToBaseCommit.map((c) => c.sha).join(', ')}`);
131
- return commitsUpToBaseCommit;
132
- }
133
-
134
- logger.info(`Base commit ${baseCommitSha} not found in the commit range, returning all ${commits.length} commits`);
135
- logger.info(`Commits being returned: ${commits.map((c) => c.sha).join(', ')}`);
136
- return commits;
137
- };
138
-
139
60
  export const parseCommit = (commitString: string): Commit => {
140
61
  const splits = commitString.split(GIT_FORMAT_SEPARATOR);
141
62
  if (splits.length !== 4) {
@@ -209,11 +130,22 @@ export const normalizeRepoUrl = (repoUrl: string): string => {
209
130
  .toLowerCase();
210
131
  };
211
132
 
133
+ /**
134
+ * Web link to a commit, e.g. https://github.com/apify-store/google-maps/commit/<sha>. Null for hosts other than
135
+ * GitHub, whose commit pages live under other paths.
136
+ */
137
+ export const getCommitUrl = (repoUrl: string, sha: string): string | null => {
138
+ const repository = normalizeRepoUrl(repoUrl);
139
+ return repository.startsWith('github.com/') ? `https://${repository}/commit/${sha}` : null;
140
+ };
141
+
212
142
  /** Repository name from its URL, e.g. `google-maps` for git@github.com:apify-store/google-maps.git */
213
143
  export const getRepoName = (repoUrl: string): string => {
214
144
  return normalizeRepoUrl(repoUrl).split('/').pop()!;
215
145
  };
216
146
 
147
+ const SHA_REGEX = /^[0-9a-f]{40}$/i;
148
+
217
149
  // Sent by e.g. GitHub as the "before" commit of a push that created the branch
218
150
  const ZERO_SHA_REGEX = /^0{40}$/;
219
151
 
@@ -223,31 +155,27 @@ const ZERO_SHA_REGEX = /^0{40}$/;
223
155
  * would rebuild the latest build of every Actor and post it to Slack, so every problem is an error.
224
156
  */
225
157
  export const resolveReleaseBaseCommit = async (baseCommit: string, head = 'HEAD'): Promise<string> => {
226
- const sha = parseBaseCommit(baseCommit);
227
- if (!sha) {
228
- throw new Error('A base commit is required for release. See the "Releasing Actors" section of docs/cli.md.');
158
+ if (!SHA_REGEX.test(baseCommit)) {
159
+ throw new Error(
160
+ `Invalid base commit SHA: "${baseCommit}". It should be a 40-character hexadecimal string. ` +
161
+ 'See the "Releasing Actors" section of docs/cli.md.',
162
+ );
229
163
  }
164
+ const sha = baseCommit;
230
165
  if (ZERO_SHA_REGEX.test(sha)) {
231
166
  throw new Error(
232
167
  `Base commit is ${sha}, which means the branch was just created and there is no previous release to diff against. ` +
233
168
  `Rerun with the base commit set to the last commit before the changes you want to release.`,
234
169
  );
235
170
  }
236
- // --quiet suppresses Git's error text for a missing commit, but Git still exits with code 1.
237
- const verifiedSha = await runGitCommand(['rev-parse', '--verify', '--quiet', `${sha}^{commit}`], {
238
- operation: 'verify the release base commit in local Git history',
239
- }).catch((error: unknown) => {
240
- if (error instanceof GitCommandError && error.exitCode === 1) return '';
241
- throw error;
242
- });
243
- if (!verifiedSha) {
171
+ if (!(await findCommitSha(sha))) {
244
172
  throw new Error(
245
173
  `Base commit ${sha} is not in the local git history. Either the checkout is shallow ` +
246
174
  `(fetch the full history, e.g. fetch-depth: 0 in actions/checkout) or the branch was force-pushed.`,
247
175
  );
248
176
  }
249
177
  // git merge-base A B outputs the common ancestor. If that equals A, then A is an ancestor of B.
250
- const mergeBase = await runGitCommand(['merge-base', verifiedSha, head], {
178
+ const mergeBase = await runGitCommand(['merge-base', sha, head], {
251
179
  operation: `check whether the release base commit is an ancestor of ${head}`,
252
180
  }).catch((error: unknown) => {
253
181
  if (error instanceof GitCommandError && error.exitCode === 1) return '';