apify-test-tools 0.10.2-beta.4 → 0.10.2-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/cli/apify-test-tools.mjs +125 -127
- package/.github/scripts/run-with-apify-tokens.mjs +1 -1
- package/CHANGELOG.md +5 -0
- package/CONTRIBUTING.md +4 -1
- package/README.md +69 -71
- package/bin/affected-actors.ts +132 -91
- package/bin/build.ts +0 -2
- package/bin/diff-changes.ts +7 -6
- package/bin/diff-json-schema.ts +5 -6
- package/bin/git.ts +21 -55
- package/bin/main.ts +7 -97
- package/bin/slack.ts +14 -27
- package/bin/types.ts +6 -0
- package/dist/bin/affected-actors.d.ts +23 -22
- package/dist/bin/affected-actors.d.ts.map +1 -1
- package/dist/bin/affected-actors.js +108 -60
- package/dist/bin/affected-actors.js.map +1 -1
- package/dist/bin/build.d.ts +0 -1
- package/dist/bin/build.d.ts.map +1 -1
- package/dist/bin/build.js +0 -2
- package/dist/bin/build.js.map +1 -1
- package/dist/bin/diff-changes.d.ts +4 -3
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +4 -4
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/diff-json-schema.d.ts +2 -2
- package/dist/bin/diff-json-schema.d.ts.map +1 -1
- package/dist/bin/diff-json-schema.js +1 -4
- package/dist/bin/diff-json-schema.js.map +1 -1
- package/dist/bin/git.d.ts +6 -9
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +20 -48
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +7 -77
- package/dist/bin/main.js.map +1 -1
- package/dist/bin/slack.d.ts +2 -7
- package/dist/bin/slack.d.ts.map +1 -1
- package/dist/bin/slack.js +12 -18
- package/dist/bin/slack.js.map +1 -1
- package/dist/bin/types.d.ts +5 -0
- package/dist/bin/types.d.ts.map +1 -1
- package/dist/test/bundle/cli-bundle.test.js +14 -16
- package/dist/test/bundle/cli-bundle.test.js.map +1 -1
- package/dist/test/e2e/cli-bundle.e2e.test.js +22 -63
- package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -1
- package/dist/test/e2e/git-build-release.e2e.test.d.ts +2 -0
- package/dist/test/e2e/git-build-release.e2e.test.d.ts.map +1 -0
- package/dist/test/e2e/git-build-release.e2e.test.js +87 -0
- package/dist/test/e2e/git-build-release.e2e.test.js.map +1 -0
- package/dist/test/e2e/helpers.d.ts +37 -0
- package/dist/test/e2e/helpers.d.ts.map +1 -0
- package/dist/test/e2e/helpers.js +57 -0
- package/dist/test/e2e/helpers.js.map +1 -0
- package/dist/test/unit/bin/affected-actors-cli.test.d.ts +2 -0
- package/dist/test/unit/bin/affected-actors-cli.test.d.ts.map +1 -0
- package/dist/test/unit/bin/affected-actors-cli.test.js +161 -0
- package/dist/test/unit/bin/affected-actors-cli.test.js.map +1 -0
- package/dist/test/unit/bin/affected-actors.test.js +65 -78
- package/dist/test/unit/bin/affected-actors.test.js.map +1 -1
- package/dist/test/unit/bin/build-cli.test.js +5 -13
- package/dist/test/unit/bin/build-cli.test.js.map +1 -1
- package/dist/test/unit/bin/compiled-cli.d.ts +6 -0
- package/dist/test/unit/bin/compiled-cli.d.ts.map +1 -0
- package/dist/test/unit/bin/compiled-cli.js +16 -0
- package/dist/test/unit/bin/compiled-cli.js.map +1 -0
- package/dist/test/unit/bin/diff-changes.test.js +55 -55
- package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
- package/dist/test/unit/bin/diff.test.js +25 -28
- package/dist/test/unit/bin/diff.test.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +29 -97
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/test/unit/bin/slack.test.js +6 -27
- package/dist/test/unit/bin/slack.test.js.map +1 -1
- package/dist/test/unit/should-built-and-test.test.js +18 -21
- 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/bundle/cli-bundle.test.ts +14 -24
- package/test/e2e/cli-bundle.e2e.test.ts +39 -84
- package/test/e2e/git-build-release.e2e.test.ts +113 -0
- package/test/e2e/helpers.ts +82 -0
- package/test/unit/bin/affected-actors-cli.test.ts +193 -0
- package/test/unit/bin/affected-actors.test.ts +83 -85
- package/test/unit/bin/build-cli.test.ts +6 -12
- package/test/unit/bin/compiled-cli.ts +17 -0
- package/test/unit/bin/diff-changes.test.ts +55 -55
- package/test/unit/bin/diff.test.ts +25 -28
- package/test/unit/bin/git.test.ts +37 -120
- package/test/unit/bin/slack.test.ts +6 -30
- package/test/unit/should-built-and-test.test.ts +19 -22
package/bin/diff-changes.ts
CHANGED
|
@@ -3,13 +3,14 @@ import picomatch from 'picomatch';
|
|
|
3
3
|
import { isCosmeticOnlyJsonSchemaChange } from './diff-json-schema.js';
|
|
4
4
|
import { type DockerIgnoreMatcher, loadDockerIgnore } from './dockerignore.js';
|
|
5
5
|
import { logger } from './logger.js';
|
|
6
|
-
import type { ActorConfig,
|
|
6
|
+
import type { ActorConfig, GitRange } from './types.js';
|
|
7
7
|
import { type RelativeDir, RelativeFile } from './utils/path/repo-relative.js';
|
|
8
8
|
|
|
9
9
|
interface ShouldBuildAndTestOptions {
|
|
10
10
|
filepathsChanged: string[];
|
|
11
11
|
actorConfigs: ActorConfig[];
|
|
12
|
-
|
|
12
|
+
/** The range the files changed in, to compare `.actor` JSON files across. */
|
|
13
|
+
range: GitRange;
|
|
13
14
|
// Glob relative to the repo root. Matching files count as changes even when .dockerignore lists them.
|
|
14
15
|
testFilesGlob?: string;
|
|
15
16
|
}
|
|
@@ -52,7 +53,7 @@ type FileChangeForActor =
|
|
|
52
53
|
const classifyFileChange = async (
|
|
53
54
|
changedFile: RelativeFile,
|
|
54
55
|
actorConfig: ActorConfig,
|
|
55
|
-
|
|
56
|
+
range: GitRange,
|
|
56
57
|
dockerIgnoreMatcher: DockerIgnoreMatcher,
|
|
57
58
|
isTestFile: (filePath: string) => boolean,
|
|
58
59
|
): Promise<FileChangeForActor> => {
|
|
@@ -91,7 +92,7 @@ const classifyFileChange = async (
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
if (lowercaseFilePath.endsWith('.json') && isUnderActorDotDir) {
|
|
94
|
-
const isCosmetic = await isCosmeticOnlyJsonSchemaChange(
|
|
95
|
+
const isCosmetic = await isCosmeticOnlyJsonSchemaChange(range, changedFile.path);
|
|
95
96
|
if (isCosmetic) return { impact: 'cosmetic', semanticallyVerified: true };
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -175,7 +176,7 @@ export interface ChangedActors {
|
|
|
175
176
|
export const getChangedActors = async ({
|
|
176
177
|
filepathsChanged,
|
|
177
178
|
actorConfigs,
|
|
178
|
-
|
|
179
|
+
range,
|
|
179
180
|
testFilesGlob,
|
|
180
181
|
}: ShouldBuildAndTestOptions): Promise<ChangedActors> => {
|
|
181
182
|
const actorsChangedMap = new Map<string, ActorChangeEntry>();
|
|
@@ -192,7 +193,7 @@ export const getChangedActors = async ({
|
|
|
192
193
|
continue;
|
|
193
194
|
}
|
|
194
195
|
|
|
195
|
-
const change = await classifyFileChange(filePath, actorConfig,
|
|
196
|
+
const change = await classifyFileChange(filePath, actorConfig, range, dockerIgnoreMatcher, isTestFile);
|
|
196
197
|
if (change.impact !== 'outside-context' && isTestFile(originalFilePath)) {
|
|
197
198
|
testFilesInContext.add(originalFilePath);
|
|
198
199
|
}
|
package/bin/diff-json-schema.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from './logger.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { GitRange } from './types.js';
|
|
3
3
|
import { runGitCommand } from './utils.js';
|
|
4
4
|
|
|
5
5
|
const COSMETIC_JSON_FIELD_NAMES = new Set([
|
|
@@ -29,11 +29,10 @@ const isCosmeticObjectChange = (oldVal: unknown, newVal: unknown, currentKey?: s
|
|
|
29
29
|
* Returns true if the two JSON strings differ only in cosmetic fields
|
|
30
30
|
* (title, description, example, enumTitles, sectionCaption, sectionDescription).
|
|
31
31
|
*/
|
|
32
|
-
export const isCosmeticOnlyJsonSchemaChange = async (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
export const isCosmeticOnlyJsonSchemaChange = async (
|
|
33
|
+
{ fromRef: oldRef, toRef: newRef }: GitRange,
|
|
34
|
+
changedFilepath: string,
|
|
35
|
+
): Promise<boolean> => {
|
|
37
36
|
const [oldJson, newJson] = await Promise.all([
|
|
38
37
|
runGitCommand(['show', `${oldRef}:${changedFilepath}`])
|
|
39
38
|
.then(JSON.parse)
|
package/bin/git.ts
CHANGED
|
@@ -9,8 +9,8 @@ const GIT_LOG_FORMAT = ['%H', '%aN<%aE>', '%aD', '%s'].join(GIT_FORMAT_SEPARATOR
|
|
|
9
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
|
-
// getCommits never returns an empty list (
|
|
13
|
-
//
|
|
12
|
+
// getCommits never returns an empty list (it throws on an empty range, and the rerun check returns all
|
|
13
|
+
// commits when the base commit is the branch HEAD), so this signals a programmer error
|
|
14
14
|
if (commits.length === 0) {
|
|
15
15
|
throw new Error('Cannot get changed files: the commit list is empty. This should never happen.');
|
|
16
16
|
}
|
|
@@ -35,59 +35,22 @@ export const getChangedFilesBetween = async (fromRef: string, toRef: string) =>
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* The commit `git diff base...head` compares against: where `head` forked from `base`, or the last commit of `base`
|
|
39
|
+
* that `head` merged in. The diff from it holds only what `head` itself changed, like GitHub's "Files changed" tab.
|
|
40
|
+
* With criss-cross merges there are several such commits and git picks one.
|
|
41
41
|
*/
|
|
42
|
-
export const
|
|
43
|
-
|
|
44
|
-
await runGitCommand(['log', '--merges', '--pretty=format:%H', `${targetBranch}..${sourceBranch}`], {
|
|
45
|
-
operation: 'find merge commits on the source branch that are absent from the target branch',
|
|
46
|
-
})
|
|
47
|
-
)
|
|
48
|
-
.split('\n')
|
|
49
|
-
.filter(Boolean);
|
|
42
|
+
export const getMergeBase = async (base: string, head: string): Promise<string> =>
|
|
43
|
+
runGitCommand(['merge-base', base, head], { operation: `find where ${head} forked from ${base}` });
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let mergeBase: string;
|
|
61
|
-
try {
|
|
62
|
-
mergeBase = await runGitCommand(['merge-base', parent, targetBranch], {
|
|
63
|
-
operation: 'check whether a merge parent is reachable from the target branch',
|
|
64
|
-
});
|
|
65
|
-
} catch (error) {
|
|
66
|
-
// Exit code 1 on merge-base means the histories have no common ancestor.
|
|
67
|
-
if (error instanceof GitCommandError && error.exitCode === 1) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
if (mergeBase === parent) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Returns all files touched by non-merge commits on the branch (full history, ignoring baseCommit).
|
|
82
|
-
* Used to check whether the branch itself has any functional changes, independent of what master merged in.
|
|
83
|
-
*/
|
|
84
|
-
export const getBranchOnlyChangedFiles = async (sourceBranch: string, targetBranch: string): Promise<string[]> => {
|
|
85
|
-
const output = await runGitCommand(
|
|
86
|
-
['log', '--no-merges', '--name-only', '--pretty=format:', `${targetBranch}..${sourceBranch}`],
|
|
87
|
-
{ operation: 'list files changed by non-merge commits on the source branch' },
|
|
88
|
-
);
|
|
89
|
-
return output.split('\n').filter(Boolean);
|
|
90
|
-
};
|
|
45
|
+
/** Resolves a ref to a commit, or undefined when it isn't in the local history (e.g. it was force-pushed away). */
|
|
46
|
+
export const findCommitSha = async (ref: string): Promise<string | undefined> =>
|
|
47
|
+
runGitCommand(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], {
|
|
48
|
+
operation: `look up ${ref} in local Git history`,
|
|
49
|
+
}).catch((error: unknown) => {
|
|
50
|
+
// --quiet suppresses Git's error text for a missing commit, but Git still exits with code 1
|
|
51
|
+
if (error instanceof GitCommandError && error.exitCode === 1) return undefined;
|
|
52
|
+
throw error;
|
|
53
|
+
});
|
|
91
54
|
|
|
92
55
|
const SHA_REGEX = /^[0-9a-f]{40}$/i;
|
|
93
56
|
|
|
@@ -120,8 +83,8 @@ export const getCommitsBetween = async (fromRef: string, toRef: string): Promise
|
|
|
120
83
|
const output = await runGitCommand(['log', `--pretty=format:${GIT_LOG_FORMAT}`, `${fromRef}..${toRef}`], {
|
|
121
84
|
operation: 'list commits reachable from the source ref but absent from the target ref',
|
|
122
85
|
});
|
|
123
|
-
|
|
124
|
-
const commits =
|
|
86
|
+
// An empty range (e.g. `x..x`) prints nothing
|
|
87
|
+
const commits = output.split('\n').filter(Boolean).map(parseCommit);
|
|
125
88
|
commits.reverse();
|
|
126
89
|
return commits;
|
|
127
90
|
};
|
|
@@ -137,6 +100,9 @@ export const getCommits = async ({
|
|
|
137
100
|
}: Pick<Config, 'sourceBranch' | 'targetBranch' | 'baseCommit'>): Promise<Commit[]> => {
|
|
138
101
|
const baseCommitSha = parseBaseCommit(baseCommit);
|
|
139
102
|
const commits = await getCommitsBetween(targetBranch, sourceBranch);
|
|
103
|
+
if (commits.length === 0) {
|
|
104
|
+
throw new Error(`${sourceBranch} has no commits that are not on ${targetBranch}.`);
|
|
105
|
+
}
|
|
140
106
|
|
|
141
107
|
// The last validated (base) commit being the branch HEAD means nothing new was pushed since the last
|
|
142
108
|
// validation — the dev reran the workflow (or force-pushed to the same state) to trigger a clean test
|
package/bin/main.ts
CHANGED
|
@@ -6,13 +6,13 @@ 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
|
|
10
|
-
import { buildActors, DEFAULT_VERSION_KEYWORD, deleteOldBuilds
|
|
9
|
+
import { getAffectedActors } from './affected-actors.js';
|
|
10
|
+
import { buildActors, DEFAULT_VERSION_KEYWORD, deleteOldBuilds } from './build.js';
|
|
11
11
|
import { runBuildsFromLocal } from './build-from-local.js';
|
|
12
|
-
import { getChangedFiles, getCommits
|
|
12
|
+
import { getChangedFiles, getCommits } from './git.js';
|
|
13
13
|
import { logger, logLevels } from './logger.js';
|
|
14
14
|
import { writeJson } from './output.js';
|
|
15
|
-
import {
|
|
15
|
+
import { notifyToSlack } from './slack.js';
|
|
16
16
|
import { parseBuildInput, parseReleaseChanges, readStdin } from './stdin-input.js';
|
|
17
17
|
import { reportTestResults } from './test-report.js';
|
|
18
18
|
import { readConfigFile } from './utils/config/load-config.js';
|
|
@@ -101,14 +101,7 @@ const buildOptions = {
|
|
|
101
101
|
},
|
|
102
102
|
} as const satisfies Record<string, Options>;
|
|
103
103
|
|
|
104
|
-
/**
|
|
105
|
-
const buildForTestOptions = {
|
|
106
|
-
'target-branch': { type: 'string', demandOption: true },
|
|
107
|
-
'base-commit': { type: 'string' },
|
|
108
|
-
'test-files-glob': { type: 'string' },
|
|
109
|
-
} as const satisfies Record<string, Options>;
|
|
110
|
-
|
|
111
|
-
/** Where `release` and `notify-release` post the release. */
|
|
104
|
+
/** Where `notify-release` posts the release. */
|
|
112
105
|
const releaseSlackOptions = {
|
|
113
106
|
'report-slack-channel': { type: 'string', describe: 'Channel for the full report: commits and changed files' },
|
|
114
107
|
'release-slack-channel': { type: 'string', describe: 'Channel for the changelog additions only' },
|
|
@@ -172,7 +165,7 @@ await yargs()
|
|
|
172
165
|
'Find the Actors whose context changed in a commit range ending at the checked-out HEAD',
|
|
173
166
|
(y) => y.options(affectedRangeOptions).options(actorSelectionOptions),
|
|
174
167
|
async (args) => {
|
|
175
|
-
writeJson(
|
|
168
|
+
writeJson(await getAffectedActors(args));
|
|
176
169
|
},
|
|
177
170
|
)
|
|
178
171
|
.command(
|
|
@@ -207,96 +200,13 @@ await yargs()
|
|
|
207
200
|
writeJson(builds);
|
|
208
201
|
},
|
|
209
202
|
)
|
|
210
|
-
.command(
|
|
211
|
-
'build-for-test',
|
|
212
|
-
'Build the Actors the checked-out branch changed functionally, for testing',
|
|
213
|
-
(y) =>
|
|
214
|
-
y
|
|
215
|
-
.options(buildForTestOptions)
|
|
216
|
-
.options(actorSelectionOptions)
|
|
217
|
-
.options(repoUrlOptions)
|
|
218
|
-
.options(dryRunOptions),
|
|
219
|
-
async (config) => {
|
|
220
|
-
logger.info(
|
|
221
|
-
'[BUILD-FOR-TEST]: Same as get-affected-actors --base <target-branch> [--since <base-commit>] ' +
|
|
222
|
-
`| jq '{ actors: .functionallyChangedActors }' | build --version-number ${TEST_VERSION_NUMBER}`,
|
|
223
|
-
);
|
|
224
|
-
// Cosmetic-only changes don't need testing, so they don't need a build either
|
|
225
|
-
const { functionallyChangedActors } = await getAffectedActors({
|
|
226
|
-
base: config.targetBranch,
|
|
227
|
-
since: config.baseCommit,
|
|
228
|
-
testFilesGlob: config.testFilesGlob,
|
|
229
|
-
actors: config.actors,
|
|
230
|
-
ignore: config.ignore,
|
|
231
|
-
});
|
|
232
|
-
const builds = await buildActors({
|
|
233
|
-
actorConfigs: functionallyChangedActors,
|
|
234
|
-
versionNumber: TEST_VERSION_NUMBER,
|
|
235
|
-
repoUrl: config.repoUrl,
|
|
236
|
-
dryRun: config.dryRun,
|
|
237
|
-
});
|
|
238
|
-
writeJson(builds);
|
|
239
|
-
},
|
|
240
|
-
)
|
|
241
|
-
.command(
|
|
242
|
-
'release',
|
|
243
|
-
'Build the default version of the Actors changed since the last release and announce it on Slack',
|
|
244
|
-
(y) =>
|
|
245
|
-
y
|
|
246
|
-
.options(actorSelectionOptions)
|
|
247
|
-
.options(repoUrlOptions)
|
|
248
|
-
.options(dryRunOptions)
|
|
249
|
-
// The last commit that is already released, everything after it up to HEAD gets released.
|
|
250
|
-
// Required, see the README section "Releasing Actors" for how to set it for each merge strategy.
|
|
251
|
-
.option('base-commit', { type: 'string', demandOption: true })
|
|
252
|
-
.options(releaseSlackOptions),
|
|
253
|
-
async (args) => {
|
|
254
|
-
logger.info(
|
|
255
|
-
'[RELEASE]: Same as get-affected-actors --since <base-commit> > affected.json, then ' +
|
|
256
|
-
"jq '{ actors: (.functionallyChangedActors + .cosmeticallyChangedActors) }' affected.json " +
|
|
257
|
-
`| build --version-number ${DEFAULT_VERSION_KEYWORD}, then notify-release < affected.json`,
|
|
258
|
-
);
|
|
259
|
-
// Fail before anything else on a detached HEAD: there is no branch to release
|
|
260
|
-
await getCurrentBranch();
|
|
261
|
-
const { commits, changedFiles, changelog, functionallyChangedActors, cosmeticallyChangedActors } =
|
|
262
|
-
await getAffectedActors({
|
|
263
|
-
since: args.baseCommit,
|
|
264
|
-
actors: args.actors,
|
|
265
|
-
ignore: args.ignore,
|
|
266
|
-
});
|
|
267
|
-
if (commits.length === 0) {
|
|
268
|
-
logger.info(`HEAD is the base commit ${args.baseCommit}, there is nothing new to release`);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const { dryRun, reportSlackChannel, releaseSlackChannel } = args;
|
|
273
|
-
const builds = await buildActors({
|
|
274
|
-
// A release ships cosmetic changes (README, input schema descriptions, ...) too
|
|
275
|
-
actorConfigs: [...functionallyChangedActors, ...cosmeticallyChangedActors],
|
|
276
|
-
versionNumber: DEFAULT_VERSION_KEYWORD,
|
|
277
|
-
repoUrl: args.repoUrl,
|
|
278
|
-
dryRun,
|
|
279
|
-
});
|
|
280
|
-
logger.info(JSON.stringify(builds));
|
|
281
|
-
|
|
282
|
-
await announceRelease({
|
|
283
|
-
commits,
|
|
284
|
-
changedFiles,
|
|
285
|
-
changelog,
|
|
286
|
-
repoUrl: args.repoUrl,
|
|
287
|
-
dryRun,
|
|
288
|
-
reportSlackChannel,
|
|
289
|
-
releaseSlackChannel,
|
|
290
|
-
});
|
|
291
|
-
},
|
|
292
|
-
)
|
|
293
203
|
.command(
|
|
294
204
|
'notify-release',
|
|
295
205
|
'Announce a release on Slack, from the get-affected-actors output on stdin',
|
|
296
206
|
(y) => y.options(releaseSlackOptions).options(repoUrlOptions).options(dryRunOptions),
|
|
297
207
|
async (args) => {
|
|
298
208
|
const { commits, changedFiles, changelog } = parseReleaseChanges(await readStdin());
|
|
299
|
-
await
|
|
209
|
+
await notifyToSlack({
|
|
300
210
|
commits,
|
|
301
211
|
changedFiles,
|
|
302
212
|
changelog,
|
package/bin/slack.ts
CHANGED
|
@@ -6,12 +6,12 @@ import type { Commit } from './types.js';
|
|
|
6
6
|
import { getEnvVar } from './utils.js';
|
|
7
7
|
|
|
8
8
|
type NotifyToSlackOptions = {
|
|
9
|
-
repository: string;
|
|
10
9
|
changedFiles: string[];
|
|
11
10
|
changelog: string | null;
|
|
12
11
|
commits: Commit[];
|
|
12
|
+
// Defaults to the `origin` remote, see resolveRepoUrl
|
|
13
|
+
repoUrl?: string;
|
|
13
14
|
dryRun: boolean;
|
|
14
|
-
author: string;
|
|
15
15
|
releaseSlackChannel?: string;
|
|
16
16
|
reportSlackChannel?: string;
|
|
17
17
|
};
|
|
@@ -38,16 +38,23 @@ export const sendingLabel = (dryRun: boolean) => (dryRun ? '[DRY RUN] Would send
|
|
|
38
38
|
// Git authors come as "Name<email>", Slack only needs the name
|
|
39
39
|
const displayName = (author: string) => author.replace(/\s*<[^>]*>$/, '');
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Posts what a release changed: its commits, changed files and changelog, as get-affected-actors reports them.
|
|
43
|
+
* Posts nothing when there are no commits, which means there is nothing new to release.
|
|
44
|
+
*/
|
|
41
45
|
export const notifyToSlack = async ({
|
|
42
46
|
changedFiles,
|
|
43
47
|
commits,
|
|
44
48
|
changelog,
|
|
45
|
-
|
|
49
|
+
repoUrl,
|
|
46
50
|
dryRun,
|
|
47
|
-
author,
|
|
48
51
|
releaseSlackChannel,
|
|
49
52
|
reportSlackChannel,
|
|
50
53
|
}: NotifyToSlackOptions) => {
|
|
54
|
+
if (commits.length === 0) {
|
|
55
|
+
logger.info('No new commits, so there is no release to announce');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
51
58
|
if (!changelog) {
|
|
52
59
|
logger.warn('No new changelog entries found, did you forget to update it?');
|
|
53
60
|
}
|
|
@@ -58,6 +65,9 @@ export const notifyToSlack = async ({
|
|
|
58
65
|
}
|
|
59
66
|
const slack = await getSlackClient('SLACK_TOKEN_RELEASES_BOT', dryRun);
|
|
60
67
|
|
|
68
|
+
const repository = getRepoName((await resolveRepoUrl(repoUrl)).repoUrl);
|
|
69
|
+
// The newest commit, same as the pushed head commit
|
|
70
|
+
const { author } = commits[commits.length - 1];
|
|
61
71
|
let shortMessage = `*${repository}* – New release (by ${displayName(author)}):\n\n`;
|
|
62
72
|
|
|
63
73
|
// This one is just for broader public that only cares about public facing changes
|
|
@@ -97,29 +107,6 @@ export const notifyToSlack = async ({
|
|
|
97
107
|
}
|
|
98
108
|
};
|
|
99
109
|
|
|
100
|
-
type AnnounceReleaseOptions = Omit<NotifyToSlackOptions, 'author' | 'repository'> & {
|
|
101
|
-
// Defaults to the `origin` remote, see resolveRepoUrl
|
|
102
|
-
repoUrl?: string;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Posts what a release changed: its commits, changed files and changelog, as get-affected-actors reports them.
|
|
107
|
-
* Posts nothing when there are no commits, which means there is nothing new to release.
|
|
108
|
-
*/
|
|
109
|
-
export const announceRelease = async ({ commits, repoUrl, ...options }: AnnounceReleaseOptions) => {
|
|
110
|
-
if (commits.length === 0) {
|
|
111
|
-
logger.info('No new commits, so there is no release to announce');
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
await notifyToSlack({
|
|
115
|
-
...options,
|
|
116
|
-
commits,
|
|
117
|
-
repository: getRepoName((await resolveRepoUrl(repoUrl)).repoUrl),
|
|
118
|
-
// The newest commit, same as the pushed head commit
|
|
119
|
-
author: commits[commits.length - 1].author,
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
110
|
export const sendSlackMessage = async (slack: WebClient, channel: string, text: string, blocks: string[]) => {
|
|
124
111
|
const { ts } = await slack.chat.postMessage({ text, channel });
|
|
125
112
|
if (blocks.length > 0 && ts) {
|
package/bin/types.ts
CHANGED
|
@@ -10,6 +10,12 @@ export interface Config {
|
|
|
10
10
|
testFilesGlob?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** The changes from `fromRef` (excluded) to `toRef`, compared as trees like `git diff fromRef toRef`. */
|
|
14
|
+
export interface GitRange {
|
|
15
|
+
fromRef: string;
|
|
16
|
+
toRef: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
export type Commit = {
|
|
14
20
|
sha: string;
|
|
15
21
|
author: string;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Commit } from './types.js';
|
|
2
2
|
interface AffectedActorsOptions {
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Base usually points to the master branch
|
|
5
|
+
* Only what HEAD changes compared to base counts, like `git diff base...HEAD` or GitHub's "Files changed" tab:
|
|
6
|
+
* the branch's own changes, not what it merged in from base.
|
|
7
|
+
*/
|
|
4
8
|
base?: string;
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
7
|
-
* ignored when it isn't
|
|
10
|
+
* With `base` it's the last validated commit of the branch: Actors that didn't change after it are left out.
|
|
11
|
+
* It's ignored when it is HEAD (a rerun) or isn't in the local history. Without `base` everything after it
|
|
12
|
+
* counts, and it must be an ancestor of HEAD (e.g. the last release).
|
|
8
13
|
*/
|
|
9
14
|
since?: string;
|
|
10
15
|
/** Glob relative to the repo root. Matching files count as changes even when .dockerignore lists them. */
|
|
@@ -12,34 +17,30 @@ interface AffectedActorsOptions {
|
|
|
12
17
|
actors: string[];
|
|
13
18
|
ignore: string[];
|
|
14
19
|
}
|
|
15
|
-
interface AffectedActors {
|
|
16
|
-
headSha: string;
|
|
17
|
-
commits: Commit[];
|
|
18
|
-
changedFiles: string[];
|
|
19
|
-
changelog: string | null;
|
|
20
|
-
/** Actors with at least one functional change. They need a new build and tests. */
|
|
21
|
-
functionallyChangedActors: ActorConfig[];
|
|
22
|
-
/** Actors whose changes are all cosmetic (README, CHANGELOG, cosmetic .actor JSON). They only need a release build. */
|
|
23
|
-
cosmeticallyChangedActors: ActorConfig[];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Finds the Actors whose context changed in the given range. `base` selects the branch's own commits
|
|
27
|
-
* (a PR), `since` the commits after a known one (a release, or the last validated commit of a PR).
|
|
28
|
-
*/
|
|
29
|
-
export declare const getAffectedActors: ({ base, since: sinceOption, testFilesGlob, actors, ignore, }: AffectedActorsOptions) => Promise<AffectedActors>;
|
|
30
20
|
/** The JSON document `get-affected-actors` prints. Versioned, since other commands and workflows consume it. */
|
|
31
|
-
|
|
32
|
-
version:
|
|
21
|
+
interface AffectedActors {
|
|
22
|
+
version: 1;
|
|
33
23
|
head: string;
|
|
24
|
+
/** The commits, changed files and changelog describe the whole range, `--since` with `--base` only narrows the Actors. */
|
|
34
25
|
commits: Commit[];
|
|
35
26
|
changedFiles: string[];
|
|
36
27
|
changelog: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Actors with at least one functional change. They need a new build and tests. Only the names: the config file
|
|
30
|
+
* stays the source of truth for the rest.
|
|
31
|
+
*/
|
|
37
32
|
functionallyChangedActors: {
|
|
38
33
|
actorFullName: string;
|
|
39
34
|
}[];
|
|
35
|
+
/** Actors whose changes are all cosmetic (README, CHANGELOG, cosmetic .actor JSON). They only need a release build. */
|
|
40
36
|
cosmeticallyChangedActors: {
|
|
41
37
|
actorFullName: string;
|
|
42
38
|
}[];
|
|
43
|
-
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Finds the Actors whose context changed in the given range. `base` selects the branch's own changes
|
|
42
|
+
* (a PR), `since` the changes after a known commit (a release, or the last validated commit of a PR).
|
|
43
|
+
*/
|
|
44
|
+
export declare const getAffectedActors: ({ base, since: sinceOption, testFilesGlob, actors, ignore, }: AffectedActorsOptions) => Promise<AffectedActors>;
|
|
44
45
|
export {};
|
|
45
46
|
//# sourceMappingURL=affected-actors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affected-actors.d.ts","sourceRoot":"","sources":["../../bin/affected-actors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"affected-actors.d.ts","sourceRoot":"","sources":["../../bin/affected-actors.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAe,MAAM,EAAY,MAAM,YAAY,CAAC;AAMhE,UAAU,qBAAqB;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;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,gHAAgH;AAChH,UAAU,cAAc;IACpB,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,0HAA0H;IAC1H,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;OAGG;IACH,yBAAyB,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,uHAAuH;IACvH,yBAAyB,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAyFD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,8DAMrC,qBAAqB,KAAG,OAAO,CAAC,cAAc,CA+ChD,CAAC"}
|