@tallyrelease/core 0.5.0-beta.52 → 0.5.0-beta.60
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/dist/builtin.js +2 -1
- package/dist/calver.d.ts +8 -0
- package/dist/calver.js +37 -0
- package/dist/codes.js +1 -1
- package/dist/facts.js +16 -2
- package/dist/flutter.d.ts +14 -0
- package/dist/flutter.js +48 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/model/facts-types.d.ts +2 -0
- package/dist/model/plan-types.d.ts +4 -0
- package/dist/plan.js +4 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/release.d.ts +2 -0
- package/dist/release.js +20 -27
- package/dist/testing/conformance.d.ts +5 -2
- package/dist/testing/conformance.js +45 -25
- package/dist/testing/index.js +1 -0
- package/dist/version-files.d.ts +4 -1
- package/dist/version-files.js +24 -1
- package/dist/version-math.d.ts +4 -1
- package/dist/version-math.js +2 -1
- package/package.json +1 -1
package/dist/builtin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { calver } from "./calver.js";
|
|
1
2
|
import { conventionalCommits } from "./conventional.js";
|
|
2
3
|
import { PLUGIN_API_VERSION } from "./plugin.js";
|
|
3
4
|
import { semver } from "./semver.js";
|
|
@@ -7,6 +8,6 @@ export const CORE_PLUGIN = "core";
|
|
|
7
8
|
export const corePlugin = {
|
|
8
9
|
apiVersion: PLUGIN_API_VERSION,
|
|
9
10
|
name: CORE_PLUGIN,
|
|
10
|
-
schemes: { semver },
|
|
11
|
+
schemes: { semver, calver },
|
|
11
12
|
intent: [conventionalCommits],
|
|
12
13
|
};
|
package/dist/calver.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VersionScheme } from "./plugin.js";
|
|
2
|
+
import type { SemVer } from "./semver.js";
|
|
3
|
+
/**
|
|
4
|
+
* Calendar versions `Y.M.N` with SemVer syntax: every bump increments `N`,
|
|
5
|
+
* releasing moves to `Y.M.0` of the planned commit's UTC date when that
|
|
6
|
+
* month is newer.
|
|
7
|
+
*/
|
|
8
|
+
export declare const calver: VersionScheme<SemVer>;
|
package/dist/calver.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { semver } from "./semver.js";
|
|
2
|
+
/** Year and month of `ctx.date` in UTC. */
|
|
3
|
+
const monthOf = (ctx) => {
|
|
4
|
+
if (ctx.date === undefined) {
|
|
5
|
+
throw new Error("calver needs the commit date; the git reader does not provide commitDate");
|
|
6
|
+
}
|
|
7
|
+
const date = Date.parse(ctx.date);
|
|
8
|
+
if (Number.isNaN(date)) {
|
|
9
|
+
throw new Error(`calver cannot parse the commit date "${ctx.date}"`);
|
|
10
|
+
}
|
|
11
|
+
const utc = new Date(date);
|
|
12
|
+
return { year: utc.getUTCFullYear(), month: utc.getUTCMonth() + 1 };
|
|
13
|
+
};
|
|
14
|
+
/** `next`, or `Y.M.0` of the commit date when that month is newer. */
|
|
15
|
+
const release = (next, ctx) => {
|
|
16
|
+
const { year, month } = monthOf(ctx);
|
|
17
|
+
const newer = year > next.major || (year === next.major && month > next.minor);
|
|
18
|
+
return newer
|
|
19
|
+
? { major: year, minor: month, patch: 0, prerelease: [], build: [] }
|
|
20
|
+
: next;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Calendar versions `Y.M.N` with SemVer syntax: every bump increments `N`,
|
|
24
|
+
* releasing moves to `Y.M.0` of the planned commit's UTC date when that
|
|
25
|
+
* month is newer.
|
|
26
|
+
*/
|
|
27
|
+
export const calver = {
|
|
28
|
+
...semver,
|
|
29
|
+
parse: (raw) => {
|
|
30
|
+
const v = semver.parse(raw);
|
|
31
|
+
return v && v.build.length === 0 ? v : undefined;
|
|
32
|
+
},
|
|
33
|
+
initial: () => ({ major: 0, minor: 0, patch: 0, prerelease: [], build: [] }),
|
|
34
|
+
bump: (v) => semver.bump(v, "patch"),
|
|
35
|
+
release,
|
|
36
|
+
prerelease: (next, ctx) => semver.prerelease(release(next, ctx), ctx),
|
|
37
|
+
};
|
package/dist/codes.js
CHANGED
|
@@ -94,7 +94,7 @@ export const coreCodes = defineCodes({
|
|
|
94
94
|
fix: "Fix the options named in the message, or remove them.",
|
|
95
95
|
},
|
|
96
96
|
E_PLUGIN_UNKNOWN: {
|
|
97
|
-
cause: "`plugins` lists a name that matches no built-in plugin (`node`, `php`, `kustomize`).",
|
|
97
|
+
cause: "`plugins` lists a name that matches no built-in plugin (`node`, `php`, `kustomize`, `dart`).",
|
|
98
98
|
fix: "Correct the plugin name in `plugins`, or remove the entry.",
|
|
99
99
|
},
|
|
100
100
|
E_RELEASE_ITEM_NAME: {
|
package/dist/facts.js
CHANGED
|
@@ -212,7 +212,12 @@ export const rangeFacts = (workspace, git, opts) => {
|
|
|
212
212
|
const changes = [];
|
|
213
213
|
for (const { plugin, source, ctx: sourceCtx } of intents) {
|
|
214
214
|
// eslint-disable-next-line no-await-in-loop -- hooks run sequentially in config order
|
|
215
|
-
const collected = await runHook(plugin, "intent", () => source.collect(sourceCtx, {
|
|
215
|
+
const collected = await runHook(plugin, "intent", () => source.collect(sourceCtx, {
|
|
216
|
+
from,
|
|
217
|
+
to: head,
|
|
218
|
+
commits,
|
|
219
|
+
units: workspace.units,
|
|
220
|
+
}));
|
|
216
221
|
warnUnknown(plugin, collected);
|
|
217
222
|
changes.push(...collected);
|
|
218
223
|
}
|
|
@@ -234,6 +239,7 @@ export const rangeFacts = (workspace, git, opts) => {
|
|
|
234
239
|
/** Runs every `GitReader` and intent source call a plan needs. */
|
|
235
240
|
export const collectPlanFacts = async (workspace, git, opts) => {
|
|
236
241
|
const { head } = opts;
|
|
242
|
+
const date = await git.commitDate?.(head);
|
|
237
243
|
const line = await lineFacts(git, head);
|
|
238
244
|
const { tags, bases, groups, isAncestor, tagNames } = await tagFacts(workspace, git, line);
|
|
239
245
|
const { range, all, warnings } = rangeFacts(workspace, git, opts);
|
|
@@ -278,5 +284,13 @@ export const collectPlanFacts = async (workspace, git, opts) => {
|
|
|
278
284
|
}))),
|
|
279
285
|
});
|
|
280
286
|
}
|
|
281
|
-
return {
|
|
287
|
+
return {
|
|
288
|
+
head,
|
|
289
|
+
...(date === undefined ? {} : { date }),
|
|
290
|
+
units,
|
|
291
|
+
ranges: all(),
|
|
292
|
+
groups,
|
|
293
|
+
tagNames,
|
|
294
|
+
warnings,
|
|
295
|
+
};
|
|
282
296
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { VersionScheme } from "./plugin.js";
|
|
2
|
+
/** A parsed flutter version `x.y.z+build`. */
|
|
3
|
+
export interface FlutterVersion {
|
|
4
|
+
major: number;
|
|
5
|
+
minor: number;
|
|
6
|
+
patch: number;
|
|
7
|
+
build: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Flutter `x.y.z+build` scheme. The build number is the larger of the
|
|
11
|
+
* first-parent total plus the owning plugin's `buildOffset` meta and the
|
|
12
|
+
* highest tagged build plus one.
|
|
13
|
+
*/
|
|
14
|
+
export declare const flutter: VersionScheme<FlutterVersion>;
|
package/dist/flutter.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const RE = /^(\d+)\.(\d+)\.(\d+)\+(\d+)$/u;
|
|
2
|
+
/** A version at the end of a tag name. */
|
|
3
|
+
const TAG_VERSION = /\d+\.\d+\.\d+\+\d+$/u;
|
|
4
|
+
const bumpCore = (v, level) => {
|
|
5
|
+
switch (level) {
|
|
6
|
+
case "major":
|
|
7
|
+
return { ...v, major: v.major + 1, minor: 0, patch: 0 };
|
|
8
|
+
case "minor":
|
|
9
|
+
return { ...v, minor: v.minor + 1, patch: 0 };
|
|
10
|
+
case "patch":
|
|
11
|
+
return { ...v, patch: v.patch + 1 };
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const parse = (raw) => {
|
|
15
|
+
const m = RE.exec(raw);
|
|
16
|
+
return m
|
|
17
|
+
? {
|
|
18
|
+
major: Number(m[1]),
|
|
19
|
+
minor: Number(m[2]),
|
|
20
|
+
patch: Number(m[3]),
|
|
21
|
+
build: Number(m[4]),
|
|
22
|
+
}
|
|
23
|
+
: undefined;
|
|
24
|
+
};
|
|
25
|
+
const buildNo = (ctx) => {
|
|
26
|
+
const tagged = ctx.tags.map((t) => parse(TAG_VERSION.exec(t)?.[0] ?? "")?.build ?? 0);
|
|
27
|
+
const offset = Number(ctx.unit.meta[ctx.unit.plugin]?.["buildOffset"] ?? 0);
|
|
28
|
+
return Math.max(ctx.firstParent.total + offset, Math.max(0, ...tagged) + 1);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Flutter `x.y.z+build` scheme. The build number is the larger of the
|
|
32
|
+
* first-parent total plus the owning plugin's `buildOffset` meta and the
|
|
33
|
+
* highest tagged build plus one.
|
|
34
|
+
*/
|
|
35
|
+
export const flutter = {
|
|
36
|
+
parse,
|
|
37
|
+
format: (v) => `${[v.major, v.minor, v.patch].join(".")}+${String(v.build)}`,
|
|
38
|
+
compare: (a, b) => a.major - b.major ||
|
|
39
|
+
a.minor - b.minor ||
|
|
40
|
+
a.patch - b.patch ||
|
|
41
|
+
a.build - b.build,
|
|
42
|
+
core: ({ major, minor, patch }) => ({ major, minor, patch }),
|
|
43
|
+
isPrerelease: () => false,
|
|
44
|
+
initial: () => ({ major: 0, minor: 1, patch: 0, build: 0 }),
|
|
45
|
+
bump: (v, level) => bumpCore(v, level),
|
|
46
|
+
release: (next, ctx) => ({ ...next, build: buildNo(ctx) }),
|
|
47
|
+
prerelease: (next, ctx) => ({ ...next, build: buildNo(ctx) }),
|
|
48
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -14,10 +14,12 @@ export type { BumpLevel, Change, Ctx, Edge, EdgeDraft, GitReader, IntentSource,
|
|
|
14
14
|
export type { Marker, MarkerRelease } from "./marker.js";
|
|
15
15
|
export type { ReleaseItem } from "./release-items.js";
|
|
16
16
|
export type { ReleaseSettings } from "./release-settings.js";
|
|
17
|
+
export type { FlutterVersion } from "./flutter.js";
|
|
17
18
|
export type { SemVer } from "./semver.js";
|
|
18
19
|
export type { TagFormat } from "./tags.js";
|
|
19
20
|
export type { IntentEntry, Workspace, WorkspaceInput } from "./workspace.js";
|
|
20
21
|
export { computeAffected } from "./affected.js";
|
|
22
|
+
export { calver } from "./calver.js";
|
|
21
23
|
export { maintenanceCut } from "./branches.js";
|
|
22
24
|
export { releaseNotes } from "./changelog.js";
|
|
23
25
|
export { coreCodes, defineCodes } from "./codes.js";
|
|
@@ -25,6 +27,7 @@ export { CONFIG_FILE } from "./config.js";
|
|
|
25
27
|
export { parseCommit } from "./conventional.js";
|
|
26
28
|
export { TallyError } from "./errors.js";
|
|
27
29
|
export { explainUnit } from "./explain.js";
|
|
30
|
+
export { flutter } from "./flutter.js";
|
|
28
31
|
export { definePlugin, defaultUnitId, PLUGIN_API_VERSION } from "./plugin.js";
|
|
29
32
|
export { computePlan } from "./plan.js";
|
|
30
33
|
export { decodeMarker, encodeMarker } from "./marker.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @deprecated Unused, will be removed in 1.0. */
|
|
2
2
|
export const version = "0.0.0";
|
|
3
3
|
export { computeAffected } from "./affected.js";
|
|
4
|
+
export { calver } from "./calver.js";
|
|
4
5
|
export { maintenanceCut } from "./branches.js";
|
|
5
6
|
export { releaseNotes } from "./changelog.js";
|
|
6
7
|
export { coreCodes, defineCodes } from "./codes.js";
|
|
@@ -8,6 +9,7 @@ export { CONFIG_FILE } from "./config.js";
|
|
|
8
9
|
export { parseCommit } from "./conventional.js";
|
|
9
10
|
export { TallyError } from "./errors.js";
|
|
10
11
|
export { explainUnit } from "./explain.js";
|
|
12
|
+
export { flutter } from "./flutter.js";
|
|
11
13
|
export { definePlugin, defaultUnitId, PLUGIN_API_VERSION } from "./plugin.js";
|
|
12
14
|
export { computePlan } from "./plan.js";
|
|
13
15
|
export { decodeMarker, encodeMarker } from "./marker.js";
|
|
@@ -70,6 +70,8 @@ export interface GroupFacts {
|
|
|
70
70
|
/** Git and intent data of every released unit, collected before release math. */
|
|
71
71
|
export interface PlanFacts {
|
|
72
72
|
readonly head: string;
|
|
73
|
+
/** Committer date of `head`; set when the git reader has `commitDate`. */
|
|
74
|
+
readonly date?: string;
|
|
73
75
|
/** Released units, sorted by id. */
|
|
74
76
|
readonly units: readonly UnitFacts[];
|
|
75
77
|
/** One entry per distinct base tag commit. */
|
|
@@ -16,6 +16,10 @@ export interface PlanChange {
|
|
|
16
16
|
via?: string;
|
|
17
17
|
/** Release-As value of this commit for the unit. */
|
|
18
18
|
releaseAs?: string;
|
|
19
|
+
/** Units the change names; set with `files`. */
|
|
20
|
+
units?: string[];
|
|
21
|
+
/** Files consumed once every unit named by the changes with them releases. */
|
|
22
|
+
files?: string[];
|
|
19
23
|
}
|
|
20
24
|
export interface UnitPlan {
|
|
21
25
|
id: string;
|
package/dist/plan.js
CHANGED
|
@@ -36,6 +36,9 @@ const planChanges = (intent) => (intent?.changes ?? []).map(({ change, via }) =>
|
|
|
36
36
|
...(change.pr === undefined ? {} : { pr: change.pr }),
|
|
37
37
|
...(via === undefined ? {} : { via }),
|
|
38
38
|
...(releaseAs === undefined ? {} : { releaseAs }),
|
|
39
|
+
...(change.files === undefined
|
|
40
|
+
? {}
|
|
41
|
+
: { units: [...(change.units ?? [])], files: [...change.files] }),
|
|
39
42
|
};
|
|
40
43
|
});
|
|
41
44
|
/**
|
|
@@ -61,6 +64,7 @@ const releaseInputs = (workspace, { facts, schemes }, channel) => {
|
|
|
61
64
|
range,
|
|
62
65
|
absorbed: reach(unit.id, workspace.graph.dependencies, released),
|
|
63
66
|
head: facts.head,
|
|
67
|
+
...(facts.date === undefined ? {} : { date: facts.date }),
|
|
64
68
|
channel,
|
|
65
69
|
},
|
|
66
70
|
]));
|
package/dist/plugin.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ export interface GitReader {
|
|
|
74
74
|
* `path`; null if none.
|
|
75
75
|
*/
|
|
76
76
|
introduced: (path: string, to: string) => MaybePromise<string | null>;
|
|
77
|
+
/** Committer date of `ref`, ISO 8601. */
|
|
78
|
+
commitDate?: (ref: string) => MaybePromise<string>;
|
|
77
79
|
}
|
|
78
80
|
export interface TagRef {
|
|
79
81
|
name: string;
|
|
@@ -174,6 +176,8 @@ export interface PreCtx {
|
|
|
174
176
|
/** Full sha of the planned commit. */
|
|
175
177
|
head: string;
|
|
176
178
|
baseTag: string | null;
|
|
179
|
+
/** Committer date of `head`, ISO 8601; set when the git reader has `commitDate`. */
|
|
180
|
+
date?: string;
|
|
177
181
|
/** All tags of this unit, reachable or not, scheme-sorted. */
|
|
178
182
|
tags: readonly string[];
|
|
179
183
|
firstParent: {
|
|
@@ -187,6 +191,8 @@ export interface IntentSource<O = unknown> {
|
|
|
187
191
|
from: string | null;
|
|
188
192
|
to: string;
|
|
189
193
|
commits: readonly RawCommit[];
|
|
194
|
+
/** All units of the workspace. */
|
|
195
|
+
units: readonly Unit[];
|
|
190
196
|
}) => MaybePromise<Change[]>;
|
|
191
197
|
}
|
|
192
198
|
/** A `Release-As` value; `unit` is set by the qualified form `<unit>@<version>`. */
|
|
@@ -209,6 +215,11 @@ export interface Change {
|
|
|
209
215
|
releaseAs?: ReleaseAsValue[];
|
|
210
216
|
pr?: number;
|
|
211
217
|
date?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Files consumed when the change is released: deleted in the release
|
|
220
|
+
* commit that releases every unit named by the changes with the file.
|
|
221
|
+
*/
|
|
222
|
+
files?: string[];
|
|
212
223
|
}
|
|
213
224
|
/** Throws `E_PLUGIN_API` or `E_PLUGIN_NAME` for an unsupported plugin. */
|
|
214
225
|
export declare const assertPlugin: (plugin: {
|
package/dist/release.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export interface ReleaseInput<V> {
|
|
|
42
42
|
/** Unreleased units absorbed by the unit (`reach`). */
|
|
43
43
|
readonly absorbed: ReadonlySet<string>;
|
|
44
44
|
readonly head: string;
|
|
45
|
+
/** Committer date of `head`. */
|
|
46
|
+
readonly date?: string;
|
|
45
47
|
readonly channel: string | null;
|
|
46
48
|
}
|
|
47
49
|
/**
|
package/dist/release.js
CHANGED
|
@@ -43,8 +43,12 @@ export const releaseUnit = (input, intent, warnings) => {
|
|
|
43
43
|
const from = fromOf(scheme, facts);
|
|
44
44
|
const initial = initialOf(unit, scheme);
|
|
45
45
|
const bumped = bumpedOf(scheme, bump, { from, initial });
|
|
46
|
+
const ctx = ctxOf(unit, input, {
|
|
47
|
+
...facts,
|
|
48
|
+
tags: facts.tags.map((t) => t.name),
|
|
49
|
+
});
|
|
46
50
|
if (from === undefined && releaseAs === undefined && bumped !== undefined) {
|
|
47
|
-
warnings.push(...initialBelowPrerelease(scheme, initial, facts));
|
|
51
|
+
warnings.push(...initialBelowPrerelease(scheme, scheme.release(initial, ctx), facts));
|
|
48
52
|
}
|
|
49
53
|
const raw = releaseAs ?? bumped;
|
|
50
54
|
return {
|
|
@@ -59,15 +63,7 @@ export const releaseUnit = (input, intent, warnings) => {
|
|
|
59
63
|
: from === undefined
|
|
60
64
|
? "initial"
|
|
61
65
|
: "bump",
|
|
62
|
-
...render({
|
|
63
|
-
scheme,
|
|
64
|
-
raw,
|
|
65
|
-
ctx: ctxOf(unit, input.head, {
|
|
66
|
-
...facts,
|
|
67
|
-
tags: facts.tags.map((t) => t.name),
|
|
68
|
-
}),
|
|
69
|
-
channel: input.channel,
|
|
70
|
-
}),
|
|
66
|
+
...render({ scheme, raw, ctx, channel: input.channel }),
|
|
71
67
|
};
|
|
72
68
|
};
|
|
73
69
|
/**
|
|
@@ -82,18 +78,13 @@ export const releaseGroup = (group, members, warnings) => {
|
|
|
82
78
|
if (!first) {
|
|
83
79
|
throw new TypeError("a group has at least one member");
|
|
84
80
|
}
|
|
85
|
-
const { scheme, facts, channel
|
|
81
|
+
const { scheme, facts, channel } = first.input;
|
|
86
82
|
const max = (a, b) => a === undefined || (b !== undefined && scheme.compare(b, a) > 0) ? b : a;
|
|
87
83
|
const from = fromOf(scheme, facts);
|
|
88
84
|
const bump = members.reduce((level, m) => maxLevel(level, m.bump), null);
|
|
89
85
|
const releaseAs = members.reduce((value, m) => max(value, m.releaseAs?.version), undefined);
|
|
90
86
|
const initial = members.reduce((value, m) => max(value, initialOf(m.input.unit, scheme)) ?? value, initialOf(first.input.unit, scheme));
|
|
91
87
|
const bumped = bumpedOf(scheme, bump, { from, initial });
|
|
92
|
-
if (from === undefined && releaseAs === undefined && bumped !== undefined) {
|
|
93
|
-
for (const m of members) {
|
|
94
|
-
warnings.push(...initialBelowPrerelease(scheme, initial, m.input.facts));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
88
|
const tags = members
|
|
98
89
|
.flatMap((m) => m.input.facts.tags)
|
|
99
90
|
.flatMap((t) => {
|
|
@@ -102,6 +93,18 @@ export const releaseGroup = (group, members, warnings) => {
|
|
|
102
93
|
})
|
|
103
94
|
.sort((a, b) => scheme.compare(a.version, b.version) || compare(a.name, b.name))
|
|
104
95
|
.map((t) => t.name);
|
|
96
|
+
const ctx = ctxOf(first.input.unit, first.input, {
|
|
97
|
+
baseTag: group.baseTag,
|
|
98
|
+
tags,
|
|
99
|
+
sinceLineBase: group.sinceLineBase,
|
|
100
|
+
total: Math.max(...members.map((m) => m.input.facts.total)),
|
|
101
|
+
});
|
|
102
|
+
if (from === undefined && releaseAs === undefined && bumped !== undefined) {
|
|
103
|
+
const released = scheme.release(initial, ctx);
|
|
104
|
+
for (const m of members) {
|
|
105
|
+
warnings.push(...initialBelowPrerelease(scheme, released, m.input.facts));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
105
108
|
const raw = max(bumped, releaseAs);
|
|
106
109
|
const byReleaseAs = releaseAs !== undefined &&
|
|
107
110
|
(bumped === undefined || scheme.compare(releaseAs, bumped) > 0);
|
|
@@ -116,16 +119,6 @@ export const releaseGroup = (group, members, warnings) => {
|
|
|
116
119
|
: from === undefined
|
|
117
120
|
? "initial"
|
|
118
121
|
: "bump",
|
|
119
|
-
...render({
|
|
120
|
-
scheme,
|
|
121
|
-
raw,
|
|
122
|
-
ctx: ctxOf(first.input.unit, head, {
|
|
123
|
-
baseTag: group.baseTag,
|
|
124
|
-
tags,
|
|
125
|
-
sinceLineBase: group.sinceLineBase,
|
|
126
|
-
total: Math.max(...members.map((m) => m.input.facts.total)),
|
|
127
|
-
}),
|
|
128
|
-
channel,
|
|
129
|
-
}),
|
|
122
|
+
...render({ scheme, raw, ctx, channel }),
|
|
130
123
|
};
|
|
131
124
|
};
|
|
@@ -16,6 +16,9 @@ export declare const gitReaderConformance: (makeReader: ReaderFactory) => void;
|
|
|
16
16
|
/**
|
|
17
17
|
* Shared `VersionScheme` spec. Versions are derived from `initial()`:
|
|
18
18
|
* `parse`/`format` round-trip, `compare` order, `bump` monotonicity per level,
|
|
19
|
-
* and `release`/`prerelease` keeping the core triple.
|
|
19
|
+
* and `release`/`prerelease` keeping the core triple. With `dateDriven` the
|
|
20
|
+
* level order, the core reset and the core-keeping checks are skipped.
|
|
20
21
|
*/
|
|
21
|
-
export declare const schemeConformance: <V>(scheme: VersionScheme<V
|
|
22
|
+
export declare const schemeConformance: <V>(scheme: VersionScheme<V>, { dateDriven }?: {
|
|
23
|
+
dateDriven?: boolean;
|
|
24
|
+
}) => void;
|
|
@@ -133,6 +133,18 @@ export const gitReaderConformance = (makeReader) => {
|
|
|
133
133
|
expect(Date.parse(byId("a")?.date ?? "")).toBe(Date.parse("2024-01-01T10:00:00Z"));
|
|
134
134
|
expect(Date.parse(byId("e")?.date ?? "")).toBe(Date.parse("2024-01-02T12:30:00Z"));
|
|
135
135
|
});
|
|
136
|
+
it("reads the committer date of a commit when supported", async () => {
|
|
137
|
+
if (git.commitDate === undefined) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const commits = await git.log(null, sha("e"));
|
|
141
|
+
for (const id of ["a", "e"]) {
|
|
142
|
+
// eslint-disable-next-line no-await-in-loop -- one commit at a time
|
|
143
|
+
const date = await git.commitDate(sha(id));
|
|
144
|
+
expect(date).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/u);
|
|
145
|
+
expect(date).toBe(commits.find((c) => c.sha === sha(id))?.date);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
136
148
|
it("lists every commit before its parents", async () => {
|
|
137
149
|
const commits = await git.log(null, sha("e"));
|
|
138
150
|
const index = new Map(commits.map((c, i) => [c.sha, i]));
|
|
@@ -219,6 +231,7 @@ const PRE_CTX = {
|
|
|
219
231
|
meta: {},
|
|
220
232
|
},
|
|
221
233
|
head: "0".repeat(40),
|
|
234
|
+
date: "2024-01-01T00:00:00Z",
|
|
222
235
|
baseTag: null,
|
|
223
236
|
tags: [],
|
|
224
237
|
firstParent: { sinceLineBase: 3, total: 7 },
|
|
@@ -227,9 +240,10 @@ const PRE_CTX = {
|
|
|
227
240
|
/**
|
|
228
241
|
* Shared `VersionScheme` spec. Versions are derived from `initial()`:
|
|
229
242
|
* `parse`/`format` round-trip, `compare` order, `bump` monotonicity per level,
|
|
230
|
-
* and `release`/`prerelease` keeping the core triple.
|
|
243
|
+
* and `release`/`prerelease` keeping the core triple. With `dateDriven` the
|
|
244
|
+
* level order, the core reset and the core-keeping checks are skipped.
|
|
231
245
|
*/
|
|
232
|
-
export const schemeConformance = (scheme) => {
|
|
246
|
+
export const schemeConformance = (scheme, { dateDriven = false } = {}) => {
|
|
233
247
|
describe("VersionScheme conformance", () => {
|
|
234
248
|
const initial = scheme.initial();
|
|
235
249
|
const bumped = (level) => scheme.bump(initial, level);
|
|
@@ -258,35 +272,41 @@ export const schemeConformance = (scheme) => {
|
|
|
258
272
|
expect(scheme.compare(next, initial)).toBeGreaterThan(0);
|
|
259
273
|
expect(scheme.isPrerelease(next)).toBe(false);
|
|
260
274
|
});
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const { major, minor, patch } = scheme.core(initial);
|
|
268
|
-
expect(scheme.core(bumped("major"))).toEqual({
|
|
269
|
-
major: major + 1,
|
|
270
|
-
minor: 0,
|
|
271
|
-
patch: 0,
|
|
275
|
+
// A date-driven scheme takes the core from the commit date.
|
|
276
|
+
if (!dateDriven) {
|
|
277
|
+
it("orders bump levels major > minor > patch", () => {
|
|
278
|
+
const [major, minor, patch] = LEVELS.map(bumped);
|
|
279
|
+
expect(scheme.compare(major, minor)).toBeGreaterThan(0);
|
|
280
|
+
expect(scheme.compare(minor, patch)).toBeGreaterThan(0);
|
|
272
281
|
});
|
|
273
|
-
|
|
274
|
-
major,
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
it("resets lower core parts on a bump", () => {
|
|
283
|
+
const { major, minor, patch } = scheme.core(initial);
|
|
284
|
+
expect(scheme.core(bumped("major"))).toEqual({
|
|
285
|
+
major: major + 1,
|
|
286
|
+
minor: 0,
|
|
287
|
+
patch: 0,
|
|
288
|
+
});
|
|
289
|
+
expect(scheme.core(bumped("minor"))).toEqual({
|
|
290
|
+
major,
|
|
291
|
+
minor: minor + 1,
|
|
292
|
+
patch: 0,
|
|
293
|
+
});
|
|
294
|
+
expect(scheme.core(bumped("patch"))).toEqual({
|
|
295
|
+
major,
|
|
296
|
+
minor,
|
|
297
|
+
patch: patch + 1,
|
|
298
|
+
});
|
|
277
299
|
});
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
300
|
+
it("keeps the core when releasing or prereleasing", () => {
|
|
301
|
+
const next = bumped("minor");
|
|
302
|
+
expect(scheme.core(scheme.release(next, PRE_CTX))).toEqual(scheme.core(next));
|
|
303
|
+
expect(scheme.core(scheme.prerelease(next, PRE_CTX))).toEqual(scheme.core(next));
|
|
282
304
|
});
|
|
283
|
-
}
|
|
284
|
-
it("
|
|
305
|
+
}
|
|
306
|
+
it("orders a prerelease at or below its release", () => {
|
|
285
307
|
const next = bumped("minor");
|
|
286
308
|
const release = scheme.release(next, PRE_CTX);
|
|
287
309
|
const pre = scheme.prerelease(next, PRE_CTX);
|
|
288
|
-
expect(scheme.core(release)).toEqual(scheme.core(next));
|
|
289
|
-
expect(scheme.core(pre)).toEqual(scheme.core(next));
|
|
290
310
|
expect(scheme.isPrerelease(release)).toBe(false);
|
|
291
311
|
expect(scheme.compare(pre, release)).toBeLessThanOrEqual(0);
|
|
292
312
|
expect(scheme.parse(scheme.format(pre))).toBeDefined();
|
package/dist/testing/index.js
CHANGED
|
@@ -137,6 +137,7 @@ export const memoryGit = (dag) => {
|
|
|
137
137
|
.find((n) => changed(n.parents[0] === undefined ? new Map() : node(n.parents[0]).tree, n.tree).some(under));
|
|
138
138
|
return intro?.id ?? null;
|
|
139
139
|
},
|
|
140
|
+
commitDate: (ref) => node(ref).date,
|
|
140
141
|
tree: (ref) => Object.fromEntries(node(ref).tree),
|
|
141
142
|
parents: (ref) => [...node(ref).parents],
|
|
142
143
|
};
|
package/dist/version-files.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export declare const versionFileClaims: (workspace: Workspace) => Promise<Readon
|
|
|
15
15
|
* Files of a release of `items` (stable `next`): plugin version files,
|
|
16
16
|
* templates, markers and `<root>/CHANGELOG.md` per unit, read through `fs`; the
|
|
17
17
|
* owning plugin's `changelog` hook may replace a unit's section. Returns
|
|
18
|
-
* only files whose content changes,
|
|
18
|
+
* only files whose content changes, the consumed files of changes whose
|
|
19
|
+
* named units all release in `items`, and each unit's changelog section;
|
|
19
20
|
* checks claims first. Templates skip first releases and warn
|
|
20
21
|
* `W_VERSION_FILE_NO_MATCH` without any match, markers warn
|
|
21
22
|
* `W_MARKER_NO_MATCH`; a missing version file is
|
|
@@ -30,6 +31,8 @@ export declare const releaseFiles: (workspace: Workspace, plan: Plan, opts: {
|
|
|
30
31
|
date: string;
|
|
31
32
|
}) => Promise<{
|
|
32
33
|
files: Map<string, string>;
|
|
34
|
+
/** Files consumed by released changes, deleted by the release. */
|
|
35
|
+
deletes: Set<string>;
|
|
33
36
|
/** Changelog section of each unit, by unit id. */
|
|
34
37
|
sections: Map<string, string>;
|
|
35
38
|
warnings: Warning[];
|
package/dist/version-files.js
CHANGED
|
@@ -259,6 +259,24 @@ const changelogStep = async (staged, step) => {
|
|
|
259
259
|
}
|
|
260
260
|
return section;
|
|
261
261
|
};
|
|
262
|
+
/**
|
|
263
|
+
* Readable files of the plan's changes where every unit named by a change
|
|
264
|
+
* with the file is in `released`; `written` files are kept.
|
|
265
|
+
*/
|
|
266
|
+
const consumedFiles = (plan, released, { fs, written }) => {
|
|
267
|
+
const named = new Map();
|
|
268
|
+
for (const { files = [], units = [] } of plan.units.flatMap((u) => u.changes)) {
|
|
269
|
+
for (const path of files) {
|
|
270
|
+
named.set(path, [...(named.get(path) ?? []), ...units]);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return new Set([...named]
|
|
274
|
+
.filter(([path, units]) => units.length > 0 &&
|
|
275
|
+
units.every((id) => released.has(id)) &&
|
|
276
|
+
!written.has(path) &&
|
|
277
|
+
fs.read(path) !== undefined)
|
|
278
|
+
.map(([path]) => path));
|
|
279
|
+
};
|
|
262
280
|
/** Checks template files and patterns (`E_CONFIG`); runs no plugin hook. */
|
|
263
281
|
export const validateTemplates = (workspace) => {
|
|
264
282
|
for (const [id, templates] of workspace.release.templates) {
|
|
@@ -310,7 +328,8 @@ export const versionFileClaims = async (workspace) => {
|
|
|
310
328
|
* Files of a release of `items` (stable `next`): plugin version files,
|
|
311
329
|
* templates, markers and `<root>/CHANGELOG.md` per unit, read through `fs`; the
|
|
312
330
|
* owning plugin's `changelog` hook may replace a unit's section. Returns
|
|
313
|
-
* only files whose content changes,
|
|
331
|
+
* only files whose content changes, the consumed files of changes whose
|
|
332
|
+
* named units all release in `items`, and each unit's changelog section;
|
|
314
333
|
* checks claims first. Templates skip first releases and warn
|
|
315
334
|
* `W_VERSION_FILE_NO_MATCH` without any match, markers warn
|
|
316
335
|
* `W_MARKER_NO_MATCH`; a missing version file is
|
|
@@ -371,6 +390,10 @@ export const releaseFiles = async (workspace, plan, opts) => {
|
|
|
371
390
|
}
|
|
372
391
|
return {
|
|
373
392
|
files: new Map([...files].filter(([path, c]) => fs.read(path) !== c)),
|
|
393
|
+
deletes: consumedFiles(plan, new Set(versions.keys()), {
|
|
394
|
+
fs,
|
|
395
|
+
written: files,
|
|
396
|
+
}),
|
|
374
397
|
sections,
|
|
375
398
|
warnings,
|
|
376
399
|
};
|
package/dist/version-math.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export declare const bumpedOf: <V>(scheme: VersionScheme<V>, bump: BumpLevel | n
|
|
|
14
14
|
from: From<V> | undefined;
|
|
15
15
|
initial: V;
|
|
16
16
|
}) => V | undefined;
|
|
17
|
-
export declare const ctxOf: (unit: Unit, head
|
|
17
|
+
export declare const ctxOf: (unit: Unit, { head, date }: {
|
|
18
|
+
head: string;
|
|
19
|
+
date?: string;
|
|
20
|
+
}, at: {
|
|
18
21
|
baseTag: {
|
|
19
22
|
name: string;
|
|
20
23
|
} | null;
|
package/dist/version-math.js
CHANGED
|
@@ -21,9 +21,10 @@ export const bumpedOf = (scheme, bump, { from, initial }) => bump === null
|
|
|
21
21
|
: from === undefined
|
|
22
22
|
? initial
|
|
23
23
|
: scheme.bump(from.version, bump);
|
|
24
|
-
export const ctxOf = (unit, head, at) => ({
|
|
24
|
+
export const ctxOf = (unit, { head, date }, at) => ({
|
|
25
25
|
unit,
|
|
26
26
|
head,
|
|
27
|
+
...(date === undefined ? {} : { date }),
|
|
27
28
|
baseTag: at.baseTag?.name ?? null,
|
|
28
29
|
tags: at.tags,
|
|
29
30
|
firstParent: { sinceLineBase: at.sinceLineBase, total: at.total },
|