apify-test-tools 0.10.2-beta.13 → 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.
- package/.github/cli/apify-test-tools.mjs +123 -128
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +1 -1
- package/.github/workflows/public_platform-tests.yaml +36 -22
- package/.github/workflows/public_pr-build-test.yaml +7 -6
- package/.github/workflows/public_push-build-latest.yaml +6 -4
- package/CHANGELOG.md +2 -0
- package/CONTRIBUTING.md +8 -6
- package/README.md +10 -11
- package/bin/actor-ids.ts +38 -0
- package/bin/build.ts +1 -1
- package/bin/git.ts +20 -92
- package/bin/main.ts +87 -73
- package/bin/notify.ts +198 -0
- package/bin/run-tests.ts +141 -0
- package/bin/slack.ts +16 -87
- package/bin/stdin-input.ts +98 -7
- package/bin/test-report.ts +141 -140
- package/bin/types.ts +0 -9
- package/dist/bin/actor-ids.d.ts +16 -0
- package/dist/bin/actor-ids.d.ts.map +1 -0
- package/dist/bin/actor-ids.js +24 -0
- package/dist/bin/actor-ids.js.map +1 -0
- package/dist/bin/build.js +1 -1
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/git.d.ts +6 -16
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +15 -75
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +80 -51
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/notify.d.ts +35 -0
- package/dist/bin/notify.d.ts.map +1 -0
- package/dist/bin/notify.js +131 -0
- package/dist/bin/notify.js.map +1 -0
- package/dist/bin/run-tests.d.ts +21 -0
- package/dist/bin/run-tests.d.ts.map +1 -0
- package/dist/bin/run-tests.js +116 -0
- package/dist/bin/run-tests.js.map +1 -0
- package/dist/bin/slack.d.ts +2 -17
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +17 -58
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/stdin-input.d.ts +71 -9
- package/dist/bin/stdin-input.d.ts.map +1 -1
- package/dist/bin/stdin-input.js +76 -6
- package/dist/bin/stdin-input.js.map +1 -1
- package/dist/bin/test-report.d.ts +69 -8
- package/dist/bin/test-report.d.ts.map +1 -1
- package/dist/bin/test-report.js +76 -67
- package/dist/bin/test-report.js.map +1 -1
- package/dist/bin/types.d.ts +0 -8
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/lib/lib.d.ts +0 -3
- package/dist/lib/lib.d.ts.map +1 -1
- package/dist/lib/lib.js +5 -10
- package/dist/lib/lib.js.map +1 -1
- package/dist/test/bundle/cli-bundle.test.js +40 -48
- package/dist/test/bundle/cli-bundle.test.js.map +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js +15 -16
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
- package/dist/test/e2e/git-build-release.e2e.test.js +2 -2
- package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -1
- package/dist/test/unit/bin/actor-ids.test.d.ts +2 -0
- package/dist/test/unit/bin/actor-ids.test.d.ts.map +1 -0
- package/dist/test/unit/bin/actor-ids.test.js +32 -0
- package/dist/test/unit/bin/actor-ids.test.js.map +1 -0
- package/dist/test/unit/bin/build-cli.test.js +8 -3
- package/dist/test/unit/bin/build-cli.test.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.js +1 -1
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +4 -142
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/notify.test.d.ts +2 -0
- package/dist/test/unit/bin/notify.test.d.ts.map +1 -0
- package/dist/test/unit/bin/notify.test.js +222 -0
- package/dist/test/unit/bin/notify.test.js.map +1 -0
- package/dist/test/unit/bin/stdin-input.test.js +51 -9
- package/dist/test/unit/bin/stdin-input.test.js.map +1 -1
- package/dist/test/unit/bin/test-cli.test.d.ts +2 -0
- package/dist/test/unit/bin/test-cli.test.d.ts.map +1 -0
- package/dist/test/unit/bin/test-cli.test.js +115 -0
- package/dist/test/unit/bin/test-cli.test.js.map +1 -0
- package/dist/test/unit/bin/test-report.test.d.ts +2 -0
- package/dist/test/unit/bin/test-report.test.d.ts.map +1 -0
- package/dist/test/unit/bin/test-report.test.js +100 -0
- package/dist/test/unit/bin/test-report.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/cli.md +131 -53
- package/docs/lib.md +21 -16
- package/docs/workflows.md +24 -23
- package/lib/lib.ts +5 -10
- package/package.json +1 -1
- package/test/bundle/cli-bundle.test.ts +47 -60
- package/test/e2e/cli-bundle.e2e.test.ts +18 -25
- package/test/e2e/git-build-release.e2e.test.ts +2 -2
- package/test/unit/bin/actor-ids.test.ts +43 -0
- package/test/unit/bin/build-cli.test.ts +9 -3
- package/test/unit/bin/diff-changes.test.ts +1 -1
- package/test/unit/bin/git.test.ts +3 -197
- package/test/unit/bin/notify.test.ts +299 -0
- package/test/unit/bin/stdin-input.test.ts +68 -9
- package/test/unit/bin/test-cli.test.ts +137 -0
- package/test/unit/bin/test-report.test.ts +118 -0
- package/dist/test/unit/bin/logging.test.d.ts +0 -2
- package/dist/test/unit/bin/logging.test.d.ts.map +0 -1
- package/dist/test/unit/bin/logging.test.js +0 -37
- package/dist/test/unit/bin/logging.test.js.map +0 -1
- package/dist/test/unit/bin/slack.test.d.ts +0 -2
- package/dist/test/unit/bin/slack.test.d.ts.map +0 -1
- package/dist/test/unit/bin/slack.test.js +0 -71
- package/dist/test/unit/bin/slack.test.js.map +0 -1
- package/test/unit/bin/logging.test.ts +0 -48
- 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
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
71
|
+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
72
72
|
run: |
|
|
73
|
-
apify-test-tools notify
|
|
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/CHANGELOG.md
CHANGED
|
@@ -24,10 +24,12 @@ All notable changes to this project will be documented in this file.
|
|
|
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
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)
|
|
27
28
|
|
|
28
29
|
### 🚜 Refactor
|
|
29
30
|
|
|
30
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)
|
|
31
33
|
|
|
32
34
|
|
|
33
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/
|
|
15
|
-
- `bin/test-report.ts`
|
|
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-
|
|
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
|
|
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 `
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
| `
|
|
312
|
-
| `
|
|
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
|
-
|
|
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,
|
package/bin/actor-ids.ts
ADDED
|
@@ -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'
|
|
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
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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',
|
|
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 '';
|