apify-test-tools 0.10.1-beta.2 → 0.10.1-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.
Files changed (147) hide show
  1. package/.github/actions/checkout-restore-dependencies/action.yaml +4 -3
  2. package/.github/workflows/public_platform-tests-claude-investigate-and-fix.yaml +7 -2
  3. package/.github/workflows/public_platform-tests.yaml +0 -1
  4. package/.github/workflows-package-version +1 -1
  5. package/CHANGELOG.md +6 -0
  6. package/CONTRIBUTING.md +13 -3
  7. package/README.md +2 -2
  8. package/bin/build-from-local.ts +8 -7
  9. package/bin/build.ts +22 -21
  10. package/bin/diff-changes.ts +11 -10
  11. package/bin/diff-json-schema.ts +14 -12
  12. package/bin/git.ts +89 -47
  13. package/bin/logger.ts +23 -0
  14. package/bin/main.ts +44 -38
  15. package/bin/output.ts +6 -0
  16. package/bin/slack.ts +8 -7
  17. package/bin/test-report.ts +22 -21
  18. package/bin/types.ts +0 -1
  19. package/bin/utils/config/actor-json.ts +84 -0
  20. package/bin/utils/config/load-config.ts +46 -46
  21. package/bin/utils/config/parser.ts +33 -28
  22. package/bin/utils/config/structures/base.ts +7 -0
  23. package/bin/utils/json-file.ts +11 -14
  24. package/bin/utils/path/repo-relative.ts +156 -0
  25. package/bin/utils/path/schema.ts +18 -0
  26. package/bin/utils.ts +63 -44
  27. package/dist/bin/build-from-local.d.ts +1 -1
  28. package/dist/bin/build-from-local.d.ts.map +1 -1
  29. package/dist/bin/build-from-local.js +8 -7
  30. package/dist/bin/build-from-local.js.map +1 -1
  31. package/dist/bin/build.d.ts.map +1 -1
  32. package/dist/bin/build.js +22 -21
  33. package/dist/bin/build.js.map +1 -1
  34. package/dist/bin/diff-changes.d.ts +1 -1
  35. package/dist/bin/diff-changes.d.ts.map +1 -1
  36. package/dist/bin/diff-changes.js +9 -8
  37. package/dist/bin/diff-changes.js.map +1 -1
  38. package/dist/bin/diff-json-schema.d.ts +1 -1
  39. package/dist/bin/diff-json-schema.d.ts.map +1 -1
  40. package/dist/bin/diff-json-schema.js +13 -12
  41. package/dist/bin/diff-json-schema.js.map +1 -1
  42. package/dist/bin/git.d.ts +10 -10
  43. package/dist/bin/git.d.ts.map +1 -1
  44. package/dist/bin/git.js +78 -37
  45. package/dist/bin/git.js.map +1 -1
  46. package/dist/bin/logger.d.ts +11 -0
  47. package/dist/bin/logger.d.ts.map +1 -0
  48. package/dist/bin/logger.js +20 -0
  49. package/dist/bin/logger.js.map +1 -0
  50. package/dist/bin/main.d.ts +1 -1
  51. package/dist/bin/main.js +44 -38
  52. package/dist/bin/main.js.map +1 -1
  53. package/dist/bin/output.d.ts +3 -0
  54. package/dist/bin/output.d.ts.map +1 -0
  55. package/dist/bin/output.js +6 -0
  56. package/dist/bin/output.js.map +1 -0
  57. package/dist/bin/slack.d.ts.map +1 -1
  58. package/dist/bin/slack.js +8 -7
  59. package/dist/bin/slack.js.map +1 -1
  60. package/dist/bin/test-report.d.ts.map +1 -1
  61. package/dist/bin/test-report.js +22 -21
  62. package/dist/bin/test-report.js.map +1 -1
  63. package/dist/bin/types.d.ts +0 -1
  64. package/dist/bin/types.d.ts.map +1 -1
  65. package/dist/bin/utils/config/actor-json.d.ts +14 -0
  66. package/dist/bin/utils/config/actor-json.d.ts.map +1 -0
  67. package/dist/bin/utils/config/actor-json.js +60 -0
  68. package/dist/bin/utils/config/actor-json.js.map +1 -0
  69. package/dist/bin/utils/config/load-config.d.ts +12 -2
  70. package/dist/bin/utils/config/load-config.d.ts.map +1 -1
  71. package/dist/bin/utils/config/load-config.js +32 -31
  72. package/dist/bin/utils/config/load-config.js.map +1 -1
  73. package/dist/bin/utils/config/parser.d.ts +4 -12
  74. package/dist/bin/utils/config/parser.d.ts.map +1 -1
  75. package/dist/bin/utils/config/parser.js +24 -23
  76. package/dist/bin/utils/config/parser.js.map +1 -1
  77. package/dist/bin/utils/config/structures/base.d.ts +5 -0
  78. package/dist/bin/utils/config/structures/base.d.ts.map +1 -1
  79. package/dist/bin/utils/config/structures/base.js.map +1 -1
  80. package/dist/bin/utils/json-file.d.ts +3 -2
  81. package/dist/bin/utils/json-file.d.ts.map +1 -1
  82. package/dist/bin/utils/json-file.js +8 -13
  83. package/dist/bin/utils/json-file.js.map +1 -1
  84. package/dist/bin/utils/path/repo-relative.d.ts +35 -0
  85. package/dist/bin/utils/path/repo-relative.d.ts.map +1 -0
  86. package/dist/bin/utils/path/repo-relative.js +137 -0
  87. package/dist/bin/utils/path/repo-relative.js.map +1 -0
  88. package/dist/bin/utils/path/schema.d.ts +7 -0
  89. package/dist/bin/utils/path/schema.d.ts.map +1 -0
  90. package/dist/bin/utils/path/schema.js +16 -0
  91. package/dist/bin/utils/path/schema.js.map +1 -0
  92. package/dist/bin/utils.d.ts +14 -6
  93. package/dist/bin/utils.d.ts.map +1 -1
  94. package/dist/bin/utils.js +52 -36
  95. package/dist/bin/utils.js.map +1 -1
  96. package/dist/test/unit/bin/build-from-local.test.js +4 -40
  97. package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
  98. package/dist/test/unit/bin/diff-changes.test.js +97 -96
  99. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  100. package/dist/test/unit/bin/diff.test.js +52 -48
  101. package/dist/test/unit/bin/diff.test.js.map +1 -1
  102. package/dist/test/unit/bin/git-command.test.d.ts +2 -0
  103. package/dist/test/unit/bin/git-command.test.d.ts.map +1 -0
  104. package/dist/test/unit/bin/git-command.test.js +54 -0
  105. package/dist/test/unit/bin/git-command.test.js.map +1 -0
  106. package/dist/test/unit/bin/git.test.js +211 -94
  107. package/dist/test/unit/bin/git.test.js.map +1 -1
  108. package/dist/test/unit/bin/logger.test.d.ts +2 -0
  109. package/dist/test/unit/bin/logger.test.d.ts.map +1 -0
  110. package/dist/test/unit/bin/logger.test.js +44 -0
  111. package/dist/test/unit/bin/logger.test.js.map +1 -0
  112. package/dist/test/unit/bin/logging.test.d.ts +2 -0
  113. package/dist/test/unit/bin/logging.test.d.ts.map +1 -0
  114. package/dist/test/unit/bin/logging.test.js +43 -0
  115. package/dist/test/unit/bin/logging.test.js.map +1 -0
  116. package/dist/test/unit/bin/utils/config/load-config.test.js +47 -25
  117. package/dist/test/unit/bin/utils/config/load-config.test.js.map +1 -1
  118. package/dist/test/unit/bin/utils/config/parser.test.js +64 -19
  119. package/dist/test/unit/bin/utils/config/parser.test.js.map +1 -1
  120. package/dist/test/unit/bin/utils/files.test.js +16 -27
  121. package/dist/test/unit/bin/utils/files.test.js.map +1 -1
  122. package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts +2 -0
  123. package/dist/test/unit/bin/utils/path/repo-relative.test.d.ts.map +1 -0
  124. package/dist/test/unit/bin/utils/path/repo-relative.test.js +196 -0
  125. package/dist/test/unit/bin/utils/path/repo-relative.test.js.map +1 -0
  126. package/dist/test/unit/bin/utils/path/schema.test.d.ts +2 -0
  127. package/dist/test/unit/bin/utils/path/schema.test.d.ts.map +1 -0
  128. package/dist/test/unit/bin/utils/path/schema.test.js +52 -0
  129. package/dist/test/unit/bin/utils/path/schema.test.js.map +1 -0
  130. package/dist/test/unit/should-built-and-test.test.js +40 -38
  131. package/dist/test/unit/should-built-and-test.test.js.map +1 -1
  132. package/dist/tsconfig.tsbuildinfo +1 -1
  133. package/eslint.config.mjs +9 -1
  134. package/package.json +5 -5
  135. package/test/unit/bin/build-from-local.test.ts +4 -54
  136. package/test/unit/bin/diff-changes.test.ts +97 -96
  137. package/test/unit/bin/diff.test.ts +53 -48
  138. package/test/unit/bin/git-command.test.ts +71 -0
  139. package/test/unit/bin/git.test.ts +246 -91
  140. package/test/unit/bin/logger.test.ts +55 -0
  141. package/test/unit/bin/logging.test.ts +52 -0
  142. package/test/unit/bin/utils/config/load-config.test.ts +63 -26
  143. package/test/unit/bin/utils/config/parser.test.ts +81 -27
  144. package/test/unit/bin/utils/files.test.ts +19 -33
  145. package/test/unit/bin/utils/path/repo-relative.test.ts +250 -0
  146. package/test/unit/bin/utils/path/schema.test.ts +65 -0
  147. package/test/unit/should-built-and-test.test.ts +40 -38
@@ -65,7 +65,8 @@ runs:
65
65
  run: npm ci
66
66
 
67
67
  # Repos can still have older version locally but on cloud we enforce uniformity.
68
- # This causes minor mismatch in package-lock.json and node_modules but it shouldn't cause any issues.
68
+ # --no-save swaps the package in node_modules only and leaves the repo's package.json and
69
+ # package-lock.json untouched, so node_modules is ahead of the lockfile but the checkout stays clean.
69
70
  - name: Enforce test tools version required by these workflows
70
71
  # We don't override beta so we can use it to test branch-specific versions of apify-test-tools
71
72
  # Needs the npm token too: a repo whose .npmrc routes all traffic through a private
@@ -79,7 +80,7 @@ runs:
79
80
  echo "Package-lock version of apify-test-tools: $locked_version"
80
81
  if [[ "$locked_version" == *"-beta"* ]]; then
81
82
  echo "Beta version detected, reinstalling $locked_version to ensure cache consistency"
82
- npm install apify-test-tools@$locked_version --audit=false --no-fund;
83
+ npm install "apify-test-tools@$locked_version" --no-save --audit=false --no-fund;
83
84
  else
84
85
  # The workflows name the exact package version they ship with, so a tag is a complete
85
86
  # statement: these workflows and this library. A frozen major tag then keeps
@@ -90,5 +91,5 @@ runs:
90
91
  # version; the release that publishes X is the commit that pins X.
91
92
  pinned_version=$(cat "${{ github.action_path }}/../../workflows-package-version")
92
93
  echo "Installing apify-test-tools@$pinned_version (pinned by these workflows)"
93
- npm install "apify-test-tools@$pinned_version" --audit=false --no-fund;
94
+ npm install "apify-test-tools@$pinned_version" --no-save --audit=false --no-fund;
94
95
  fi
@@ -70,6 +70,9 @@ on:
70
70
  TESTER_APIFY_TOKEN_READ_ONLY:
71
71
  required: true
72
72
  description: Apify token used only for read-only investigation — reading the failing run, its log and its storages.
73
+ NPM_TOKEN:
74
+ required: false
75
+ description: Read-only token for installing private npm packages before Claude runs.
73
76
 
74
77
  jobs:
75
78
  investigate:
@@ -166,8 +169,10 @@ jobs:
166
169
  id-token: write
167
170
  actions: read
168
171
  steps:
169
- - name: Checkout repository
170
- uses: actions/checkout@v5
172
+ - name: Setup repository and dependencies
173
+ uses: apify/apify-test-tools/.github/actions/checkout-restore-dependencies@workflows-v0
174
+ with:
175
+ npm-token: ${{ secrets.NPM_TOKEN }}
171
176
 
172
177
  - name: Run Claude Code
173
178
  id: claude
@@ -49,7 +49,6 @@ jobs:
49
49
  return_code=$?
50
50
  npx apify-test-tools report-tests \
51
51
  --report-file ./test-output.json \
52
- --workspace . \
53
52
  --workflow-name "${{ github.workflow }}" \
54
53
  --job-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
55
54
  --report-slack-channel "${{ inputs.report-slack-channel || format('#notif-{0}', github.event.repository.name) }}"
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.10.1
package/CHANGELOG.md CHANGED
@@ -5,10 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  <!-- git-cliff-unreleased-start -->
6
6
  ## 0.10.1 - **not yet released**
7
7
 
8
+ ### 🚀 Features
9
+
10
+ - **relative-path:** Path safety and manipulation lib ([#154](https://github.com/apify/apify-test-tools/pull/154)) ([c151a1c](https://github.com/apify/apify-test-tools/commit/c151a1c2755fef98b335d48a3461f5042b90def9)) by [@JuanGalilea](https://github.com/JuanGalilea)
11
+ - **logger:** Add logger and ban console statements ([#170](https://github.com/apify/apify-test-tools/pull/170)) ([b295d36](https://github.com/apify/apify-test-tools/commit/b295d363276c92fc6c30dd34d7a90acef5d380bf)) by [@JuanGalilea](https://github.com/JuanGalilea), closes [#156](https://github.com/apify/apify-test-tools/issues/156)
12
+
8
13
  ### 🐛 Bug Fixes
9
14
 
10
15
  - **internal-workflows:** Wait out npm&#x27;s async publish before holding the major tag ([#160](https://github.com/apify/apify-test-tools/pull/160)) ([78434f0](https://github.com/apify/apify-test-tools/commit/78434f03d288ecbd383d55d56a1610f06ded298f)) by [@metalwarrior665](https://github.com/metalwarrior665)
11
16
  - Skip builds and tests when the net diff is empty ([#171](https://github.com/apify/apify-test-tools/pull/171)) ([874d70c](https://github.com/apify/apify-test-tools/commit/874d70ca5506f7ec742a1808c9bf6c670f08552c)) by [@artogahr](https://github.com/artogahr)
17
+ - **workflows:** Add npm install step to Claude fix workflow ([#161](https://github.com/apify/apify-test-tools/pull/161)) ([d8884e0](https://github.com/apify/apify-test-tools/commit/d8884e07f778de14c8d9d7f0ec9e0a1672c224ad)) by [@foxt451](https://github.com/foxt451), closes [#39](https://github.com/apify/apify-test-tools/issues/39)
12
18
 
13
19
 
14
20
  <!-- git-cliff-unreleased-end -->
package/CONTRIBUTING.md CHANGED
@@ -14,6 +14,15 @@ The package consists of three parts:
14
14
  - `bin/slack.ts` sending notifications to slack
15
15
  - `bin/test-report.ts` processing vitest's test reports
16
16
 
17
+ CLI command results use `writeJson` from `bin/output.ts` to write newline-terminated JSON to stdout.
18
+ Use the CLI `logger` from `bin/logger.ts` for diagnostics; every level writes to stderr.
19
+ ESLint rejects `console.log` throughout the codebase and all direct `console` calls in `bin/`.
20
+ The importable library in `lib/` keeps its own diagnostics; CLI log levels apply only to `bin/`.
21
+
22
+ The CLI's global `--log-level` option accepts `debug`, `info` (default), `warn`, `error`, or `silent`.
23
+ It filters diagnostics without affecting command results. Use `logger.debug` for detailed troubleshooting,
24
+ `logger.info` for progress, `logger.warn` for recoverable problems, and `logger.error` for failures.
25
+
17
26
  ## Test library
18
27
 
19
28
  - `lib/extend-expect.ts` - custom matchers
@@ -39,11 +48,12 @@ git clone git@github.com:apify-store/testing-repo-for-github-actions.git
39
48
 
40
49
  #### Working on the CLI
41
50
 
42
- To work on the library, point the cli to your repo with `--workspace` (it defaults to the current directory):
51
+ To work on the library, run the cli from the root of your testing repo:
43
52
 
44
53
  ```sh
45
- npx tsx bin/main.ts --help
46
- npx tsx bin/main.ts --workspace ../path/to/testing-repo-for-github-actions get-commits --target-branch master --source-branch feat/testing-feature-branch
54
+ cd /path/to/testing-repo-for-github-actions
55
+ npx tsx /path/to/apify-test-tools/bin/main.ts --help
56
+ npx tsx /path/to/apify-test-tools/bin/main.ts get-commits --target-branch master --source-branch feat/testing-feature-branch
47
57
  ```
48
58
 
49
59
  #### Working on the library
package/README.md CHANGED
@@ -595,7 +595,7 @@ The main local flow is:
595
595
  4. Build Actors on Apify (with your new code)
596
596
  5. Run tests against those builds. You can change tests and run on the same builds.
597
597
 
598
- Run every command from the root of the actor repository you want to work with (or point `--workspace` at it).
598
+ Run every command from the root of the actor repository you want to work with.
599
599
 
600
600
  #### 4. Build affected Actors
601
601
 
@@ -725,5 +725,5 @@ skips the check.
725
725
  For development on `apify-test-tools` itself, use `tsx` directly:
726
726
 
727
727
  ```bash
728
- tsx bin/main.ts --workspace local-clone get-actor-configs
728
+ cd local-clone && tsx ../bin/main.ts get-actor-configs
729
729
  ```
@@ -8,6 +8,7 @@ import { ActorSourceType } from 'apify-client';
8
8
 
9
9
  import { dryRunBuildData, LOCAL_SOURCE_VERSION_NUMBER, runAndSummarizeBuilds } from './build.js';
10
10
  import { buildDockerIgnoreMatcher } from './dockerignore.js';
11
+ import { logger } from './logger.js';
11
12
  import { isPathWithinScope } from './path-utils.js';
12
13
  import type { ActorConfig, BuildData } from './types.js';
13
14
  import type { SourceFile } from './utils.js';
@@ -34,7 +35,7 @@ export const collectSourceFiles = async (actorName: string, actorDir: string): P
34
35
  const isMonorepoActor = !!contextAbsDir && isOutsideDir(contextAbsDir, absActorDir);
35
36
 
36
37
  const dockerContextDirAbs = isMonorepoActor ? contextAbsDir! : absActorDir;
37
- const keptFilePaths = collectNonIgnoredFiles(dockerContextDirAbs, repoRoot);
38
+ const keptFilePaths = await collectNonIgnoredFiles(dockerContextDirAbs, repoRoot);
38
39
 
39
40
  if (!isMonorepoActor) {
40
41
  return Promise.all(keptFilePaths.map(async (filePath) => readSourceFile(filePath, dockerContextDirAbs)));
@@ -64,9 +65,9 @@ export const collectSourceFiles = async (actorName: string, actorDir: string): P
64
65
  // regardless of .gitignore/.dockerignore, matching Apify CLI's own behavior. Files matching the
65
66
  // hardcoded secret-pattern backstop (keys, certs, .env variants) are dropped unconditionally,
66
67
  // .actor/ included, since those should never ship regardless of what the ignore files say.
67
- export const collectNonIgnoredFiles = (dockerContextDir: string, repoRoot: string): string[] => {
68
- const relativePaths = listRepoFilePaths(repoRoot, dockerContextDir);
69
- const ignoredPaths = getGitignoredPaths(relativePaths);
68
+ export const collectNonIgnoredFiles = async (dockerContextDir: string, repoRoot: string): Promise<string[]> => {
69
+ const relativePaths = await listRepoFilePaths(repoRoot, dockerContextDir);
70
+ const ignoredPaths = await getGitignoredPaths(relativePaths, repoRoot);
70
71
  const rootRelativePaths = new Map(
71
72
  relativePaths.map((relPath) => [
72
73
  relPath,
@@ -104,7 +105,7 @@ export const flattenMonorepoContext = async (
104
105
  actorJson: Record<string, unknown>,
105
106
  keptContextFiles: string[],
106
107
  ): Promise<{ tempDir: string; filePaths: string[] }> => {
107
- console.error(`[${actorName}]: monorepo actor detected — flattening from Docker context`);
108
+ logger.info(`[${actorName}]: monorepo actor detected — flattening from Docker context`);
108
109
 
109
110
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), `apify-build-${actorName.replace('/', '_')}-`));
110
111
  const filePaths: string[] = [];
@@ -194,9 +195,9 @@ export const runBuildsFromLocal = async ({
194
195
  dryRun: boolean;
195
196
  }): Promise<BuildData[]> => {
196
197
  if (dryRun) {
197
- console.error('[DRY RUN] Would build from local source:');
198
+ logger.info('[DRY RUN] Would build from local source:');
198
199
  for (const { actorFullName, folder } of actorConfigs) {
199
- console.error(` ${actorFullName} (${folder})`);
200
+ logger.info(` ${actorFullName} (${folder})`);
200
201
  }
201
202
  return actorConfigs.map(({ actorFullName }) => dryRunBuildData(actorFullName, LOCAL_SOURCE_VERSION_NUMBER));
202
203
  }
package/bin/build.ts CHANGED
@@ -2,6 +2,7 @@ import type * as ApifyClientTypes from 'apify-client';
2
2
  import { ActorSourceType, ApifyClient } from 'apify-client';
3
3
 
4
4
  import { normalizeRepoUrl } from './git.js';
5
+ import { logger } from './logger.js';
5
6
  import type { ActorConfig, BuildData } from './types.js';
6
7
 
7
8
  type BuildPrActorOptions = {
@@ -28,7 +29,7 @@ const ACTOR_SETUP_REQUIREMENT =
28
29
  */
29
30
  export const resolveDefaultVersion = (actorFullName: string, actorInfo: ApifyClientTypes.Actor) => {
30
31
  const defaultBuildTag = actorInfo.defaultRunOptions.build;
31
- console.error(`Default build tag for ${actorFullName} is ${defaultBuildTag}`);
32
+ logger.info(`Default build tag for ${actorFullName} is ${defaultBuildTag}`);
32
33
 
33
34
  // We could technically allow this but in most cases this is accidentally set wrongly and there is a workaround
34
35
  if (defaultBuildTag.match(/\d+\.\d+\.\d+/)) {
@@ -43,7 +44,7 @@ export const resolveDefaultVersion = (actorFullName: string, actorInfo: ApifyCli
43
44
  throw new Error(`[${actorFullName}] No build found for tag "${defaultBuildTag}". ${ACTOR_SETUP_REQUIREMENT}`);
44
45
  }
45
46
  const defaultVersionNumber = defaultBuildNumber.match(/(\d+\.\d+)\.\d+/)![1];
46
- console.error(`Default version for ${actorFullName} is ${defaultVersionNumber}`);
47
+ logger.info(`Default version for ${actorFullName} is ${defaultVersionNumber}`);
47
48
 
48
49
  const defaultVersion = actorInfo.versions.find((version) => version.versionNumber === defaultVersionNumber);
49
50
 
@@ -139,7 +140,7 @@ export class ApifyBuilder {
139
140
  // We also get back actId so the testing actor can both match by actor ID and name
140
141
  const { id, actId, buildNumber } = await actorClient.build(versionNumber, { useCache });
141
142
 
142
- console.error(`[${this.actorFullName}]: ${id} (${buildNumber})`);
143
+ logger.info(`[${this.actorFullName}]: ${id} (${buildNumber})`);
143
144
  return { buildId: id, actorRawId: actId, buildNumber, actorFullName: this.actorFullName };
144
145
  };
145
146
 
@@ -147,20 +148,20 @@ export class ApifyBuilder {
147
148
  const build = await this.apifyClient.build(buildId).waitForFinish();
148
149
  const versionNumber = build.buildNumber;
149
150
  if (build.status === 'FAILED' || build.status === 'TIMED-OUT') {
150
- console.error(`[${this.actorFullName}]: ${versionNumber}`);
151
+ logger.error(`[${this.actorFullName}]: ${versionNumber}`);
151
152
  try {
152
153
  const log = await this.apifyClient.build(buildId).log().get();
153
154
  const logTail = log?.split('\n').slice(-40).join('\n');
154
- console.error(`\n--- BUILD LOG (last 40 lines) ---\n${logTail}\n---`);
155
+ logger.error(`\n--- BUILD LOG (last 40 lines) ---\n${logTail}\n---`);
155
156
  } catch (err) {
156
- console.error(`[${this.actorFullName}]: Failed to fetch build log: ${err}`);
157
+ logger.error(`[${this.actorFullName}]: Failed to fetch build log: ${err}`);
157
158
  }
158
159
  throw new Error(
159
160
  `[BUILD][${this.actorFullName}]: Build ${buildId} (${versionNumber}) failed. ` +
160
161
  `Not continuing with other builds and tests.`,
161
162
  );
162
163
  }
163
- console.error(`[${this.actorFullName}]: ${versionNumber}`);
164
+ logger.info(`[${this.actorFullName}]: ${versionNumber}`);
164
165
  return build;
165
166
  };
166
167
 
@@ -227,7 +228,7 @@ export class ApifyBuilder {
227
228
  type CorrectBuildColletionItem = (typeof items)[0] & { buildNumber: string };
228
229
  const buildsToDelete = (items as CorrectBuildColletionItem[]).filter((build) => {
229
230
  if (build.buildNumber === defaultBuildNumber) {
230
- console.error(
231
+ logger.info(
231
232
  `[DELETE OLD BUILDS][${this.actorFullName}]: Skipping default build ${defaultBuildNumber} (${defaultBuildTag}). ` +
232
233
  `We never delete default builds`,
233
234
  );
@@ -238,7 +239,7 @@ export class ApifyBuilder {
238
239
  (protectedBuildNumber) => protectedBuildNumber.buildNumber === build.buildNumber,
239
240
  );
240
241
  if (protectedTagFound) {
241
- console.error(
242
+ logger.info(
242
243
  `[DELETE OLD BUILDS][${this.actorFullName}]: Skipping protected build ${protectedTagFound.buildNumber} (${protectedTagFound.tag}).`,
243
244
  );
244
245
  return false;
@@ -247,7 +248,7 @@ export class ApifyBuilder {
247
248
  if (taggedDevelBuildNumber && build.buildNumber === taggedDevelBuildNumber) {
248
249
  const shouldDeleteDevelBuild = build.startedAt.getTime() < daysAgoUnixDevel;
249
250
  if (shouldDeleteDevelBuild) {
250
- console.error(
251
+ logger.info(
251
252
  `[DELETE OLD BUILDS][${this.actorFullName}]: Removing olf devel build ${taggedDevelBuildNumber}.`,
252
253
  );
253
254
  }
@@ -256,7 +257,7 @@ export class ApifyBuilder {
256
257
  return build.startedAt.getTime() < daysAgoUnixProd;
257
258
  });
258
259
 
259
- console.error(
260
+ logger.info(
260
261
  `[DELETE OLD BUILDS][${this.actorFullName}]: Deleting ${buildsToDelete.length} old builds that are non-default and ` +
261
262
  `older than 30 days from total ${items.length}`,
262
263
  );
@@ -271,8 +272,8 @@ export const waitAndSummarizeBuilds = async (
271
272
  buildersMap: Map<string, ApifyBuilder>,
272
273
  label: string,
273
274
  ): Promise<BuildData[]> => {
274
- console.error('=========================================');
275
- console.error(`FINISHED ${label}:`);
275
+ logger.info('=========================================');
276
+ logger.info(`FINISHED ${label}:`);
276
277
  await Promise.all(
277
278
  startedBuilds.map(async (buildData) => {
278
279
  const builder = buildersMap.get(buildData.actorFullName)!;
@@ -280,12 +281,12 @@ export const waitAndSummarizeBuilds = async (
280
281
  }),
281
282
  );
282
283
 
283
- console.error('=========================================');
284
- console.error('SUMMARY:');
284
+ logger.info('=========================================');
285
+ logger.info('SUMMARY:');
285
286
  for (const buildData of startedBuilds.sort((a, b) => a.actorFullName.localeCompare(b.actorFullName))) {
286
- console.error(`[${buildData.actorFullName}]: ${buildData.buildNumber}`);
287
+ logger.info(`[${buildData.actorFullName}]: ${buildData.buildNumber}`);
287
288
  }
288
- console.error('=========================================');
289
+ logger.info('=========================================');
289
290
 
290
291
  return startedBuilds;
291
292
  };
@@ -298,8 +299,8 @@ export const runAndSummarizeBuilds = async (
298
299
  const buildersByActorFullName = new Map<string, ApifyBuilder>(
299
300
  actorConfigs.map((actorConfig) => [actorConfig.actorFullName, ApifyBuilder.fromActorConfig(actorConfig)]),
300
301
  );
301
- console.error('=========================================');
302
- console.error(`STARTED ${label}:`);
302
+ logger.info('=========================================');
303
+ logger.info(`STARTED ${label}:`);
303
304
  const startedBuilds = await Promise.all(
304
305
  actorConfigs.map(async (actorConfig) =>
305
306
  buildOneActor(actorConfig, buildersByActorFullName.get(actorConfig.actorFullName)!),
@@ -366,9 +367,9 @@ export const runBuilds = async ({
366
367
  );
367
368
 
368
369
  if (dryRun) {
369
- console.error('[DRY RUN] Would build:');
370
+ logger.info('[DRY RUN] Would build:');
370
371
  for (const { actorConfig, versionNumber } of buildConfigs) {
371
- console.error(` ${actorConfig.actorFullName} (${versionNumber})`);
372
+ logger.info(` ${actorConfig.actorFullName} (${versionNumber})`);
372
373
  }
373
374
  return buildConfigs.map(({ actorConfig, versionNumber }) =>
374
375
  dryRunBuildData(actorConfig.actorFullName, versionNumber),
@@ -1,5 +1,6 @@
1
1
  import { isCosmeticOnlyJsonSchemaChange } from './diff-json-schema.js';
2
2
  import { type DockerIgnoreMatcher, loadDockerIgnore } from './dockerignore.js';
3
+ import { logger } from './logger.js';
3
4
  import { findContainingScope, hoistPath, isPathWithinScope } from './path-utils.js';
4
5
  import type { ActorConfig, Commit } from './types.js';
5
6
 
@@ -44,12 +45,12 @@ type FileChangeForActor =
44
45
  * 6. .json inside the actor's own `.actor/` dir with only cosmetic schema diffs → cosmetic (semantically verified)
45
46
  * 7. Everything else → functional
46
47
  */
47
- const classifyFileChange = (
48
+ const classifyFileChange = async (
48
49
  originalFilePath: string,
49
50
  actorConfig: ActorConfig,
50
51
  commits: Commit[],
51
52
  dockerIgnoreMatcher: DockerIgnoreMatcher,
52
- ): FileChangeForActor => {
53
+ ): Promise<FileChangeForActor> => {
53
54
  const lowercaseFilePath = originalFilePath.toLowerCase();
54
55
 
55
56
  // TODO: hardcodes that there's a single repo-wide changelog belonging to every actor. Should instead
@@ -89,7 +90,7 @@ const classifyFileChange = (
89
90
  }
90
91
 
91
92
  if (lowercaseFilePath.endsWith('.json') && isUnderActorDotDir) {
92
- const isCosmetic = isCosmeticOnlyJsonSchemaChange(commits, originalFilePath);
93
+ const isCosmetic = await isCosmeticOnlyJsonSchemaChange(commits, originalFilePath);
93
94
  if (isCosmetic) {
94
95
  return { impact: 'cosmetic', semanticallyVerified: true };
95
96
  }
@@ -159,19 +160,19 @@ const groupFilesByActorSet = (fileToActors: Map<string, Set<string>>): ChangeGro
159
160
  const logChangeGroups = (groups: ChangeGroup[]): void => {
160
161
  for (const { actors, files } of groups) {
161
162
  if (actors.length > 1) {
162
- console.error(`[DIFF]: Shared changes for actors ${actors.join(', ')}: ${files.join(', ')}`);
163
+ logger.info(`[DIFF]: Shared changes for actors ${actors.join(', ')}: ${files.join(', ')}`);
163
164
  } else {
164
- console.error(`[DIFF]: Changes specific to actor ${actors[0]}: ${files.join(', ')}`);
165
+ logger.info(`[DIFF]: Changes specific to actor ${actors[0]}: ${files.join(', ')}`);
165
166
  }
166
167
  }
167
168
  };
168
169
 
169
- export const getChangedActors = ({
170
+ export const getChangedActors = async ({
170
171
  filepathsChanged,
171
172
  actorConfigs,
172
173
  isLatest = false,
173
174
  commits,
174
- }: ShouldBuildAndTestOptions): ActorConfig[] => {
175
+ }: ShouldBuildAndTestOptions): Promise<ActorConfig[]> => {
175
176
  const actorsChangedMap = new Map<string, ActorChangeEntry>();
176
177
 
177
178
  for (const actorConfig of actorConfigs) {
@@ -184,7 +185,7 @@ export const getChangedActors = ({
184
185
  continue;
185
186
  }
186
187
 
187
- const change = classifyFileChange(originalFilePath, actorConfig, commits, dockerIgnoreMatcher);
188
+ const change = await classifyFileChange(originalFilePath, actorConfig, commits, dockerIgnoreMatcher);
188
189
 
189
190
  if (change.impact === 'ignored' || change.impact === 'outside-context') continue;
190
191
  if (change.impact === 'cosmetic' && !isLatest) continue;
@@ -205,9 +206,9 @@ export const getChangedActors = ({
205
206
 
206
207
  if (actorsChanged.length > 0) {
207
208
  const actors = actorsChanged.map((config) => config.actorFullName);
208
- console.error(`[DIFF]: Actors to be built and tested: ${actors.join(', ')}`);
209
+ logger.info(`[DIFF]: Actors to be built and tested: ${actors.join(', ')}`);
209
210
  } else {
210
- console.error(`[DIFF]: No relevant files changed, skipping builds and tests`);
211
+ logger.info(`[DIFF]: No relevant files changed, skipping builds and tests`);
211
212
  }
212
213
 
213
214
  return actorsChanged;
@@ -1,5 +1,6 @@
1
+ import { logger } from './logger.js';
1
2
  import type { Commit } from './types.js';
2
- import { spawnCommandInGhWorkspace } from './utils.js';
3
+ import { runGitCommand } from './utils.js';
3
4
 
4
5
  const COSMETIC_JSON_FIELD_NAMES = new Set([
5
6
  'title',
@@ -28,21 +29,22 @@ const isCosmeticObjectChange = (oldVal: unknown, newVal: unknown, currentKey?: s
28
29
  * Returns true if the two JSON strings differ only in cosmetic fields
29
30
  * (title, description, example, enumTitles, sectionCaption, sectionDescription).
30
31
  */
31
- export const isCosmeticOnlyJsonSchemaChange = (commits: Commit[], changedFilepath: string): boolean => {
32
+ export const isCosmeticOnlyJsonSchemaChange = async (commits: Commit[], changedFilepath: string): Promise<boolean> => {
32
33
  // TODO: validate this is the right commit range
33
34
  const oldRef = `${commits[0].sha}~`;
34
35
  const newRef = commits[commits.length - 1].sha;
35
- let oldJson: unknown;
36
- let newJson: unknown;
37
- try {
38
- const oldContent = spawnCommandInGhWorkspace(`git show ${oldRef}:${changedFilepath}`);
39
- const newContent = spawnCommandInGhWorkspace(`git show ${newRef}:${changedFilepath}`);
40
36
 
41
- oldJson = JSON.parse(oldContent);
42
- newJson = JSON.parse(newContent);
43
- } catch {
44
- console.error(
45
- `Failed to get or parse JSON content for ${changedFilepath} at refs ${oldRef} and ${newRef}, maybe it is new file or deleted? Treating it as a non-cosmetic change.`,
37
+ const [oldJson, newJson] = await Promise.all([
38
+ runGitCommand(['show', `${oldRef}:${changedFilepath}`])
39
+ .then(JSON.parse)
40
+ .catch(() => undefined),
41
+ runGitCommand(['show', `${newRef}:${changedFilepath}`])
42
+ .then(JSON.parse)
43
+ .catch(() => undefined),
44
+ ]);
45
+ if (!oldJson || !newJson) {
46
+ logger.info(
47
+ `Failed to get JSON content for ${changedFilepath} at refs ${oldRef} and ${newRef}, maybe it is new file or deleted? Treating it as a non-cosmetic change.`,
46
48
  );
47
49
  return false;
48
50
  }