apify-test-tools 0.10.2-beta.3 → 0.10.2-beta.4
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 +126 -125
- package/.github/scripts/run-with-apify-tokens.mjs +1 -1
- package/.github/workflows/public_pr-build-test.yaml +25 -25
- package/.github/workflows/public_push-build-latest.yaml +20 -4
- package/CHANGELOG.md +1 -0
- package/README.md +67 -17
- package/bin/affected-actors.ts +16 -25
- package/bin/build.ts +55 -38
- package/bin/git.ts +3 -14
- package/bin/main.ts +96 -40
- package/bin/slack.ts +24 -0
- package/bin/stdin-input.ts +65 -0
- package/dist/bin/affected-actors.d.ts +4 -9
- package/dist/bin/affected-actors.d.ts.map +1 -1
- package/dist/bin/affected-actors.js +17 -19
- package/dist/bin/affected-actors.js.map +1 -1
- package/dist/bin/build.d.ts +12 -5
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +45 -23
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/git.d.ts +1 -6
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +3 -13
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +78 -40
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts +8 -0
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +18 -0
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/stdin-input.d.ts +17 -0
- package/dist/bin/stdin-input.d.ts.map +1 -0
- package/dist/bin/stdin-input.js +54 -0
- package/dist/bin/stdin-input.js.map +1 -0
- package/dist/test/bundle/cli-bundle.test.js +27 -4
- package/dist/test/bundle/cli-bundle.test.js.map +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js +16 -9
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
- package/dist/test/unit/bin/affected-actors.test.js +7 -13
- package/dist/test/unit/bin/affected-actors.test.js.map +1 -1
- package/dist/test/unit/bin/build-cli.test.d.ts +2 -0
- package/dist/test/unit/bin/build-cli.test.d.ts.map +1 -0
- package/dist/test/unit/bin/build-cli.test.js +52 -0
- package/dist/test/unit/bin/build-cli.test.js.map +1 -0
- package/dist/test/unit/bin/build.test.js +61 -4
- package/dist/test/unit/bin/build.test.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +2 -14
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/slack.test.js +39 -1
- package/dist/test/unit/bin/slack.test.js.map +1 -1
- package/dist/test/unit/bin/stdin-input.test.d.ts +2 -0
- package/dist/test/unit/bin/stdin-input.test.d.ts.map +1 -0
- package/dist/test/unit/bin/stdin-input.test.js +50 -0
- package/dist/test/unit/bin/stdin-input.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/test/bundle/cli-bundle.test.ts +50 -4
- package/test/e2e/cli-bundle.e2e.test.ts +26 -11
- package/test/unit/bin/affected-actors.test.ts +7 -13
- package/test/unit/bin/build-cli.test.ts +61 -0
- package/test/unit/bin/build.test.ts +71 -3
- package/test/unit/bin/git.test.ts +1 -15
- package/test/unit/bin/slack.test.ts +44 -1
- package/test/unit/bin/stdin-input.test.ts +61 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Runs apify-test-tools and passes it only secrets needed for the Actors, no other secrets are leaked.
|
|
3
3
|
//
|
|
4
|
-
// node .github/scripts/run-with-apify-tokens.mjs apify-test-tools build --target-branch ...
|
|
4
|
+
// node .github/scripts/run-with-apify-tokens.mjs apify-test-tools build-for-test --target-branch ...
|
|
5
5
|
//
|
|
6
6
|
// The step passes the whole secrets map as ALL_SECRETS. The token names come from `tokenEnvVar` in
|
|
7
7
|
// the repo's apify-test-tools.config.json, which is the same file apify-test-tools itself reads, so
|
|
@@ -41,31 +41,39 @@ jobs:
|
|
|
41
41
|
# This is needed to force overriding the cache with new entry at the end
|
|
42
42
|
restore-keys: base_commit-
|
|
43
43
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
- name: Build
|
|
48
|
-
id: build
|
|
44
|
+
# Needs no tokens, it only reads git and apify-test-tools.config.json. The setup action checks out
|
|
45
|
+
# the PR branch itself (not GitHub's merge commit), so the range ends at the branch's HEAD.
|
|
46
|
+
- name: Find affected Actors
|
|
49
47
|
env:
|
|
50
|
-
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
51
48
|
# Branch names are attacker-controlled on fork PRs, so they reach the script as
|
|
52
49
|
# environment variables rather than being interpolated into it.
|
|
53
|
-
HEAD_REF: ${{ github.head_ref }}
|
|
54
50
|
BASE_REF: ${{ github.base_ref }}
|
|
55
|
-
SCRIPTS_PATH: ${{ steps.setup.outputs.scripts-path }}
|
|
56
51
|
run: |
|
|
57
52
|
base_commit=$(cat base_commit.txt 2>/dev/null || echo "")
|
|
58
53
|
echo "Last validated (base) commit to be used for build & test: $base_commit"
|
|
59
54
|
args=(
|
|
60
|
-
--
|
|
61
|
-
--target-branch "origin/$BASE_REF"
|
|
55
|
+
--base "origin/$BASE_REF"
|
|
62
56
|
# TODO: Temporary hotfix until build glob matching aligns with Vitest's test selection glob.
|
|
63
57
|
--test-files-glob 'test/platform/**'
|
|
64
|
-
--use-docker-cache
|
|
65
58
|
)
|
|
66
|
-
[ -n "$base_commit" ] && args+=(--
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
[ -n "$base_commit" ] && args+=(--since "$base_commit")
|
|
60
|
+
apify-test-tools get-affected-actors "${args[@]}" > affected.json
|
|
61
|
+
|
|
62
|
+
# Cosmetic-only changes need no tests, so only the functionally changed Actors are built, into the
|
|
63
|
+
# test version 0.99 that users never run.
|
|
64
|
+
# run-with-apify-tokens.mjs reads the token names from in apify-test-tools.config.json
|
|
65
|
+
# and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
|
|
66
|
+
# The library never sees secrets it doesn't need
|
|
67
|
+
- name: Build
|
|
68
|
+
id: build
|
|
69
|
+
# bash rather than the default shell, for pipefail
|
|
70
|
+
shell: bash
|
|
71
|
+
env:
|
|
72
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
73
|
+
SCRIPTS_PATH: ${{ steps.setup.outputs.scripts-path }}
|
|
74
|
+
run: |
|
|
75
|
+
actor_builds=$(jq '{ actors: .functionallyChangedActors }' affected.json \
|
|
76
|
+
| node "$SCRIPTS_PATH/run-with-apify-tokens.mjs" apify-test-tools build --version-number 0.99)
|
|
69
77
|
echo "actor_builds=$actor_builds" | tee -a "$GITHUB_OUTPUT"
|
|
70
78
|
|
|
71
79
|
# Runs the repo's test code and its whole import graph, so it gets the tester token only.
|
|
@@ -77,18 +85,10 @@ jobs:
|
|
|
77
85
|
|
|
78
86
|
# 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
|
|
79
87
|
- name: Store last validated commit to cache
|
|
80
|
-
env:
|
|
81
|
-
HEAD_REF: ${{ github.head_ref }}
|
|
82
|
-
BASE_REF: ${{ github.base_ref }}
|
|
83
88
|
run: |
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
--source-branch "origin/$HEAD_REF"
|
|
88
|
-
--target-branch "origin/$BASE_REF"
|
|
89
|
-
)
|
|
90
|
-
[ -n "$base_commit" ] && args+=(--base-commit "$base_commit")
|
|
91
|
-
apify-test-tools get-latest-commit "${args[@]}" > ./base_commit.txt
|
|
89
|
+
echo "Old last validated (base) commit: $(cat base_commit.txt 2>/dev/null || echo "")"
|
|
90
|
+
# The HEAD that was just built and tested
|
|
91
|
+
jq -r .head affected.json > base_commit.txt
|
|
92
92
|
echo "New last validated (base) commit: $(cat base_commit.txt)"
|
|
93
93
|
|
|
94
94
|
# Pure static checks against the repo. Needs no Apify or Slack credentials, only the npm
|
|
@@ -25,18 +25,34 @@ jobs:
|
|
|
25
25
|
with:
|
|
26
26
|
npm-token: ${{ secrets.NPM_TOKEN }}
|
|
27
27
|
|
|
28
|
-
# --
|
|
28
|
+
# --since is the last commit that is already released: the push's "before" commit.
|
|
29
29
|
# Everything after it up to HEAD gets released. See the "Releasing Actors" section of the README.
|
|
30
|
+
# Needs no tokens, it only reads git and apify-test-tools.config.json.
|
|
31
|
+
- name: Find changes since the last release
|
|
32
|
+
run: |
|
|
33
|
+
apify-test-tools get-affected-actors --since ${{ github.event.before }} > affected.json
|
|
34
|
+
|
|
35
|
+
# A release ships cosmetic changes too (README, input schema descriptions), so both lists are built,
|
|
36
|
+
# into each Actor's default version: the one its default build tag points to, which users run.
|
|
30
37
|
# run-with-apify-tokens.mjs reads the token names from in apify-test-tools.config.json
|
|
31
38
|
# and hands the build those secrets and nothing else, so the tokens stay in this one command instead of the whole job.
|
|
32
39
|
# The library never sees secrets it doesn't need
|
|
33
|
-
- name:
|
|
40
|
+
- name: Build the default versions
|
|
41
|
+
# bash rather than the default shell, for pipefail
|
|
42
|
+
shell: bash
|
|
34
43
|
env:
|
|
35
44
|
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
45
|
+
run: |
|
|
46
|
+
jq '{ actors: (.functionallyChangedActors + .cosmeticallyChangedActors) }' affected.json \
|
|
47
|
+
| node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
|
|
48
|
+
apify-test-tools build --version-number default
|
|
49
|
+
|
|
50
|
+
# Skips the announcement when the push brought no new commits
|
|
51
|
+
- name: Announce the release on Slack
|
|
52
|
+
env:
|
|
36
53
|
SLACK_TOKEN_RELEASES_BOT: ${{ secrets.SLACK_TOKEN_RELEASES_BOT }}
|
|
37
54
|
run: |
|
|
38
|
-
|
|
39
|
-
apify-test-tools release --base-commit ${{ github.event.before }} --release-slack-channel "#delivery-public-actors" --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}" --use-docker-cache
|
|
55
|
+
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
|
|
40
56
|
|
|
41
57
|
- name: Delete old builds
|
|
42
58
|
env:
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
|
|
|
9
9
|
|
|
10
10
|
- **ci:** Ship the workflows' CLI as a bundle in the action instead of installing it ([#200](https://github.com/apify/apify-test-tools/pull/200)) ([a0ef975](https://github.com/apify/apify-test-tools/commit/a0ef9756123a30a8210590b080a2a289c6a9c17c)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
11
11
|
- [**breaking**] Revamp get-affected-actors to be a core logic command ([#190](https://github.com/apify/apify-test-tools/pull/190)) ([ea5f244](https://github.com/apify/apify-test-tools/commit/ea5f244cfd42bf3fa9ffd38cd2ad499dde2e83f8)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
12
|
+
- [**breaking**] Add logic-less build command and use command chaining in workflows ([#193](https://github.com/apify/apify-test-tools/pull/193)) ([e5b0b86](https://github.com/apify/apify-test-tools/commit/e5b0b86ee06fca51d4be0ddc81b0a4a469d31603)) by [@metalwarrior665](https://github.com/metalwarrior665)
|
|
12
13
|
|
|
13
14
|
### 🐛 Bug Fixes
|
|
14
15
|
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ When a PR is opened or code is pushed, the tool determines which actors need to
|
|
|
57
57
|
2. **CHANGELOG classification** — a `CHANGELOG.md` file is always `cosmetic` (only triggers a release build, not tests), for every actor, regardless of context or folder. (See [issue #106](https://github.com/apify/apify-test-tools/issues/106).)
|
|
58
58
|
3. **Context matching** — the file must fall within one of the actor's context paths (`dockerContextDir` from `actor.json` by default, or `overrideActorContext` from config if set). Files outside every context path are skipped.
|
|
59
59
|
4. **Hardcoded ignore list, context-aware** — the file path is first "hoisted" relative to the context path it matched (e.g. a standalone actor's own `.eslintrc` is checked as just `.eslintrc`, not the full repo-root-relative path), then checked against repo-level dev file patterns (`.vscode/`, `.gitignore`, `.husky/`, `.eslintrc`, `eslint.config.mjs`, `.prettierrc`, `.editorconfig`). There's no hardcoded special-casing for legacy `code/`/`shared/` layouts — repos that need those directories treated as top-level must list them explicitly in `overrideActorContext`.
|
|
60
|
-
5. **`.dockerignore` filtering** — if a `.dockerignore` exists at the root of the actor's `dockerContextDir`, matching files are ignored. Patterns are resolved relative to `dockerContextDir`, matching Docker's own behavior. Test files are the exception: `build --test-files-glob <glob>` (relative to the repo root) skips this check for matching files, so a changed test triggers a build, and with it its tests, even when `.dockerignore` lists it. The PR workflow passes `test/platform/**`. Context matching still applies, so tests outside an actor's `dockerContextDir` (e.g. a root `test/` with standalone actors) need to be listed in its `overrideActorContext`; `build` logs a warning for changed test files outside every actor's context.
|
|
60
|
+
5. **`.dockerignore` filtering** — if a `.dockerignore` exists at the root of the actor's `dockerContextDir`, matching files are ignored. Patterns are resolved relative to `dockerContextDir`, matching Docker's own behavior. Test files are the exception: `build-for-test --test-files-glob <glob>` (relative to the repo root, also taken by `get-affected-actors`) skips this check for matching files, so a changed test triggers a build, and with it its tests, even when `.dockerignore` lists it. The PR workflow passes `test/platform/**`. Context matching still applies, so tests outside an actor's `dockerContextDir` (e.g. a root `test/` with standalone actors) need to be listed in its `overrideActorContext`; `build-for-test` logs a warning for changed test files outside every actor's context.
|
|
61
61
|
6. **README classification** — a `README.md` file is `cosmetic` (only triggers a release build, not tests) if it lives inside the actor's own `folder`; otherwise it's ignored entirely, since it isn't documentation for this actor.
|
|
62
62
|
7. **Cosmetic JSON classification** — `.json` files inside the actor's own `.actor/` directory with only cosmetic schema changes (whitespace, key ordering) only trigger a release build.
|
|
63
63
|
8. **Functional** — everything else triggers both build and tests.
|
|
@@ -226,9 +226,10 @@ jobs:
|
|
|
226
226
|
secrets: inherit
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
This workflow
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
This workflow does what `apify-test-tools release --base-commit ${{ github.event.before }}` does, one
|
|
230
|
+
step per command: `get-affected-actors --since ${{ github.event.before }}`, then `build --version-number default`
|
|
231
|
+
on every changed Actor, then `notify-release`. See [Releasing Actors](#releasing-actors-release) for how the
|
|
232
|
+
released range is determined and what to pass if you call the CLI from somewhere else.
|
|
232
233
|
|
|
233
234
|
### `claude-review.yaml`
|
|
234
235
|
|
|
@@ -261,9 +262,9 @@ environment variables, so a secret is only visible to the step that needs it:
|
|
|
261
262
|
| Secret | Reaches |
|
|
262
263
|
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
263
264
|
| `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. |
|
|
264
|
-
| the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | the
|
|
265
|
+
| the Actor tokens named by `tokenEnvVar` in `apify-test-tools.config.json` | the build and `delete-old-builds` steps only |
|
|
265
266
|
| `TESTER_APIFY_TOKEN` | the vitest step, and the Claude investigation step: the Apify MCP server's bearer token for reading the failing run, its log and its storages, and Claude's token for re-running it to measure how often the failure happens |
|
|
266
|
-
| `SLACK_TOKEN_TESTS_BOT` / `SLACK_TOKEN_RELEASES_BOT` | the reporting and
|
|
267
|
+
| `SLACK_TOKEN_TESTS_BOT` / `SLACK_TOKEN_RELEASES_BOT` | the reporting and announcement steps only |
|
|
267
268
|
|
|
268
269
|
The Actor tokens are the one set that cannot be listed in the workflow, because each Actor names its
|
|
269
270
|
own token via `tokenEnvVar` in `apify-test-tools.config.json`. Those steps pass
|
|
@@ -277,7 +278,7 @@ secrets to pass on:
|
|
|
277
278
|
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
278
279
|
run: |
|
|
279
280
|
node "${{ steps.setup.outputs.scripts-path }}/run-with-apify-tokens.mjs" \
|
|
280
|
-
npx apify-test-tools build --target-branch ...
|
|
281
|
+
npx apify-test-tools build-for-test --target-branch ...
|
|
281
282
|
```
|
|
282
283
|
|
|
283
284
|
The wrapper passes only the tokens the config declares and drops `ALL_SECRETS`, so neither `npx` nor
|
|
@@ -568,12 +569,12 @@ The package includes a CLI binary used by CI workflows to build Actors, detect c
|
|
|
568
569
|
|
|
569
570
|
### Dry runs (`--dry-run`)
|
|
570
571
|
|
|
571
|
-
Every command that changes something (`build`, `build-from-local`, `release`, `delete-old-builds`, `report-tests`) takes `--dry-run`. A dry run:
|
|
572
|
+
Every command that changes something (`build`, `build-for-test`, `build-from-local`, `release`, `notify-release`, `delete-old-builds`, `report-tests`) takes `--dry-run`. A dry run:
|
|
572
573
|
|
|
573
574
|
- **Changes nothing.** No versions or builds are created, nothing is uploaded or deleted, and no Slack message is posted.
|
|
574
575
|
- **Does every read and check the real run does**, so it fails wherever the real run would: change detection, the Apify token and Actor lookup, the [repository check](#repository-check), collecting and zipping local sources, and the Slack token (verified with Slack's read-only `auth.test`).
|
|
575
576
|
- **Needs the same tokens as the real run.** Read-only tokens are enough, since nothing is written. A dry run can't tell whether a token is also allowed to write, because the Apify API has no dry-run mode of its own.
|
|
576
|
-
- **Prints a plan, not results.** Logs start with `[DRY RUN] Would …`. `build` and `build-from-local` write `{ "dryRun": true, "builds": [...] }` to stdout instead of the build array, so the plan can't be passed to tests as `ACTOR_BUILDS` by mistake.
|
|
577
|
+
- **Prints a plan, not results.** Logs start with `[DRY RUN] Would …`. `build`, `build-for-test` and `build-from-local` write `{ "dryRun": true, "builds": [...] }` to stdout instead of the build array, so the plan can't be passed to tests as `ACTOR_BUILDS` by mistake.
|
|
577
578
|
|
|
578
579
|
### Running locally
|
|
579
580
|
|
|
@@ -599,13 +600,15 @@ Requires `APIFY_TOKEN_<USERNAME>` for all Apify users that own your Actors (e.g.
|
|
|
599
600
|
|
|
600
601
|
```bash
|
|
601
602
|
APIFY_TOKEN_JOHN_DOE=<token> \
|
|
602
|
-
npx apify-test-tools build \
|
|
603
|
+
npx apify-test-tools build-for-test \
|
|
603
604
|
--target-branch origin/master \
|
|
604
|
-
--source-branch origin/my-dummy-branch \
|
|
605
605
|
--dry-run
|
|
606
606
|
```
|
|
607
607
|
|
|
608
|
-
|
|
608
|
+
Run it with your dummy branch checked out: it builds the Actors that branch changed, from the branch as it is on
|
|
609
|
+
the remote. With `--dry-run`, it prints the plan (the version and Git URL each Actor would be built from). Remove
|
|
610
|
+
`--dry-run` to actually trigger builds and update the target branch name. The command outputs a JSON array of build
|
|
611
|
+
objects to stdout:
|
|
609
612
|
|
|
610
613
|
```json
|
|
611
614
|
[{ "buildId": "...", "actorRawId": "...", "buildNumber": "...", "actorFullName": "john.doe/my-actor" }]
|
|
@@ -649,9 +652,8 @@ Tests only run for Actors in `ACTOR_BUILDS`, so a custom workflow has to build (
|
|
|
649
652
|
```bash
|
|
650
653
|
# Build and capture output
|
|
651
654
|
BUILDS=$(APIFY_TOKEN_JOHN_DOE=apify_api_xxx \
|
|
652
|
-
npx apify-test-tools build \
|
|
655
|
+
npx apify-test-tools build-for-test \
|
|
653
656
|
--target-branch origin/master \
|
|
654
|
-
--source-branch origin/my-dummy-branch \
|
|
655
657
|
--test-files-glob 'test/platform/**')
|
|
656
658
|
|
|
657
659
|
# Run tests with the builds
|
|
@@ -698,7 +700,6 @@ The output is a JSON document:
|
|
|
698
700
|
```json
|
|
699
701
|
{
|
|
700
702
|
"version": 1,
|
|
701
|
-
"ref": "my-branch",
|
|
702
703
|
"head": "<sha of HEAD>",
|
|
703
704
|
"commits": [{ "sha": "…", "author": "…", "date": "…", "message": "…" }],
|
|
704
705
|
"changedFiles": ["src/main.ts", "CHANGELOG.md"],
|
|
@@ -710,10 +711,50 @@ The output is a JSON document:
|
|
|
710
711
|
|
|
711
712
|
Each changed Actor is in exactly one of the lists. `functionallyChangedActors` have at least one functional change, so
|
|
712
713
|
they need a new build and tests. `cosmeticallyChangedActors` only have cosmetic changes (see
|
|
713
|
-
[Change detection](#change-detection)), so they need a new build only when released: `build` builds
|
|
714
|
+
[Change detection](#change-detection)), so they need a new build only when released: `build-for-test` builds
|
|
714
715
|
`functionallyChangedActors`, `release` builds both.
|
|
715
716
|
|
|
716
|
-
`
|
|
717
|
+
`build-for-test` and `release` use the same detection internally.
|
|
718
|
+
|
|
719
|
+
### Building Actors (`build`)
|
|
720
|
+
|
|
721
|
+
`build` builds exactly the Actors it is given on stdin, from the checked-out branch as it is on the remote, and
|
|
722
|
+
prints the builds as the same JSON array `build-for-test` and `build-from-local` print. A detached HEAD is an
|
|
723
|
+
error, since there is no branch to build from. It doesn't look at git history: which Actors to build is
|
|
724
|
+
up to you, usually picked from the output of [`get-affected-actors`](#finding-affected-actors-get-affected-actors).
|
|
725
|
+
|
|
726
|
+
```bash
|
|
727
|
+
# What build-for-test does: the Actors a branch changed functionally, built into the test version
|
|
728
|
+
npx apify-test-tools get-affected-actors --base origin/master \
|
|
729
|
+
| jq '{ actors: .functionallyChangedActors }' \
|
|
730
|
+
| npx apify-test-tools build --version-number 0.99
|
|
731
|
+
|
|
732
|
+
# What release builds: every Actor changed since the last release, into the version users run
|
|
733
|
+
npx apify-test-tools get-affected-actors --since <sha> \
|
|
734
|
+
| jq '{ actors: (.functionallyChangedActors + .cosmeticallyChangedActors) }' \
|
|
735
|
+
| npx apify-test-tools build --version-number default
|
|
736
|
+
|
|
737
|
+
# One Actor, whatever changed
|
|
738
|
+
echo '{ "actors": [{ "actorFullName": "john.doe/my-actor" }] }' | npx apify-test-tools build --version-number 0.99
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
The input is `{ "actors": [{ "actorFullName": "<owner/name>" }] }`; other fields are ignored. An empty `actors` list builds nothing and prints `[]`. Empty or invalid input is an error,
|
|
742
|
+
so a failed command earlier in the pipe can't turn into a successful run that built nothing. In GitHub
|
|
743
|
+
Actions, give such a step `shell: bash` so the pipe also fails on its own (the default shell has no `pipefail`).
|
|
744
|
+
|
|
745
|
+
| Flag | Default | Meaning |
|
|
746
|
+
| ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
747
|
+
| `--version-number` | required | Version to build, like `0.99`, or `default` for each Actor's default version (the one its default build tag points to) |
|
|
748
|
+
| `--repo-url` | the `origin` remote | Repository to build from, see [Repository check](#repository-check) |
|
|
749
|
+
|
|
750
|
+
`build` never sets or changes build tags. A build gets the tag its version is configured with on the platform,
|
|
751
|
+
so building the default version moves its tag (usually `latest`) to the new build, and building a version
|
|
752
|
+
without a tag leaves every tag where it is. Builds always use the Docker cache.
|
|
753
|
+
|
|
754
|
+
`build-for-test` and `release` are shortcuts for the two pipelines above, with the flags they always had.
|
|
755
|
+
`build-for-test` takes `--target-branch`, `--base-commit` and `--test-files-glob`, inspects and builds the
|
|
756
|
+
checked-out branch, and builds into version `0.99`. `release` takes `--base-commit`, and announces the release on Slack after building, the same
|
|
757
|
+
way `notify-release` does.
|
|
717
758
|
|
|
718
759
|
### Releasing Actors (`release`)
|
|
719
760
|
|
|
@@ -726,6 +767,15 @@ the `origin` remote.
|
|
|
726
767
|
npx apify-test-tools release --base-commit <sha> --release-slack-channel "#releases" --dry-run
|
|
727
768
|
```
|
|
728
769
|
|
|
770
|
+
The announcement is also its own command, `notify-release`, which reads the output of `get-affected-actors` on
|
|
771
|
+
stdin. It posts the changelog additions to `--release-slack-channel` and the commits and changed files to
|
|
772
|
+
`--report-slack-channel`, and posts nothing when there are no new commits. It needs `SLACK_TOKEN_RELEASES_BOT`.
|
|
773
|
+
|
|
774
|
+
```bash
|
|
775
|
+
npx apify-test-tools get-affected-actors --since <sha> \
|
|
776
|
+
| npx apify-test-tools notify-release --release-slack-channel "#releases" --dry-run
|
|
777
|
+
```
|
|
778
|
+
|
|
729
779
|
#### `--base-commit`
|
|
730
780
|
|
|
731
781
|
The last commit that is **already released**. Everything after it, up to `HEAD`, is released. It is
|
package/bin/affected-actors.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
getCommits,
|
|
7
7
|
getCommitsBetween,
|
|
8
8
|
hasMergeFromTarget,
|
|
9
|
-
resolveBranchName,
|
|
10
9
|
resolveCommitSha,
|
|
11
10
|
resolveReleaseBaseCommit,
|
|
12
11
|
} from './git.js';
|
|
@@ -14,14 +13,15 @@ import { logger } from './logger.js';
|
|
|
14
13
|
import type { ActorConfig, Commit } from './types.js';
|
|
15
14
|
import { readConfigFile } from './utils/config/load-config.js';
|
|
16
15
|
|
|
16
|
+
// The range always ends at the checked-out commit: check out what you want to inspect
|
|
17
|
+
const HEAD = 'HEAD';
|
|
18
|
+
|
|
17
19
|
interface AffectedActorsOptions {
|
|
18
|
-
/**
|
|
19
|
-
head: string;
|
|
20
|
-
/** Only the commits on head that are not on base count, i.e. the branch's own changes. */
|
|
20
|
+
/** Only the commits on HEAD that are not on base count, i.e. the branch's own changes. */
|
|
21
21
|
base?: string;
|
|
22
22
|
/**
|
|
23
23
|
* Only changes after this commit count. With `base` it's the last validated commit of the branch and is
|
|
24
|
-
* ignored when it isn't on the branch. Without `base` it must be an ancestor of
|
|
24
|
+
* ignored when it isn't on the branch. Without `base` it must be an ancestor of HEAD (e.g. the last release).
|
|
25
25
|
*/
|
|
26
26
|
since?: string;
|
|
27
27
|
/** Glob relative to the repo root. Matching files count as changes even when .dockerignore lists them. */
|
|
@@ -32,8 +32,6 @@ interface AffectedActorsOptions {
|
|
|
32
32
|
|
|
33
33
|
interface AffectedActors {
|
|
34
34
|
headSha: string;
|
|
35
|
-
/** Branch head points to, which is what git builds are made from. Null for a SHA or a detached HEAD. */
|
|
36
|
-
ref: string | null;
|
|
37
35
|
commits: Commit[];
|
|
38
36
|
changedFiles: string[];
|
|
39
37
|
changelog: string | null;
|
|
@@ -50,14 +48,14 @@ interface CommitRange {
|
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
/** The branch's own commits, after `since` when it is on the branch (see getCommits). */
|
|
53
|
-
const getBranchRange = async (
|
|
54
|
-
const commits = await getCommits({ sourceBranch:
|
|
51
|
+
const getBranchRange = async (base: string, since: string | undefined): Promise<CommitRange> => {
|
|
52
|
+
const commits = await getCommits({ sourceBranch: HEAD, targetBranch: base, baseCommit: since });
|
|
55
53
|
return { commits, fromRef: `${commits[0].sha}~`, toRef: commits[commits.length - 1].sha };
|
|
56
54
|
};
|
|
57
55
|
|
|
58
|
-
/** Everything after `since` up to
|
|
59
|
-
const getRangeSince = async (
|
|
60
|
-
const sinceSha = await resolveReleaseBaseCommit(since
|
|
56
|
+
/** Everything after `since` up to HEAD. Unlike on a branch, `since` must be an ancestor of HEAD. */
|
|
57
|
+
const getRangeSince = async (headSha: string, since: string): Promise<CommitRange> => {
|
|
58
|
+
const sinceSha = await resolveReleaseBaseCommit(since);
|
|
61
59
|
const commits = sinceSha === headSha ? [] : await getCommitsBetween(sinceSha, headSha);
|
|
62
60
|
return { commits, fromRef: sinceSha, toRef: headSha };
|
|
63
61
|
};
|
|
@@ -76,21 +74,20 @@ const getRangeSince = async (head: string, headSha: string, since: string): Prom
|
|
|
76
74
|
* branch's own keeps every Actor, and the log messages below describe the whole branch.
|
|
77
75
|
*/
|
|
78
76
|
const getOwnChangesIfMergedFromBase = async (
|
|
79
|
-
head: string,
|
|
80
77
|
base: string,
|
|
81
78
|
actorConfigs: ActorConfig[],
|
|
82
79
|
testFilesGlob: string | undefined,
|
|
83
80
|
): Promise<ChangedActors | undefined> => {
|
|
84
|
-
if (!(await hasMergeFromTarget(
|
|
81
|
+
if (!(await hasMergeFromTarget(HEAD, base))) return undefined;
|
|
85
82
|
|
|
86
83
|
logger.info(
|
|
87
84
|
'[MERGE-FROM-TARGET-OPTIMIZATION]: There is merge from target branch, checking if there are no functional changes in our own branch. If so, we can skip tests',
|
|
88
85
|
);
|
|
89
86
|
const own = await getChangedActors({
|
|
90
|
-
filepathsChanged: await getBranchOnlyChangedFiles(
|
|
87
|
+
filepathsChanged: await getBranchOnlyChangedFiles(HEAD, base),
|
|
91
88
|
actorConfigs,
|
|
92
89
|
// Omit since to get full branch history. Validated functional commits can still interact with merged ones
|
|
93
|
-
commits: await getCommits({ sourceBranch:
|
|
90
|
+
commits: await getCommits({ sourceBranch: HEAD, targetBranch: base }),
|
|
94
91
|
testFilesGlob,
|
|
95
92
|
});
|
|
96
93
|
logger.info(
|
|
@@ -106,7 +103,6 @@ const getOwnChangesIfMergedFromBase = async (
|
|
|
106
103
|
* (a PR), `since` the commits after a known one (a release, or the last validated commit of a PR).
|
|
107
104
|
*/
|
|
108
105
|
export const getAffectedActors = async ({
|
|
109
|
-
head,
|
|
110
106
|
base,
|
|
111
107
|
since: sinceOption,
|
|
112
108
|
testFilesGlob,
|
|
@@ -119,21 +115,18 @@ export const getAffectedActors = async ({
|
|
|
119
115
|
throw new Error('Pass --base, --since, or both to select the commits to inspect.');
|
|
120
116
|
}
|
|
121
117
|
|
|
122
|
-
const headSha = await resolveCommitSha(
|
|
123
|
-
const { commits, fromRef, toRef } = base
|
|
124
|
-
? await getBranchRange(head, base, since)
|
|
125
|
-
: await getRangeSince(head, headSha, since!);
|
|
118
|
+
const headSha = await resolveCommitSha(HEAD);
|
|
119
|
+
const { commits, fromRef, toRef } = base ? await getBranchRange(base, since) : await getRangeSince(headSha, since!);
|
|
126
120
|
const actorConfigs = await readConfigFile({ actors, ignore });
|
|
127
121
|
|
|
128
122
|
// What the branch only merged in from base is already validated. Without functional changes of its own
|
|
129
123
|
// the branch has nothing to test, and without any changes of its own nothing to release either.
|
|
130
|
-
const own = base ? await getOwnChangesIfMergedFromBase(
|
|
124
|
+
const own = base ? await getOwnChangesIfMergedFromBase(base, actorConfigs, testFilesGlob) : undefined;
|
|
131
125
|
const changedFiles = await getChangedFilesBetween(fromRef, toRef);
|
|
132
126
|
const changed = await getChangedActors({ filepathsChanged: changedFiles, actorConfigs, commits, testFilesGlob });
|
|
133
127
|
|
|
134
128
|
return {
|
|
135
129
|
headSha,
|
|
136
|
-
ref: await resolveBranchName(head),
|
|
137
130
|
commits,
|
|
138
131
|
changedFiles,
|
|
139
132
|
changelog: await getChangelogAdditions(fromRef, toRef, changedFiles),
|
|
@@ -145,7 +138,6 @@ export const getAffectedActors = async ({
|
|
|
145
138
|
/** The JSON document `get-affected-actors` prints. Versioned, since other commands and workflows consume it. */
|
|
146
139
|
export const toAffectedActorsDocument = ({
|
|
147
140
|
headSha,
|
|
148
|
-
ref,
|
|
149
141
|
commits,
|
|
150
142
|
changedFiles,
|
|
151
143
|
changelog,
|
|
@@ -155,7 +147,6 @@ export const toAffectedActorsDocument = ({
|
|
|
155
147
|
const names = (configs: ActorConfig[]) => configs.map(({ actorFullName }) => ({ actorFullName }));
|
|
156
148
|
return {
|
|
157
149
|
version: 1,
|
|
158
|
-
ref,
|
|
159
150
|
head: headSha,
|
|
160
151
|
commits,
|
|
161
152
|
changedFiles,
|
package/bin/build.ts
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import type * as ApifyClientTypes from 'apify-client';
|
|
2
2
|
import { ActorSourceType, ApifyClient } from 'apify-client';
|
|
3
3
|
|
|
4
|
-
import { normalizeRepoUrl } from './git.js';
|
|
4
|
+
import { getCurrentBranch, normalizeRepoUrl, resolveRepoUrl } from './git.js';
|
|
5
5
|
import { logger } from './logger.js';
|
|
6
6
|
import type { ActorConfig, BuildData, DryRunPlan } from './types.js';
|
|
7
7
|
|
|
8
8
|
type BuildPrActorOptions = {
|
|
9
|
-
buildTag?: string;
|
|
10
9
|
versionNumber: string;
|
|
11
10
|
gitRepoUrl: string;
|
|
12
11
|
actorConfig: ActorConfig;
|
|
13
12
|
actorInfo: ApifyClientTypes.Actor;
|
|
14
|
-
useDockerCache: boolean;
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
// Fixed version number used to build from local source files, since there is no real version to track.
|
|
18
16
|
export const LOCAL_SOURCE_VERSION_NUMBER = '0.98';
|
|
19
|
-
|
|
17
|
+
// Version that build-for-test builds, so tests never touch the version users run
|
|
18
|
+
export const TEST_VERSION_NUMBER = '0.99';
|
|
19
|
+
// Stands for each Actor's default version: the one its default build tag points to
|
|
20
|
+
export const DEFAULT_VERSION_KEYWORD = 'default';
|
|
20
21
|
|
|
21
22
|
// Shown whenever an Actor isn't set up the way CI builds need it
|
|
22
23
|
const ACTOR_SETUP_REQUIREMENT =
|
|
@@ -319,20 +320,18 @@ export const runAndSummarizeBuilds = async (
|
|
|
319
320
|
type GitBuildPlan = {
|
|
320
321
|
actorFullName: string;
|
|
321
322
|
versionNumber: string;
|
|
322
|
-
buildTag?: string;
|
|
323
323
|
gitRepoUrl: string;
|
|
324
|
-
useDockerCache: boolean;
|
|
325
324
|
};
|
|
326
325
|
|
|
327
326
|
type RunBuildsOptions = {
|
|
328
327
|
actorConfigs: ActorConfig[];
|
|
329
|
-
|
|
328
|
+
// A version number like "0.99", or DEFAULT_VERSION_KEYWORD for each Actor's default version
|
|
329
|
+
versionNumber: string;
|
|
330
330
|
repoUrl: string;
|
|
331
331
|
// False when the repo URL was passed explicitly, see assertRepoUrlMatchesDefaultVersion
|
|
332
332
|
shouldVerifyRepoUrl: boolean;
|
|
333
333
|
branch: string;
|
|
334
334
|
dryRun: boolean;
|
|
335
|
-
useDockerCache: boolean;
|
|
336
335
|
};
|
|
337
336
|
|
|
338
337
|
export const runBuilds = async ({
|
|
@@ -340,9 +339,8 @@ export const runBuilds = async ({
|
|
|
340
339
|
shouldVerifyRepoUrl,
|
|
341
340
|
branch,
|
|
342
341
|
actorConfigs,
|
|
343
|
-
|
|
342
|
+
versionNumber,
|
|
344
343
|
dryRun,
|
|
345
|
-
useDockerCache,
|
|
346
344
|
}: RunBuildsOptions): Promise<BuildData[] | DryRunPlan<GitBuildPlan>> => {
|
|
347
345
|
// Fetched once per Actor and reused for the checks, the version numbers and the build itself
|
|
348
346
|
const buildConfigs: BuildPrActorOptions[] = await Promise.all(
|
|
@@ -352,6 +350,15 @@ export const runBuilds = async ({
|
|
|
352
350
|
actorConfig.actorFullName,
|
|
353
351
|
actorInfo,
|
|
354
352
|
);
|
|
353
|
+
// Builds keep the tags their version gives them, so a default version without the default build tag
|
|
354
|
+
// would build fine but leave users on the previous build
|
|
355
|
+
if (versionNumber === DEFAULT_VERSION_KEYWORD && defaultVersion?.buildTag !== defaultBuildTag) {
|
|
356
|
+
logger.warn(
|
|
357
|
+
`[${actorConfig.actorFullName}] Default version ${defaultVersionNumber} has build tag ` +
|
|
358
|
+
`"${defaultVersion?.buildTag ?? 'none'}", not "${defaultBuildTag}", so users keep running the previous build. ` +
|
|
359
|
+
'Set the build tag of the version on the platform to change that.',
|
|
360
|
+
);
|
|
361
|
+
}
|
|
355
362
|
if (shouldVerifyRepoUrl) {
|
|
356
363
|
assertRepoUrlMatchesDefaultVersion(actorConfig.actorFullName, defaultVersion, repoUrl);
|
|
357
364
|
}
|
|
@@ -365,9 +372,7 @@ export const runBuilds = async ({
|
|
|
365
372
|
actorConfig,
|
|
366
373
|
actorInfo,
|
|
367
374
|
gitRepoUrl,
|
|
368
|
-
versionNumber:
|
|
369
|
-
buildTag: isLatest ? defaultBuildTag : undefined,
|
|
370
|
-
useDockerCache,
|
|
375
|
+
versionNumber: versionNumber === DEFAULT_VERSION_KEYWORD ? defaultVersionNumber : versionNumber,
|
|
371
376
|
};
|
|
372
377
|
}),
|
|
373
378
|
);
|
|
@@ -375,21 +380,15 @@ export const runBuilds = async ({
|
|
|
375
380
|
// Everything above only reads, so a dry run has checked what the real run would, short of writing
|
|
376
381
|
if (dryRun) {
|
|
377
382
|
logger.info('[DRY RUN] Would build:');
|
|
378
|
-
|
|
379
|
-
|
|
383
|
+
const builds = buildConfigs.map((buildConfig) => ({
|
|
384
|
+
actorFullName: buildConfig.actorConfig.actorFullName,
|
|
385
|
+
versionNumber: buildConfig.versionNumber,
|
|
386
|
+
gitRepoUrl: buildConfig.gitRepoUrl,
|
|
387
|
+
}));
|
|
388
|
+
for (const build of builds) {
|
|
389
|
+
logger.info(` ${build.actorFullName} (${build.versionNumber}) from ${build.gitRepoUrl}`);
|
|
380
390
|
}
|
|
381
|
-
return {
|
|
382
|
-
dryRun: true,
|
|
383
|
-
builds: buildConfigs.map(
|
|
384
|
-
({ actorConfig, versionNumber, buildTag, gitRepoUrl, useDockerCache: useCache }) => ({
|
|
385
|
-
actorFullName: actorConfig.actorFullName,
|
|
386
|
-
versionNumber,
|
|
387
|
-
buildTag,
|
|
388
|
-
gitRepoUrl,
|
|
389
|
-
useDockerCache: useCache,
|
|
390
|
-
}),
|
|
391
|
-
),
|
|
392
|
-
};
|
|
391
|
+
return { dryRun: true, builds };
|
|
393
392
|
}
|
|
394
393
|
|
|
395
394
|
const buildConfigsByActorFullName = new Map(
|
|
@@ -397,20 +396,38 @@ export const runBuilds = async ({
|
|
|
397
396
|
);
|
|
398
397
|
|
|
399
398
|
return runAndSummarizeBuilds(actorConfigs, 'BUILDS', async (actorConfig, builder) => {
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
buildTag,
|
|
403
|
-
versionNumber,
|
|
404
|
-
gitRepoUrl,
|
|
405
|
-
useDockerCache: useCache,
|
|
406
|
-
} = buildConfigsByActorFullName.get(actorConfig.actorFullName)!;
|
|
399
|
+
const buildConfig = buildConfigsByActorFullName.get(actorConfig.actorFullName)!;
|
|
400
|
+
// No buildTag: the version keeps the one it has, so its build gets the tag the version already gives it
|
|
407
401
|
const actorVersion: ApifyClientTypes.ActorVersion = {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
gitRepoUrl,
|
|
402
|
+
versionNumber: buildConfig.versionNumber,
|
|
403
|
+
gitRepoUrl: buildConfig.gitRepoUrl,
|
|
411
404
|
sourceType: ActorSourceType.GitRepo,
|
|
412
405
|
};
|
|
413
|
-
|
|
406
|
+
// Always with the Docker cache: CI always asked for it, and a clean build can still be started on the platform
|
|
407
|
+
return builder.createVersionAndBuild(buildConfig.versionNumber, actorVersion, true, buildConfig.actorInfo);
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
type BuildActorsOptions = Omit<RunBuildsOptions, 'repoUrl' | 'shouldVerifyRepoUrl' | 'branch'> & {
|
|
412
|
+
// Defaults to the `origin` remote, see resolveRepoUrl
|
|
413
|
+
repoUrl?: string;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Builds exactly the given Actors from the checked-out Git branch, as it is on the remote. Fails on a detached HEAD,
|
|
418
|
+
* where there is no branch to build. Used by `build` and the commands built on top of it.
|
|
419
|
+
*/
|
|
420
|
+
export const buildActors = async ({ actorConfigs, repoUrl, ...options }: BuildActorsOptions) => {
|
|
421
|
+
if (actorConfigs.length === 0) {
|
|
422
|
+
logger.info('No Actors to build');
|
|
423
|
+
// Still the dry-run shape in a dry run, so it can't pass for a real (empty) result
|
|
424
|
+
return options.dryRun ? { dryRun: true as const, builds: [] } : [];
|
|
425
|
+
}
|
|
426
|
+
return runBuilds({
|
|
427
|
+
...options,
|
|
428
|
+
...(await resolveRepoUrl(repoUrl)),
|
|
429
|
+
actorConfigs,
|
|
430
|
+
branch: await getCurrentBranch(),
|
|
414
431
|
});
|
|
415
432
|
};
|
|
416
433
|
|
package/bin/git.ts
CHANGED
|
@@ -181,16 +181,16 @@ export const parseCommit = (commitString: string): Commit => {
|
|
|
181
181
|
};
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
|
-
* Returns the currently checked-out branch.
|
|
184
|
+
* Returns the currently checked-out branch. Builds point the Actor version at this branch,
|
|
185
185
|
* so a detached HEAD (no branch to point at) is an error rather than a guess.
|
|
186
186
|
*/
|
|
187
187
|
export const getCurrentBranch = async (): Promise<string> => {
|
|
188
188
|
const branch = await runGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
189
|
-
operation: 'determine the
|
|
189
|
+
operation: 'determine the checked-out branch',
|
|
190
190
|
});
|
|
191
191
|
if (branch === 'HEAD') {
|
|
192
192
|
throw new Error(
|
|
193
|
-
'Cannot determine the branch to
|
|
193
|
+
'Cannot determine the branch to build from: HEAD is detached. Check out the branch you want to build.',
|
|
194
194
|
);
|
|
195
195
|
}
|
|
196
196
|
return branch;
|
|
@@ -200,17 +200,6 @@ export const getCurrentBranch = async (): Promise<string> => {
|
|
|
200
200
|
export const resolveCommitSha = async (ref: string): Promise<string> =>
|
|
201
201
|
runGitCommand(['rev-parse', '--verify', `${ref}^{commit}`], { operation: `resolve ${ref} to a commit` });
|
|
202
202
|
|
|
203
|
-
/**
|
|
204
|
-
* Branch name a ref points to, without the remote: `origin/foo` and `foo` both give `foo`, and `HEAD`
|
|
205
|
-
* gives the checked-out branch. Null when there is no branch to point at (a SHA, a tag, a detached HEAD).
|
|
206
|
-
*/
|
|
207
|
-
export const resolveBranchName = async (ref: string): Promise<string | null> => {
|
|
208
|
-
const fullName = await runGitCommand(['rev-parse', '--symbolic-full-name', ref], {
|
|
209
|
-
operation: `resolve the branch name of ${ref}`,
|
|
210
|
-
});
|
|
211
|
-
return fullName.match(/^refs\/(?:heads|remotes\/[^/]+)\/(.+)$/)?.[1] ?? null;
|
|
212
|
-
};
|
|
213
|
-
|
|
214
203
|
/**
|
|
215
204
|
* Reads the repository URL from the `origin` remote, rewritten to the SSH form the Apify platform
|
|
216
205
|
* uses for Git repo sources, e.g. git@github.com:apify-store/google-maps
|