apify-test-tools 0.10.2-beta.1 → 0.10.2-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/.gitattributes +2 -0
- package/.github/actions/checkout-restore-dependencies/action.yaml +13 -29
- package/.github/cli/apify-test-tools.mjs +361 -0
- package/.github/scripts/run-with-apify-tokens.mjs +2 -2
- package/.github/workflows/_check_code.yaml +25 -0
- package/.github/workflows/_move_major_tag.yaml +3 -81
- package/.github/workflows/_tests.yaml +34 -0
- package/.github/workflows/_update_release_metadata.yaml +0 -12
- package/.github/workflows/manual_move_major_tag.yaml +3 -3
- package/.github/workflows/manual_release_stable.yaml +3 -13
- package/.github/workflows/on_master.yaml +9 -5
- package/.github/workflows/on_pull_request.yaml +3 -48
- package/.github/workflows/public_platform-tests.yaml +1 -1
- package/.github/workflows/public_pr-build-test.yaml +2 -2
- package/.github/workflows/public_push-build-latest.yaml +2 -2
- package/.husky/pre-commit +5 -0
- package/.prettierignore +3 -0
- package/CHANGELOG.md +5 -0
- package/CONTRIBUTING.md +8 -18
- package/README.md +94 -44
- package/bin/affected-actors.ts +166 -0
- package/bin/build-from-local.ts +47 -13
- package/bin/build.ts +35 -19
- package/bin/diff-changes.ts +25 -17
- package/bin/git.ts +52 -41
- package/bin/main.ts +63 -78
- package/bin/slack.ts +28 -6
- package/bin/test-report.ts +6 -9
- package/bin/types.ts +9 -0
- package/dist/bin/affected-actors.d.ts +50 -0
- package/dist/bin/affected-actors.d.ts.map +1 -0
- package/dist/bin/affected-actors.js +89 -0
- package/dist/bin/affected-actors.js.map +1 -0
- package/dist/bin/build-from-local.d.ts +10 -2
- package/dist/bin/build-from-local.d.ts.map +1 -1
- package/dist/bin/build-from-local.js +29 -11
- package/dist/bin/build-from-local.js.map +1 -1
- package/dist/bin/build.d.ts +15 -5
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +24 -15
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +8 -2
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +16 -16
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/git.d.ts +23 -13
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +44 -32
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +57 -57
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts +8 -1
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +26 -5
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/test-report.d.ts.map +1 -1
- package/dist/bin/test-report.js +6 -8
- package/dist/bin/test-report.js.map +1 -1
- package/dist/bin/types.d.ts +8 -0
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/test/bundle/cli-bundle.test.d.ts +2 -0
- package/dist/test/bundle/cli-bundle.test.d.ts.map +1 -0
- package/dist/test/bundle/cli-bundle.test.js +169 -0
- package/dist/test/bundle/cli-bundle.test.js.map +1 -0
- package/dist/test/e2e/cli-bundle.e2e.test.d.ts +2 -0
- package/dist/test/e2e/cli-bundle.e2e.test.d.ts.map +1 -0
- package/dist/test/e2e/cli-bundle.e2e.test.js +172 -0
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -0
- package/dist/test/unit/bin/affected-actors.test.d.ts +2 -0
- package/dist/test/unit/bin/affected-actors.test.d.ts.map +1 -0
- package/dist/test/unit/bin/affected-actors.test.js +160 -0
- package/dist/test/unit/bin/affected-actors.test.js.map +1 -0
- package/dist/test/unit/bin/build-from-local.test.js +53 -1
- package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
- package/dist/test/unit/bin/build.test.js +113 -3
- package/dist/test/unit/bin/build.test.js.map +1 -1
- package/dist/test/unit/bin/diff-changes.test.js +78 -49
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +50 -53
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/slack.test.d.ts +2 -0
- package/dist/test/unit/bin/slack.test.d.ts.map +1 -0
- package/dist/test/unit/bin/slack.test.js +54 -0
- package/dist/test/unit/bin/slack.test.js.map +1 -0
- package/dist/test/unit/should-built-and-test.test.js +23 -17
- package/dist/test/unit/should-built-and-test.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -3
- package/test/bundle/cli-bundle.test.ts +199 -0
- package/test/e2e/cli-bundle.e2e.test.ts +237 -0
- package/test/unit/bin/affected-actors.test.ts +190 -0
- package/test/unit/bin/build-from-local.test.ts +55 -0
- package/test/unit/bin/build.test.ts +135 -3
- package/test/unit/bin/diff-changes.test.ts +87 -49
- package/test/unit/bin/git.test.ts +56 -53
- package/test/unit/bin/slack.test.ts +65 -0
- package/test/unit/should-built-and-test.test.ts +27 -17
- package/.github/scripts/check-package-version-bump.mjs +0 -74
- package/.github/workflows-package-version +0 -1
package/bin/git.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const GIT_FORMAT_SEPARATOR = '»¦«';
|
|
|
6
6
|
const GIT_LOG_FORMAT = ['%H', '%aN<%aE>', '%aD', '%s'].join(GIT_FORMAT_SEPARATOR);
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Gets the list of changed files between the given commits (inclusive).
|
|
9
|
+
* Gets the list of changed files between the given commits (inclusive), so it diffs from the parent of the oldest.
|
|
10
10
|
*/
|
|
11
11
|
export const getChangedFiles = async (commits: Commit[]) => {
|
|
12
12
|
// getCommits never returns an empty list (the rerun check returns all commits when the base commit
|
|
@@ -15,10 +15,19 @@ export const getChangedFiles = async (commits: Commit[]) => {
|
|
|
15
15
|
throw new Error('Cannot get changed files: the commit list is empty. This should never happen.');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
return getChangedFilesBetween(`${commits[0].sha}~`, commits[commits.length - 1].sha);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Files that differ between two commits, i.e. changed by the commits in the range (fromRef, toRef]. fromRef's own
|
|
23
|
+
* changes are not included: to include them, pass its parent, `${sha}~` (as getChangedFiles does).
|
|
24
|
+
* Diffing the endpoints, rather than walking a commit list, is what keeps merges right: the oldest commit of a
|
|
25
|
+
* merged branch can predate fromRef, and its changes are already in fromRef.
|
|
26
|
+
*/
|
|
27
|
+
export const getChangedFilesBetween = async (fromRef: string, toRef: string) => {
|
|
28
|
+
const changedFilesString = await runGitCommand(['diff', '--name-only', `${fromRef}..${toRef}`], {
|
|
29
|
+
operation: 'get the list of changed files between commits',
|
|
30
|
+
});
|
|
22
31
|
|
|
23
32
|
const changedFiles = changedFilesString.split('\n').filter(Boolean);
|
|
24
33
|
logger.info(`Changed files (up to 50): ${changedFiles.slice(0, 50).join(', ')}`);
|
|
@@ -103,11 +112,14 @@ export const parseBaseCommit = (shaOrCommit: string | undefined): string | undef
|
|
|
103
112
|
return sha;
|
|
104
113
|
};
|
|
105
114
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Commits reachable from toRef but not from fromRef, oldest first: the range (fromRef, toRef], so fromRef itself is
|
|
117
|
+
* not included. To include it, pass its parent, `${sha}~`.
|
|
118
|
+
*/
|
|
119
|
+
export const getCommitsBetween = async (fromRef: string, toRef: string): Promise<Commit[]> => {
|
|
120
|
+
const output = await runGitCommand(['log', `--pretty=format:${GIT_LOG_FORMAT}`, `${fromRef}..${toRef}`], {
|
|
121
|
+
operation: 'list commits reachable from the source ref but absent from the target ref',
|
|
122
|
+
});
|
|
111
123
|
const commitsStrings = output.split('\n');
|
|
112
124
|
const commits = commitsStrings.map((commitString) => parseCommit(commitString));
|
|
113
125
|
commits.reverse();
|
|
@@ -124,7 +136,7 @@ export const getCommits = async ({
|
|
|
124
136
|
baseCommit,
|
|
125
137
|
}: Pick<Config, 'sourceBranch' | 'targetBranch' | 'baseCommit'>): Promise<Commit[]> => {
|
|
126
138
|
const baseCommitSha = parseBaseCommit(baseCommit);
|
|
127
|
-
const commits = await
|
|
139
|
+
const commits = await getCommitsBetween(targetBranch, sourceBranch);
|
|
128
140
|
|
|
129
141
|
// The last validated (base) commit being the branch HEAD means nothing new was pushed since the last
|
|
130
142
|
// validation — the dev reran the workflow (or force-pushed to the same state) to trigger a clean test
|
|
@@ -184,6 +196,21 @@ export const getCurrentBranch = async (): Promise<string> => {
|
|
|
184
196
|
return branch;
|
|
185
197
|
};
|
|
186
198
|
|
|
199
|
+
/** Resolves a ref (branch, remote branch, SHA, HEAD, ...) to the full SHA of the commit it points to. */
|
|
200
|
+
export const resolveCommitSha = async (ref: string): Promise<string> =>
|
|
201
|
+
runGitCommand(['rev-parse', '--verify', `${ref}^{commit}`], { operation: `resolve ${ref} to a commit` });
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Branch name a ref points to, without the remote: `origin/foo` and `foo` both give `foo`, and `HEAD`
|
|
205
|
+
* gives the checked-out branch. Null when there is no branch to point at (a SHA, a tag, a detached HEAD).
|
|
206
|
+
*/
|
|
207
|
+
export const resolveBranchName = async (ref: string): Promise<string | null> => {
|
|
208
|
+
const fullName = await runGitCommand(['rev-parse', '--symbolic-full-name', ref], {
|
|
209
|
+
operation: `resolve the branch name of ${ref}`,
|
|
210
|
+
});
|
|
211
|
+
return fullName.match(/^refs\/(?:heads|remotes\/[^/]+)\/(.+)$/)?.[1] ?? null;
|
|
212
|
+
};
|
|
213
|
+
|
|
187
214
|
/**
|
|
188
215
|
* Reads the repository URL from the `origin` remote, rewritten to the SSH form the Apify platform
|
|
189
216
|
* uses for Git repo sources, e.g. git@github.com:apify-store/google-maps
|
|
@@ -236,15 +263,15 @@ const ZERO_SHA_REGEX = /^0{40}$/;
|
|
|
236
263
|
* Unlike the PR path (getCommits), there is no lenient fallback. Falling back to "everything"
|
|
237
264
|
* would rebuild the latest build of every Actor and post it to Slack, so every problem is an error.
|
|
238
265
|
*/
|
|
239
|
-
export const resolveReleaseBaseCommit = async (baseCommit: string): Promise<string> => {
|
|
266
|
+
export const resolveReleaseBaseCommit = async (baseCommit: string, head = 'HEAD'): Promise<string> => {
|
|
240
267
|
const sha = parseBaseCommit(baseCommit);
|
|
241
268
|
if (!sha) {
|
|
242
|
-
throw new Error('
|
|
269
|
+
throw new Error('A base commit is required for release. See the README section "Releasing Actors".');
|
|
243
270
|
}
|
|
244
271
|
if (ZERO_SHA_REGEX.test(sha)) {
|
|
245
272
|
throw new Error(
|
|
246
273
|
`Base commit is ${sha}, which means the branch was just created and there is no previous release to diff against. ` +
|
|
247
|
-
`Rerun with
|
|
274
|
+
`Rerun with the base commit set to the last commit before the changes you want to release.`,
|
|
248
275
|
);
|
|
249
276
|
}
|
|
250
277
|
// --quiet suppresses Git's error text for a missing commit, but Git still exits with code 1.
|
|
@@ -261,16 +288,16 @@ export const resolveReleaseBaseCommit = async (baseCommit: string): Promise<stri
|
|
|
261
288
|
);
|
|
262
289
|
}
|
|
263
290
|
// git merge-base A B outputs the common ancestor. If that equals A, then A is an ancestor of B.
|
|
264
|
-
const mergeBase = await runGitCommand(['merge-base', verifiedSha,
|
|
265
|
-
operation:
|
|
291
|
+
const mergeBase = await runGitCommand(['merge-base', verifiedSha, head], {
|
|
292
|
+
operation: `check whether the release base commit is an ancestor of ${head}`,
|
|
266
293
|
}).catch((error: unknown) => {
|
|
267
294
|
if (error instanceof GitCommandError && error.exitCode === 1) return '';
|
|
268
295
|
throw error;
|
|
269
296
|
});
|
|
270
297
|
if (mergeBase !== sha) {
|
|
271
298
|
throw new Error(
|
|
272
|
-
`Base commit ${sha} is not an ancestor of
|
|
273
|
-
`The changed files cannot be determined reliably. Rerun with
|
|
299
|
+
`Base commit ${sha} is not an ancestor of ${head}, most likely because the branch was force-pushed. ` +
|
|
300
|
+
`The changed files cannot be determined reliably. Rerun with the base commit set to an ancestor of ${head} ` +
|
|
274
301
|
`from before the changes you want to release.`,
|
|
275
302
|
);
|
|
276
303
|
}
|
|
@@ -279,12 +306,16 @@ export const resolveReleaseBaseCommit = async (baseCommit: string): Promise<stri
|
|
|
279
306
|
|
|
280
307
|
const CHANGELOG_PATH = 'CHANGELOG.md';
|
|
281
308
|
|
|
282
|
-
/** Returns the lines added to the root CHANGELOG.md between
|
|
283
|
-
const getChangelogAdditions = async (
|
|
309
|
+
/** Returns the lines added to the root CHANGELOG.md between fromRef and toRef, or null if it didn't change. */
|
|
310
|
+
export const getChangelogAdditions = async (
|
|
311
|
+
fromRef: string,
|
|
312
|
+
toRef: string,
|
|
313
|
+
changedFiles: string[],
|
|
314
|
+
): Promise<string | null> => {
|
|
284
315
|
if (!changedFiles.includes(CHANGELOG_PATH)) {
|
|
285
316
|
return null;
|
|
286
317
|
}
|
|
287
|
-
const diff = await runGitCommand(['diff',
|
|
318
|
+
const diff = await runGitCommand(['diff', fromRef, toRef, '--', CHANGELOG_PATH], {
|
|
288
319
|
operation: 'read changelog changes since the last release',
|
|
289
320
|
});
|
|
290
321
|
|
|
@@ -307,23 +338,3 @@ const getChangelogAdditions = async (baseSha: string, changedFiles: string[]): P
|
|
|
307
338
|
}
|
|
308
339
|
return added.join('\n').trim();
|
|
309
340
|
};
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Everything that changed since the last release (baseSha, exclusive) up to HEAD, or null when there
|
|
313
|
-
* are no new commits. Changed files come from diffing the range directly instead of from the commit
|
|
314
|
-
* list: with a merge commit, the oldest commit of the merged branch can be older than baseSha, and
|
|
315
|
-
* diffing from its parent would pull in already-released changes.
|
|
316
|
-
*/
|
|
317
|
-
export const getReleaseChanges = async (baseSha: string) => {
|
|
318
|
-
if (
|
|
319
|
-
(await runGitCommand(['rev-parse', 'HEAD'], { operation: 'identify the current release commit' })) === baseSha
|
|
320
|
-
) {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
const commits = await fetchAllBranchCommits('HEAD', baseSha);
|
|
324
|
-
const changedFiles = await runGitCommand(['diff', '--name-only', baseSha, 'HEAD'], {
|
|
325
|
-
operation: 'list files changed since the last release',
|
|
326
|
-
}).then((output) => output.split('\n').filter(Boolean));
|
|
327
|
-
const changelog = await getChangelogAdditions(baseSha, changedFiles);
|
|
328
|
-
return { commits, changedFiles, changelog };
|
|
329
|
-
};
|
package/bin/main.ts
CHANGED
|
@@ -6,25 +6,14 @@ import yargs, { type Options } from 'yargs';
|
|
|
6
6
|
// eslint-disable-next-line import/extensions --- With .js, it cannot find types
|
|
7
7
|
import { hideBin } from 'yargs/helpers';
|
|
8
8
|
|
|
9
|
+
import { getAffectedActors, toAffectedActorsDocument } from './affected-actors.js';
|
|
9
10
|
import { deleteOldBuilds, runBuilds } from './build.js';
|
|
10
11
|
import { runBuildsFromLocal } from './build-from-local.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
getBranchOnlyChangedFiles,
|
|
14
|
-
getChangedFiles,
|
|
15
|
-
getCommits,
|
|
16
|
-
getCurrentBranch,
|
|
17
|
-
getReleaseChanges,
|
|
18
|
-
getRepoName,
|
|
19
|
-
hasMergeFromTarget,
|
|
20
|
-
resolveReleaseBaseCommit,
|
|
21
|
-
resolveRepoUrl,
|
|
22
|
-
} from './git.js';
|
|
12
|
+
import { getChangedFiles, getCommits, getCurrentBranch, getRepoName, resolveRepoUrl } from './git.js';
|
|
23
13
|
import { logger, logLevels } from './logger.js';
|
|
24
14
|
import { writeJson } from './output.js';
|
|
25
15
|
import { notifyToSlack } from './slack.js';
|
|
26
16
|
import { reportTestResults } from './test-report.js';
|
|
27
|
-
import type { Config } from './types.js';
|
|
28
17
|
import { readConfigFile } from './utils/config/load-config.js';
|
|
29
18
|
|
|
30
19
|
/**
|
|
@@ -64,6 +53,36 @@ const actorSelectionOptions = {
|
|
|
64
53
|
ignore: { type: 'string', array: true, default: [] as string[] },
|
|
65
54
|
} as const satisfies Record<string, Options>;
|
|
66
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Commit range for get-affected-actors, which always ends at the checked-out HEAD. `base` and `since` combine:
|
|
58
|
+
* `base` alone is a branch's own commits (a PR), `since` alone is everything after a commit (a release), both is
|
|
59
|
+
* a branch after its last validated commit.
|
|
60
|
+
*/
|
|
61
|
+
const affectedRangeOptions = {
|
|
62
|
+
base: { type: 'string', describe: 'Only count commits on HEAD that are not on this branch' },
|
|
63
|
+
since: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
describe: 'Only count commits after this one. Ignored with --base if it is not on the branch',
|
|
66
|
+
},
|
|
67
|
+
'test-files-glob': {
|
|
68
|
+
type: 'string',
|
|
69
|
+
describe: 'Glob (from the repo root) of test files that count as changes even when .dockerignore lists them',
|
|
70
|
+
},
|
|
71
|
+
} as const satisfies Record<string, Options>;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* For commands that change something (builds, deletes, Slack posts). A dry run still does every read and check
|
|
75
|
+
* the real run does, credentials included, and only skips the writes. So it needs the same tokens, though
|
|
76
|
+
* read-only ones are enough. Read-only commands don't take it since they have nothing to skip.
|
|
77
|
+
*/
|
|
78
|
+
const dryRunOptions = {
|
|
79
|
+
'dry-run': {
|
|
80
|
+
type: 'boolean',
|
|
81
|
+
default: false,
|
|
82
|
+
describe: 'Do every read and check (tokens included) but change nothing; print what would be done',
|
|
83
|
+
},
|
|
84
|
+
} as const satisfies Record<string, Options>;
|
|
85
|
+
|
|
67
86
|
/** Flags for commands that trigger Apify builds. */
|
|
68
87
|
const buildOptions = {
|
|
69
88
|
'use-docker-cache': { type: 'boolean', default: false },
|
|
@@ -77,51 +96,8 @@ const globalOptions = {
|
|
|
77
96
|
default: 'info',
|
|
78
97
|
describe: 'Minimum diagnostic level written to stderr; silent disables logging',
|
|
79
98
|
},
|
|
80
|
-
'dry-run': { type: 'boolean', default: false },
|
|
81
99
|
} as const satisfies Record<string, Options>;
|
|
82
100
|
|
|
83
|
-
const resolveChangedActors = async (config: Config, { isLatest }: { isLatest: boolean }) => {
|
|
84
|
-
const actorConfigs = await readConfigFile(config);
|
|
85
|
-
|
|
86
|
-
// This is an optimization for the common case where a branch only has cosmetic changes but had to smerge in
|
|
87
|
-
// functional changes from master (being up-to-date is a CI requirement). Master is already validated, and
|
|
88
|
-
// since the branch has no functional changes of its own, there is nothing new to validate.
|
|
89
|
-
// Exception: if the branch has any functional changes alongside the merge, we must re-test — even
|
|
90
|
-
// individually validated changes can have novel interactions when combined.
|
|
91
|
-
if (await hasMergeFromTarget(config.sourceBranch, config.targetBranch)) {
|
|
92
|
-
logger.info(
|
|
93
|
-
'[MERGE-FROM-TARGET-OPTIMIZATION]: There is merge from target branch, checking if there are no functional changes in our own branch. If so, we can skip tests',
|
|
94
|
-
);
|
|
95
|
-
const branchOnlyFiles = await getBranchOnlyChangedFiles(config.sourceBranch, config.targetBranch);
|
|
96
|
-
// Omit baseCommit to get full branch history. Validated functional commits can still interact with merged ones
|
|
97
|
-
const allBranchCommits = await getCommits({ ...config, baseCommit: undefined });
|
|
98
|
-
const branchOnlyActorsChanged = await getChangedActors({
|
|
99
|
-
filepathsChanged: branchOnlyFiles,
|
|
100
|
-
actorConfigs,
|
|
101
|
-
commits: allBranchCommits,
|
|
102
|
-
testFilesGlob: config.testFilesGlob,
|
|
103
|
-
});
|
|
104
|
-
if (branchOnlyActorsChanged.length === 0) {
|
|
105
|
-
logger.info('[MERGE-FROM-TARGET-OPTIMIZATION]: Branch itself has no functional changes, skipping tests');
|
|
106
|
-
return [];
|
|
107
|
-
}
|
|
108
|
-
logger.info(
|
|
109
|
-
`[MERGE-FROM-TARGET-OPTIMIZATION]: Branch has ${branchOnlyActorsChanged.length} functional changes, cannot optimize, we continue with full check`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// If the optimization doesn't apply, we check all branch commits including merges for full coverage. We don't reuse the merge optimization results because here we can apply baseCommit and check merge commits (they might be functional or just cosmetic)
|
|
114
|
-
const commits = await getCommits(config);
|
|
115
|
-
const changedFiles = await getChangedFiles(commits);
|
|
116
|
-
return getChangedActors({
|
|
117
|
-
filepathsChanged: changedFiles,
|
|
118
|
-
actorConfigs,
|
|
119
|
-
isLatest,
|
|
120
|
-
commits,
|
|
121
|
-
testFilesGlob: config.testFilesGlob,
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
101
|
await yargs()
|
|
126
102
|
.scriptName('public-actors-utils')
|
|
127
103
|
.options(globalOptions)
|
|
@@ -167,11 +143,10 @@ await yargs()
|
|
|
167
143
|
)
|
|
168
144
|
.command(
|
|
169
145
|
'get-affected-actors',
|
|
170
|
-
'',
|
|
171
|
-
(y) => y.options(
|
|
172
|
-
async (
|
|
173
|
-
|
|
174
|
-
writeJson(actorsChanged);
|
|
146
|
+
'Find the Actors whose context changed in a commit range ending at the checked-out HEAD',
|
|
147
|
+
(y) => y.options(affectedRangeOptions).options(actorSelectionOptions),
|
|
148
|
+
async (args) => {
|
|
149
|
+
writeJson(toAffectedActorsDocument(await getAffectedActors({ ...args, head: 'HEAD' })));
|
|
175
150
|
},
|
|
176
151
|
)
|
|
177
152
|
.command(
|
|
@@ -179,6 +154,7 @@ await yargs()
|
|
|
179
154
|
'',
|
|
180
155
|
(y) =>
|
|
181
156
|
y
|
|
157
|
+
.options(dryRunOptions)
|
|
182
158
|
.option('report-file', { type: 'string', demandOption: true })
|
|
183
159
|
.option('report-slack-channel', { type: 'string' })
|
|
184
160
|
.option('job-url', { type: 'string' })
|
|
@@ -196,9 +172,18 @@ await yargs()
|
|
|
196
172
|
.options(actorSelectionOptions)
|
|
197
173
|
.options(buildOptions)
|
|
198
174
|
.options(repoUrlOptions)
|
|
175
|
+
.options(dryRunOptions)
|
|
199
176
|
.option('test-files-glob', { type: 'string' }),
|
|
200
177
|
async (config) => {
|
|
201
|
-
|
|
178
|
+
// Cosmetic-only changes don't need testing, so they don't need a build either
|
|
179
|
+
const { functionallyChangedActors: actorsChanged } = await getAffectedActors({
|
|
180
|
+
head: config.sourceBranch,
|
|
181
|
+
base: config.targetBranch,
|
|
182
|
+
since: config.baseCommit,
|
|
183
|
+
testFilesGlob: config.testFilesGlob,
|
|
184
|
+
actors: config.actors,
|
|
185
|
+
ignore: config.ignore,
|
|
186
|
+
});
|
|
202
187
|
const builds = await runBuilds({
|
|
203
188
|
...(await resolveRepoUrl(config.repoUrl)),
|
|
204
189
|
actorConfigs: actorsChanged,
|
|
@@ -217,30 +202,30 @@ await yargs()
|
|
|
217
202
|
.options(actorSelectionOptions)
|
|
218
203
|
.options(buildOptions)
|
|
219
204
|
.options(repoUrlOptions)
|
|
205
|
+
.options(dryRunOptions)
|
|
220
206
|
// The last commit that is already released, everything after it up to HEAD gets released.
|
|
221
207
|
// Required, see the README section "Releasing Actors" for how to set it for each merge strategy.
|
|
222
208
|
.option('base-commit', { type: 'string', demandOption: true })
|
|
223
209
|
.option('report-slack-channel', { type: 'string' })
|
|
224
210
|
.option('release-slack-channel', { type: 'string' }),
|
|
225
211
|
async (args) => {
|
|
226
|
-
const baseSha = await resolveReleaseBaseCommit(args.baseCommit);
|
|
227
212
|
const branch = await getCurrentBranch();
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
213
|
+
const { commits, changedFiles, changelog, functionallyChangedActors, cosmeticallyChangedActors } =
|
|
214
|
+
await getAffectedActors({
|
|
215
|
+
head: 'HEAD',
|
|
216
|
+
since: args.baseCommit,
|
|
217
|
+
actors: args.actors,
|
|
218
|
+
ignore: args.ignore,
|
|
219
|
+
});
|
|
220
|
+
// A release ships cosmetic changes (README, input schema descriptions, ...) too
|
|
221
|
+
const actorsChanged = [...functionallyChangedActors, ...cosmeticallyChangedActors];
|
|
222
|
+
if (commits.length === 0) {
|
|
223
|
+
logger.info(`HEAD is the base commit ${args.baseCommit}, there is nothing new to release`);
|
|
231
224
|
return;
|
|
232
225
|
}
|
|
233
|
-
const { commits, changedFiles, changelog } = changes;
|
|
234
226
|
const { repoUrl, shouldVerifyRepoUrl } = await resolveRepoUrl(args.repoUrl);
|
|
235
227
|
|
|
236
228
|
const isLatest = true;
|
|
237
|
-
const actorConfigs = await readConfigFile(args);
|
|
238
|
-
const actorsChanged = await getChangedActors({
|
|
239
|
-
filepathsChanged: changedFiles,
|
|
240
|
-
actorConfigs,
|
|
241
|
-
isLatest,
|
|
242
|
-
commits,
|
|
243
|
-
});
|
|
244
229
|
const { dryRun, reportSlackChannel, releaseSlackChannel } = args;
|
|
245
230
|
const builds = await runBuilds({
|
|
246
231
|
isLatest,
|
|
@@ -269,7 +254,7 @@ await yargs()
|
|
|
269
254
|
.command(
|
|
270
255
|
'build-from-local',
|
|
271
256
|
'',
|
|
272
|
-
(y) => y.options(actorSelectionOptions),
|
|
257
|
+
(y) => y.options(actorSelectionOptions).options(dryRunOptions),
|
|
273
258
|
async ({ actors, ignore, dryRun }) => {
|
|
274
259
|
const actorConfigs = await readConfigFile({ actors, ignore });
|
|
275
260
|
const builds = await runBuildsFromLocal({ actorConfigs, dryRun });
|
|
@@ -279,10 +264,10 @@ await yargs()
|
|
|
279
264
|
.command(
|
|
280
265
|
'delete-old-builds',
|
|
281
266
|
'',
|
|
282
|
-
(y) => y.options(actorSelectionOptions),
|
|
283
|
-
async ({ actors, ignore }) => {
|
|
267
|
+
(y) => y.options(actorSelectionOptions).options(dryRunOptions),
|
|
268
|
+
async ({ actors, ignore, dryRun }) => {
|
|
284
269
|
const actorConfigs = await readConfigFile({ actors, ignore });
|
|
285
|
-
await deleteOldBuilds(actorConfigs);
|
|
270
|
+
await deleteOldBuilds(actorConfigs, { dryRun });
|
|
286
271
|
},
|
|
287
272
|
)
|
|
288
273
|
.strictCommands()
|
package/bin/slack.ts
CHANGED
|
@@ -15,6 +15,25 @@ type NotifyToSlackOptions = {
|
|
|
15
15
|
reportSlackChannel?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Creates the client the real run posts with. A dry run posts nothing but still requires the token and checks
|
|
20
|
+
* Slack accepts it (`auth.test` is read-only), so it fails wherever the real run would.
|
|
21
|
+
*/
|
|
22
|
+
export const getSlackClient = async (tokenEnvVar: string, dryRun: boolean): Promise<WebClient> => {
|
|
23
|
+
const slack = new WebClient(getEnvVar(tokenEnvVar));
|
|
24
|
+
if (dryRun) {
|
|
25
|
+
try {
|
|
26
|
+
await slack.auth.test();
|
|
27
|
+
} catch (err) {
|
|
28
|
+
throw new Error(`${tokenEnvVar} was rejected by Slack: ${(err as Error).message}`);
|
|
29
|
+
}
|
|
30
|
+
logger.info(`[DRY RUN] ${tokenEnvVar} is valid`);
|
|
31
|
+
}
|
|
32
|
+
return slack;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const sendingLabel = (dryRun: boolean) => (dryRun ? '[DRY RUN] Would send' : 'Sending');
|
|
36
|
+
|
|
18
37
|
// Git authors come as "Name<email>", Slack only needs the name
|
|
19
38
|
const displayName = (author: string) => author.replace(/\s*<[^>]*>$/, '');
|
|
20
39
|
|
|
@@ -28,19 +47,23 @@ export const notifyToSlack = async ({
|
|
|
28
47
|
releaseSlackChannel,
|
|
29
48
|
reportSlackChannel,
|
|
30
49
|
}: NotifyToSlackOptions) => {
|
|
31
|
-
const slack = new WebClient(getEnvVar('SLACK_TOKEN_RELEASES_BOT'));
|
|
32
|
-
|
|
33
50
|
if (!changelog) {
|
|
34
51
|
logger.warn('No new changelog entries found, did you forget to update it?');
|
|
35
52
|
}
|
|
36
53
|
|
|
54
|
+
if (!releaseSlackChannel && !reportSlackChannel) {
|
|
55
|
+
logger.info('Skipping slack notification, no --release-slack-channel or --report-slack-channel passed');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const slack = await getSlackClient('SLACK_TOKEN_RELEASES_BOT', dryRun);
|
|
59
|
+
|
|
37
60
|
let shortMessage = `*${repository}* – New release (by ${displayName(author)}):\n\n`;
|
|
38
61
|
|
|
39
62
|
// This one is just for broader public that only cares about public facing changes
|
|
40
63
|
if (changelog && releaseSlackChannel) {
|
|
41
64
|
shortMessage += `*Additions to the changelog*:\n\n${changelog}\n`;
|
|
42
65
|
logger.info(`=========================================`);
|
|
43
|
-
logger.info(
|
|
66
|
+
logger.info(`**${sendingLabel(dryRun)} slack message to channel**: ${releaseSlackChannel}.\n\n${shortMessage}`);
|
|
44
67
|
logger.info(`=========================================`);
|
|
45
68
|
if (!dryRun) {
|
|
46
69
|
await slack.chat.postMessage({
|
|
@@ -62,7 +85,7 @@ export const notifyToSlack = async ({
|
|
|
62
85
|
// This one is for devs and project managers that need to know more details
|
|
63
86
|
if (reportSlackChannel) {
|
|
64
87
|
logger.info(`=========================================`);
|
|
65
|
-
logger.info(
|
|
88
|
+
logger.info(`${sendingLabel(dryRun)} slack message to channel: ${reportSlackChannel}.\n\n${longMessage}`);
|
|
66
89
|
logger.info(`=========================================`);
|
|
67
90
|
if (!dryRun) {
|
|
68
91
|
await slack.chat.postMessage({
|
|
@@ -73,8 +96,7 @@ export const notifyToSlack = async ({
|
|
|
73
96
|
}
|
|
74
97
|
};
|
|
75
98
|
|
|
76
|
-
export const sendSlackMessage = async (channel: string, text: string, blocks: string[]
|
|
77
|
-
const slack = new WebClient(token);
|
|
99
|
+
export const sendSlackMessage = async (slack: WebClient, channel: string, text: string, blocks: string[]) => {
|
|
78
100
|
const { ts } = await slack.chat.postMessage({ text, channel });
|
|
79
101
|
if (blocks.length > 0 && ts) {
|
|
80
102
|
await slack.chat.postMessage({
|
package/bin/test-report.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
|
|
3
3
|
import { logger } from './logger.js';
|
|
4
|
-
import { sendSlackMessage } from './slack.js';
|
|
5
|
-
import { getEnvVar } from './utils.js';
|
|
4
|
+
import { getSlackClient, sendingLabel, sendSlackMessage } from './slack.js';
|
|
6
5
|
|
|
7
6
|
interface ReportTestResultsOptions {
|
|
8
7
|
reportFile: string;
|
|
@@ -80,6 +79,9 @@ export const reportTestResults = async ({
|
|
|
80
79
|
);
|
|
81
80
|
return;
|
|
82
81
|
}
|
|
82
|
+
// Checked before knowing if there's anything to send, so a broken token shows up on a green run
|
|
83
|
+
// (and in dry runs) rather than on the day tests fail
|
|
84
|
+
const slack = await getSlackClient('SLACK_TOKEN_TESTS_BOT', dryRun);
|
|
83
85
|
|
|
84
86
|
if (failedAssertions.length === 0) {
|
|
85
87
|
return;
|
|
@@ -95,16 +97,11 @@ export const reportTestResults = async ({
|
|
|
95
97
|
.slice(1)
|
|
96
98
|
.map(({ message, runLink, actorId }) => `• ${message} --- <${runLink}|${actorId}>`);
|
|
97
99
|
|
|
98
|
-
logger.info(
|
|
100
|
+
logger.info(`${sendingLabel(dryRun)} slack message to channel ${reportSlackChannel}:`, slackMessage);
|
|
99
101
|
logger.info('\tblocks:', blocks.join('\n\t\t'));
|
|
100
102
|
|
|
101
|
-
if (!reportSlackChannel) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
103
|
if (!dryRun) {
|
|
106
|
-
|
|
107
|
-
await sendSlackMessage(reportSlackChannel, slackMessage, blocks, slackToken);
|
|
104
|
+
await sendSlackMessage(slack, reportSlackChannel, slackMessage, blocks);
|
|
108
105
|
}
|
|
109
106
|
};
|
|
110
107
|
|
package/bin/types.ts
CHANGED
|
@@ -24,6 +24,15 @@ export interface BuildData {
|
|
|
24
24
|
buildNumber: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* What a dry run writes to stdout instead of the command's real result. It's an object, not an array, so
|
|
29
|
+
* it can't be mistaken for real builds: `ACTOR_BUILDS` rejects it before any test runs.
|
|
30
|
+
*/
|
|
31
|
+
export interface DryRunPlan<T> {
|
|
32
|
+
dryRun: true;
|
|
33
|
+
builds: T[];
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
export interface ActorConfig {
|
|
28
37
|
actorFullName: string;
|
|
29
38
|
folder: ExistingDir;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ActorConfig, Commit } from './types.js';
|
|
2
|
+
interface AffectedActorsOptions {
|
|
3
|
+
/** End of the range. */
|
|
4
|
+
head: string;
|
|
5
|
+
/** Only the commits on head that are not on base count, i.e. the branch's own changes. */
|
|
6
|
+
base?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Only changes after this commit count. With `base` it's the last validated commit of the branch and is
|
|
9
|
+
* ignored when it isn't on the branch. Without `base` it must be an ancestor of head (e.g. the last release).
|
|
10
|
+
*/
|
|
11
|
+
since?: string;
|
|
12
|
+
/** Glob relative to the repo root. Matching files count as changes even when .dockerignore lists them. */
|
|
13
|
+
testFilesGlob?: string;
|
|
14
|
+
actors: string[];
|
|
15
|
+
ignore: string[];
|
|
16
|
+
}
|
|
17
|
+
interface AffectedActors {
|
|
18
|
+
headSha: string;
|
|
19
|
+
/** Branch head points to, which is what git builds are made from. Null for a SHA or a detached HEAD. */
|
|
20
|
+
ref: string | null;
|
|
21
|
+
commits: Commit[];
|
|
22
|
+
changedFiles: string[];
|
|
23
|
+
changelog: string | null;
|
|
24
|
+
/** Actors with at least one functional change. They need a new build and tests. */
|
|
25
|
+
functionallyChangedActors: ActorConfig[];
|
|
26
|
+
/** Actors whose changes are all cosmetic (README, CHANGELOG, cosmetic .actor JSON). They only need a release build. */
|
|
27
|
+
cosmeticallyChangedActors: ActorConfig[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Finds the Actors whose context changed in the given range. `base` selects the branch's own commits
|
|
31
|
+
* (a PR), `since` the commits after a known one (a release, or the last validated commit of a PR).
|
|
32
|
+
*/
|
|
33
|
+
export declare const getAffectedActors: ({ head, base, since: sinceOption, testFilesGlob, actors, ignore, }: AffectedActorsOptions) => Promise<AffectedActors>;
|
|
34
|
+
/** The JSON document `get-affected-actors` prints. Versioned, since other commands and workflows consume it. */
|
|
35
|
+
export declare const toAffectedActorsDocument: ({ headSha, ref, commits, changedFiles, changelog, functionallyChangedActors, cosmeticallyChangedActors, }: AffectedActors) => {
|
|
36
|
+
version: number;
|
|
37
|
+
ref: string | null;
|
|
38
|
+
head: string;
|
|
39
|
+
commits: Commit[];
|
|
40
|
+
changedFiles: string[];
|
|
41
|
+
changelog: string | null;
|
|
42
|
+
functionallyChangedActors: {
|
|
43
|
+
actorFullName: string;
|
|
44
|
+
}[];
|
|
45
|
+
cosmeticallyChangedActors: {
|
|
46
|
+
actorFullName: string;
|
|
47
|
+
}[];
|
|
48
|
+
};
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=affected-actors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"affected-actors.d.ts","sourceRoot":"","sources":["../../bin/affected-actors.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGtD,UAAU,qBAAqB;IAC3B,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,UAAU,cAAc;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,wGAAwG;IACxG,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mFAAmF;IACnF,yBAAyB,EAAE,WAAW,EAAE,CAAC;IACzC,uHAAuH;IACvH,yBAAyB,EAAE,WAAW,EAAE,CAAC;CAC5C;AA4DD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,oEAOrC,qBAAqB,KAAG,OAAO,CAAC,cAAc,CA4BhD,CAAC;AAEF,gHAAgH;AAChH,eAAO,MAAM,wBAAwB,GAAI,2GAQtC,cAAc;;;;;;;;;;;;;CAYhB,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getChangedActors } from './diff-changes.js';
|
|
2
|
+
import { getBranchOnlyChangedFiles, getChangedFilesBetween, getChangelogAdditions, getCommits, getCommitsBetween, hasMergeFromTarget, resolveBranchName, resolveCommitSha, resolveReleaseBaseCommit, } from './git.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
import { readConfigFile } from './utils/config/load-config.js';
|
|
5
|
+
/** The branch's own commits, after `since` when it is on the branch (see getCommits). */
|
|
6
|
+
const getBranchRange = async (head, base, since) => {
|
|
7
|
+
const commits = await getCommits({ sourceBranch: head, targetBranch: base, baseCommit: since });
|
|
8
|
+
return { commits, fromRef: `${commits[0].sha}~`, toRef: commits[commits.length - 1].sha };
|
|
9
|
+
};
|
|
10
|
+
/** Everything after `since` up to head. Unlike on a branch, `since` must be an ancestor of head. */
|
|
11
|
+
const getRangeSince = async (head, headSha, since) => {
|
|
12
|
+
const sinceSha = await resolveReleaseBaseCommit(since, head);
|
|
13
|
+
const commits = sinceSha === headSha ? [] : await getCommitsBetween(sinceSha, headSha);
|
|
14
|
+
return { commits, fromRef: sinceSha, toRef: headSha };
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* This is an optimization for the common case where a branch only has cosmetic changes but had to merge in
|
|
18
|
+
* functional changes from base (being up-to-date is a CI requirement). Base is already validated, and
|
|
19
|
+
* since the branch has no functional changes of its own, there is nothing new to validate.
|
|
20
|
+
* Exception: if the branch has any functional changes alongside the merge, we must re-test — even
|
|
21
|
+
* individually validated changes can have novel interactions when combined.
|
|
22
|
+
* Returns the Actors the branch's own commits changed, or undefined when it didn't merge base in.
|
|
23
|
+
*
|
|
24
|
+
* TODO: Apply this per Actor instead of to the whole branch. Changes merged in from base only interact with the
|
|
25
|
+
* branch's own changes in Actors whose context both touch, so an Actor that only base changed is already validated
|
|
26
|
+
* and can be skipped even when the branch changes other Actors functionally. Today any functional change of the
|
|
27
|
+
* branch's own keeps every Actor, and the log messages below describe the whole branch.
|
|
28
|
+
*/
|
|
29
|
+
const getOwnChangesIfMergedFromBase = async (head, base, actorConfigs, testFilesGlob) => {
|
|
30
|
+
if (!(await hasMergeFromTarget(head, base)))
|
|
31
|
+
return undefined;
|
|
32
|
+
logger.info('[MERGE-FROM-TARGET-OPTIMIZATION]: There is merge from target branch, checking if there are no functional changes in our own branch. If so, we can skip tests');
|
|
33
|
+
const own = await getChangedActors({
|
|
34
|
+
filepathsChanged: await getBranchOnlyChangedFiles(head, base),
|
|
35
|
+
actorConfigs,
|
|
36
|
+
// Omit since to get full branch history. Validated functional commits can still interact with merged ones
|
|
37
|
+
commits: await getCommits({ sourceBranch: head, targetBranch: base }),
|
|
38
|
+
testFilesGlob,
|
|
39
|
+
});
|
|
40
|
+
logger.info(own.functional.length > 0
|
|
41
|
+
? `[MERGE-FROM-TARGET-OPTIMIZATION]: Branch has ${own.functional.length} functional changes, cannot optimize, we continue with full check`
|
|
42
|
+
: '[MERGE-FROM-TARGET-OPTIMIZATION]: Branch itself has no functional changes, skipping tests');
|
|
43
|
+
return own;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Finds the Actors whose context changed in the given range. `base` selects the branch's own commits
|
|
47
|
+
* (a PR), `since` the commits after a known one (a release, or the last validated commit of a PR).
|
|
48
|
+
*/
|
|
49
|
+
export const getAffectedActors = async ({ head, base, since: sinceOption, testFilesGlob, actors, ignore, }) => {
|
|
50
|
+
// An empty --since (e.g. an empty cache file) means the same as not passing it
|
|
51
|
+
const since = sinceOption || undefined;
|
|
52
|
+
if (!base && !since) {
|
|
53
|
+
throw new Error('Pass --base, --since, or both to select the commits to inspect.');
|
|
54
|
+
}
|
|
55
|
+
const headSha = await resolveCommitSha(head);
|
|
56
|
+
const { commits, fromRef, toRef } = base
|
|
57
|
+
? await getBranchRange(head, base, since)
|
|
58
|
+
: await getRangeSince(head, headSha, since);
|
|
59
|
+
const actorConfigs = await readConfigFile({ actors, ignore });
|
|
60
|
+
// What the branch only merged in from base is already validated. Without functional changes of its own
|
|
61
|
+
// the branch has nothing to test, and without any changes of its own nothing to release either.
|
|
62
|
+
const own = base ? await getOwnChangesIfMergedFromBase(head, base, actorConfigs, testFilesGlob) : undefined;
|
|
63
|
+
const changedFiles = await getChangedFilesBetween(fromRef, toRef);
|
|
64
|
+
const changed = await getChangedActors({ filepathsChanged: changedFiles, actorConfigs, commits, testFilesGlob });
|
|
65
|
+
return {
|
|
66
|
+
headSha,
|
|
67
|
+
ref: await resolveBranchName(head),
|
|
68
|
+
commits,
|
|
69
|
+
changedFiles,
|
|
70
|
+
changelog: await getChangelogAdditions(fromRef, toRef, changedFiles),
|
|
71
|
+
functionallyChangedActors: own && own.functional.length === 0 ? [] : changed.functional,
|
|
72
|
+
cosmeticallyChangedActors: own && own.functional.length + own.cosmetic.length === 0 ? [] : changed.cosmetic,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/** The JSON document `get-affected-actors` prints. Versioned, since other commands and workflows consume it. */
|
|
76
|
+
export const toAffectedActorsDocument = ({ headSha, ref, commits, changedFiles, changelog, functionallyChangedActors, cosmeticallyChangedActors, }) => {
|
|
77
|
+
const names = (configs) => configs.map(({ actorFullName }) => ({ actorFullName }));
|
|
78
|
+
return {
|
|
79
|
+
version: 1,
|
|
80
|
+
ref,
|
|
81
|
+
head: headSha,
|
|
82
|
+
commits,
|
|
83
|
+
changedFiles,
|
|
84
|
+
changelog,
|
|
85
|
+
functionallyChangedActors: names(functionallyChangedActors),
|
|
86
|
+
cosmeticallyChangedActors: names(cosmeticallyChangedActors),
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=affected-actors.js.map
|