rman 2.10.1 → 2.11.1

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.
Files changed (37) hide show
  1. package/builtins/platforms/node/services/ci.service.d.ts +1 -1
  2. package/builtins/platforms/node/services/ci.service.js +1 -1
  3. package/builtins/platforms/node/services/clean.service.d.ts +1 -1
  4. package/builtins/platforms/node/services/clean.service.js +1 -1
  5. package/builtins/platforms/node/services/publish.service.d.ts +10 -5
  6. package/builtins/platforms/node/services/publish.service.js +46 -11
  7. package/builtins/publish-targets/npm/npm-publish-target.js +3 -1
  8. package/cli.js +81 -23
  9. package/commands/changelog.command.js +1 -1
  10. package/commands/version.command.js +42 -29
  11. package/constants.js +1 -1
  12. package/core/application.d.ts +8 -1
  13. package/core/application.js +7 -1
  14. package/core/classes/log-sink.d.ts +99 -0
  15. package/core/classes/log-sink.js +101 -0
  16. package/{utils → core/classes}/logger.d.ts +1 -1
  17. package/core/classes/run-reporters.d.ts +64 -0
  18. package/core/classes/run-reporters.js +135 -0
  19. package/core/classes/service.d.ts +1 -1
  20. package/core/command-builder.js +3 -0
  21. package/core/interfaces/custom-command.d.ts +5 -1
  22. package/core/interfaces/run-step.d.ts +5 -5
  23. package/index.d.ts +2 -2
  24. package/index.js +1 -1
  25. package/package.json +1 -1
  26. package/services/exec.service.d.ts +1 -1
  27. package/services/exec.service.js +1 -1
  28. package/services/run.service.d.ts +22 -3
  29. package/services/run.service.js +190 -125
  30. package/services/version-plan.service.js +3 -3
  31. package/utils/progress-panel.d.ts +6 -0
  32. package/utils/progress-panel.js +22 -7
  33. package/utils/run-bin.d.ts +10 -2
  34. package/utils/run-bin.js +57 -11
  35. package/utils/version-group.d.ts +9 -0
  36. package/utils/version-group.js +11 -0
  37. /package/{utils → core/classes}/logger.js +0 -0
package/utils/run-bin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import colors from 'ansi-colors';
3
+ import { LOG_LEVELS } from '../core/classes/logger.js';
3
4
  import { BinPath } from './bin-path.js';
4
5
  import { trackChild } from './child-tracker.js';
5
- import { LOG_LEVELS } from './logger.js';
6
6
  /**
7
7
  * Runs one of the repository's locally installed binaries, passing **argv as an array** - no shell.
8
8
  *
@@ -62,7 +62,9 @@ export async function runBin(bin, argv, options = {}) {
62
62
  * while a status region is live had it, which since 2.3.0 is every one of them. */
63
63
  env: BinPath.env({
64
64
  cwd,
65
- env: region && atLeast('info') ? { FORCE_COLOR: '1', ...(options.env ?? process.env) } : options.env,
65
+ env: forceColor(region, options, atLeast('info'))
66
+ ? { FORCE_COLOR: '1', ...(options.env ?? process.env) }
67
+ : options.env,
66
68
  app: options.app,
67
69
  }),
68
70
  windowsHide: true,
@@ -71,31 +73,46 @@ export async function runBin(bin, argv, options = {}) {
71
73
  * which meant a plugin command's child outlived a Ctrl-C. See `trackChild`. */
72
74
  trackChild(child);
73
75
  let output = '';
76
+ /**
77
+ * **Split into lines per stream, holding the unfinished tail until the rest arrives** - the way
78
+ * `exec` already does. A `data` event ends wherever the pipe's buffer did, not at a newline, so
79
+ * splitting each chunk on its own cut a line arriving in two pieces into two lines. Harmless while
80
+ * the only reader was a panel row showing the last line; wrong once a caller prints every line, as
81
+ * the panel-off `run` does, where it put a newline in the middle of whatever the child wrote.
82
+ */
83
+ const pending = { stdout: '', stderr: '' };
84
+ const emit = (stream, text, flush) => {
85
+ const lines = (pending[stream] + text).split(/\r?\n/);
86
+ pending[stream] = flush ? '' : lines.pop();
87
+ for (const line of lines)
88
+ if (line)
89
+ options.onLine(line, stream);
90
+ };
74
91
  /** **Streamed through the region as it arrives, not held to the end.** Piping is how the region
75
92
  * stays intact; buffering would additionally make a long command look silent, which is the very
76
93
  * thing the region exists to fix. Only when a region is live - otherwise `pipe` keeps meaning
77
94
  * "capture, and surface it if this fails". */
78
- const collect = (d) => {
95
+ const collect = (stream) => (d) => {
79
96
  const text = d.toString();
80
97
  output += text;
81
98
  /** A caller taking the lines owns them - it is showing them somewhere of its own, and writing
82
99
  * them here as well would both double them and scroll whatever it is drawing. */
83
- if (options.onLine) {
84
- for (const line of text.split(/\r?\n/))
85
- if (line)
86
- options.onLine(line);
87
- return;
88
- }
100
+ if (options.onLine)
101
+ return emit(stream, text, false);
89
102
  if (region && atLeast('info'))
90
103
  region.passThrough(text);
91
104
  };
92
- child.stdout?.on('data', collect);
93
- child.stderr?.on('data', collect);
105
+ child.stdout?.on('data', collect('stdout'));
106
+ child.stderr?.on('data', collect('stderr'));
94
107
  return new Promise((resolve, reject) => {
95
108
  child.on('error', (e) => {
96
109
  reject(e?.code === 'ENOENT' ? new Error(`"${bin}" was not found - is it installed in this repository?`) : e);
97
110
  });
98
111
  child.on('close', code => {
112
+ /** A last line with no newline after it is still a line. */
113
+ if (options.onLine)
114
+ for (const stream of ['stdout', 'stderr'])
115
+ emit(stream, '', true);
99
116
  if (code === 0)
100
117
  return resolve({ code: 0, output });
101
118
  /** Captured output has to be surfaced here or it is lost with the process - the one thing
@@ -111,3 +128,32 @@ export async function runBin(bin, argv, options = {}) {
111
128
  });
112
129
  });
113
130
  }
131
+ /**
132
+ * Whether a piped child is told it may colour anyway.
133
+ *
134
+ * A child checks `isTTY` to decide whether to colour, and a pipe is not one - so a child whose output
135
+ * is shown rather than read loses the colour it had with the terminal. Two cases show it: a live
136
+ * region routing it through `passThrough`, and a caller taking the lines to print (`onLine`) while
137
+ * our own output really is a terminal. Not when stdout is redirected, where escapes would land in a
138
+ * file, and not under `NO_COLOR`, which is the user saying no.
139
+ *
140
+ * **`FORCE_COLOR` turns on colour and nothing else.** A tool's live output - a spinner, a progress
141
+ * bar - is gated on `isTTY`, which stays false, so a child piped this way prints plain lines in
142
+ * colour: exactly what a run with no progress panel is after.
143
+ */
144
+ function forceColor(region, options, info) {
145
+ if (!info)
146
+ return false;
147
+ if (region)
148
+ return true;
149
+ return !!options.onLine && colorsPrintedOutput();
150
+ }
151
+ /**
152
+ * Whether a child whose lines rman prints itself should be told to colour them: our own stdout is a
153
+ * terminal, and `NO_COLOR` is not set. The one rule for both kinds of step - `runBin`'s, above, and a
154
+ * shell step `exec` runs with its lines printed by `RunService` - so the two cannot disagree about
155
+ * when a piped child gets its colour back.
156
+ */
157
+ export function colorsPrintedOutput() {
158
+ return !!process.stdout.isTTY && !process.env.NO_COLOR;
159
+ }
@@ -26,6 +26,15 @@ export declare function groupKeyOf(pkg: Package): string;
26
26
  * opted out of grouping has no group to name.
27
27
  */
28
28
  export declare function isSoloGroupKey(key: string): boolean;
29
+ /** Whether a group key is one the repository **named** (`group: "core"`) - a real value, as opposed
30
+ * to the implicit default group or the root's own entry, which have descriptions rather than names. */
31
+ export declare function isNamedGroupKey(key: string): boolean;
32
+ /**
33
+ * The group key of a monorepo root's own plan entry. Not something `groupKeyOf` ever returns: the
34
+ * root is never a member of a release group - its number is the repository's identity - so the
35
+ * planner gives it a key of its own, and this is that key's one spelling.
36
+ */
37
+ export declare const ROOT_GROUP_KEY = "__root__";
29
38
  /**
30
39
  * How many distinct version lines this package's repository releases along - 1 when every package
31
40
  * moves together, more once anything is grouped apart.
@@ -34,6 +34,17 @@ export function groupKeyOf(pkg) {
34
34
  export function isSoloGroupKey(key) {
35
35
  return key.startsWith('solo:');
36
36
  }
37
+ /** Whether a group key is one the repository **named** (`group: "core"`) - a real value, as opposed
38
+ * to the implicit default group or the root's own entry, which have descriptions rather than names. */
39
+ export function isNamedGroupKey(key) {
40
+ return key.startsWith('named:');
41
+ }
42
+ /**
43
+ * The group key of a monorepo root's own plan entry. Not something `groupKeyOf` ever returns: the
44
+ * root is never a member of a release group - its number is the repository's identity - so the
45
+ * planner gives it a key of its own, and this is that key's one spelling.
46
+ */
47
+ export const ROOT_GROUP_KEY = '__root__';
37
48
  /**
38
49
  * How many distinct version lines this package's repository releases along - 1 when every package
39
50
  * moves together, more once anything is grouped apart.
File without changes