apify-test-tools 0.6.0 → 0.6.2-beta.0
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/workflows/_check_code.yaml +17 -19
- package/.github/workflows/_update_release_metadata.yaml +1 -1
- package/.github/workflows/manual_release_stable.yaml +1 -0
- package/.github/workflows/on_master.yaml +2 -0
- package/.husky/pre-commit +13 -0
- package/CHANGELOG.md +17 -3
- package/CONTRIBUTING.md +5 -0
- package/README.md +39 -43
- package/bin/build.ts +138 -87
- package/bin/diff-changes.ts +156 -0
- package/bin/diff-json-schema.ts +50 -0
- package/bin/git.ts +42 -11
- package/bin/github.ts +7 -6
- package/bin/main.ts +31 -13
- package/bin/slack.ts +6 -2
- package/bin/test-report.ts +55 -44
- package/bin/types.ts +56 -59
- package/bin/utils.ts +19 -131
- package/dist/bin/build.d.ts +1 -1
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +49 -31
- package/dist/bin/build.js.map +1 -1
- 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 +103 -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 +45 -0
- package/dist/bin/diff-json-schema.js.map +1 -0
- package/dist/bin/git.d.ts +8 -3
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +35 -10
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/github.d.ts +1 -3
- package/dist/bin/github.d.ts.map +1 -1
- package/dist/bin/github.js +5 -5
- package/dist/bin/github.js.map +1 -1
- package/dist/bin/main.js +19 -7
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts +1 -1
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +1 -1
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/test-report.d.ts.map +1 -1
- package/dist/bin/test-report.js +9 -3
- package/dist/bin/test-report.js.map +1 -1
- package/dist/bin/types.d.ts +0 -1
- package/dist/bin/types.d.ts.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 +12 -95
- package/dist/bin/utils.js.map +1 -1
- package/dist/lib/extend-expect.d.ts.map +1 -1
- package/dist/lib/extend-expect.js +11 -11
- package/dist/lib/extend-expect.js.map +1 -1
- package/dist/lib/lib.d.ts +3 -2
- package/dist/lib/lib.d.ts.map +1 -1
- package/dist/lib/lib.js +7 -3
- package/dist/lib/lib.js.map +1 -1
- package/dist/lib/run-test-result.d.ts +1 -1
- package/dist/lib/run-test-result.d.ts.map +1 -1
- package/dist/lib/run-test-result.js.map +1 -1
- package/dist/lib/types.d.ts +2 -3
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +4 -1
- package/dist/lib/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 +159 -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 +143 -0
- package/dist/test/unit/bin/diff.test.js.map +1 -0
- package/dist/test/unit/bin/git.test.js +48 -19
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/custom-matchers.test.js +3 -8
- package/dist/test/unit/custom-matchers.test.js.map +1 -1
- 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 +159 -39
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eslint.config.mjs +4 -2
- package/knip.json +1 -1
- package/lib/extend-expect.ts +21 -21
- package/lib/lib.ts +17 -17
- package/lib/run-test-result.ts +6 -2
- package/lib/types.ts +5 -8
- package/lib/utils.ts +13 -9
- package/package.json +61 -54
- package/test/tsconfig.json +2 -4
- package/test/unit/bin/diff-changes.test.ts +182 -0
- package/test/unit/bin/diff.test.ts +212 -0
- package/test/unit/bin/git.test.ts +59 -20
- package/test/unit/custom-matchers.test.ts +10 -9
- package/test/unit/parse-commit.test.ts +2 -1
- package/test/unit/should-built-and-test.test.ts +181 -40
- package/bin/consts.ts +0 -6
- package/dist/bin/consts.d.ts +0 -4
- package/dist/bin/consts.d.ts.map +0 -1
- package/dist/bin/consts.js +0 -6
- package/dist/bin/consts.js.map +0 -1
- package/example-github-events/pull-request.json +0 -523
- package/example-github-events/push.json +0 -216
package/bin/github.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import type { Commit, GitHubEventPush } from './types.js';
|
|
3
4
|
import { spawnCommandInGhWorkspace } from './utils.js';
|
|
4
5
|
|
|
5
6
|
export const getPushData = async (path: string) => {
|
|
@@ -10,8 +11,8 @@ export const getPushData = async (path: string) => {
|
|
|
10
11
|
repository: { ssh_url: repoUrl, name },
|
|
11
12
|
head_commit: { author },
|
|
12
13
|
} = event;
|
|
13
|
-
const commits: Commit[] = ghCommits.map(({ author, message, id, timestamp }) => ({
|
|
14
|
-
author:
|
|
14
|
+
const commits: Commit[] = ghCommits.map(({ author: commitAuthor, message, id, timestamp }) => ({
|
|
15
|
+
author: commitAuthor.username,
|
|
15
16
|
sha: id,
|
|
16
17
|
message,
|
|
17
18
|
date: timestamp,
|
|
@@ -29,7 +30,7 @@ export const getPushData = async (path: string) => {
|
|
|
29
30
|
};
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
const loadGitHubEvent = async (path: string): Promise<GitHubEventPush> => {
|
|
33
34
|
const pushEvent = JSON.parse((await fs.readFile(path)).toString()) as GitHubEventPush;
|
|
34
35
|
return pushEvent;
|
|
35
36
|
};
|
|
@@ -39,7 +40,7 @@ const getChangedFiles = ({ after, before }: GitHubEventPush): string[] => {
|
|
|
39
40
|
return changedFiles.split('\n');
|
|
40
41
|
};
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
const getChangelogChanges = (changedFiles: string[], event: GitHubEventPush): string | null => {
|
|
43
44
|
const changelogPath = 'CHANGELOG.md';
|
|
44
45
|
if (!changedFiles.includes(changelogPath)) {
|
|
45
46
|
return null;
|
package/bin/main.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
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';
|
|
6
|
+
// eslint-disable-next-line import/extensions --- With .js, it cannot find types
|
|
5
7
|
import { hideBin } from 'yargs/helpers';
|
|
6
8
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
+
import { deleteOldBuilds, runBuilds } from './build.js';
|
|
10
|
+
import { getChangedActors } from './diff-changes.js';
|
|
9
11
|
import { getChangedFiles, getCommits } from './git.js';
|
|
10
12
|
import { getPushData } from './github.js';
|
|
11
13
|
import { notifyToSlack } from './slack.js';
|
|
12
14
|
import { reportTestResults } from './test-report.js';
|
|
15
|
+
import { getRepoActors, setCwd, spawnCommandInGhWorkspace } from './utils.js';
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Middlewares to be run before every command execution
|
|
@@ -63,13 +66,18 @@ await yargs()
|
|
|
63
66
|
async () => {
|
|
64
67
|
const actorConfigs = await getRepoActors();
|
|
65
68
|
console.log(JSON.stringify(actorConfigs));
|
|
66
|
-
}
|
|
69
|
+
},
|
|
67
70
|
)
|
|
68
71
|
.command('get-affected-actors', '', buildOptions, async (args) => {
|
|
69
72
|
const commits = getCommits(args);
|
|
70
73
|
const changedFiles = getChangedFiles(commits);
|
|
71
74
|
const actorConfigs = await getRepoActors();
|
|
72
|
-
const
|
|
75
|
+
const actorsChanged = getChangedActors({
|
|
76
|
+
filepathsChanged: changedFiles,
|
|
77
|
+
actorConfigs,
|
|
78
|
+
isLatest: false,
|
|
79
|
+
commits,
|
|
80
|
+
});
|
|
73
81
|
console.log(JSON.stringify(actorsChanged));
|
|
74
82
|
})
|
|
75
83
|
.command(
|
|
@@ -83,7 +91,7 @@ await yargs()
|
|
|
83
91
|
.option('workflow-name', { type: 'string' }),
|
|
84
92
|
async (args) => {
|
|
85
93
|
await reportTestResults(args);
|
|
86
|
-
}
|
|
94
|
+
},
|
|
87
95
|
)
|
|
88
96
|
.command(
|
|
89
97
|
'build',
|
|
@@ -93,15 +101,16 @@ await yargs()
|
|
|
93
101
|
const commits = getCommits(args);
|
|
94
102
|
const changedFiles = getChangedFiles(commits);
|
|
95
103
|
const actorConfigs = await getRepoActors();
|
|
96
|
-
const
|
|
104
|
+
const actorsChanged = getChangedActors({
|
|
97
105
|
filepathsChanged: changedFiles,
|
|
98
106
|
actorConfigs,
|
|
107
|
+
commits,
|
|
99
108
|
});
|
|
100
109
|
// https://github.com/apify-store/google-maps#:actors/lukaskrivka_google-maps-with-contact-details
|
|
101
110
|
// git@github.com:apify-store/google-maps#:actors/lukaskrivka_google-maps-with-contact-details
|
|
102
111
|
const repoUrl = spawnCommandInGhWorkspace(`git remote get-url origin`).replace(
|
|
103
112
|
/^https:\/\/github\.com\//,
|
|
104
|
-
'git@github.com:'
|
|
113
|
+
'git@github.com:',
|
|
105
114
|
);
|
|
106
115
|
|
|
107
116
|
const builds = await runBuilds({
|
|
@@ -111,7 +120,7 @@ await yargs()
|
|
|
111
120
|
dryRun: args.dryRun,
|
|
112
121
|
});
|
|
113
122
|
console.log(JSON.stringify(builds));
|
|
114
|
-
}
|
|
123
|
+
},
|
|
115
124
|
)
|
|
116
125
|
.command(
|
|
117
126
|
'release',
|
|
@@ -124,14 +133,15 @@ await yargs()
|
|
|
124
133
|
.option('release-slack-channel', { type: 'string' }),
|
|
125
134
|
async (args) => {
|
|
126
135
|
const { branch, changedFiles, repoUrl, commits, changelog, repository, author } = await getPushData(
|
|
127
|
-
args.pushEventPath
|
|
136
|
+
args.pushEventPath,
|
|
128
137
|
);
|
|
129
138
|
const isLatest = true;
|
|
130
139
|
const actorConfigs = await getRepoActors();
|
|
131
|
-
const
|
|
140
|
+
const actorsChanged = getChangedActors({
|
|
132
141
|
filepathsChanged: changedFiles,
|
|
133
142
|
actorConfigs,
|
|
134
143
|
isLatest,
|
|
144
|
+
commits,
|
|
135
145
|
});
|
|
136
146
|
const { dryRun, reportSlackChannel, releaseSlackChannel } = args;
|
|
137
147
|
const builds = await runBuilds({
|
|
@@ -143,7 +153,6 @@ await yargs()
|
|
|
143
153
|
});
|
|
144
154
|
console.error(JSON.stringify(builds));
|
|
145
155
|
|
|
146
|
-
// TODO: build circle actors
|
|
147
156
|
await notifyToSlack({
|
|
148
157
|
changedFiles,
|
|
149
158
|
commits,
|
|
@@ -154,7 +163,16 @@ await yargs()
|
|
|
154
163
|
reportSlackChannel,
|
|
155
164
|
releaseSlackChannel,
|
|
156
165
|
});
|
|
157
|
-
}
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
.command(
|
|
169
|
+
'delete-old-builds',
|
|
170
|
+
'',
|
|
171
|
+
(_) => _,
|
|
172
|
+
async () => {
|
|
173
|
+
const actorConfigs = await getRepoActors();
|
|
174
|
+
await deleteOldBuilds(actorConfigs);
|
|
175
|
+
},
|
|
158
176
|
)
|
|
159
177
|
.strictCommands()
|
|
160
178
|
.demandCommand(1, 'Command is required')
|
package/bin/slack.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WebClient } from '@slack/web-api';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type { Commit } from './types.js';
|
|
3
4
|
import { getEnvVar } from './utils.js';
|
|
4
5
|
|
|
5
6
|
type NotifyToSlackOptions = {
|
|
@@ -46,7 +47,10 @@ export const notifyToSlack = async ({
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const commitsMessage = `${commits
|
|
49
|
-
.map(
|
|
50
|
+
.map(
|
|
51
|
+
({ author: commitAuthor, message }, index) =>
|
|
52
|
+
`${index + 1}. Commit message: ${message}\n\tAuthor: ${commitAuthor}.`,
|
|
53
|
+
)
|
|
50
54
|
.join('\n')}`;
|
|
51
55
|
const changedFilesMessage = `**Files changed**: ${changedFiles.join(', ')}`;
|
|
52
56
|
const longMessage = `${shortMessage}\n**Commit list**:\n${commitsMessage}\n\n${changedFilesMessage}`;
|
package/bin/test-report.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
2
3
|
import { sendSlackMessage } from './slack.js';
|
|
3
4
|
import { getEnvVar } from './utils.js';
|
|
4
5
|
|
|
5
6
|
interface ReportTestResultsOptions {
|
|
6
|
-
reportFile: string
|
|
7
|
-
dryRun: boolean
|
|
8
|
-
reportSlackChannel?: string
|
|
9
|
-
jobUrl?: string
|
|
10
|
-
workflowName?: string
|
|
7
|
+
reportFile: string;
|
|
8
|
+
dryRun: boolean;
|
|
9
|
+
reportSlackChannel?: string;
|
|
10
|
+
jobUrl?: string;
|
|
11
|
+
workflowName?: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const reportTestResults = async ({
|
|
@@ -35,7 +36,7 @@ export const reportTestResults = async ({
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const failedAssertions: { message: string; runLink: string
|
|
39
|
+
const failedAssertions: { message: string; runLink: string; actorName: string }[] = [];
|
|
39
40
|
|
|
40
41
|
console.error();
|
|
41
42
|
console.error(`PASSED: ${passed.length}, FAILED: ${failed.length}`);
|
|
@@ -57,7 +58,13 @@ export const reportTestResults = async ({
|
|
|
57
58
|
for (const [i, aResult] of failed.entries()) {
|
|
58
59
|
const { failureMessages, fullName, meta } = aResult;
|
|
59
60
|
if (failureMessages) {
|
|
60
|
-
failedAssertions.push(
|
|
61
|
+
failedAssertions.push(
|
|
62
|
+
...failureMessages.map((message) => ({
|
|
63
|
+
message: message.split('\n')?.[0],
|
|
64
|
+
runLink: meta.runLink,
|
|
65
|
+
actorName: meta.actorName,
|
|
66
|
+
})),
|
|
67
|
+
);
|
|
61
68
|
}
|
|
62
69
|
console.error(`${i + 1}) ${fullName} ... ${meta.runLink}`);
|
|
63
70
|
console.error();
|
|
@@ -65,9 +72,11 @@ export const reportTestResults = async ({
|
|
|
65
72
|
console.error();
|
|
66
73
|
console.error(`PASSED: ${passed.length}, FAILED: ${failed.length}`);
|
|
67
74
|
console.error();
|
|
68
|
-
|
|
75
|
+
|
|
69
76
|
if (!reportSlackChannel) {
|
|
70
|
-
console.error(
|
|
77
|
+
console.error(
|
|
78
|
+
`Skipping slack notification. If you want to enable it, add --report-slack-channel flag and make sure SLACK_TOKEN_TESTS_BOT env variable is set.`,
|
|
79
|
+
);
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
82
|
|
|
@@ -81,7 +90,9 @@ export const reportTestResults = async ({
|
|
|
81
90
|
let slackMessage = `\`${workflowName ?? '-'}\``;
|
|
82
91
|
slackMessage += `: has ${failedAssertions.length} failed assertions. Failing test suites: ${failed.length}/${total}.${jobLink}`;
|
|
83
92
|
slackMessage += `\n\n${failedAssertions[0].message} --- <${failedAssertions[0].runLink}|${failedAssertions[0].actorName}>`;
|
|
84
|
-
const blocks = failedAssertions
|
|
93
|
+
const blocks = failedAssertions
|
|
94
|
+
.slice(1)
|
|
95
|
+
.map(({ message, runLink, actorName }) => `• ${message} --- <${runLink}|${actorName}>`);
|
|
85
96
|
|
|
86
97
|
console.error('SLACK:', slackMessage);
|
|
87
98
|
console.error('\tblocks:', blocks.join('\n\t\t'));
|
|
@@ -96,52 +107,52 @@ export const reportTestResults = async ({
|
|
|
96
107
|
}
|
|
97
108
|
};
|
|
98
109
|
|
|
99
|
-
type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled'
|
|
100
|
-
type Milliseconds = number
|
|
110
|
+
type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo' | 'disabled';
|
|
111
|
+
type Milliseconds = number;
|
|
101
112
|
interface Callsite {
|
|
102
|
-
line: number
|
|
103
|
-
column: number
|
|
113
|
+
line: number;
|
|
114
|
+
column: number;
|
|
104
115
|
}
|
|
105
116
|
|
|
106
117
|
interface JsonAssertionResult {
|
|
107
|
-
ancestorTitles:
|
|
108
|
-
fullName: string
|
|
109
|
-
status: Status
|
|
110
|
-
title: string
|
|
118
|
+
ancestorTitles: string[];
|
|
119
|
+
fullName: string;
|
|
120
|
+
status: Status;
|
|
121
|
+
title: string;
|
|
111
122
|
meta: {
|
|
112
|
-
runId: string
|
|
113
|
-
runLink: string
|
|
114
|
-
actorName: string
|
|
115
|
-
}
|
|
116
|
-
duration?: Milliseconds | null
|
|
117
|
-
failureMessages:
|
|
118
|
-
location?: Callsite | null
|
|
123
|
+
runId: string;
|
|
124
|
+
runLink: string;
|
|
125
|
+
actorName: string;
|
|
126
|
+
};
|
|
127
|
+
duration?: Milliseconds | null;
|
|
128
|
+
failureMessages: string[] | null;
|
|
129
|
+
location?: Callsite | null;
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
interface JsonTestResult {
|
|
122
|
-
message: string
|
|
123
|
-
name: string
|
|
124
|
-
status: 'failed' | 'passed'
|
|
125
|
-
startTime: number
|
|
126
|
-
endTime: number
|
|
127
|
-
assertionResults:
|
|
133
|
+
message: string;
|
|
134
|
+
name: string;
|
|
135
|
+
status: 'failed' | 'passed';
|
|
136
|
+
startTime: number;
|
|
137
|
+
endTime: number;
|
|
138
|
+
assertionResults: JsonAssertionResult[];
|
|
128
139
|
// summary: string
|
|
129
140
|
// coverage: unknown
|
|
130
141
|
}
|
|
131
142
|
|
|
132
143
|
interface JsonTestResults {
|
|
133
|
-
numFailedTests: number
|
|
134
|
-
numFailedTestSuites: number
|
|
135
|
-
numPassedTests: number
|
|
136
|
-
numPassedTestSuites: number
|
|
137
|
-
numPendingTests: number
|
|
138
|
-
numPendingTestSuites: number
|
|
139
|
-
numTodoTests: number
|
|
140
|
-
numTotalTests: number
|
|
141
|
-
numTotalTestSuites: number
|
|
142
|
-
startTime: number
|
|
143
|
-
success: boolean
|
|
144
|
-
testResults:
|
|
144
|
+
numFailedTests: number;
|
|
145
|
+
numFailedTestSuites: number;
|
|
146
|
+
numPassedTests: number;
|
|
147
|
+
numPassedTestSuites: number;
|
|
148
|
+
numPendingTests: number;
|
|
149
|
+
numPendingTestSuites: number;
|
|
150
|
+
numTodoTests: number;
|
|
151
|
+
numTotalTests: number;
|
|
152
|
+
numTotalTestSuites: number;
|
|
153
|
+
startTime: number;
|
|
154
|
+
success: boolean;
|
|
155
|
+
testResults: JsonTestResult[];
|
|
145
156
|
// snapshot: SnapshotSummary
|
|
146
157
|
// coverageMap?: CoverageMap | null | undefined
|
|
147
158
|
// numRuntimeErrorTestSuites: number
|
package/bin/types.ts
CHANGED
|
@@ -1,91 +1,88 @@
|
|
|
1
1
|
export interface Config {
|
|
2
|
-
targetBranch: string
|
|
3
|
-
sourceBranch: string
|
|
4
|
-
baseCommit?: string
|
|
5
|
-
workspace?: string
|
|
2
|
+
targetBranch: string;
|
|
3
|
+
sourceBranch: string;
|
|
4
|
+
baseCommit?: string;
|
|
5
|
+
workspace?: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export type Commit = {
|
|
9
|
-
sha: string
|
|
10
|
-
author: string
|
|
11
|
-
date: string
|
|
12
|
-
message: string
|
|
13
|
-
}
|
|
9
|
+
sha: string;
|
|
10
|
+
author: string;
|
|
11
|
+
date: string;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
14
|
|
|
15
15
|
// NOTE: The GitHub types are incomplete, feel free to add fields/complete (not sure how stable GitHub API is)
|
|
16
16
|
export interface GitHubHeadCommit {
|
|
17
|
-
added: string[]
|
|
18
|
-
removed: string[]
|
|
19
|
-
modified: string[]
|
|
17
|
+
added: string[];
|
|
18
|
+
removed: string[];
|
|
19
|
+
modified: string[];
|
|
20
20
|
author: {
|
|
21
|
-
name: string
|
|
22
|
-
}
|
|
23
|
-
message: string
|
|
24
|
-
id: string
|
|
21
|
+
name: string;
|
|
22
|
+
};
|
|
23
|
+
message: string;
|
|
24
|
+
id: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface Repository {
|
|
28
|
-
full_name: string
|
|
29
|
-
name: string
|
|
30
|
-
ssh_url: string
|
|
28
|
+
full_name: string;
|
|
29
|
+
name: string;
|
|
30
|
+
ssh_url: string;
|
|
31
31
|
owner: {
|
|
32
|
-
login: string
|
|
33
|
-
}
|
|
32
|
+
login: string;
|
|
33
|
+
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export type GitHubEvent = GitHubEventPullRequest | GitHubEventPush;
|
|
37
37
|
|
|
38
|
-
// All optional type so we check that we have all required fields for each event type
|
|
39
|
-
export type RawGitHubEvent = Partial<Omit<GitHubEventPullRequest, 'type'> & Omit<GitHubEventPush, 'type'>>;
|
|
40
|
-
|
|
41
38
|
export interface GitHubEventPullRequest {
|
|
42
39
|
// Type is our own variable we inject to have discriminated union
|
|
43
40
|
// The rest of the properties are raw from GitHub API
|
|
44
|
-
type: 'pull_request'
|
|
41
|
+
type: 'pull_request';
|
|
45
42
|
pull_request: {
|
|
46
|
-
number: number
|
|
43
|
+
number: number;
|
|
47
44
|
base: {
|
|
48
|
-
ref: string
|
|
49
|
-
sha: string
|
|
50
|
-
}
|
|
45
|
+
ref: string;
|
|
46
|
+
sha: string;
|
|
47
|
+
};
|
|
51
48
|
head: {
|
|
52
|
-
ref: string
|
|
53
|
-
sha: string
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
repository: Repository
|
|
49
|
+
ref: string;
|
|
50
|
+
sha: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
repository: Repository;
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
export interface GitHubEventPush {
|
|
60
|
-
type: 'push'
|
|
61
|
-
head_commit: GitHubHeadCommit
|
|
62
|
-
repository: Repository
|
|
63
|
-
ref: string
|
|
64
|
-
commits: GithubCommit[]
|
|
65
|
-
before: string
|
|
66
|
-
after: string
|
|
57
|
+
type: 'push';
|
|
58
|
+
head_commit: GitHubHeadCommit;
|
|
59
|
+
repository: Repository;
|
|
60
|
+
ref: string;
|
|
61
|
+
commits: GithubCommit[];
|
|
62
|
+
before: string;
|
|
63
|
+
after: string;
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
export interface GithubCommit {
|
|
70
|
-
id: string
|
|
71
|
-
tree_id: string
|
|
72
|
-
distinct: boolean
|
|
73
|
-
message: string
|
|
74
|
-
timestamp: string
|
|
75
|
-
url: string
|
|
67
|
+
id: string;
|
|
68
|
+
tree_id: string;
|
|
69
|
+
distinct: boolean;
|
|
70
|
+
message: string;
|
|
71
|
+
timestamp: string;
|
|
72
|
+
url: string;
|
|
76
73
|
author: {
|
|
77
|
-
name: string
|
|
78
|
-
email: string
|
|
79
|
-
username: string
|
|
80
|
-
}
|
|
74
|
+
name: string;
|
|
75
|
+
email: string;
|
|
76
|
+
username: string;
|
|
77
|
+
};
|
|
81
78
|
committer: {
|
|
82
|
-
name: string
|
|
83
|
-
email: string
|
|
84
|
-
username: string
|
|
85
|
-
}
|
|
86
|
-
added: string[]
|
|
87
|
-
removed: string[]
|
|
88
|
-
modified: string[]
|
|
79
|
+
name: string;
|
|
80
|
+
email: string;
|
|
81
|
+
username: string;
|
|
82
|
+
};
|
|
83
|
+
added: string[];
|
|
84
|
+
removed: string[];
|
|
85
|
+
modified: string[];
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
export interface BuildData {
|
|
@@ -99,5 +96,5 @@ export interface BuildData {
|
|
|
99
96
|
export interface ActorConfig {
|
|
100
97
|
actorName: string;
|
|
101
98
|
folder: string;
|
|
102
|
-
isStandalone: boolean
|
|
99
|
+
isStandalone: boolean;
|
|
103
100
|
}
|
package/bin/utils.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
1
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
GitHubEvent,
|
|
7
|
-
} from './types.js';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
import type { ActorConfig, GitHubEvent } from './types.js';
|
|
8
5
|
|
|
9
6
|
export const spawnCommandInGhWorkspace = (command: string, args: string[] = []) => {
|
|
10
7
|
console.error(command, args.join(' '));
|
|
@@ -49,14 +46,14 @@ export const getRepoActors = async (): Promise<ActorConfig[]> => {
|
|
|
49
46
|
let actorDirs: string[];
|
|
50
47
|
try {
|
|
51
48
|
actorDirs = (await fs.readdir(`./actors`)).map((dir) => `actors/${dir}`);
|
|
52
|
-
} catch
|
|
49
|
+
} catch {
|
|
53
50
|
console.warn(`No /actors directory found in repo`);
|
|
54
51
|
actorDirs = [];
|
|
55
52
|
}
|
|
56
53
|
let standaloneActorDirs: string[];
|
|
57
54
|
try {
|
|
58
55
|
standaloneActorDirs = (await fs.readdir(`./standalone-actors`)).map((dir) => `standalone-actors/${dir}`);
|
|
59
|
-
} catch
|
|
56
|
+
} catch {
|
|
60
57
|
console.warn(`No /standalone-actors directory found in repo`);
|
|
61
58
|
standaloneActorDirs = [];
|
|
62
59
|
}
|
|
@@ -73,8 +70,18 @@ export const getRepoActors = async (): Promise<ActorConfig[]> => {
|
|
|
73
70
|
isStandalone: folderType === 'standalone-actors',
|
|
74
71
|
});
|
|
75
72
|
}
|
|
76
|
-
console.error(
|
|
77
|
-
|
|
73
|
+
console.error(
|
|
74
|
+
`Actors in repo: ${actorConfigs
|
|
75
|
+
.filter(({ isStandalone }) => !isStandalone)
|
|
76
|
+
.map(({ actorName }) => actorName)
|
|
77
|
+
.join(', ')}`,
|
|
78
|
+
);
|
|
79
|
+
console.error(
|
|
80
|
+
`Standalone actors in repo: ${actorConfigs
|
|
81
|
+
.filter(({ isStandalone }) => !!isStandalone)
|
|
82
|
+
.map(({ actorName }) => actorName)
|
|
83
|
+
.join(', ')}`,
|
|
84
|
+
);
|
|
78
85
|
return actorConfigs;
|
|
79
86
|
};
|
|
80
87
|
|
|
@@ -85,127 +92,8 @@ export const setCwd = ({ workspace }: { workspace: string | undefined }) => {
|
|
|
85
92
|
}
|
|
86
93
|
const ghWorkspace = getEnvVar('GITHUB_WORKSPACE', process.cwd());
|
|
87
94
|
process.chdir(ghWorkspace);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export const getHeadCommitSha = (githubEvent: GitHubEvent) => {
|
|
91
|
-
return githubEvent.type === 'pull_request'
|
|
92
|
-
? githubEvent.pull_request.head.sha
|
|
93
|
-
: githubEvent.head_commit.id;
|
|
94
95
|
};
|
|
95
96
|
|
|
96
|
-
export
|
|
97
|
-
|
|
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
|
-
};
|
|
97
|
+
export const getHeadCommitSha = (githubEvent: GitHubEvent) => {
|
|
98
|
+
return githubEvent.type === 'pull_request' ? githubEvent.pull_request.head.sha : githubEvent.head_commit.id;
|
|
211
99
|
};
|
package/dist/bin/build.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ type RunBuildsOptions = {
|
|
|
12
12
|
branch: string;
|
|
13
13
|
dryRun: boolean;
|
|
14
14
|
};
|
|
15
|
-
export declare const runBuilds: ({ repoUrl, branch, actorConfigs, isLatest, dryRun
|
|
15
|
+
export declare const runBuilds: ({ repoUrl, branch, actorConfigs, isLatest, dryRun }: RunBuildsOptions) => Promise<BuildPrActorOptions[] | BuildData[]>;
|
|
16
16
|
export declare const deleteOldBuilds: (actorConfigs: ActorConfig[]) => Promise<void>;
|
|
17
17
|
export {};
|
|
18
18
|
//# sourceMappingURL=build.d.ts.map
|