@tallyrelease/core 0.5.0-beta.21 → 0.5.0-beta.31

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/plan.d.ts CHANGED
@@ -1,111 +1,8 @@
1
- import type { AffectedDoc, AffectedOptions, AffectedReason } from "./affected.js";
2
- import type { Line } from "./branches.js";
3
- import type { PlanFacts } from "./facts.js";
4
- import type { BumpLevel, GitReader, ReadonlyFs, VersionScheme } from "./plugin.js";
1
+ import type { PlanFacts } from "./model/facts-types.js";
2
+ import type { Plan, PlanOptions, ResolveInput } from "./model/plan-types.js";
3
+ import type { GitReader } from "./plugin.js";
5
4
  import type { UnitRelease } from "./release.js";
6
5
  import type { Workspace } from "./workspace.js";
7
- export interface PlanChange {
8
- /** Absent for file-based changes. */
9
- sha?: string;
10
- type: string;
11
- breaking: boolean;
12
- scope?: string;
13
- subject: string;
14
- /** `BREAKING CHANGE` footer text. */
15
- notes?: string;
16
- pr?: number;
17
- /** Absorbed unit the change came through. */
18
- via?: string;
19
- /** Release-As value of this commit for the unit. */
20
- releaseAs?: string;
21
- }
22
- export interface UnitPlan {
23
- id: string;
24
- /** Unit root, `.` for the repo root. */
25
- path: string;
26
- plugin: string;
27
- scheme: string;
28
- tags: string[];
29
- group: string | null;
30
- affected: {
31
- reason: AffectedReason;
32
- via: string[];
33
- } | null;
34
- kind: "prerelease" | "rebuild" | "none";
35
- baseTag: string | null;
36
- lineBaseTag: string | null;
37
- /** Null on a first release. */
38
- from: string | null;
39
- bump: BumpLevel | null;
40
- releaseAs: string | null;
41
- propagatedFrom: string[];
42
- absorbedFrom: string[];
43
- next: string | null;
44
- version: string | null;
45
- /** The planned commit. */
46
- sha: string;
47
- /** Core commit order. */
48
- changes: PlanChange[];
49
- }
50
- export interface GroupPlan {
51
- name: string;
52
- members: string[];
53
- baseTag: string | null;
54
- from: string | null;
55
- bump: BumpLevel | null;
56
- next: string | null;
57
- version: string | null;
58
- }
59
- export interface ReleasedUnit {
60
- id: string;
61
- path: string;
62
- version: string;
63
- tag: string;
64
- floatingTags: string[];
65
- releaseSha: string;
66
- /** Not built from HEAD by this run (release sha differs from HEAD, or the unit is outside the build set). */
67
- catchUp: boolean;
68
- /** Null when not looked up (no forge, or a forge error in `plan`). */
69
- forge: ReleasedForge | null;
70
- line: "default" | "maintenance";
71
- /** Highest stable version tagged for the unit, never on a maintenance line. */
72
- latest: boolean;
73
- }
74
- export interface ReleasedForge {
75
- /** PR that merged the release sha. */
76
- pr: {
77
- number: number;
78
- url: string;
79
- } | null;
80
- /** Forge release of the tag. */
81
- release: {
82
- url: string;
83
- } | null;
84
- }
85
- /** `tallyrelease plan --json`, schema 1. */
86
- export interface Plan extends Omit<AffectedDoc, "units"> {
87
- line: Line | null;
88
- /** Topological order, cycles by id. */
89
- units: UnitPlan[];
90
- /** Sorted by name. */
91
- groups: GroupPlan[];
92
- /** Stable releases in `(build.base, head]`; empty until filled. */
93
- released: ReleasedUnit[];
94
- }
95
- export interface PlanOptions extends AffectedOptions {
96
- /** Files at `head`, for intent source contexts. */
97
- fs: ReadonlyFs;
98
- /** Also list released units outside the build set with a pending release. */
99
- all?: boolean;
100
- debug?: (message: string) => void;
101
- }
102
- /** Inputs of `resolvePlan`, all collected before release math. */
103
- export interface ResolveInput {
104
- affected: AffectedDoc;
105
- facts: PlanFacts;
106
- schemes: ReadonlyMap<string, VersionScheme<any>>;
107
- all: boolean;
108
- }
109
6
  /** Release math over collected facts; no I/O. */
110
7
  export declare const resolvePlan: (workspace: Workspace, input: ResolveInput) => Plan;
111
8
  /** A plan with the facts it was resolved from. */
@@ -122,4 +19,3 @@ export declare const planWithFacts: (workspace: Workspace, git: GitReader, opts:
122
19
  * git and intent facts and resolves versions and kinds.
123
20
  */
124
21
  export declare const computePlan: (workspace: Workspace, git: GitReader, opts: PlanOptions) => Promise<Plan>;
125
- export type { Line as PlanLine } from "./branches.js";
package/dist/plan.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { computeAffected } from "./affected.js";
2
2
  import { checkLimits, matchBranch } from "./branches.js";
3
+ import { changeLevel, reach } from "./bump.js";
3
4
  import { coreCodes } from "./codes.js";
4
5
  import { collectPlanFacts } from "./facts.js";
5
6
  import { propagate } from "./propagate.js";
6
- import { changeLevel, footerOf, reach, releaseAsWarnings, releaseGroup, releaseUnit, unitIntent, } from "./release.js";
7
+ import { releaseAsWarnings } from "./release-as.js";
8
+ import { footerOf, releaseGroup, releaseUnit, unitIntent } from "./release.js";
7
9
  import { noMatchingTag } from "./tags.js";
8
10
  import { maxLevel } from "./util.js";
9
11
  import { validateRelease } from "./validate.js";
@@ -0,0 +1,33 @@
1
+ import type { Warning } from "./errors.js";
2
+ import type { AttributedChange, RangeFacts } from "./model/facts-types.js";
3
+ import type { Unit, VersionScheme } from "./plugin.js";
4
+ import type { From } from "./version-math.js";
5
+ interface CommitValues {
6
+ sha?: string;
7
+ /** Qualified values for the unit, else plain values of an attributed commit. */
8
+ values: string[];
9
+ }
10
+ /** The Release-As footer value that applies to a unit. */
11
+ export interface ReleaseAsWinner<V> {
12
+ readonly sha?: string;
13
+ /** As written in the footer. */
14
+ readonly raw: string;
15
+ readonly version: V;
16
+ }
17
+ /** Release-As values per commit for one unit, in range order. */
18
+ export declare const commitValues: (id: string, changes: readonly AttributedChange[]) => CommitValues[];
19
+ /** Newest valid value wins; invalid values and other values warn. */
20
+ export declare const pickReleaseAs: <V>(input: {
21
+ unit: Unit;
22
+ scheme: VersionScheme<V>;
23
+ from: From<V> | undefined;
24
+ commits: readonly CommitValues[];
25
+ }, warnings: Warning[]) => ReleaseAsWinner<V> | undefined;
26
+ /** Normalized value of the first valid Release-As of a commit. */
27
+ export declare const appliedValue: <V>(scheme: VersionScheme<V>, values: readonly string[]) => string | undefined;
28
+ /**
29
+ * Warns for qualified Release-As values naming unknown or unreleased units and
30
+ * for plain values on commits attributed to no released unit.
31
+ */
32
+ export declare const releaseAsWarnings: (units: readonly Unit[], ranges: readonly RangeFacts[]) => Warning[];
33
+ export {};
@@ -0,0 +1,101 @@
1
+ import { coreCodes } from "./codes.js";
2
+ const at = (sha) => (sha === undefined ? "" : ` in ${sha}`);
3
+ /** A Release-As value without a single leading `v`, parsed by the scheme. */
4
+ const parseReleaseAs = (scheme, raw) => scheme.parse(raw.startsWith("v") ? raw.slice(1) : raw);
5
+ /** Why a parsed Release-As value is ignored, else undefined. */
6
+ const releaseAsProblem = (scheme, version, from) => {
7
+ if (scheme.isPrerelease(version)) {
8
+ return "is a prerelease";
9
+ }
10
+ return from && scheme.compare(version, from.version) <= 0
11
+ ? `does not exceed ${from.label}`
12
+ : undefined;
13
+ };
14
+ /** Release-As values per commit for one unit, in range order. */
15
+ export const commitValues = (id, changes) => {
16
+ const commits = new Map();
17
+ changes.forEach(({ change, units }, i) => {
18
+ const key = change.sha ?? `#${String(i)}`;
19
+ const entry = commits.get(key) ?? {
20
+ ...(change.sha === undefined ? {} : { sha: change.sha }),
21
+ qualified: [],
22
+ plain: [],
23
+ };
24
+ commits.set(key, entry);
25
+ for (const r of change.releaseAs ?? []) {
26
+ if (r.unit === id) {
27
+ entry.qualified.push(r.version);
28
+ }
29
+ else if (r.unit === undefined && units.includes(id)) {
30
+ entry.plain.push(r.version);
31
+ }
32
+ }
33
+ });
34
+ return [...commits.values()].map(({ qualified, plain, ...rest }) => ({
35
+ ...rest,
36
+ values: qualified.length > 0 ? qualified : plain,
37
+ }));
38
+ };
39
+ /** Newest valid value wins; invalid values and other values warn. */
40
+ export const pickReleaseAs = (input, warnings) => {
41
+ const { unit, scheme, from, commits } = input;
42
+ let winner;
43
+ for (const { sha, values } of commits) {
44
+ for (const raw of values) {
45
+ const version = parseReleaseAs(scheme, raw);
46
+ const problem = version === undefined
47
+ ? `is not a ${unit.scheme} version`
48
+ : releaseAsProblem(scheme, version, from);
49
+ if (problem !== undefined || version === undefined) {
50
+ warnings.push(coreCodes.warning("W_RELEASE_AS_INVALID", `unit "${unit.id}": Release-As "${raw}"${at(sha)} ${problem ?? ""}; ignored`, { unit: unit.id }));
51
+ }
52
+ else if (!winner) {
53
+ winner = { ...(sha === undefined ? {} : { sha }), raw, version };
54
+ }
55
+ else if (scheme.compare(version, winner.version) !== 0) {
56
+ warnings.push(coreCodes.warning("W_RELEASE_AS_CONFLICT", `unit "${unit.id}": Release-As "${winner.raw}"${at(winner.sha)} wins over "${raw}"${at(sha)}`, { unit: unit.id }));
57
+ }
58
+ }
59
+ }
60
+ return winner;
61
+ };
62
+ /** Normalized value of the first valid Release-As of a commit. */
63
+ export const appliedValue = (scheme, values) => {
64
+ for (const raw of values) {
65
+ const version = parseReleaseAs(scheme, raw);
66
+ if (version !== undefined && !scheme.isPrerelease(version)) {
67
+ return scheme.format(version);
68
+ }
69
+ }
70
+ return undefined;
71
+ };
72
+ /**
73
+ * Warns for qualified Release-As values naming unknown or unreleased units and
74
+ * for plain values on commits attributed to no released unit.
75
+ */
76
+ export const releaseAsWarnings = (units, ranges) => {
77
+ const known = new Set(units.map((u) => u.id));
78
+ const released = new Set(units.filter((u) => u.version !== "none").map((u) => u.id));
79
+ const warnings = new Map();
80
+ const warn = (code, message, unit) => {
81
+ warnings.set(`${code}\0${message}`, coreCodes.warning(code, message, unit === undefined ? {} : { unit }));
82
+ };
83
+ for (const { changes } of ranges) {
84
+ for (const { change, units: owners } of changes) {
85
+ for (const { unit, version } of change.releaseAs ?? []) {
86
+ if (unit === undefined) {
87
+ if (!owners.some((id) => released.has(id))) {
88
+ warn("W_RELEASE_AS_IGNORED", `Release-As "${version}"${at(change.sha)} is not attributed to a released unit`);
89
+ }
90
+ }
91
+ else if (!known.has(unit)) {
92
+ warn("W_RELEASE_AS_UNKNOWN", `Release-As "${unit}@${version}"${at(change.sha)} names unknown unit "${unit}"`);
93
+ }
94
+ else if (!released.has(unit)) {
95
+ warn("W_RELEASE_AS_IGNORED", `Release-As "${unit}@${version}"${at(change.sha)} names unreleased unit "${unit}"`, unit);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ return [...warnings.values()];
101
+ };
@@ -1,6 +1,6 @@
1
1
  import type { ReleasePrMode } from "./config.js";
2
2
  import type { MarkerRelease } from "./marker.js";
3
- import type { Plan } from "./plan.js";
3
+ import type { Plan } from "./model/plan-types.js";
4
4
  import type { Workspace } from "./workspace.js";
5
5
  /** A group or an ungrouped released unit; releases as one. */
6
6
  export interface ReleaseItem {
@@ -0,0 +1,30 @@
1
+ import type { ReleasePrMode, TallyConfig, VersionTemplate } from "./config.js";
2
+ import type { BranchRule } from "./model/branch-types.js";
3
+ import type { BumpLevel, Unit } from "./plugin.js";
4
+ /** Release config with defaults applied. */
5
+ export interface ReleaseSettings {
6
+ /** Commit type to level. */
7
+ readonly bumps: ReadonlyMap<string, BumpLevel>;
8
+ /** Levels used while `core(from).major == 0`. */
9
+ readonly preMajor: {
10
+ readonly breaking: BumpLevel;
11
+ readonly feat: BumpLevel;
12
+ };
13
+ /** Level cap of absorbed changes with a bump. */
14
+ readonly absorbed: "patch" | "minor";
15
+ /** In config order. */
16
+ readonly branches: readonly BranchRule[];
17
+ readonly releasePr: {
18
+ readonly default: ReleasePrMode;
19
+ /** Set by unit overlays, by unit id. */
20
+ readonly units: ReadonlyMap<string, ReleasePrMode>;
21
+ /** Set by groups, by group name. */
22
+ readonly groups: ReadonlyMap<string, ReleasePrMode>;
23
+ };
24
+ /** Floating tag formats. */
25
+ readonly floating: readonly string[];
26
+ /** Version file templates by unit id. */
27
+ readonly templates: ReadonlyMap<string, readonly VersionTemplate[]>;
28
+ }
29
+ /** Applies the defaults to `bumps` and `branches`. */
30
+ export declare const releaseSettings: (config: TallyConfig, units: readonly Unit[]) => ReleaseSettings;
@@ -0,0 +1,38 @@
1
+ import { normalizeRoot } from "./discover.js";
2
+ const DEFAULT_TYPES = {
3
+ feat: "minor",
4
+ fix: "patch",
5
+ perf: "patch",
6
+ revert: "patch",
7
+ };
8
+ /** Unit overlays of the config with the id of the unit at their root. */
9
+ const unitOverlays = (config, units) => Object.entries(config.units ?? {}).flatMap(([root, overlay]) => {
10
+ const unit = units.find((u) => u.root === normalizeRoot(root));
11
+ return unit ? [[unit.id, overlay]] : [];
12
+ });
13
+ /** Applies the defaults to `bumps` and `branches`. */
14
+ export const releaseSettings = (config, units) => {
15
+ const overlays = unitOverlays(config, units);
16
+ return {
17
+ bumps: new Map(Object.entries({ ...DEFAULT_TYPES, ...config.bumps?.types })),
18
+ absorbed: config.bumps?.absorbed ?? "patch",
19
+ preMajor: {
20
+ breaking: config.bumps?.preMajor?.breaking ?? "minor",
21
+ feat: config.bumps?.preMajor?.feat ?? "minor",
22
+ },
23
+ branches: (config.branches ?? []).map((b) => ({
24
+ name: b.name,
25
+ line: b.line,
26
+ channel: b.channel ?? null,
27
+ unit: b.unit ?? null,
28
+ group: b.group ?? null,
29
+ })),
30
+ releasePr: {
31
+ default: config.defaults?.releasePr ?? "shared",
32
+ units: new Map(overlays.flatMap(([id, overlay]) => overlay.releasePr ? [[id, overlay.releasePr]] : [])),
33
+ groups: new Map(Object.entries(config.groups ?? {}).flatMap(([name, group]) => group.releasePr ? [[name, group.releasePr]] : [])),
34
+ },
35
+ floating: config.tags?.floating ?? [],
36
+ templates: new Map(overlays.flatMap(([id, overlay]) => overlay.versionFiles ? [[id, overlay.versionFiles]] : [])),
37
+ };
38
+ };
package/dist/release.d.ts CHANGED
@@ -1,63 +1,9 @@
1
- import type { ReleasePrMode, TallyConfig, VersionTemplate } from "./config.js";
1
+ import type { UnitChange } from "./bump.js";
2
2
  import type { Warning } from "./errors.js";
3
- import type { GroupFacts, RangeFacts, UnitFacts } from "./facts.js";
4
- import type { BumpLevel, Change, Unit, VersionScheme } from "./plugin.js";
5
- /**
6
- * Level of one change. While the major is 0, breaking changes use
7
- * `preMajor.breaking` and changes whose type level is minor `preMajor.feat`.
8
- * An absorbed change contributes `bumps.absorbed` when its own level is set
9
- * (`minor` caps it at minor).
10
- */
11
- declare const changeLevel: ({ change, via }: UnitChange, settings: ReleaseSettings, preMajor: boolean) => BumpLevel | null;
12
- /** The Release-As footer value that applies to a unit. */
13
- export interface ReleaseAsWinner<V> {
14
- readonly sha?: string;
15
- /** As written in the footer. */
16
- readonly raw: string;
17
- readonly version: V;
18
- }
19
- /** A normalized branch rule. */
20
- export interface BranchRule {
21
- readonly name: string;
22
- readonly line: "default" | "maintenance";
23
- readonly channel: string | null;
24
- /** Release item of the absolute maintenance check. */
25
- readonly unit: string | null;
26
- readonly group: string | null;
27
- }
28
- /** Release config with defaults applied. */
29
- export interface ReleaseSettings {
30
- /** Commit type to level. */
31
- readonly bumps: ReadonlyMap<string, BumpLevel>;
32
- /** Levels used while `core(from).major == 0`. */
33
- readonly preMajor: {
34
- readonly breaking: BumpLevel;
35
- readonly feat: BumpLevel;
36
- };
37
- /** Level cap of absorbed changes with a bump. */
38
- readonly absorbed: "patch" | "minor";
39
- /** In config order. */
40
- readonly branches: readonly BranchRule[];
41
- readonly releasePr: {
42
- readonly default: ReleasePrMode;
43
- /** Set by unit overlays, by unit id. */
44
- readonly units: ReadonlyMap<string, ReleasePrMode>;
45
- /** Set by groups, by group name. */
46
- readonly groups: ReadonlyMap<string, ReleasePrMode>;
47
- };
48
- /** Floating tag formats. */
49
- readonly floating: readonly string[];
50
- /** Version file templates by unit id. */
51
- readonly templates: ReadonlyMap<string, readonly VersionTemplate[]>;
52
- }
53
- /**
54
- * A Change of a released unit; `via` names the first absorbed unit (by id) a
55
- * change not attributed to the unit came through.
56
- */
57
- export interface UnitChange {
58
- readonly change: Change;
59
- readonly via?: string;
60
- }
3
+ import type { GroupFacts, RangeFacts, UnitFacts } from "./model/facts-types.js";
4
+ import type { BumpLevel, Unit, VersionScheme } from "./plugin.js";
5
+ import type { ReleaseAsWinner } from "./release-as.js";
6
+ import type { ReleaseSettings } from "./release-settings.js";
61
7
  /** Bump inputs of a released unit, before propagation. */
62
8
  export interface UnitIntent<V> {
63
9
  /** Own and absorbed level. */
@@ -98,18 +44,6 @@ export interface ReleaseInput<V> {
98
44
  readonly head: string;
99
45
  readonly channel: string | null;
100
46
  }
101
- /**
102
- * Unreleased units reachable over `id`'s dependencies, never stepping into or
103
- * through a released unit.
104
- */
105
- export declare const reach: (id: string, dependencies: ReadonlyMap<string, readonly string[]>, released: ReadonlySet<string>) => Set<string>;
106
- /** Applies the defaults to `bumps` and `branches`. */
107
- export declare const releaseSettings: (config: TallyConfig, units: readonly Unit[]) => ReleaseSettings;
108
- /**
109
- * Warns for qualified Release-As values naming unknown or unreleased units and
110
- * for plain values on commits attributed to no released unit.
111
- */
112
- export declare const releaseAsWarnings: (units: readonly Unit[], ranges: readonly RangeFacts[]) => Warning[];
113
47
  /**
114
48
  * Own and absorbed bump and Release-As of a released unit. Release-As values
115
49
  * are read newest first (core commit order); a single leading `v` is
@@ -141,4 +75,3 @@ export interface GroupMember<V> {
141
75
  * counter, max total.
142
76
  */
143
77
  export declare const releaseGroup: <V>(group: GroupFacts, members: readonly GroupMember<V>[], warnings: Warning[]) => Omit<UnitRelease, "releaseAsFooter">;
144
- export { changeLevel };