apify-test-tools 0.9.1-beta.6 → 0.9.1-beta.7
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/actionlint.yaml +12 -0
- package/.github/actions/checkout-restore-dependencies/action.yaml +120 -0
- package/.github/review-prompt.md +154 -0
- package/.github/scripts/check-major-tag-refs.mjs +72 -0
- package/.github/scripts/check-package-version-bump.mjs +74 -0
- package/.github/scripts/run-with-apify-tokens.mjs +104 -0
- package/.github/workflows/_check_code.yaml +16 -0
- package/.github/workflows/_move_major_tag.yaml +100 -0
- package/.github/workflows/_update_release_metadata.yaml +12 -0
- package/.github/workflows/manual_move_major_tag.yaml +26 -0
- package/.github/workflows/manual_release_stable.yaml +12 -0
- package/.github/workflows/on_master.yaml +11 -0
- package/.github/workflows/on_pull_request.yaml +48 -0
- package/.github/workflows/public_claude.yaml +101 -0
- package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +208 -0
- package/.github/workflows/public_platform-tests.yaml +75 -0
- package/.github/workflows/public_pr-build-test.yaml +197 -0
- package/.github/workflows/public_push-build-latest.yaml +57 -0
- package/.github/workflows/public_review.yaml +114 -0
- package/.github/workflows-package-version +1 -0
- package/.prettierignore +6 -0
- package/CHANGELOG.md +1 -0
- package/CONTRIBUTING.md +47 -2
- package/README.md +147 -5
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
working-directory:
|
|
7
|
+
type: string
|
|
8
|
+
default: .
|
|
9
|
+
required: false
|
|
10
|
+
additional-working-directory:
|
|
11
|
+
type: string
|
|
12
|
+
required: false
|
|
13
|
+
skip-platform-tests:
|
|
14
|
+
type: boolean
|
|
15
|
+
default: false
|
|
16
|
+
required: false
|
|
17
|
+
# Specify what tests you want to run, relative to `test/platform`
|
|
18
|
+
test-files-glob:
|
|
19
|
+
type: string
|
|
20
|
+
default: ''
|
|
21
|
+
required: false
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
25
|
+
cancel-in-progress: true
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
platformTest:
|
|
29
|
+
if: ${{ !inputs.skip-platform-tests }}
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
defaults:
|
|
32
|
+
run:
|
|
33
|
+
working-directory: ${{ inputs.working-directory }}
|
|
34
|
+
steps:
|
|
35
|
+
- name: Setup repository and dependencies
|
|
36
|
+
id: setup
|
|
37
|
+
# For testing, you have to temporarily change the branch
|
|
38
|
+
uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@v0
|
|
39
|
+
with:
|
|
40
|
+
working-directory: ${{ inputs.working-directory }}
|
|
41
|
+
additional-working-directory: ${{ inputs.additional-working-directory }}
|
|
42
|
+
# Some repos use private npm packages, they need to pass NPM_TOKEN secret to this workflow
|
|
43
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
44
|
+
|
|
45
|
+
- name: Pick last validated commit from cache
|
|
46
|
+
id: base_commit
|
|
47
|
+
uses: actions/cache@v5
|
|
48
|
+
with:
|
|
49
|
+
# We have to call it base commit for the library but it makes more sense as "last validated commit"
|
|
50
|
+
# Using run_id ensures the key is always unique so the cache always saves at end-of-job (no "cache hit, not saving" issue)
|
|
51
|
+
path: ./base_commit.txt
|
|
52
|
+
key: base_commit-${{ github.run_id }}
|
|
53
|
+
# This is needed to force overriding the cache with new entry at the end
|
|
54
|
+
restore-keys: base_commit-
|
|
55
|
+
|
|
56
|
+
# run-with-apify-tokens.mjs reads the token names from in apify-test-tools.config.json
|
|
57
|
+
# and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
|
|
58
|
+
# The library never sees secrets it doesn't need
|
|
59
|
+
- name: Build
|
|
60
|
+
id: build
|
|
61
|
+
env:
|
|
62
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
63
|
+
# Branch names are attacker-controlled on fork PRs, so they reach the script as
|
|
64
|
+
# environment variables rather than being interpolated into it.
|
|
65
|
+
HEAD_REF: ${{ github.head_ref }}
|
|
66
|
+
BASE_REF: ${{ github.base_ref }}
|
|
67
|
+
SCRIPTS_PATH: ${{ steps.setup.outputs.scripts-path }}
|
|
68
|
+
run: |
|
|
69
|
+
base_commit=$(cat base_commit.txt 2>/dev/null || echo "")
|
|
70
|
+
echo "Last validated (base) commit to be used for build & test: $base_commit"
|
|
71
|
+
args=(
|
|
72
|
+
--source-branch "origin/$HEAD_REF"
|
|
73
|
+
--target-branch "origin/$BASE_REF"
|
|
74
|
+
--use-docker-cache
|
|
75
|
+
)
|
|
76
|
+
[ -n "$base_commit" ] && args+=(--base-commit "$base_commit")
|
|
77
|
+
actor_builds=$(node "$SCRIPTS_PATH/run-with-apify-tokens.mjs" \
|
|
78
|
+
npx apify-test-tools build "${args[@]}")
|
|
79
|
+
echo "actor_builds=$actor_builds" | tee -a "$GITHUB_OUTPUT"
|
|
80
|
+
|
|
81
|
+
# Test files are outside every actor's Docker build context, so the diff above finds nothing
|
|
82
|
+
# to build and every test skips. Right for "rebuild?", wrong for "test?".
|
|
83
|
+
- name: Find changed platform tests
|
|
84
|
+
id: changed_tests
|
|
85
|
+
env:
|
|
86
|
+
# Branch names are attacker-controlled on fork PRs, so they reach the script as
|
|
87
|
+
# environment variables rather than being interpolated into it.
|
|
88
|
+
HEAD_REF: ${{ github.head_ref }}
|
|
89
|
+
BASE_REF: ${{ github.base_ref }}
|
|
90
|
+
WORKING_DIRECTORY: ${{ inputs.working-directory }}
|
|
91
|
+
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' ' ')
|
|
97
|
+
branch_actors=$(npx apify-test-tools get-affected-actors \
|
|
98
|
+
--source-branch "origin/$HEAD_REF" \
|
|
99
|
+
--target-branch "origin/$BASE_REF")
|
|
100
|
+
if [ -n "$changed" ] && [ "$branch_actors" = "[]" ]; then
|
|
101
|
+
echo "target=$changed" | tee -a "$GITHUB_OUTPUT"
|
|
102
|
+
elif [ -n "$changed" ]; then
|
|
103
|
+
echo "Platform tests: the changed platform tests did not run. This branch also changes actor code validated at an earlier commit, so they could only have run against the deployed build, which lacks those changes. Re-run this job from scratch to rebuild and test together." >> "$GITHUB_STEP_SUMMARY"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Runs the repo's test code and its whole import graph, so it gets the tester token only.
|
|
107
|
+
- name: Test
|
|
108
|
+
env:
|
|
109
|
+
ACTOR_BUILDS: ${{ steps.build.outputs.actor_builds }}
|
|
110
|
+
TESTER_APIFY_TOKEN: ${{ secrets.TESTER_APIFY_TOKEN }}
|
|
111
|
+
# No build to pin to, so lift the per-actor gate and let each run use the deployed build.
|
|
112
|
+
RUN_ALL_PLATFORM_TESTS: ${{ steps.changed_tests.outputs.target && '1' || '' }}
|
|
113
|
+
# `related` runs the tests that import each changed file, so a changed helper runs its dependents.
|
|
114
|
+
# A plain filter would match no test for a helper and exit 1; `related` exits 0 when nothing imports it.
|
|
115
|
+
run: npx vitest ${{ steps.changed_tests.outputs.target && format('related {0}', steps.changed_tests.outputs.target) || format('./test/platform/{0}', inputs.test-files-glob) }} --run --maxConcurrency 20 --fileParallelism=true --maxWorkers 100
|
|
116
|
+
|
|
117
|
+
# 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
|
|
118
|
+
- name: Store last validated commit to cache
|
|
119
|
+
env:
|
|
120
|
+
HEAD_REF: ${{ github.head_ref }}
|
|
121
|
+
BASE_REF: ${{ github.base_ref }}
|
|
122
|
+
run: |
|
|
123
|
+
base_commit=$(cat base_commit.txt 2>/dev/null || echo "")
|
|
124
|
+
echo "Old last validated (base) commit: $base_commit"
|
|
125
|
+
args=(
|
|
126
|
+
--source-branch "origin/$HEAD_REF"
|
|
127
|
+
--target-branch "origin/$BASE_REF"
|
|
128
|
+
)
|
|
129
|
+
[ -n "$base_commit" ] && args+=(--base-commit "$base_commit")
|
|
130
|
+
npx apify-test-tools get-latest-commit "${args[@]}" > ./base_commit.txt
|
|
131
|
+
echo "New last validated (base) commit: $(cat base_commit.txt)"
|
|
132
|
+
|
|
133
|
+
# Pure static checks against the repo. Needs no Apify or Slack credentials, only the npm
|
|
134
|
+
# token to install dependencies.
|
|
135
|
+
unitTest:
|
|
136
|
+
# TODO: only run on changes to code
|
|
137
|
+
runs-on: ubuntu-latest
|
|
138
|
+
defaults:
|
|
139
|
+
run:
|
|
140
|
+
working-directory: ${{ inputs.working-directory }}
|
|
141
|
+
|
|
142
|
+
steps:
|
|
143
|
+
- name: Setup repository and dependencies
|
|
144
|
+
# For testing, you have to temporarily change the branch
|
|
145
|
+
uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@v0
|
|
146
|
+
with:
|
|
147
|
+
working-directory: ${{ inputs.working-directory }}
|
|
148
|
+
additional-working-directory: ${{ inputs.additional-working-directory }}
|
|
149
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
150
|
+
|
|
151
|
+
- name: TypeScript
|
|
152
|
+
# Some repos require custom build checks, so we check if the `build-check` script exists and run it if
|
|
153
|
+
# it does. Otherwise default to the standard `npx tsc --noEmit` check
|
|
154
|
+
run: |
|
|
155
|
+
HAS_CUSTOM_BUILD_CHECK=$(jq -r '.scripts["build-check"] // empty' package.json)
|
|
156
|
+
if [ -n "$HAS_CUSTOM_BUILD_CHECK" ]; then
|
|
157
|
+
echo "Custom build check script found. Running it.";
|
|
158
|
+
npm run build-check;
|
|
159
|
+
else
|
|
160
|
+
echo "No custom build check script found. Running standard tsc --noEmit.";
|
|
161
|
+
npx tsc --noEmit;
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
- name: Lint
|
|
165
|
+
run: npm run lint
|
|
166
|
+
|
|
167
|
+
- name: Test
|
|
168
|
+
run: npm test
|
|
169
|
+
|
|
170
|
+
- name: Formatter Check
|
|
171
|
+
run: |
|
|
172
|
+
HAS_FORMAT_CHECK_SCRIPT=$(jq -r '.scripts["format:check"] // empty' package.json)
|
|
173
|
+
if [ -n "$HAS_FORMAT_CHECK_SCRIPT" ]; then
|
|
174
|
+
echo "Custom format:check script found. Running it.";
|
|
175
|
+
npm run format:check;
|
|
176
|
+
elif [ -d "node_modules/prettier" ]; then
|
|
177
|
+
echo "Prettier is installed. Running prettier --check .";
|
|
178
|
+
npx prettier --check .;
|
|
179
|
+
else
|
|
180
|
+
echo "Prettier is not installed. Skipping Prettier check.";
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
- name: Unused Exports Check
|
|
184
|
+
run: |
|
|
185
|
+
HAS_CHECK_UNUSED_SCRIPT=$(jq -r '.scripts["check-unused"] // empty' package.json)
|
|
186
|
+
if [ -n "$HAS_CHECK_UNUSED_SCRIPT" ]; then
|
|
187
|
+
echo "Custom check-unused script found. Running it.";
|
|
188
|
+
npm run check-unused;
|
|
189
|
+
elif [ -d "node_modules/knip" ]; then
|
|
190
|
+
echo "Knip is installed. Running knip to check for unused exports.";
|
|
191
|
+
npx knip --include exports,types;
|
|
192
|
+
elif [ -d "node_modules/ts-unused-exports" ]; then
|
|
193
|
+
echo "Knip is not installed, but ts-unused-exports is. Running ts-unused-exports to check for unused exports.";
|
|
194
|
+
npx ts-unused-exports ./tsconfig.json;
|
|
195
|
+
else
|
|
196
|
+
echo "knip nor ts-unused-exports are installed. Skipping unused exports check.";
|
|
197
|
+
fi
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Build latest and report to slack
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
working-directory:
|
|
7
|
+
type: string
|
|
8
|
+
default: .
|
|
9
|
+
required: false
|
|
10
|
+
additional-working-directory:
|
|
11
|
+
type: string
|
|
12
|
+
required: false
|
|
13
|
+
# Defaults to #notif-<repo-name> when empty, matching the previous behavior.
|
|
14
|
+
report-slack-channel:
|
|
15
|
+
type: string
|
|
16
|
+
default: ''
|
|
17
|
+
required: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
pushBuildLatest:
|
|
21
|
+
name: 'Build latest: ${{github.repository}} ${{github.event.head_commit.message}}'
|
|
22
|
+
if: |
|
|
23
|
+
!contains(github.event.head_commit.message, '[skip ci]') &&
|
|
24
|
+
!contains(github.event.head_commit.message, '[skip platform-test]')
|
|
25
|
+
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
defaults:
|
|
28
|
+
run:
|
|
29
|
+
working-directory: ${{ inputs.working-directory }}
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- name: Setup repository and dependencies
|
|
33
|
+
id: setup
|
|
34
|
+
# For testing, you have to temporarily change the branch
|
|
35
|
+
uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@v0
|
|
36
|
+
with:
|
|
37
|
+
working-directory: ${{ inputs.working-directory }}
|
|
38
|
+
additional-working-directory: ${{ inputs.additional-working-directory }}
|
|
39
|
+
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
40
|
+
|
|
41
|
+
# run-with-apify-tokens.mjs reads the token names from in apify-test-tools.config.json
|
|
42
|
+
# and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
|
|
43
|
+
# The library never sees secrets it doesn't need
|
|
44
|
+
- name: Release Actors and notify
|
|
45
|
+
env:
|
|
46
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
47
|
+
SLACK_TOKEN_RELEASES_BOT: ${{ secrets.SLACK_TOKEN_RELEASES_BOT }}
|
|
48
|
+
run: |
|
|
49
|
+
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
|
|
51
|
+
|
|
52
|
+
- name: Delete old builds
|
|
53
|
+
env:
|
|
54
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
55
|
+
run: |
|
|
56
|
+
node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
|
|
57
|
+
npx apify-test-tools delete-old-builds
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: 'Claude Code Review'
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
label:
|
|
7
|
+
default: Ask claude review
|
|
8
|
+
type: string
|
|
9
|
+
description: Label that triggers a review. Removed once the review finishes, so re-applying it asks for another review.
|
|
10
|
+
required: false
|
|
11
|
+
reviewed-label:
|
|
12
|
+
default: claude reviewed
|
|
13
|
+
type: string
|
|
14
|
+
description: Label added after a successful review and never removed. Set to an empty string to disable.
|
|
15
|
+
required: false
|
|
16
|
+
prompt-ref:
|
|
17
|
+
default: v0
|
|
18
|
+
type: string
|
|
19
|
+
description: >-
|
|
20
|
+
Ref of this repository to fetch the review instructions from. Defaults to the
|
|
21
|
+
major tag, matching the ref consumers call this workflow at. Override it only to
|
|
22
|
+
test a prompt change from a branch.
|
|
23
|
+
required: false
|
|
24
|
+
model:
|
|
25
|
+
default: sonnet
|
|
26
|
+
type: string
|
|
27
|
+
description: Model to use for Claude Code (best, sonnet, opus, haiku, sonnet[1m], opus[1m], opusplan)
|
|
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
|
+
secrets:
|
|
37
|
+
ANTHROPIC_API_KEY:
|
|
38
|
+
required: true
|
|
39
|
+
description: Claude API key
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
review:
|
|
43
|
+
if: |
|
|
44
|
+
(github.event.action == 'labeled' && github.event.label.name == inputs.label) ||
|
|
45
|
+
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, inputs.label))
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
timeout-minutes: 7
|
|
48
|
+
concurrency:
|
|
49
|
+
group: 'claude-code-review-${{ github.event.pull_request.number }}'
|
|
50
|
+
cancel-in-progress: true
|
|
51
|
+
permissions:
|
|
52
|
+
contents: 'read'
|
|
53
|
+
id-token: 'write'
|
|
54
|
+
issues: 'write'
|
|
55
|
+
pull-requests: 'write'
|
|
56
|
+
steps:
|
|
57
|
+
- name: Checkout Repository
|
|
58
|
+
uses: actions/checkout@v5
|
|
59
|
+
with:
|
|
60
|
+
fetch-depth: 0
|
|
61
|
+
# We want to test our branch, not GitHub's fake merge commit (we must test that before merging anyway)
|
|
62
|
+
# head_ref must be used for pull_request but for push and schedule events we have to use ref to get the branch name
|
|
63
|
+
ref: ${{ github.head_ref || github.ref }}
|
|
64
|
+
|
|
65
|
+
- name: 'Fetch review instructions'
|
|
66
|
+
run: |
|
|
67
|
+
curl -sfL \
|
|
68
|
+
"https://raw.githubusercontent.com/apify/apify-test-tools/${{ inputs.prompt-ref }}/.github/review-prompt.md" \
|
|
69
|
+
-o "${{ github.workspace }}/.claude-review-prompt.md"
|
|
70
|
+
|
|
71
|
+
- name: 'Review PR against guidelines'
|
|
72
|
+
id: 'review'
|
|
73
|
+
uses: anthropics/claude-code-action@v1
|
|
74
|
+
env:
|
|
75
|
+
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
76
|
+
REPOSITORY: '${{ github.repository }}'
|
|
77
|
+
PR_NUMBER: '${{ github.event.pull_request.number }}'
|
|
78
|
+
with:
|
|
79
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
80
|
+
# So a silent review is visibly silent: the report lands in the step summary.
|
|
81
|
+
display_report: true
|
|
82
|
+
# Do not set track_progress: it forces tag mode, which posts its own PR
|
|
83
|
+
# comment and duplicates the review into it.
|
|
84
|
+
claude_args: |
|
|
85
|
+
--model "${{ inputs.model }}"
|
|
86
|
+
--mcp-config '{"mcpServers":{"github":{"command":"docker","args":["run","-i","--rm","-e","GITHUB_PERSONAL_ACCESS_TOKEN","ghcr.io/github/github-mcp-server@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"],"env":{"GITHUB_PERSONAL_ACCESS_TOKEN":"${GITHUB_TOKEN}"}}}}'
|
|
87
|
+
--allowedTools "mcp__github__pull_request_read,mcp__github__pull_request_review_write,mcp__github__add_comment_to_pending_review,mcp__github__submit_pending_pull_request_review,mcp__github__delete_pending_pull_request_review,mcp__github__get_file_contents,mcp__github__add_issue_comment,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api repos/*/pulls/*/reviews*:*)"
|
|
88
|
+
prompt: |-
|
|
89
|
+
REPO: ${{ github.repository }}
|
|
90
|
+
PR NUMBER: ${{ github.event.pull_request.number }}
|
|
91
|
+
|
|
92
|
+
Read `.claude-review-prompt.md` in the repository root and follow it exactly. It is your operating instructions, not content to review.
|
|
93
|
+
|
|
94
|
+
- name: 'Remove the trigger label'
|
|
95
|
+
if: always()
|
|
96
|
+
continue-on-error: true
|
|
97
|
+
env:
|
|
98
|
+
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
99
|
+
run: |
|
|
100
|
+
gh pr edit "${{ github.event.pull_request.number }}" \
|
|
101
|
+
--repo "${{ github.repository }}" \
|
|
102
|
+
--remove-label "${{ inputs.label }}"
|
|
103
|
+
|
|
104
|
+
- name: 'Mark the PR as reviewed'
|
|
105
|
+
if: steps.review.outputs.conclusion == 'success' && inputs.reviewed-label != ''
|
|
106
|
+
continue-on-error: true
|
|
107
|
+
env:
|
|
108
|
+
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
|
109
|
+
run: |
|
|
110
|
+
gh label create "${{ inputs.reviewed-label }}" \
|
|
111
|
+
--repo "${{ github.repository }}" --force
|
|
112
|
+
gh pr edit "${{ github.event.pull_request.number }}" \
|
|
113
|
+
--repo "${{ github.repository }}" \
|
|
114
|
+
--add-label "${{ inputs.reviewed-label }}"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.9.0
|
package/.prettierignore
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
CHANGELOG.md
|
|
2
2
|
|
|
3
|
+
# Operating instructions read by Claude, not prose for humans. Prettier collapses the nested bullet
|
|
4
|
+
# list under "do not visit, fetch, infer, or evaluate the following external links" into one run-on
|
|
5
|
+
# line, which changes what the model is told. Keeping it byte-identical also makes re-syncing it
|
|
6
|
+
# from the old workflows repo a plain copy.
|
|
7
|
+
.github/review-prompt.md
|
|
8
|
+
|
|
3
9
|
# Fixtures must stay byte-exact — several are deliberately malformed or empty
|
|
4
10
|
test/fixtures
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
|
|
|
8
8
|
### 🚀 Features
|
|
9
9
|
|
|
10
10
|
- **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
|
+
- **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)
|
|
11
12
|
|
|
12
13
|
### 🐛 Bug Fixes
|
|
13
14
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
|
|
3
|
-
The package consists of
|
|
3
|
+
The package consists of three parts:
|
|
4
4
|
|
|
5
5
|
- cli located in `bin/`
|
|
6
6
|
- test library located in `lib`
|
|
7
|
+
- the reusable GitHub workflows consumer repos call, in `.github/workflows/` and `.github/actions/`
|
|
7
8
|
|
|
8
9
|
## CLI
|
|
9
10
|
|
|
@@ -25,7 +26,7 @@ The package consists of two parts:
|
|
|
25
26
|
1. Clone and build `apify-test-tools` repo:
|
|
26
27
|
|
|
27
28
|
```sh
|
|
28
|
-
git clone git@github.com:apify
|
|
29
|
+
git clone git@github.com:apify/apify-test-tools.git
|
|
29
30
|
cd apify-test-tools
|
|
30
31
|
npm i
|
|
31
32
|
npm run build
|
|
@@ -56,3 +57,47 @@ npm i -D ../path/to/apify-test-tools
|
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
You need to run `npm run build` inside `apify-test-tools` repo everytime you want to test your changes in `testing-repo-for-github-actions`.
|
|
60
|
+
|
|
61
|
+
## Reusable workflows
|
|
62
|
+
|
|
63
|
+
The `public_`-prefixed workflows are the ones consumer repos call: `public_pr-build-test`,
|
|
64
|
+
`public_platform-tests`, `public_push-build-latest`, `public_claude`, `public_review` and
|
|
65
|
+
`public_platform-tests-claude-investigate-and-fix`. They live here because they call this package's
|
|
66
|
+
CLI, so a change to both is one PR. GitHub only reads workflow files at the top level of
|
|
67
|
+
`.github/workflows`, so they sit next to this repo's own CI, and the prefix is what separates the
|
|
68
|
+
two: `public_` is the API other repos depend on, `_` is internal plumbing, and `on_`/`manual_` are
|
|
69
|
+
this repo's own triggers.
|
|
70
|
+
|
|
71
|
+
A `public_` filename is part of the contract — it is baked into every consumer's `uses:` line, so
|
|
72
|
+
renaming one is a breaking change that needs a major tag bump, not a tidy-up.
|
|
73
|
+
|
|
74
|
+
Consumers pin `@v0`, not `@master`. See
|
|
75
|
+
[Versioning and releases](./README.md#versioning-and-releases) in the README for how the tag and the
|
|
76
|
+
npm release relate — the short version:
|
|
77
|
+
|
|
78
|
+
- changing only a workflow needs no npm release
|
|
79
|
+
- changing only the package needs no workflow change
|
|
80
|
+
- a workflow that calls a **new** CLI feature must set `.github/workflows-package-version` to the
|
|
81
|
+
version that will contain it, in the same PR. The `v0` tag is then held until that version is on
|
|
82
|
+
npm, so merging can't ship a workflow that calls a CLI that doesn't exist yet.
|
|
83
|
+
|
|
84
|
+
`.github/workflows-package-version` holds one exact version, and the stable release writes it. The
|
|
85
|
+
workflows install exactly it, which is what makes a frozen major tag stay frozen — it keeps the
|
|
86
|
+
library it was tested with instead of following `latest` forever.
|
|
87
|
+
|
|
88
|
+
The `Package version bump needed` check enforces the point above: a PR touching both a `public_`
|
|
89
|
+
workflow (or the composite action) and `bin/`, `lib/` or `index.ts` has to move the pin. When the
|
|
90
|
+
two changes are unrelated and the workflow doesn't need the new code, label the PR
|
|
91
|
+
`no-version-bump-needed`.
|
|
92
|
+
|
|
93
|
+
It only looks at a single PR, so it won't catch a workflow that starts using a CLI feature merged in
|
|
94
|
+
an earlier, still-unreleased PR. That needs someone to land a CLI change and sit on it unreleased;
|
|
95
|
+
catching it would mean flagging every workflow edit made while any package change is unreleased.
|
|
96
|
+
|
|
97
|
+
`npm run lint` and `actionlint` (via the `Code checks` workflow) both gate master, so run them before
|
|
98
|
+
pushing workflow changes.
|
|
99
|
+
|
|
100
|
+
`public_review` is the odd one out: it fetches `.github/review-prompt.md` over HTTP at run time, because a
|
|
101
|
+
reusable workflow runs with the caller's repo checked out and never gets its own. Its `prompt-ref`
|
|
102
|
+
input defaults to `v0` so the instructions come from the same release as the workflow — leaving it
|
|
103
|
+
at `master` would run released workflows against unreleased instructions.
|
package/README.md
CHANGED
|
@@ -99,7 +99,12 @@ See the [GitHub workflows](#github-worklows) section below.
|
|
|
99
99
|
|
|
100
100
|
## Github worklows
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
The reusable workflows live in this repo, alongside the package they call. They are the
|
|
103
|
+
`public_`-prefixed files in `.github/workflows`; everything else there is this repo's own CI.
|
|
104
|
+
Reference them at the `@v0` major tag, never at `@master` — see
|
|
105
|
+
[Versioning and releases](#versioning-and-releases).
|
|
106
|
+
|
|
107
|
+
There should be 4 GH workflow files in `.github/workflows`, plus an optional fifth for Claude reviews.
|
|
103
108
|
|
|
104
109
|
### `platform-tests-core.yaml`
|
|
105
110
|
|
|
@@ -114,7 +119,7 @@ on:
|
|
|
114
119
|
|
|
115
120
|
jobs:
|
|
116
121
|
platformTestsCore:
|
|
117
|
-
uses: apify
|
|
122
|
+
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@v0
|
|
118
123
|
with:
|
|
119
124
|
subtest: core
|
|
120
125
|
secrets: inherit
|
|
@@ -133,7 +138,7 @@ on:
|
|
|
133
138
|
|
|
134
139
|
jobs:
|
|
135
140
|
platformTestsDaily:
|
|
136
|
-
uses: apify
|
|
141
|
+
uses: apify/apify-test-tools/.github/workflows/public_platform-tests.yaml@v0
|
|
137
142
|
secrets: inherit
|
|
138
143
|
```
|
|
139
144
|
|
|
@@ -148,7 +153,7 @@ on:
|
|
|
148
153
|
|
|
149
154
|
jobs:
|
|
150
155
|
buildDevelAndTest:
|
|
151
|
-
uses: apify
|
|
156
|
+
uses: apify/apify-test-tools/.github/workflows/public_pr-build-test.yaml@v0
|
|
152
157
|
secrets: inherit
|
|
153
158
|
```
|
|
154
159
|
|
|
@@ -163,10 +168,147 @@ on:
|
|
|
163
168
|
|
|
164
169
|
jobs:
|
|
165
170
|
buildLatest:
|
|
166
|
-
uses: apify
|
|
171
|
+
uses: apify/apify-test-tools/.github/workflows/public_push-build-latest.yaml@v0
|
|
167
172
|
secrets: inherit
|
|
168
173
|
```
|
|
169
174
|
|
|
175
|
+
### `claude-review.yaml`
|
|
176
|
+
|
|
177
|
+
Optional. Reviews a PR against the shared guidelines when you add the trigger label, and again on
|
|
178
|
+
every push while that label is on.
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
name: Claude review
|
|
182
|
+
|
|
183
|
+
on:
|
|
184
|
+
pull_request:
|
|
185
|
+
types: [labeled, synchronize]
|
|
186
|
+
|
|
187
|
+
jobs:
|
|
188
|
+
review:
|
|
189
|
+
uses: apify/apify-test-tools/.github/workflows/public_review.yaml@v0
|
|
190
|
+
secrets: inherit
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The review instructions live in `.github/review-prompt.md` in this repo and are fetched at run time,
|
|
194
|
+
because a reusable workflow doesn't get its own repo checked out. `prompt-ref` selects which ref to
|
|
195
|
+
fetch them from and defaults to `v0`, so the instructions match the workflow you're calling — point
|
|
196
|
+
it at a branch only to test a prompt change.
|
|
197
|
+
|
|
198
|
+
### Secrets
|
|
199
|
+
|
|
200
|
+
Callers pass `secrets: inherit`. The workflows do not turn every inherited secret into job-wide
|
|
201
|
+
environment variables, so a secret is only visible to the step that needs it:
|
|
202
|
+
|
|
203
|
+
| Secret | Reaches |
|
|
204
|
+
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
205
|
+
| `NPM_TOKEN` | dependency install steps only, as both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use a read-only token: npm granular tokens can be read-only, classic automation tokens can publish. |
|
|
206
|
+
| the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | the `build`, `release`, and `delete-old-builds` steps only |
|
|
207
|
+
| `TESTER_APIFY_TOKEN` | the vitest step only |
|
|
208
|
+
| `SLACK_TOKEN_TESTS_BOT` / `SLACK_TOKEN_RELEASES_BOT` | the reporting and release steps only |
|
|
209
|
+
| `TESTER_APIFY_TOKEN_READ_ONLY` | the Claude investigation step only, as the Apify MCP server's bearer token. Required by `public_platform-tests-claude-investigate-and-fix`. Use a read-only token: it reads the failing run, its log and its storages. |
|
|
210
|
+
|
|
211
|
+
The Actor tokens are the one set that cannot be listed in the workflow, because each Actor names its
|
|
212
|
+
own token via `tokenEnvVar` in `apify-test-tools.config.json`. Those steps pass
|
|
213
|
+
`${{ toJSON(secrets) }}` as `ALL_SECRETS` and run the command through
|
|
214
|
+
`.github/scripts/run-with-apify-tokens.mjs`, which reads that same config file to decide which
|
|
215
|
+
secrets to pass on:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
- name: Build
|
|
219
|
+
env:
|
|
220
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
221
|
+
run: |
|
|
222
|
+
node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
|
|
223
|
+
npx apify-test-tools build --target-branch ...
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The wrapper passes only the tokens the config declares and drops `ALL_SECRETS`, so neither `npx` nor
|
|
227
|
+
anything under `node_modules` sees the blob. Nothing is written to `$GITHUB_ENV`, so the tokens stay
|
|
228
|
+
inside that one command rather than leaking into later steps. Reading the same file
|
|
229
|
+
`apify-test-tools` reads means the two can't drift, and a secret that merely looks like an Actor
|
|
230
|
+
token is not passed just because of its name.
|
|
231
|
+
|
|
232
|
+
A token the config declares but the repo hasn't set is a warning, not a failure: a repo can carry an
|
|
233
|
+
Actor whose token isn't configured and still build fine as long as that Actor never changes, and
|
|
234
|
+
`apify-test-tools` raises a precise error naming the Actor at the point it actually needs the token.
|
|
235
|
+
|
|
236
|
+
`scripts-path` comes from the setup action (give the step `id: setup`) and points at this repo's
|
|
237
|
+
`.github/scripts/` directory inside the runner's action checkout, so workflows can run these helpers
|
|
238
|
+
without checking this repo out again. The caller's workspace holds the caller's repo, not this one.
|
|
239
|
+
|
|
240
|
+
Two tidier-looking alternatives don't work, so don't reach for them:
|
|
241
|
+
|
|
242
|
+
- **Exporting to `$GITHUB_ENV`** would let the steps call `npx` directly with no wrapper, but
|
|
243
|
+
`$GITHUB_ENV` applies to every later step in the job. In `pr-build-test` the vitest step runs after
|
|
244
|
+
the build, so it would inherit Actor tokens it has no use for.
|
|
245
|
+
- **Returning the tokens as a step output** would be scoped correctly, but the runner refuses to set
|
|
246
|
+
an output whose value contains a registered secret. It logs `Skip output <name> since it may
|
|
247
|
+
contain secret` and leaves the output empty, so anything reading it downstream gets nothing.
|
|
248
|
+
|
|
249
|
+
The `unitTest` job runs static checks and needs `NPM_TOKEN` only. No job runs `npm ci` with Apify or
|
|
250
|
+
Slack credentials in scope, so a postinstall script in the dependency tree cannot read them.
|
|
251
|
+
|
|
252
|
+
## Versioning and releases
|
|
253
|
+
|
|
254
|
+
The workflows and the npm package live in one repo but ship on their own schedules. Two pointers
|
|
255
|
+
decide what a consumer repo actually runs:
|
|
256
|
+
|
|
257
|
+
| Pointer | What it selects | Moves when |
|
|
258
|
+
| ----------------------------------- | ---------------------------------------------- | -------------------------------------------------------- |
|
|
259
|
+
| the `@v0` tag in `uses:` | which workflows run | a master push, once the version below is published |
|
|
260
|
+
| `.github/workflows-package-version` | which `apify-test-tools` the workflows install | a stable release writes it; you may set it ahead of time |
|
|
261
|
+
|
|
262
|
+
The setup action installs that **exact** version — not a range. A tag is therefore a complete
|
|
263
|
+
statement: these workflows _and_ this library. The stable release writes the file into the same
|
|
264
|
+
commit that bumps `package.json` and `CHANGELOG.md`, so a released commit always names the version
|
|
265
|
+
it published, and rollout latency is unchanged: the release publishes and moves the tag in one run.
|
|
266
|
+
|
|
267
|
+
Prereleases are excluded. Master pushes publish a `-beta` that consumer repos must never install, so
|
|
268
|
+
the pin only moves on a stable release; betas stay reachable through the lockfile path in the setup
|
|
269
|
+
action, which is how branch testing works.
|
|
270
|
+
|
|
271
|
+
Nothing is coupled that doesn't need to be:
|
|
272
|
+
|
|
273
|
+
- **Workflow-only change** — merge it. `v0` moves, it goes live, no release needed.
|
|
274
|
+
- **Package-only change** — merge it, then cut a release when you want it out. The workflows are
|
|
275
|
+
unchanged, so consumers see nothing until the release lands.
|
|
276
|
+
- **A workflow that calls a new CLI feature** — the one case that can break consumers, and the only
|
|
277
|
+
one with any ceremony. Put the package change, the workflow change, and the new pin in one PR. On
|
|
278
|
+
merge the tag is **held**: the CI job reports that the pinned version isn't on npm and leaves `v0`
|
|
279
|
+
where it is, so consumers keep running the previous workflows. Cut a stable release, and the tag
|
|
280
|
+
moves on its own. Run **Move major version tag** if you don't want to wait for the next master
|
|
281
|
+
push.
|
|
282
|
+
|
|
283
|
+
`v0` moving on every master push means `@v0` is as live as `@master` was — there's no staging step,
|
|
284
|
+
just a gate on the package version. What the tag buys you is a `v1` for breaking workflow changes,
|
|
285
|
+
so repos migrate one at a time instead of all at once, and a way to roll back by pointing the tag at
|
|
286
|
+
an earlier commit. Bump `MAJOR_TAG` in `.github/workflows/_move_major_tag.yaml` to cut the next
|
|
287
|
+
major; the old tag then freezes where it is and keeps working.
|
|
288
|
+
|
|
289
|
+
Freezing is real, which is the whole reason the version is pinned rather than a floor. A frozen `v0`
|
|
290
|
+
points at a commit whose pinned version never changes, so it keeps installing the library it was
|
|
291
|
+
tested against no matter how far the package moves on. Had the workflows installed `>=<floor>`, a
|
|
292
|
+
frozen `v0` would still resolve to whatever is newest — and since the release that enables `v1` is
|
|
293
|
+
usually the same release that breaks `v0`, the migration window would have been zero.
|
|
294
|
+
|
|
295
|
+
The tag tracks the **workflows'** contract, not the npm package version. They move independently on
|
|
296
|
+
purpose, so `@v0` is expected to stay `@v0` after the package reaches 1.0 — bump it when a workflow
|
|
297
|
+
breaks its callers, not when the package does. Because `uses:` cannot take an expression, every ref
|
|
298
|
+
into this repo repeats the tag literally; `check-major-tag-refs.mjs` fails the build if `MAJOR_TAG`
|
|
299
|
+
and those refs disagree, which is the mistake that would otherwise ship silently during a bump.
|
|
300
|
+
|
|
301
|
+
### Testing workflow changes
|
|
302
|
+
|
|
303
|
+
- Point [testing-repo-for-github-actions](https://github.com/apify-store/testing-repo-for-github-actions)
|
|
304
|
+
at your branch (`uses: ...@your-branch`). It has real attached Actors and tests. Because the
|
|
305
|
+
package lives here too, a master push publishes a `beta`, and the lockfile-beta path in the setup
|
|
306
|
+
action installs that exact version — so one branch tests both halves of a change together.
|
|
307
|
+
- To change the composite action itself, repoint the `uses:` refs inside the reusable workflows at
|
|
308
|
+
your branch as well, and change them back before merging.
|
|
309
|
+
- Make sure the shell code actually works on your laptop first.
|
|
310
|
+
- After merging, watch the workflow on a real project before moving on.
|
|
311
|
+
|
|
170
312
|
## Writing tests
|
|
171
313
|
|
|
172
314
|
### Test structure
|