apify-test-tools 0.5.7-beta.2 → 0.5.7-beta.3
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/bin/diff-changes.ts +127 -0
- package/bin/diff-json-schema.ts +43 -0
- package/bin/git.ts +13 -4
- package/bin/main.ts +9 -5
- package/bin/utils.ts +2 -119
- package/dist/bin/diff-changes.d.ts +16 -0
- package/dist/bin/diff-changes.d.ts.map +1 -0
- package/dist/bin/diff-changes.js +91 -0
- package/dist/bin/diff-changes.js.map +1 -0
- package/dist/bin/diff-json-schema.d.ts +7 -0
- package/dist/bin/diff-json-schema.d.ts.map +1 -0
- package/dist/bin/diff-json-schema.js +40 -0
- package/dist/bin/diff-json-schema.js.map +1 -0
- package/dist/bin/git.d.ts +1 -1
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +12 -4
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +8 -5
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/utils.d.ts +0 -14
- package/dist/bin/utils.d.ts.map +1 -1
- package/dist/bin/utils.js +1 -88
- package/dist/bin/utils.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.d.ts +2 -0
- package/dist/test/unit/bin/diff-changes.test.d.ts.map +1 -0
- package/dist/test/unit/bin/diff-changes.test.js +152 -0
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -0
- package/dist/test/unit/bin/diff.test.d.ts +2 -0
- package/dist/test/unit/bin/diff.test.d.ts.map +1 -0
- package/dist/test/unit/bin/diff.test.js +133 -0
- package/dist/test/unit/bin/diff.test.js.map +1 -0
- package/dist/test/unit/parse-commit.test.js +1 -1
- package/dist/test/unit/parse-commit.test.js.map +1 -1
- package/dist/test/unit/should-built-and-test.test.js +93 -30
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/test/unit/bin/diff-changes.test.ts +175 -0
- package/test/unit/bin/diff.test.ts +214 -0
- package/test/unit/parse-commit.test.ts +2 -1
- package/test/unit/should-built-and-test.test.ts +115 -31
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { isCosmeticOnlyJsonSchemaChange } from './diff-json-schema.js';
|
|
2
|
+
import type { ActorConfig, Commit } from './types.js';
|
|
3
|
+
|
|
4
|
+
interface ShouldBuildAndTestOptions {
|
|
5
|
+
filepathsChanged: string[];
|
|
6
|
+
actorConfigs: ActorConfig[];
|
|
7
|
+
isLatest?: boolean;
|
|
8
|
+
commits: Commit[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const maybeParseActorFolder = (lowercaseFilePath: string): { isActorFolder: true, actorName: string } | { isActorFolder: false } => {
|
|
12
|
+
const match = lowercaseFilePath.match(/^(?:standalone-)?actors\/([^/]+)\/.+/);
|
|
13
|
+
if (match) {
|
|
14
|
+
// Some usernames weirdly use underscores, e.g. google_maps_email_extractor_standby-contact-details-scraper so we only need replace the last one
|
|
15
|
+
return { isActorFolder: true, actorName: match[1].replace(/_(?=[^_]*$)/, '/') };
|
|
16
|
+
}
|
|
17
|
+
return { isActorFolder: false };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Also works for folders
|
|
22
|
+
*/
|
|
23
|
+
const isIgnoredTopLevelFile = (lowercaseFilePath: string) => {
|
|
24
|
+
// On top level, we should only have dev-only readme and .actor/ is just for apify push CLI (real Actor configs are in /actors)
|
|
25
|
+
const IGNORED_TOP_LEVEL_FILES = ['.vscode/', '.gitignore', 'readme.md', '.husky/', '.eslintrc', 'eslint.config.mjs', '.prettierrc', '.editorconfig', '.actor/'];
|
|
26
|
+
// Strip out deprecated /code and /shared folders, treat them as top-level code
|
|
27
|
+
const sanitizedLowercaseFilePath = lowercaseFilePath.replace(/^code\//, '').replace(/^shared\//, '');
|
|
28
|
+
|
|
29
|
+
return IGNORED_TOP_LEVEL_FILES.some((ignoredFile) => sanitizedLowercaseFilePath.startsWith(ignoredFile));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type FileChange =
|
|
33
|
+
{ impact: 'ignored' } |
|
|
34
|
+
// Only things that influence how the Actor looks - e.g. README and CHANGELOG files, schema titles, descriptions, reordering, etc. We only need to rebuild on release
|
|
35
|
+
{ impact: 'cosmetic', includes: 'all-actors' | ActorConfig } |
|
|
36
|
+
// Influences how the Actor works - we need to run tests
|
|
37
|
+
{
|
|
38
|
+
impact: 'functional', includes: 'all-actors' | ActorConfig
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const classifyFileChange = (lowercaseFilePath: string, actorConfigs: ActorConfig[], commits: Commit[]): FileChange => {
|
|
42
|
+
if (isIgnoredTopLevelFile(lowercaseFilePath)) {
|
|
43
|
+
return { impact: 'ignored' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (lowercaseFilePath.endsWith('changelog.md')) {
|
|
47
|
+
return { impact: 'cosmetic', includes: 'all-actors' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const actorFolderInfo = maybeParseActorFolder(lowercaseFilePath);
|
|
51
|
+
if (actorFolderInfo.isActorFolder) {
|
|
52
|
+
const actorConfigChanged = actorConfigs.find(({ actorName }) => actorName.toLowerCase() === actorFolderInfo.actorName);
|
|
53
|
+
// This is some super weird case that happened once in the past but I don't remember the context anymore
|
|
54
|
+
if (actorConfigChanged === undefined) {
|
|
55
|
+
console.error('SHOULD NEVER HAPPEN: changes was found in an actor folder which no longer exists in the current commit, skipping this file', {
|
|
56
|
+
actorName: actorFolderInfo.actorName,
|
|
57
|
+
lowercaseFilePath,
|
|
58
|
+
});
|
|
59
|
+
return { impact: 'ignored' };
|
|
60
|
+
}
|
|
61
|
+
if (lowercaseFilePath.endsWith('readme.md')) {
|
|
62
|
+
return { impact: 'cosmetic', includes: actorConfigChanged };
|
|
63
|
+
}
|
|
64
|
+
if (lowercaseFilePath.endsWith('.json') && isCosmeticOnlyJsonSchemaChange(commits, lowercaseFilePath)) {
|
|
65
|
+
return { impact: 'cosmetic', includes: actorConfigChanged };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { impact: 'functional', includes: actorConfigChanged };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// For any other files, we assume they can interact with the code
|
|
72
|
+
return { impact: 'functional', includes: 'all-actors' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const getChangedActors = (
|
|
76
|
+
{ filepathsChanged, actorConfigs, isLatest = false, commits }: ShouldBuildAndTestOptions,
|
|
77
|
+
): ActorConfig[] => {
|
|
78
|
+
// folder -> ActorConfig
|
|
79
|
+
const actorsChangedMap = new Map<string, ActorConfig>();
|
|
80
|
+
|
|
81
|
+
const actorConfigsWithoutStandalone = actorConfigs.filter(({ isStandalone }) => !isStandalone);
|
|
82
|
+
|
|
83
|
+
const lowercaseFiles = filepathsChanged.map((file) => file.toLowerCase());
|
|
84
|
+
|
|
85
|
+
for (const lowercaseFilePath of lowercaseFiles) {
|
|
86
|
+
const fileChange = classifyFileChange(lowercaseFilePath, actorConfigs, commits);
|
|
87
|
+
if (fileChange.impact === 'ignored') {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (fileChange.impact === 'cosmetic' && !isLatest) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (fileChange.includes !== 'all-actors') {
|
|
96
|
+
actorsChangedMap.set(fileChange.includes.folder, fileChange.includes);
|
|
97
|
+
} else if (fileChange.includes === 'all-actors') {
|
|
98
|
+
// Standalone Actors are handled always via specific actors change, not all-actors
|
|
99
|
+
for (const actorConfig of actorConfigsWithoutStandalone) {
|
|
100
|
+
actorsChangedMap.set(actorConfig.folder, actorConfig);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const actorsChanged = Array.from(actorsChangedMap.values());
|
|
106
|
+
|
|
107
|
+
// All below here is just for logging
|
|
108
|
+
const ignoredFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'ignored');
|
|
109
|
+
console.error(`[DIFF]: Ignored files (don't trigger test or build): ${ignoredFilesChanged.join(', ')}`);
|
|
110
|
+
|
|
111
|
+
const cosmeticFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'cosmetic');
|
|
112
|
+
console.error(`[DIFF]: Cosmetic files (should only trigger release build): ${cosmeticFilesChanged.join(', ')}`);
|
|
113
|
+
|
|
114
|
+
const functionalFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'functional');
|
|
115
|
+
console.error(`[DIFF]: Functional files (trigger test & release build): ${functionalFilesChanged.join(', ')}`);
|
|
116
|
+
|
|
117
|
+
if (actorsChanged.length > 0) {
|
|
118
|
+
const miniactors = actorsChanged.filter((config) => !config.isStandalone).map((config) => config.actorName);
|
|
119
|
+
const standaloneActors = actorsChanged.filter((config) => config.isStandalone).map((config) => config.actorName);
|
|
120
|
+
console.error(`[DIFF]: MiniActors to be built and tested: ${miniactors.join(', ')}`);
|
|
121
|
+
console.error(`[DIFF]: Standalone Actors to be built and tested: ${standaloneActors.join(', ')}`);
|
|
122
|
+
} else {
|
|
123
|
+
console.error(`[DIFF]: No relevant files changed, skipping builds and tests`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return actorsChanged;
|
|
127
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Commit } from './types.js';
|
|
2
|
+
import { spawnCommandInGhWorkspace } from './utils.js';
|
|
3
|
+
|
|
4
|
+
const COSMETIC_JSON_FIELD_NAMES = new Set([
|
|
5
|
+
'title', 'description', 'example', 'enumTitles', 'sectionCaption', 'sectionDescription',
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
const isPlainObject = (val: unknown): val is Record<string, unknown> =>
|
|
9
|
+
typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
10
|
+
|
|
11
|
+
const isCosmeticObjectChange = (oldVal: unknown, newVal: unknown, currentKey?: string): boolean => {
|
|
12
|
+
// If the key itself is cosmetic, any change under it is fine
|
|
13
|
+
if (currentKey && COSMETIC_JSON_FIELD_NAMES.has(currentKey)) return true;
|
|
14
|
+
if (JSON.stringify(oldVal) === JSON.stringify(newVal)) return true;
|
|
15
|
+
if (isPlainObject(oldVal) && isPlainObject(newVal)) {
|
|
16
|
+
const allKeys = new Set([...Object.keys(oldVal), ...Object.keys(newVal)]);
|
|
17
|
+
return [...allKeys].every((key) => isCosmeticObjectChange(oldVal[key], newVal[key], key));
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns true if the two JSON strings differ only in cosmetic fields
|
|
24
|
+
* (title, description, example, enumTitles, sectionCaption, sectionDescription).
|
|
25
|
+
*/
|
|
26
|
+
export const isCosmeticOnlyJsonSchemaChange = (commits: Commit[], changedFilepath: string): boolean => {
|
|
27
|
+
// TODO: validate this is the right commit range
|
|
28
|
+
const oldRef = `${commits[0].sha}~`;
|
|
29
|
+
const newRef = commits[commits.length - 1].sha;
|
|
30
|
+
let oldJson: unknown;
|
|
31
|
+
let newJson: unknown;
|
|
32
|
+
try {
|
|
33
|
+
const oldContent = spawnCommandInGhWorkspace(`git show ${oldRef}:${changedFilepath}`);
|
|
34
|
+
const newContent = spawnCommandInGhWorkspace(`git show ${newRef}:${changedFilepath}`);
|
|
35
|
+
|
|
36
|
+
oldJson = JSON.parse(oldContent);
|
|
37
|
+
newJson = JSON.parse(newContent);
|
|
38
|
+
} catch {
|
|
39
|
+
console.error(`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.`);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return isCosmeticObjectChange(oldJson, newJson);
|
|
43
|
+
};
|
package/bin/git.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Commit, Config } from './types.js';
|
|
1
|
+
import type { Commit, Config } from './types.js';
|
|
2
2
|
import { spawnCommandInGhWorkspace } from './utils.js';
|
|
3
3
|
|
|
4
4
|
export const GIT_FORMAT_SEPARATOR = '»¦«';
|
|
@@ -8,11 +8,13 @@ const GIT_LOG_FORMAT = ['%H', '%aN<%aE>', '%aD', '%s'].join(GIT_FORMAT_SEPARATOR
|
|
|
8
8
|
* Gets the list of changed files between the given commits (inclusive).
|
|
9
9
|
*/
|
|
10
10
|
export const getChangedFiles = (commits: Commit[]) => {
|
|
11
|
-
const
|
|
11
|
+
const changedFilesString = spawnCommandInGhWorkspace(
|
|
12
12
|
`git diff --name-only ${commits[0].sha}~..${commits[commits.length - 1].sha}`,
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const changedFiles = changedFilesString.split('\n');
|
|
16
|
+
console.error(`Changed files (up to 50): ${changedFiles.slice(0, 50).join(', ')}`);
|
|
17
|
+
return changedFiles;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -29,8 +31,15 @@ export const getCommits = ({ sourceBranch, targetBranch, baseCommit: baseCommitS
|
|
|
29
31
|
const baseCommitIndex = commits.findIndex((commit) => commit.sha === baseCommitSha);
|
|
30
32
|
|
|
31
33
|
const hasBaseCommit = baseCommitIndex !== -1;
|
|
32
|
-
if (hasBaseCommit)
|
|
34
|
+
if (hasBaseCommit) {
|
|
35
|
+
const commitsUpToBaseCommit = commits.slice(baseCommitIndex + 1);
|
|
36
|
+
console.error(`Found base commit ${baseCommitSha} at index ${baseCommitIndex}, returning ${commitsUpToBaseCommit.length} commits after it`);
|
|
37
|
+
console.error(`Commits being returned: ${commitsUpToBaseCommit.map((c) => c.sha).join(', ')}`);
|
|
38
|
+
return commitsUpToBaseCommit;
|
|
39
|
+
}
|
|
33
40
|
|
|
41
|
+
console.error(`Base commit ${baseCommitSha} not found in the commit range, returning all ${commits.length} commits`);
|
|
42
|
+
console.error(`Commits being returned: ${commits.map((c) => c.sha).join(', ')}`);
|
|
34
43
|
return commits;
|
|
35
44
|
};
|
|
36
45
|
|
package/bin/main.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import process from 'process';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
|
|
4
5
|
import yargs, { type Argv } from 'yargs';
|
|
5
6
|
import { hideBin } from 'yargs/helpers';
|
|
6
7
|
|
|
7
|
-
import { getRepoActors, getChangedActors, spawnCommandInGhWorkspace, setCwd } from './utils.js';
|
|
8
8
|
import { runBuilds } from './build.js';
|
|
9
|
+
import { getChangedActors } from './diff-changes.js';
|
|
9
10
|
import { getChangedFiles, getCommits } from './git.js';
|
|
10
11
|
import { getPushData } from './github.js';
|
|
11
12
|
import { notifyToSlack } from './slack.js';
|
|
12
13
|
import { reportTestResults } from './test-report.js';
|
|
14
|
+
import { getRepoActors, setCwd,spawnCommandInGhWorkspace } from './utils.js';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Middlewares to be run before every command execution
|
|
@@ -69,7 +71,7 @@ await yargs()
|
|
|
69
71
|
const commits = getCommits(args);
|
|
70
72
|
const changedFiles = getChangedFiles(commits);
|
|
71
73
|
const actorConfigs = await getRepoActors();
|
|
72
|
-
const
|
|
74
|
+
const actorsChanged = getChangedActors({ filepathsChanged: changedFiles, actorConfigs, isLatest: false, commits });
|
|
73
75
|
console.log(JSON.stringify(actorsChanged));
|
|
74
76
|
})
|
|
75
77
|
.command(
|
|
@@ -93,9 +95,10 @@ await yargs()
|
|
|
93
95
|
const commits = getCommits(args);
|
|
94
96
|
const changedFiles = getChangedFiles(commits);
|
|
95
97
|
const actorConfigs = await getRepoActors();
|
|
96
|
-
const
|
|
98
|
+
const actorsChanged = getChangedActors({
|
|
97
99
|
filepathsChanged: changedFiles,
|
|
98
100
|
actorConfigs,
|
|
101
|
+
commits,
|
|
99
102
|
});
|
|
100
103
|
// https://github.com/apify-store/google-maps#:actors/lukaskrivka_google-maps-with-contact-details
|
|
101
104
|
// git@github.com:apify-store/google-maps#:actors/lukaskrivka_google-maps-with-contact-details
|
|
@@ -128,10 +131,11 @@ await yargs()
|
|
|
128
131
|
);
|
|
129
132
|
const isLatest = true;
|
|
130
133
|
const actorConfigs = await getRepoActors();
|
|
131
|
-
const
|
|
134
|
+
const actorsChanged = getChangedActors({
|
|
132
135
|
filepathsChanged: changedFiles,
|
|
133
136
|
actorConfigs,
|
|
134
137
|
isLatest,
|
|
138
|
+
commits,
|
|
135
139
|
});
|
|
136
140
|
const { dryRun, reportSlackChannel, releaseSlackChannel } = args;
|
|
137
141
|
const builds = await runBuilds({
|
package/bin/utils.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
|
|
3
4
|
import type {
|
|
4
5
|
ActorConfig,
|
|
5
|
-
Commit,
|
|
6
6
|
GitHubEvent,
|
|
7
7
|
} from './types.js';
|
|
8
8
|
|
|
@@ -92,120 +92,3 @@ export const getHeadCommitSha = (githubEvent: GitHubEvent) => {
|
|
|
92
92
|
? githubEvent.pull_request.head.sha
|
|
93
93
|
: githubEvent.head_commit.id;
|
|
94
94
|
};
|
|
95
|
-
|
|
96
|
-
export interface GetChangedActorsResult {
|
|
97
|
-
actorsChanged: ActorConfig[];
|
|
98
|
-
codeChanged: boolean;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface ShouldBuildAndTestOptions {
|
|
102
|
-
filepathsChanged: string[];
|
|
103
|
-
actorConfigs: ActorConfig[];
|
|
104
|
-
// Just for logging
|
|
105
|
-
isLatest?: boolean;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Also works for folders
|
|
110
|
-
*/
|
|
111
|
-
const isIgnoredTopLevelFile = (lowercaseFilePath: string) => {
|
|
112
|
-
// On top level, we should only have dev-only readme and .actor/ is just for apify push CLI (real Actor configs are in /actors)
|
|
113
|
-
const IGNORED_TOP_LEVEL_FILES = ['.vscode/', '.gitignore', 'readme.md', '.husky/', '.eslintrc', '.editorconfig', '.actor/'];
|
|
114
|
-
// Strip out deprecated /code and /shared folders, treat them as top-level code
|
|
115
|
-
const sanitizedLowercaseFilePath = lowercaseFilePath.replace(/^code\//, '').replace(/^shared\//, '');
|
|
116
|
-
|
|
117
|
-
return IGNORED_TOP_LEVEL_FILES.some((ignoredFile) => sanitizedLowercaseFilePath.startsWith(ignoredFile));
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const isLatestBuildOnlyFile = (lowercaseFilePath: string) => {
|
|
121
|
-
if (lowercaseFilePath.endsWith('changelog.md')) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Either in /actors or /standalone-actors, we need to rebuild readme but we don't rebuild top-level dev-only readme
|
|
126
|
-
if ((lowercaseFilePath.startsWith('actors/') || lowercaseFilePath.startsWith('standalone-actors/')) && lowercaseFilePath.endsWith('readme.md')) {
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return false;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Latest and devel are the same except that for latest we also rebuild with README and CHANGELOG files
|
|
135
|
-
*/
|
|
136
|
-
export const getChangedActors = (
|
|
137
|
-
{ filepathsChanged, actorConfigs, isLatest = false }: ShouldBuildAndTestOptions,
|
|
138
|
-
): GetChangedActorsResult => {
|
|
139
|
-
let codeChanged = false;
|
|
140
|
-
// folder -> ActorConfig
|
|
141
|
-
const actorsChangedMap = new Map<string, ActorConfig>();
|
|
142
|
-
|
|
143
|
-
const actorConfigsWithoutStandalone = actorConfigs.filter(({ isStandalone }) => !isStandalone);
|
|
144
|
-
|
|
145
|
-
const lowercaseFiles = filepathsChanged.map((file) => file.toLowerCase());
|
|
146
|
-
|
|
147
|
-
for (const lowercaseFilePath of lowercaseFiles) {
|
|
148
|
-
if (isIgnoredTopLevelFile(lowercaseFilePath)) {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
// First we check for specific actors that have configs in /actors or standalone actors in /standalone-actors
|
|
152
|
-
// This matches both actors/username_actorName and standalone-actors/username_actorName
|
|
153
|
-
const changedActorConfigMatch = lowercaseFilePath.match(/^(?:standalone-)?actors\/([^/]+)\/.+/);
|
|
154
|
-
if (changedActorConfigMatch) {
|
|
155
|
-
const sanitizedActorName = changedActorConfigMatch[1].replace('_', '/');
|
|
156
|
-
const actorConfigChanged = actorConfigs.find(({ actorName }) => actorName.toLowerCase() === sanitizedActorName);
|
|
157
|
-
if (actorConfigChanged === undefined) {
|
|
158
|
-
console.warn('changes was found in an actor folder which no longer exists in the current commit', {
|
|
159
|
-
actorName: sanitizedActorName,
|
|
160
|
-
actorFolderName: changedActorConfigMatch[1],
|
|
161
|
-
});
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
console.error(`actorConfigChanged ${actorConfigChanged.actorName}: sanitizedActorName ${sanitizedActorName} ${lowercaseFilePath} `);
|
|
166
|
-
// These can be nested at various folders inside the actor folder
|
|
167
|
-
if (isLatest || !isLatestBuildOnlyFile(lowercaseFilePath)) {
|
|
168
|
-
// We assume other files will are either actor.json or input_schema.json and those needs to be tested
|
|
169
|
-
// TODO: Check what changed in schema, we don't need to test description changes
|
|
170
|
-
actorsChangedMap.set(actorConfigChanged.folder, actorConfigChanged);
|
|
171
|
-
}
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// We check top level files (formerly in /code and /shared folders) that are shared among all non-standalone Actors
|
|
176
|
-
// Standalone actors are always handled separately by name via changedActorConfigMatch
|
|
177
|
-
if (isLatest || !isLatestBuildOnlyFile(lowercaseFilePath)) {
|
|
178
|
-
codeChanged = !isLatest; // NOTE: code is changed only in PR
|
|
179
|
-
for (const actorConfig of actorConfigsWithoutStandalone) {
|
|
180
|
-
actorsChangedMap.set(actorConfig.folder, actorConfig);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const actorsChanged = Array.from(actorsChangedMap.values());
|
|
186
|
-
|
|
187
|
-
// All below here is just for logging
|
|
188
|
-
const ignoredFilesChanged = lowercaseFiles.filter((file) => isIgnoredTopLevelFile(file));
|
|
189
|
-
console.error(`[DIFF]: Top level files changed that we ignore (don't trigger test or build): ${ignoredFilesChanged.join(', ')}`);
|
|
190
|
-
|
|
191
|
-
const onlyLatestFilesChanged = lowercaseFiles.filter((file) => isLatestBuildOnlyFile(file));
|
|
192
|
-
console.error(`[DIFF]: Files changed that only trigger latest build: ${onlyLatestFilesChanged.join(', ')}`);
|
|
193
|
-
|
|
194
|
-
if (!isLatest && codeChanged) {
|
|
195
|
-
console.error(`[DIFF]: All non-standalone Actors need to be built and tested (changes in top-level code)`);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (actorsChanged.length > 0) {
|
|
199
|
-
const miniactors = actorsChanged.filter((config) => !config.isStandalone).map((config) => config.actorName);
|
|
200
|
-
const standaloneActors = actorsChanged.filter((config) => config.isStandalone).map((config) => config.actorName);
|
|
201
|
-
console.error(`[DIFF]: MiniActors to be built and tested: ${miniactors.join(', ')}`);
|
|
202
|
-
console.error(`[DIFF]: Standalone Actors to be built and tested: ${standaloneActors.join(', ')}`);
|
|
203
|
-
} else {
|
|
204
|
-
console.error(`[DIFF]: No relevant files changed, skipping builds and tests`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
actorsChanged,
|
|
209
|
-
codeChanged,
|
|
210
|
-
};
|
|
211
|
-
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ActorConfig, Commit } from './types.js';
|
|
2
|
+
interface ShouldBuildAndTestOptions {
|
|
3
|
+
filepathsChanged: string[];
|
|
4
|
+
actorConfigs: ActorConfig[];
|
|
5
|
+
isLatest?: boolean;
|
|
6
|
+
commits: Commit[];
|
|
7
|
+
}
|
|
8
|
+
export declare const maybeParseActorFolder: (lowercaseFilePath: string) => {
|
|
9
|
+
isActorFolder: true;
|
|
10
|
+
actorName: string;
|
|
11
|
+
} | {
|
|
12
|
+
isActorFolder: false;
|
|
13
|
+
};
|
|
14
|
+
export declare const getChangedActors: ({ filepathsChanged, actorConfigs, isLatest, commits }: ShouldBuildAndTestOptions) => ActorConfig[];
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=diff-changes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-changes.d.ts","sourceRoot":"","sources":["../../bin/diff-changes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD,UAAU,yBAAyB;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,eAAO,MAAM,qBAAqB,GAAI,mBAAmB,MAAM,KAAG;IAAE,aAAa,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,KAAK,CAAA;CAOpI,CAAA;AAyDD,eAAO,MAAM,gBAAgB,GACzB,uDAA+D,yBAAyB,KACzF,WAAW,EAkDb,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { isCosmeticOnlyJsonSchemaChange } from './diff-json-schema.js';
|
|
2
|
+
export const maybeParseActorFolder = (lowercaseFilePath) => {
|
|
3
|
+
const match = lowercaseFilePath.match(/^(?:standalone-)?actors\/([^/]+)\/.+/);
|
|
4
|
+
if (match) {
|
|
5
|
+
// Some usernames weirdly use underscores, e.g. google_maps_email_extractor_standby-contact-details-scraper so we only need replace the last one
|
|
6
|
+
return { isActorFolder: true, actorName: match[1].replace(/_(?=[^_]*$)/, '/') };
|
|
7
|
+
}
|
|
8
|
+
return { isActorFolder: false };
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Also works for folders
|
|
12
|
+
*/
|
|
13
|
+
const isIgnoredTopLevelFile = (lowercaseFilePath) => {
|
|
14
|
+
// On top level, we should only have dev-only readme and .actor/ is just for apify push CLI (real Actor configs are in /actors)
|
|
15
|
+
const IGNORED_TOP_LEVEL_FILES = ['.vscode/', '.gitignore', 'readme.md', '.husky/', '.eslintrc', 'eslint.config.mjs', '.prettierrc', '.editorconfig', '.actor/'];
|
|
16
|
+
// Strip out deprecated /code and /shared folders, treat them as top-level code
|
|
17
|
+
const sanitizedLowercaseFilePath = lowercaseFilePath.replace(/^code\//, '').replace(/^shared\//, '');
|
|
18
|
+
return IGNORED_TOP_LEVEL_FILES.some((ignoredFile) => sanitizedLowercaseFilePath.startsWith(ignoredFile));
|
|
19
|
+
};
|
|
20
|
+
const classifyFileChange = (lowercaseFilePath, actorConfigs, commits) => {
|
|
21
|
+
if (isIgnoredTopLevelFile(lowercaseFilePath)) {
|
|
22
|
+
return { impact: 'ignored' };
|
|
23
|
+
}
|
|
24
|
+
if (lowercaseFilePath.endsWith('changelog.md')) {
|
|
25
|
+
return { impact: 'cosmetic', includes: 'all-actors' };
|
|
26
|
+
}
|
|
27
|
+
const actorFolderInfo = maybeParseActorFolder(lowercaseFilePath);
|
|
28
|
+
if (actorFolderInfo.isActorFolder) {
|
|
29
|
+
const actorConfigChanged = actorConfigs.find(({ actorName }) => actorName.toLowerCase() === actorFolderInfo.actorName);
|
|
30
|
+
// This is some super weird case that happened once in the past but I don't remember the context anymore
|
|
31
|
+
if (actorConfigChanged === undefined) {
|
|
32
|
+
console.error('SHOULD NEVER HAPPEN: changes was found in an actor folder which no longer exists in the current commit, skipping this file', {
|
|
33
|
+
actorName: actorFolderInfo.actorName,
|
|
34
|
+
lowercaseFilePath,
|
|
35
|
+
});
|
|
36
|
+
return { impact: 'ignored' };
|
|
37
|
+
}
|
|
38
|
+
if (lowercaseFilePath.endsWith('readme.md')) {
|
|
39
|
+
return { impact: 'cosmetic', includes: actorConfigChanged };
|
|
40
|
+
}
|
|
41
|
+
if (lowercaseFilePath.endsWith('.json') && isCosmeticOnlyJsonSchemaChange(commits, lowercaseFilePath)) {
|
|
42
|
+
return { impact: 'cosmetic', includes: actorConfigChanged };
|
|
43
|
+
}
|
|
44
|
+
return { impact: 'functional', includes: actorConfigChanged };
|
|
45
|
+
}
|
|
46
|
+
// For any other files, we assume they can interact with the code
|
|
47
|
+
return { impact: 'functional', includes: 'all-actors' };
|
|
48
|
+
};
|
|
49
|
+
export const getChangedActors = ({ filepathsChanged, actorConfigs, isLatest = false, commits }) => {
|
|
50
|
+
// folder -> ActorConfig
|
|
51
|
+
const actorsChangedMap = new Map();
|
|
52
|
+
const actorConfigsWithoutStandalone = actorConfigs.filter(({ isStandalone }) => !isStandalone);
|
|
53
|
+
const lowercaseFiles = filepathsChanged.map((file) => file.toLowerCase());
|
|
54
|
+
for (const lowercaseFilePath of lowercaseFiles) {
|
|
55
|
+
const fileChange = classifyFileChange(lowercaseFilePath, actorConfigs, commits);
|
|
56
|
+
if (fileChange.impact === 'ignored') {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (fileChange.impact === 'cosmetic' && !isLatest) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (fileChange.includes !== 'all-actors') {
|
|
63
|
+
actorsChangedMap.set(fileChange.includes.folder, fileChange.includes);
|
|
64
|
+
}
|
|
65
|
+
else if (fileChange.includes === 'all-actors') {
|
|
66
|
+
// Standalone Actors are handled always via specific actors change, not all-actors
|
|
67
|
+
for (const actorConfig of actorConfigsWithoutStandalone) {
|
|
68
|
+
actorsChangedMap.set(actorConfig.folder, actorConfig);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const actorsChanged = Array.from(actorsChangedMap.values());
|
|
73
|
+
// All below here is just for logging
|
|
74
|
+
const ignoredFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'ignored');
|
|
75
|
+
console.error(`[DIFF]: Ignored files (don't trigger test or build): ${ignoredFilesChanged.join(', ')}`);
|
|
76
|
+
const cosmeticFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'cosmetic');
|
|
77
|
+
console.error(`[DIFF]: Cosmetic files (should only trigger release build): ${cosmeticFilesChanged.join(', ')}`);
|
|
78
|
+
const functionalFilesChanged = lowercaseFiles.filter((file) => classifyFileChange(file, actorConfigs, commits).impact === 'functional');
|
|
79
|
+
console.error(`[DIFF]: Functional files (trigger test & release build): ${functionalFilesChanged.join(', ')}`);
|
|
80
|
+
if (actorsChanged.length > 0) {
|
|
81
|
+
const miniactors = actorsChanged.filter((config) => !config.isStandalone).map((config) => config.actorName);
|
|
82
|
+
const standaloneActors = actorsChanged.filter((config) => config.isStandalone).map((config) => config.actorName);
|
|
83
|
+
console.error(`[DIFF]: MiniActors to be built and tested: ${miniactors.join(', ')}`);
|
|
84
|
+
console.error(`[DIFF]: Standalone Actors to be built and tested: ${standaloneActors.join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.error(`[DIFF]: No relevant files changed, skipping builds and tests`);
|
|
88
|
+
}
|
|
89
|
+
return actorsChanged;
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=diff-changes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-changes.js","sourceRoot":"","sources":["../../bin/diff-changes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Commit } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the two JSON strings differ only in cosmetic fields
|
|
4
|
+
* (title, description, example, enumTitles, sectionCaption, sectionDescription).
|
|
5
|
+
*/
|
|
6
|
+
export declare const isCosmeticOnlyJsonSchemaChange: (commits: Commit[], changedFilepath: string) => boolean;
|
|
7
|
+
//# sourceMappingURL=diff-json-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-json-schema.d.ts","sourceRoot":"","sources":["../../bin/diff-json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAqBzC;;;GAGG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,MAAM,EAAE,EAAE,iBAAiB,MAAM,KAAG,OAiB3F,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { spawnCommandInGhWorkspace } from './utils.js';
|
|
2
|
+
const COSMETIC_JSON_FIELD_NAMES = new Set([
|
|
3
|
+
'title', 'description', 'example', 'enumTitles', 'sectionCaption', 'sectionDescription',
|
|
4
|
+
]);
|
|
5
|
+
const isPlainObject = (val) => typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
6
|
+
const isCosmeticObjectChange = (oldVal, newVal, currentKey) => {
|
|
7
|
+
// If the key itself is cosmetic, any change under it is fine
|
|
8
|
+
if (currentKey && COSMETIC_JSON_FIELD_NAMES.has(currentKey))
|
|
9
|
+
return true;
|
|
10
|
+
if (JSON.stringify(oldVal) === JSON.stringify(newVal))
|
|
11
|
+
return true;
|
|
12
|
+
if (isPlainObject(oldVal) && isPlainObject(newVal)) {
|
|
13
|
+
const allKeys = new Set([...Object.keys(oldVal), ...Object.keys(newVal)]);
|
|
14
|
+
return [...allKeys].every((key) => isCosmeticObjectChange(oldVal[key], newVal[key], key));
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Returns true if the two JSON strings differ only in cosmetic fields
|
|
20
|
+
* (title, description, example, enumTitles, sectionCaption, sectionDescription).
|
|
21
|
+
*/
|
|
22
|
+
export const isCosmeticOnlyJsonSchemaChange = (commits, changedFilepath) => {
|
|
23
|
+
// TODO: validate this is the right commit range
|
|
24
|
+
const oldRef = `${commits[0].sha}~`;
|
|
25
|
+
const newRef = commits[commits.length - 1].sha;
|
|
26
|
+
let oldJson;
|
|
27
|
+
let newJson;
|
|
28
|
+
try {
|
|
29
|
+
const oldContent = spawnCommandInGhWorkspace(`git show ${oldRef}:${changedFilepath}`);
|
|
30
|
+
const newContent = spawnCommandInGhWorkspace(`git show ${newRef}:${changedFilepath}`);
|
|
31
|
+
oldJson = JSON.parse(oldContent);
|
|
32
|
+
newJson = JSON.parse(newContent);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
console.error(`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.`);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return isCosmeticObjectChange(oldJson, newJson);
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=diff-json-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-json-schema.js","sourceRoot":"","sources":["../../bin/diff-json-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC;IACtC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB;CAC1F,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,GAAY,EAAkC,EAAE,CACnE,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAEnE,MAAM,sBAAsB,GAAG,CAAC,MAAe,EAAE,MAAe,EAAE,UAAmB,EAAW,EAAE;IAC9F,6DAA6D;IAC7D,IAAI,UAAU,IAAI,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,OAAiB,EAAE,eAAuB,EAAW,EAAE;IAClG,gDAAgD;IAChD,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC/C,IAAI,OAAgB,CAAC;IACrB,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACD,MAAM,UAAU,GAAG,yBAAyB,CAAC,YAAY,MAAM,IAAI,eAAe,EAAE,CAAC,CAAC;QACtF,MAAM,UAAU,GAAG,yBAAyB,CAAC,YAAY,MAAM,IAAI,eAAe,EAAE,CAAC,CAAC;QAEtF,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACjC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,KAAK,CAAC,2CAA2C,eAAe,YAAY,MAAM,QAAQ,MAAM,0EAA0E,CAAC,CAAC;QACpL,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC,CAAC"}
|
package/dist/bin/git.d.ts
CHANGED
package/dist/bin/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../bin/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../bin/git.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGjD,eAAO,MAAM,oBAAoB,uBAAQ,CAAC;AAG1C;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,EAAE,aAQhD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,2DAA2D,MAAM,KAAG,MAAM,EAoBpG,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,KAAG,MAIjD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,cAAc,MAAM,KAAG,MAYlD,CAAC"}
|
package/dist/bin/git.js
CHANGED
|
@@ -5,8 +5,10 @@ const GIT_LOG_FORMAT = ['%H', '%aN<%aE>', '%aD', '%s'].join(GIT_FORMAT_SEPARATOR
|
|
|
5
5
|
* Gets the list of changed files between the given commits (inclusive).
|
|
6
6
|
*/
|
|
7
7
|
export const getChangedFiles = (commits) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const changedFilesString = spawnCommandInGhWorkspace(`git diff --name-only ${commits[0].sha}~..${commits[commits.length - 1].sha}`);
|
|
9
|
+
const changedFiles = changedFilesString.split('\n');
|
|
10
|
+
console.error(`Changed files (up to 50): ${changedFiles.slice(0, 50).join(', ')}`);
|
|
11
|
+
return changedFiles;
|
|
10
12
|
};
|
|
11
13
|
/**
|
|
12
14
|
* Gets the commits between sourceBranch and targetBranch (exclusive).
|
|
@@ -18,8 +20,14 @@ export const getCommits = ({ sourceBranch, targetBranch, baseCommit: baseCommitS
|
|
|
18
20
|
commits.reverse();
|
|
19
21
|
const baseCommitIndex = commits.findIndex((commit) => commit.sha === baseCommitSha);
|
|
20
22
|
const hasBaseCommit = baseCommitIndex !== -1;
|
|
21
|
-
if (hasBaseCommit)
|
|
22
|
-
|
|
23
|
+
if (hasBaseCommit) {
|
|
24
|
+
const commitsUpToBaseCommit = commits.slice(baseCommitIndex + 1);
|
|
25
|
+
console.error(`Found base commit ${baseCommitSha} at index ${baseCommitIndex}, returning ${commitsUpToBaseCommit.length} commits after it`);
|
|
26
|
+
console.error(`Commits being returned: ${commitsUpToBaseCommit.map((c) => c.sha).join(', ')}`);
|
|
27
|
+
return commitsUpToBaseCommit;
|
|
28
|
+
}
|
|
29
|
+
console.error(`Base commit ${baseCommitSha} not found in the commit range, returning all ${commits.length} commits`);
|
|
30
|
+
console.error(`Commits being returned: ${commits.map((c) => c.sha).join(', ')}`);
|
|
23
31
|
return commits;
|
|
24
32
|
};
|
|
25
33
|
export const getCommitInfo = (commitSha) => {
|
package/dist/bin/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../bin/git.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAC1C,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAElF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAiB,EAAE,EAAE;IACjD,MAAM,
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../bin/git.ts"],"names":[],"mappings":"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"}
|