rman 1.2.5 → 2.0.0-beta.2

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 (153) hide show
  1. package/README.md +63 -19
  2. package/cli.d.ts +5 -0
  3. package/cli.js +214 -88
  4. package/commands/build.command.d.ts +177 -3
  5. package/commands/build.command.js +20 -10
  6. package/commands/changed.command.d.ts +80 -3
  7. package/commands/changed.command.js +19 -12
  8. package/commands/changelog.command.d.ts +192 -3
  9. package/commands/changelog.command.js +87 -43
  10. package/commands/config.command.d.ts +44 -3
  11. package/commands/config.command.js +30 -19
  12. package/commands/diff.command.d.ts +37 -3
  13. package/commands/diff.command.js +25 -16
  14. package/commands/exec.command.d.ts +193 -3
  15. package/commands/exec.command.js +60 -58
  16. package/commands/github-release.command.d.ts +154 -3
  17. package/commands/github-release.command.js +67 -37
  18. package/commands/import.command.d.ts +36 -3
  19. package/commands/import.command.js +28 -20
  20. package/commands/info.command.d.ts +35 -7
  21. package/commands/info.command.js +36 -30
  22. package/commands/list.command.d.ts +163 -3
  23. package/commands/list.command.js +109 -71
  24. package/commands/publish.command.d.ts +231 -0
  25. package/commands/publish.command.js +304 -0
  26. package/commands/run.command.d.ts +186 -6
  27. package/commands/run.command.js +26 -72
  28. package/commands/test.command.d.ts +173 -3
  29. package/commands/test.command.js +16 -10
  30. package/commands/version.command.d.ts +317 -3
  31. package/commands/version.command.js +149 -69
  32. package/commands.d.ts +32 -0
  33. package/commands.js +28 -0
  34. package/constants.js +1 -1
  35. package/core/application.d.ts +116 -0
  36. package/core/application.js +143 -0
  37. package/core/command-builder.d.ts +14 -0
  38. package/core/command-builder.js +78 -0
  39. package/core/config.d.ts +180 -50
  40. package/core/config.js +332 -153
  41. package/core/core-services.d.ts +14 -0
  42. package/core/core-services.js +30 -0
  43. package/core/core-targets.d.ts +14 -0
  44. package/core/core-targets.js +16 -0
  45. package/core/custom-command.d.ts +42 -6
  46. package/core/custom-command.js +44 -17
  47. package/core/extends-config.d.ts +13 -5
  48. package/core/extends-config.js +52 -12
  49. package/core/load-config-module.d.ts +28 -0
  50. package/core/load-config-module.js +42 -0
  51. package/core/manifest.d.ts +47 -25
  52. package/core/manifest.js +51 -69
  53. package/core/merge-config.d.ts +33 -34
  54. package/core/merge-config.js +138 -93
  55. package/core/package.d.ts +145 -17
  56. package/core/package.js +128 -36
  57. package/core/plugin-loader.d.ts +65 -0
  58. package/core/plugin-loader.js +234 -0
  59. package/core/plugin.d.ts +135 -90
  60. package/core/plugin.js +70 -173
  61. package/core/publish-target.d.ts +124 -0
  62. package/core/publish-target.js +30 -0
  63. package/core/registry.d.ts +30 -0
  64. package/core/registry.js +47 -0
  65. package/core/repository.d.ts +72 -9
  66. package/core/repository.js +293 -43
  67. package/core/resolve-target.d.ts +1 -1
  68. package/core/resolve-target.js +1 -1
  69. package/core/service.d.ts +49 -0
  70. package/core/service.js +40 -0
  71. package/core/version-scheme.d.ts +23 -1
  72. package/core/version-scheme.js +29 -1
  73. package/core/workspace.d.ts +84 -33
  74. package/core/workspace.js +63 -22
  75. package/index.d.ts +111 -14
  76. package/index.js +85 -9
  77. package/interfaces/rman-config.interface.d.ts +739 -212
  78. package/interfaces/rman-config.interface.js +61 -1
  79. package/package.json +2 -1
  80. package/plugins/builtins.d.ts +44 -0
  81. package/plugins/builtins.js +33 -0
  82. package/plugins/detect.d.ts +78 -0
  83. package/plugins/detect.js +70 -0
  84. package/plugins/node/augmentation/rman.augmentation.d.ts +84 -0
  85. package/plugins/node/augmentation/rman.augmentation.js +1 -0
  86. package/plugins/node/augmentation/system-info.augmentation.d.ts +26 -0
  87. package/plugins/node/augmentation/system-info.augmentation.js +79 -0
  88. package/plugins/node/commands/ci.command.d.ts +131 -0
  89. package/plugins/node/commands/ci.command.js +59 -0
  90. package/plugins/node/commands/clean.command.d.ts +183 -0
  91. package/plugins/node/commands/clean.command.js +73 -0
  92. package/plugins/node/index.d.ts +29 -0
  93. package/plugins/node/index.js +40 -0
  94. package/plugins/node/node-config.interface.d.ts +77 -0
  95. package/plugins/node/node-config.interface.js +7 -0
  96. package/plugins/node/node-manifest.provider.d.ts +68 -0
  97. package/plugins/node/node-manifest.provider.js +125 -0
  98. package/plugins/node/node.platform.d.ts +53 -0
  99. package/plugins/node/node.platform.js +134 -0
  100. package/plugins/node/npm-publish-target.d.ts +73 -0
  101. package/plugins/node/npm-publish-target.js +96 -0
  102. package/plugins/node/services/ci.service.d.ts +47 -0
  103. package/plugins/node/services/ci.service.js +213 -0
  104. package/plugins/node/services/clean.service.d.ts +53 -0
  105. package/plugins/node/services/clean.service.js +237 -0
  106. package/plugins/node/services/publish.service.d.ts +114 -0
  107. package/plugins/node/services/publish.service.js +371 -0
  108. package/plugins/node/services/version-plan.service.d.ts +44 -0
  109. package/plugins/node/services/version-plan.service.js +58 -0
  110. package/plugins/node/utils/npm-view.d.ts +48 -0
  111. package/plugins/node/utils/npm-view.js +71 -0
  112. package/plugins/node/utils/workspace-range.d.ts +26 -0
  113. package/plugins/node/utils/workspace-range.js +28 -0
  114. package/services/change-hash.service.d.ts +2 -2
  115. package/services/change-hash.service.js +2 -2
  116. package/services/changelog.service.d.ts +62 -51
  117. package/services/changelog.service.js +14 -11
  118. package/services/docker-publish.service.d.ts +50 -29
  119. package/services/docker-publish.service.js +43 -20
  120. package/services/exec.service.d.ts +23 -12
  121. package/services/exec.service.js +14 -9
  122. package/services/github-release.service.d.ts +44 -33
  123. package/services/github-release.service.js +13 -10
  124. package/services/import.service.d.ts +25 -14
  125. package/services/import.service.js +9 -5
  126. package/services/list.service.d.ts +62 -10
  127. package/services/list.service.js +62 -15
  128. package/services/run.service.d.ts +22 -13
  129. package/services/run.service.js +262 -223
  130. package/services/version-plan.service.d.ts +27 -4
  131. package/services/version-plan.service.js +42 -15
  132. package/services/version.service.d.ts +31 -11
  133. package/services/version.service.js +29 -13
  134. package/targets/docker.target.d.ts +53 -0
  135. package/targets/docker.target.js +40 -0
  136. package/utils/bin-path.d.ts +6 -7
  137. package/utils/bin-path.js +7 -18
  138. package/utils/branch-guard.d.ts +29 -0
  139. package/utils/branch-guard.js +31 -0
  140. package/utils/exec.d.ts +10 -0
  141. package/utils/exec.js +1 -1
  142. package/utils/logger.d.ts +1 -1
  143. package/utils/logger.js +1 -1
  144. package/utils/package-filter.d.ts +127 -7
  145. package/utils/package-filter.js +197 -16
  146. package/utils/printable-config.d.ts +1 -1
  147. package/utils/printable-config.js +1 -1
  148. package/utils/run-bin.d.ts +10 -0
  149. package/utils/run-bin.js +1 -1
  150. package/utils/run-options.d.ts +97 -0
  151. package/utils/run-options.js +81 -0
  152. package/utils/version-stamp.d.ts +1 -1
  153. package/utils/version-stamp.js +1 -1
@@ -24,7 +24,7 @@ export declare namespace ChangeHashService {
24
24
  *
25
25
  * The keyword used to be `"npm"`, which named a *source* - and the wrong one: auto-detection is
26
26
  * mostly git, and the registry it may consult is now the ecosystem's business (see
27
- * `ManifestProvider.publishedVersion`). What is being chosen here is a *mode*, so it is spelled
27
+ * `Plugin.publishedVersion`). What is being chosen here is a *mode*, so it is spelled
28
28
  * as one. **A rename, not an alias**: `--from npm` now means a ref literally called `npm`,
29
29
  * which is what it should have meant all along.
30
30
  */
@@ -76,7 +76,7 @@ export declare namespace ChangeHashService {
76
76
  * so all three commands agree on "since when" for any repo whose tags are the ones `rman version`
77
77
  * actually created; (2) failing that (no tag at all yet - e.g. onboarding `rman` onto a repo with
78
78
  * real release history but no `rman`-created tags), whatever this package's **own ecosystem**
79
- * reports as its published version (`ManifestProvider.publishedVersion`), mapped to a git tag via
79
+ * reports as its published version (`Plugin.publishedVersion`), mapped to a git tag via
80
80
  * `.rmanrc changelog.tagPattern` and used only if that tag actually exists. Either way, if
81
81
  * `catchUpFile` is given and exists, the result is widened to also cover anything that file
82
82
  * hasn't caught up on yet (see its doc comment). Returns `undefined` when nothing can be resolved
@@ -77,7 +77,7 @@ export var ChangeHashService;
77
77
  * so all three commands agree on "since when" for any repo whose tags are the ones `rman version`
78
78
  * actually created; (2) failing that (no tag at all yet - e.g. onboarding `rman` onto a repo with
79
79
  * real release history but no `rman`-created tags), whatever this package's **own ecosystem**
80
- * reports as its published version (`ManifestProvider.publishedVersion`), mapped to a git tag via
80
+ * reports as its published version (`Plugin.publishedVersion`), mapped to a git tag via
81
81
  * `.rmanrc changelog.tagPattern` and used only if that tag actually exists. Either way, if
82
82
  * `catchUpFile` is given and exists, the result is widened to also cover anything that file
83
83
  * hasn't caught up on yet (see its doc comment). Returns `undefined` when nothing can be resolved
@@ -91,7 +91,7 @@ export var ChangeHashService;
91
91
  let tagHash = await findLatestTag(git, pkg);
92
92
  if (!tagHash) {
93
93
  /** Through the package's own ecosystem, not through npm: which registry (if any) knows about
94
- * this package is the `ManifestProvider`'s answer, and in a polyglot repository it differs
94
+ * this package is the `Plugin`'s manifest members's answer, and in a polyglot repository it differs
95
95
  * per package. A repository naming no plugin gets `undefined` here and git tags decide
96
96
  * alone. */
97
97
  const publishedVersion = await Manifest.publishedVersion(pkg);
@@ -1,20 +1,74 @@
1
1
  import type { Package } from '../core/package.js';
2
- import type { Repository } from '../core/repository.js';
2
+ import { Service } from '../core/service.js';
3
3
  import { type PackageFilterOptions } from '../utils/package-filter.js';
4
+ /**
5
+ * A service class - see `ListService` for the shape and `Service` for the three measured
6
+ * consequences a namespace had. `repository` left the signature because the application carries it.
7
+ */
8
+ export declare class ChangelogService extends Service {
9
+ /**
10
+ * Same as `getEntries`, and additionally - for every returned entry, when `options.write` is
11
+ * set - prepends `entry.content` into that package's own changelog file (see `ChangelogService.Entry.filePath`).
12
+ * Still pure with respect to console output: writing a file is a real, callable-for-its-own-
13
+ * sake side effect (a "save this" request), not presentation, so it stays here rather than in
14
+ * the CLI command - printing what happened is the command's job.
15
+ */
16
+ generateToFile(options?: ChangelogService.Options): Promise<ChangelogService.Entry[]>;
17
+ /**
18
+ * Computes a changelog entry per package (root included) from real commits only - either
19
+ * everything since a given `--from <hash>` (applied the same way to every package), or, by
20
+ * default, auto-detected per package instead (see `detectChangeHash`). Pure: returns the
21
+ * entries, never prints or touches `CHANGELOG.md` - see `generate` for that.
22
+ *
23
+ * A commit is attributed to every package its files fall under (root included, for anything
24
+ * outside every package) - unless it's broad enough to count as a repo-wide change (see
25
+ * `BROAD_COMMIT_THRESHOLD`/`ownersOf`), in which case it goes to root alone instead of being
26
+ * repeated verbatim across most of the repository. Subject lines are grouped ✨ Features/🐛 Bug
27
+ * Fixes/🔧 Other Changes on a best-effort Conventional Commits read; anything that doesn't parse
28
+ * just lands in Other Changes as-is, so a repo that doesn't follow that convention still gets a
29
+ * usable list. `.rmanrc changelog.ignoreTypes` (cascaded, e.g. `[chore, dev]`) drops commits of
30
+ * those types entirely instead - see `ignoreTypesConfig`. A release marker - a bare version-bump
31
+ * commit (`"6.0.1"`), or any of the messages `version` itself writes - is always dropped
32
+ * outright, regardless of `ignoreTypes`; see `isReleaseCommit`.
33
+ *
34
+ * By default (or with `--from npm` explicitly), the boundary is auto-detected per package
35
+ * instead of one shared one - see `detectChangeHash`. A package that can't be resolved this way
36
+ * (unpublished, no network, no matching tag) has never been released at all, so its whole
37
+ * history counts as unreleased - the same view `version` takes, so a first-ever release still
38
+ * produces a real changelog. Packages that end up resolving to
39
+ * the same hash (an explicit one, or several packages sharing one tag under fixed versioning)
40
+ * only have their commits fetched once, not once per package.
41
+ *
42
+ * Formatting comes from `.rmanrc changelog.template` - a *path* to a template file (not the
43
+ * template text itself, to keep `.rmanrc` readable), with `{{package}}`/`{{version}}`/
44
+ * `{{date}}`/`{{commits}}` (the full grouped block) and `{{features}}`/`{{fixes}}`/`{{other}}`
45
+ * (their bullet lists alone, for templates that want their own headings/order) - see
46
+ * `resolveTemplate`. `{{package}}` for the repository root is `"<repo dir name> repository"`
47
+ * (e.g. "sqb repository"), not its raw package.json name - which is often a private,
48
+ * non-published placeholder (`"sqb.v4"`) that reads like a stray version marker rather than a
49
+ * recognizable label. `{{version}}` comes from git tags, not package.json (which can drift out
50
+ * of sync with what's actually been released) - see `resolveVersion`/`.rmanrc
51
+ * changelog.tagPattern`.
52
+ *
53
+ * Run from inside a single package's own directory, it only covers that package unless
54
+ * `options.fromRoot` says otherwise (see `Repository.currentPackage`).
55
+ */
56
+ getEntries(options?: ChangelogService.Options): Promise<ChangelogService.Entry[]>;
57
+ }
4
58
  export declare namespace ChangelogService {
5
59
  interface Options extends PackageFilterOptions {
6
60
  /** Generate the changelog since this commit/hash - applied the same way to every package.
7
61
  * Default (also `"auto"` explicitly): auto-detect it per package instead, from that package's
8
62
  * own most recent release tag first - the same lookup `VersionService`/`changed` use, so this
9
63
  * never disagrees with them - falling back to whatever its own ecosystem's registry reports
10
- * only when it has no tag yet (`ManifestProvider.publishedVersion`, and only ever to guess a
64
+ * only when it has no tag yet (`Plugin.publishedVersion`, and only ever to guess a
11
65
  * tag name - see `detectChangeHash`); a package this can't be resolved for either way (never
12
66
  * tagged, unpublished, no plugin) has never been released at all, so its whole history counts
13
67
  * as unreleased - the same view `version` takes. */
14
68
  from?: string;
15
69
  /** Generate for the whole repository even when the current directory is inside a single
16
70
  * package (which otherwise scopes it to just that package) - see `Repository.currentPackage`. */
17
- root?: boolean;
71
+ fromRoot?: boolean;
18
72
  /** Where a package's changelog file lives, relative to *that package's own* directory -
19
73
  * default `'CHANGELOG.md'`. Applies the same way to every package; for a package that wants
20
74
  * its own filename instead, use `.rmanrc changelog.filePath` (cascaded, per-package
@@ -55,52 +109,9 @@ export declare namespace ChangelogService {
55
109
  * package's own directory - see `GetOptions.filePath`. */
56
110
  filePath: string;
57
111
  }
58
- /**
59
- * Same as `getEntries`, and additionally - for every returned entry, when `options.write` is
60
- * set - prepends `entry.content` into that package's own changelog file (see `Entry.filePath`).
61
- * Still pure with respect to console output: writing a file is a real, callable-for-its-own-
62
- * sake side effect (a "save this" request), not presentation, so it stays here rather than in
63
- * the CLI command - printing what happened is the command's job.
64
- */
65
- function generateToFile(repository: Repository, options?: Options): Promise<Entry[]>;
66
- /**
67
- * Computes a changelog entry per package (root included) from real commits only - either
68
- * everything since a given `--from <hash>` (applied the same way to every package), or, by
69
- * default, auto-detected per package instead (see `detectChangeHash`). Pure: returns the
70
- * entries, never prints or touches `CHANGELOG.md` - see `generate` for that.
71
- *
72
- * A commit is attributed to every package its files fall under (root included, for anything
73
- * outside every package) - unless it's broad enough to count as a repo-wide change (see
74
- * `BROAD_COMMIT_THRESHOLD`/`ownersOf`), in which case it goes to root alone instead of being
75
- * repeated verbatim across most of the repository. Subject lines are grouped ✨ Features/🐛 Bug
76
- * Fixes/🔧 Other Changes on a best-effort Conventional Commits read; anything that doesn't parse
77
- * just lands in Other Changes as-is, so a repo that doesn't follow that convention still gets a
78
- * usable list. `.rmanrc changelog.ignoreTypes` (cascaded, e.g. `[chore, dev]`) drops commits of
79
- * those types entirely instead - see `ignoreTypesConfig`. A release marker - a bare version-bump
80
- * commit (`"6.0.1"`), or any of the messages `version` itself writes - is always dropped
81
- * outright, regardless of `ignoreTypes`; see `isReleaseCommit`.
82
- *
83
- * By default (or with `--from npm` explicitly), the boundary is auto-detected per package
84
- * instead of one shared one - see `detectChangeHash`. A package that can't be resolved this way
85
- * (unpublished, no network, no matching tag) has never been released at all, so its whole
86
- * history counts as unreleased - the same view `version` takes, so a first-ever release still
87
- * produces a real changelog. Packages that end up resolving to
88
- * the same hash (an explicit one, or several packages sharing one tag under fixed versioning)
89
- * only have their commits fetched once, not once per package.
90
- *
91
- * Formatting comes from `.rmanrc changelog.template` - a *path* to a template file (not the
92
- * template text itself, to keep `.rmanrc` readable), with `{{package}}`/`{{version}}`/
93
- * `{{date}}`/`{{commits}}` (the full grouped block) and `{{features}}`/`{{fixes}}`/`{{other}}`
94
- * (their bullet lists alone, for templates that want their own headings/order) - see
95
- * `resolveTemplate`. `{{package}}` for the repository root is `"<repo dir name> repository"`
96
- * (e.g. "sqb repository"), not its raw package.json name - which is often a private,
97
- * non-published placeholder (`"sqb.v4"`) that reads like a stray version marker rather than a
98
- * recognizable label. `{{version}}` comes from git tags, not package.json (which can drift out
99
- * of sync with what's actually been released) - see `resolveVersion`/`.rmanrc
100
- * changelog.tagPattern`.
101
- *
102
- * Run from inside a single package's own directory, it only covers that package unless
103
- * `options.root` says otherwise (see `Repository.currentPackage`).
104
- */
105
- function getEntries(repository: Repository, options?: Options): Promise<Entry[]>;
112
+ }
113
+ declare module '../core/service.js' {
114
+ interface ServiceMap {
115
+ changelog: ChangelogService;
116
+ }
106
117
  }
@@ -1,25 +1,28 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { Service } from '../core/service.js';
3
4
  import { GitHelper } from '../utils/git.js';
4
5
  import { filterPackages } from '../utils/package-filter.js';
5
6
  import { ChangeHashService } from './change-hash.service.js';
6
7
  import { ConventionalCommitsService } from './conventional-commits.service.js';
7
- export var ChangelogService;
8
- (function (ChangelogService) {
8
+ /**
9
+ * A service class - see `ListService` for the shape and `Service` for the three measured
10
+ * consequences a namespace had. `repository` left the signature because the application carries it.
11
+ */
12
+ export class ChangelogService extends Service {
9
13
  /**
10
14
  * Same as `getEntries`, and additionally - for every returned entry, when `options.write` is
11
- * set - prepends `entry.content` into that package's own changelog file (see `Entry.filePath`).
15
+ * set - prepends `entry.content` into that package's own changelog file (see `ChangelogService.Entry.filePath`).
12
16
  * Still pure with respect to console output: writing a file is a real, callable-for-its-own-
13
17
  * sake side effect (a "save this" request), not presentation, so it stays here rather than in
14
18
  * the CLI command - printing what happened is the command's job.
15
19
  */
16
- async function generateToFile(repository, options = {}) {
17
- const entries = await getEntries(repository, options);
20
+ async generateToFile(options = {}) {
21
+ const entries = await this.getEntries(options);
18
22
  for (const entry of entries)
19
23
  prependToChangelogFile(entry.package, entry.filePath, entry.content);
20
24
  return entries;
21
25
  }
22
- ChangelogService.generateToFile = generateToFile;
23
26
  /**
24
27
  * Computes a changelog entry per package (root included) from real commits only - either
25
28
  * everything since a given `--from <hash>` (applied the same way to every package), or, by
@@ -57,10 +60,11 @@ export var ChangelogService;
57
60
  * changelog.tagPattern`.
58
61
  *
59
62
  * Run from inside a single package's own directory, it only covers that package unless
60
- * `options.root` says otherwise (see `Repository.currentPackage`).
63
+ * `options.fromRoot` says otherwise (see `Repository.currentPackage`).
61
64
  */
62
- async function getEntries(repository, options = {}) {
63
- const cwdScope = options.root ? undefined : repository.currentPackage;
65
+ async getEntries(options = {}) {
66
+ const repository = this.repository;
67
+ const cwdScope = options.fromRoot ? undefined : repository.currentPackage;
64
68
  const packages = repository.getPackages().filter(p => p !== repository.rootPackage);
65
69
  const targets = (cwdScope ? [cwdScope] : filterPackages([repository.rootPackage, ...packages], options)).filter(pkg => options.includeSkipped || !pkg.config.publish?.skip);
66
70
  const git = new GitHelper({ cwd: repository.dirname });
@@ -118,8 +122,7 @@ export var ChangelogService;
118
122
  }
119
123
  return entries;
120
124
  }
121
- ChangelogService.getEntries = getEntries;
122
- })(ChangelogService || (ChangelogService = {}));
125
+ }
123
126
  /** `.rmanrc changelog.ignoreTypes` (cascaded, per-package overridable) - Conventional Commits
124
127
  * `type`s to drop entirely (e.g. `[chore, dev]`), not just fold into "Other Changes". Only
125
128
  * applies to commits that actually parse as `type: ...` - a non-conventional message always
@@ -1,31 +1,19 @@
1
- import type { Package } from '../core/package.js';
2
- import type { Repository } from '../core/repository.js';
1
+ import { type PublishTarget } from '../core/publish-target.js';
2
+ import { Service } from '../core/service.js';
3
3
  import { type PackageFilterOptions } from '../utils/package-filter.js';
4
- export declare namespace DockerPublishService {
5
- /** Injectable "does this tag already exist" check - mainly for tests, so they don't depend on
6
- * network access or a real Docker daemon. Same shape as `PublishService.Deps.npmViewVersion`. */
7
- interface Deps {
8
- imageExists?: (image: string, tag: string) => Promise<boolean>;
9
- }
10
- interface Options extends PackageFilterOptions {
11
- /** A package with uncommitted local changes is excluded (status `'skip'`) instead of aborting
12
- * the whole plan (status `'error'`) - same as `version`/`publish --target npm`'s own option. */
13
- ignoreDirty?: boolean;
14
- /** Prefixed onto a bare (no `/`) `publish.docker.image` - falls back to the
15
- * `DOCKERHUB_NAMESPACE` environment variable. */
16
- namespace?: string;
17
- }
18
- type ApplyOptions = Options;
19
- /** One package's outcome in a docker-publish plan - see `getPlan`. */
20
- interface Entry {
21
- package: Package;
22
- version: string;
23
- status: 'publish' | 'skip' | 'up-to-date' | 'error';
24
- /** The fully-qualified `<namespace>/<image>` this entry publishes to - unset only when the
25
- * package's own `publish.docker.image` config is missing entirely (an `'error'` entry). */
26
- image?: string;
27
- reason?: string;
28
- }
4
+ /**
5
+ * The name this target answers to in `publish.target` and `--target`.
6
+ *
7
+ * Declared beside the implementation rather than in `publish-target.ts`: the seam is general and
8
+ * must not know any one target's name, and the adapter in `targets/docker.target.ts` reads it from
9
+ * here, which keeps the dependency pointing one way.
10
+ */
11
+ export declare const DOCKER_TARGET = "docker";
12
+ /**
13
+ * A service class - see `ListService` for the shape and `Service` for the three measured
14
+ * consequences a namespace had. `repository` left the signature because the application carries it.
15
+ */
16
+ export declare class DockerPublishService extends Service {
29
17
  /**
30
18
  * Computes what `publish --target docker` *would* do. Unlike the npm side (opt-out via
31
19
  * `"private"`), the docker target is opt-in: only packages whose own (cascaded) `.rmanrc
@@ -39,7 +27,7 @@ export declare namespace DockerPublishService {
39
27
  * already exists on the registry (via `docker manifest inspect`, queried concurrently) decides
40
28
  * the rest: `'up-to-date'` if so, `'publish'` if not.
41
29
  */
42
- function getPlan(repository: Repository, options?: Options, deps?: Deps): Promise<Entry[]>;
30
+ getPlan(options?: DockerPublishService.Options, deps?: DockerPublishService.Deps): Promise<DockerPublishService.Entry[]>;
43
31
  /**
44
32
  * Publishes every `'publish'` entry in `plan`: one `docker login` and one `docker buildx create`
45
33
  * up front (each package's own build reuses them), then per package a single `docker buildx
@@ -48,5 +36,38 @@ export declare namespace DockerPublishService {
48
36
  * `DOCKER_README.md`), if present, updates the DockerHub repo description afterward. A package's
49
37
  * own failure doesn't stop unrelated packages elsewhere in the plan.
50
38
  */
51
- function applyPlan(repository: Repository, plan: Entry[]): Promise<Entry[]>;
39
+ applyPlan(plan: DockerPublishService.Entry[]): Promise<DockerPublishService.Entry[]>;
40
+ }
41
+ export declare namespace DockerPublishService {
42
+ /** Injectable "does this tag already exist" check - mainly for tests, so they don't depend on
43
+ * network access or a real Docker daemon. Same shape as `PublishService.Deps.npmViewVersion`. */
44
+ interface Deps {
45
+ imageExists?: (image: string, tag: string) => Promise<boolean>;
46
+ }
47
+ interface Options extends PackageFilterOptions {
48
+ /** A package with uncommitted local changes is excluded (status `'skip'`) instead of aborting
49
+ * the whole plan (status `'error'`) - same as `version`/`publish --target npm`'s own option. */
50
+ ignoreDirty?: boolean;
51
+ /** Prefixed onto a bare (no `/`) `publish.docker.image` - falls back to the
52
+ * `DOCKERHUB_NAMESPACE` environment variable. */
53
+ namespace?: string;
54
+ }
55
+ type ApplyOptions = Options;
56
+ /**
57
+ * One package's outcome in a docker-publish plan - see `getPlan`.
58
+ *
59
+ * A `PublishTarget.Entry` with one field of its own, which is the shape that interface expects: a
60
+ * target knows things about its own registry that no other target has a word for, and `detail` is
61
+ * where it puts whatever the command should print beside the package.
62
+ */
63
+ interface Entry extends PublishTarget.Entry {
64
+ /** The fully-qualified `<namespace>/<image>` this entry publishes to - unset only when the
65
+ * package's own `publish.docker.image` config is missing entirely (an `'error'` entry). */
66
+ image?: string;
67
+ }
68
+ }
69
+ declare module '../core/service.js' {
70
+ interface ServiceMap {
71
+ dockerPublish: DockerPublishService;
72
+ }
52
73
  }
@@ -2,11 +2,24 @@ import { execFile } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { promisify } from 'node:util';
5
+ import { targetsOf } from '../core/publish-target.js';
6
+ import { Service } from '../core/service.js';
5
7
  import { exec } from '../utils/exec.js';
6
8
  import { GitHelper } from '../utils/git.js';
7
9
  import { filterPackages } from '../utils/package-filter.js';
8
- export var DockerPublishService;
9
- (function (DockerPublishService) {
10
+ /**
11
+ * The name this target answers to in `publish.target` and `--target`.
12
+ *
13
+ * Declared beside the implementation rather than in `publish-target.ts`: the seam is general and
14
+ * must not know any one target's name, and the adapter in `targets/docker.target.ts` reads it from
15
+ * here, which keeps the dependency pointing one way.
16
+ */
17
+ export const DOCKER_TARGET = 'docker';
18
+ /**
19
+ * A service class - see `ListService` for the shape and `Service` for the three measured
20
+ * consequences a namespace had. `repository` left the signature because the application carries it.
21
+ */
22
+ export class DockerPublishService extends Service {
10
23
  /**
11
24
  * Computes what `publish --target docker` *would* do. Unlike the npm side (opt-out via
12
25
  * `"private"`), the docker target is opt-in: only packages whose own (cascaded) `.rmanrc
@@ -20,9 +33,14 @@ export var DockerPublishService;
20
33
  * already exists on the registry (via `docker manifest inspect`, queried concurrently) decides
21
34
  * the rest: `'up-to-date'` if so, `'publish'` if not.
22
35
  */
23
- async function getPlan(repository, options = {}, deps = {}) {
36
+ async getPlan(options = {}, deps = {}) {
37
+ const repository = this.repository;
24
38
  const git = new GitHelper({ cwd: repository.dirname });
25
- const packages = filterPackages(repository.getPackages({ toposort: true }), options).filter(pkg => targetsDocker(pkg) && !pkg.config.publish?.skip);
39
+ /** Which packages are the docker target's is the one question `PublishTarget` answers for every
40
+ * target alike (`publish.target`, or `claims` when a package declares none) - asked through
41
+ * `targetsOf` rather than re-read here, so `publish` and `list --json` cannot disagree about
42
+ * where a package ships. */
43
+ const packages = filterPackages(repository.getPackages({ toposort: true }), options).filter(pkg => targetsOf(this.app, pkg).some(t => t.name === DOCKER_TARGET) && !pkg.config.publish?.skip);
26
44
  const dirtyFiles = await git.listDirtyFiles({ absolute: true });
27
45
  const isDirty = (pkg) => dirtyFiles.some(f => !path.relative(pkg.dirname, f).startsWith('..'));
28
46
  const imageExists = deps.imageExists ?? defaultImageExists;
@@ -52,6 +70,7 @@ export var DockerPublishService;
52
70
  package: pkg,
53
71
  version: pkg.version,
54
72
  image,
73
+ detail: image,
55
74
  status: options.ignoreDirty ? 'skip' : 'error',
56
75
  reason: 'uncommitted local changes',
57
76
  });
@@ -65,13 +84,13 @@ export var DockerPublishService;
65
84
  package: pkg,
66
85
  version: pkg.version,
67
86
  image,
87
+ detail: image,
68
88
  status: exists ? 'up-to-date' : 'publish',
69
89
  reason: exists ? `registry already has ${image}:${pkg.version}` : 'never published',
70
90
  });
71
91
  }));
72
92
  return packages.map(pkg => entries.get(pkg.name));
73
93
  }
74
- DockerPublishService.getPlan = getPlan;
75
94
  /**
76
95
  * Publishes every `'publish'` entry in `plan`: one `docker login` and one `docker buildx create`
77
96
  * up front (each package's own build reuses them), then per package a single `docker buildx
@@ -80,12 +99,18 @@ export var DockerPublishService;
80
99
  * `DOCKER_README.md`), if present, updates the DockerHub repo description afterward. A package's
81
100
  * own failure doesn't stop unrelated packages elsewhere in the plan.
82
101
  */
83
- async function applyPlan(repository, plan) {
102
+ async applyPlan(plan) {
103
+ const repository = this.repository;
84
104
  const toPublish = plan.filter(e => e.status === 'publish');
85
105
  if (!toPublish.length)
86
106
  return plan;
87
- await dockerLogin(repository.dirname);
88
- await exec('docker buildx create --use', { cwd: repository.dirname, stdio: 'inherit', throwOnError: false });
107
+ await dockerLogin(repository.app, repository.dirname);
108
+ await exec('docker buildx create --use', {
109
+ cwd: repository.dirname,
110
+ app: repository.app,
111
+ stdio: 'inherit',
112
+ throwOnError: false,
113
+ });
89
114
  const result = [];
90
115
  for (const entry of plan) {
91
116
  if (entry.status !== 'publish') {
@@ -103,12 +128,11 @@ export var DockerPublishService;
103
128
  }
104
129
  return result;
105
130
  }
106
- DockerPublishService.applyPlan = applyPlan;
107
- })(DockerPublishService || (DockerPublishService = {}));
131
+ }
108
132
  const execFileAsync = promisify(execFile);
109
133
  /** `docker manifest inspect <image>:<tag>` - `false` for any failure (tag doesn't exist yet, no
110
- * network, not logged in, ...), same catch-everything shape as `PublishService`'s own
111
- * `defaultNpmViewVersion`. */
134
+ * network, not logged in, ...), the same catch-everything shape every target's registry check has:
135
+ * "is this version out there" must never fail the run for a reason that is not an answer. */
112
136
  async function defaultImageExists(image, tag) {
113
137
  try {
114
138
  await execFileAsync('docker', ['manifest', 'inspect', `${image}:${tag}`]);
@@ -118,11 +142,6 @@ async function defaultImageExists(image, tag) {
118
142
  return false;
119
143
  }
120
144
  }
121
- function targetsDocker(pkg) {
122
- const target = pkg.config.publish?.target;
123
- const targets = Array.isArray(target) ? target : target ? [target] : ['npm'];
124
- return targets.includes('docker');
125
- }
126
145
  function resolveImageRef(image, namespaceOverride) {
127
146
  if (image.includes('/'))
128
147
  return image;
@@ -138,13 +157,17 @@ function expandEnvValue(value) {
138
157
  const match = /^\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);
139
158
  return match ? (process.env[match[1]] ?? '') : value;
140
159
  }
141
- async function dockerLogin(cwd) {
160
+ async function dockerLogin(app, cwd) {
142
161
  const username = process.env.DOCKERHUB_USERNAME;
143
162
  const password = process.env.DOCKERHUB_PASSWORD;
144
163
  if (!username || !password) {
145
164
  throw new Error('DOCKERHUB_USERNAME/DOCKERHUB_PASSWORD environment variables are required to publish to Docker');
146
165
  }
147
- await exec(`echo "${password}" | docker login --username "${username}" --password-stdin`, { cwd, stdio: 'inherit' });
166
+ await exec(`echo "${password}" | docker login --username "${username}" --password-stdin`, {
167
+ cwd,
168
+ app,
169
+ stdio: 'inherit',
170
+ });
148
171
  }
149
172
  async function buildAndPush(repository, entry) {
150
173
  const pkg = entry.package;
@@ -160,7 +183,7 @@ async function buildAndPush(repository, entry) {
160
183
  args.push('--build-arg', `${name}="${expandEnvValue(value)}"`);
161
184
  }
162
185
  args.push('-f', `"${dockerfile}"`, '-t', `"${entry.image}:${entry.version}"`, '-t', `"${entry.image}:latest"`, '--push', '.');
163
- await exec(`docker ${args.join(' ')}`, { cwd, stdio: 'inherit' });
186
+ await exec(`docker ${args.join(' ')}`, { cwd, app: repository.app, stdio: 'inherit' });
164
187
  }
165
188
  async function updateDescription(entry) {
166
189
  const pkg = entry.package;
@@ -1,6 +1,22 @@
1
- import type { Repository } from '../core/repository.js';
1
+ import { Service } from '../core/service.js';
2
2
  import { type LogLevel } from '../utils/logger.js';
3
3
  import { type PackageFilterOptions } from '../utils/package-filter.js';
4
+ /**
5
+ * A service class - see `ListService` for the shape and `Service` for the three measured
6
+ * consequences a namespace had. `repository` left the signature because the application carries it.
7
+ */
8
+ export declare class ExecService extends Service {
9
+ /**
10
+ * `exec`: runs `command` (an arbitrary shell command, not an npm script) directly in every
11
+ * matching package's own directory - unlike `run`, there's no `package.json` script to resolve
12
+ * and no `pre<script>`/`post<script>` npm lifecycle convention to honor, since the command isn't
13
+ * tied to any script name at all. Everything else about how packages are selected and scheduled
14
+ * matches `run`: topological order and per-package bail by default, the same live progress panel
15
+ * (falling back to a classic one-line-per-package log when it's off), and the same
16
+ * `--scope`/`--ignore`/`--deps`/`--dependents`/`--changed` package filtering.
17
+ */
18
+ exec(command: string, options?: ExecService.Options): Promise<void>;
19
+ }
4
20
  export declare namespace ExecService {
5
21
  interface Options extends PackageFilterOptions {
6
22
  /** Max packages at once: `true`/omitted = CPU count, a number = that many, `false` = serial (1). */
@@ -20,16 +36,11 @@ export declare namespace ExecService {
20
36
  /** Run across the whole repository even when the current directory is inside a single package
21
37
  * (which otherwise scopes the run to just that package). Has no effect when already at the
22
38
  * repository root, or outside any known package. */
23
- root?: boolean;
39
+ fromRoot?: boolean;
40
+ }
41
+ }
42
+ declare module '../core/service.js' {
43
+ interface ServiceMap {
44
+ exec: ExecService;
24
45
  }
25
- /**
26
- * `exec`: runs `command` (an arbitrary shell command, not an npm script) directly in every
27
- * matching package's own directory - unlike `run`, there's no `package.json` script to resolve
28
- * and no `pre<script>`/`post<script>` npm lifecycle convention to honor, since the command isn't
29
- * tied to any script name at all. Everything else about how packages are selected and scheduled
30
- * matches `run`: topological order and per-package bail by default, the same live progress panel
31
- * (falling back to a classic one-line-per-package log when it's off), and the same
32
- * `--scope`/`--ignore`/`--deps`/`--dependents`/`--changed` package filtering.
33
- */
34
- function exec(repository: Repository, command: string, options?: Options): Promise<void>;
35
46
  }
@@ -1,12 +1,16 @@
1
1
  import os from 'node:os';
2
2
  import colors from 'ansi-colors';
3
3
  import { Task } from 'power-tasks';
4
+ import { Service } from '../core/service.js';
4
5
  import { exec as execCommand } from '../utils/exec.js';
5
6
  import { Logger, resolveRootLogLevel } from '../utils/logger.js';
6
7
  import { filterPackages } from '../utils/package-filter.js';
7
8
  import { ProgressPanel } from '../utils/progress-panel.js';
8
- export var ExecService;
9
- (function (ExecService) {
9
+ /**
10
+ * A service class - see `ListService` for the shape and `Service` for the three measured
11
+ * consequences a namespace had. `repository` left the signature because the application carries it.
12
+ */
13
+ export class ExecService extends Service {
10
14
  /**
11
15
  * `exec`: runs `command` (an arbitrary shell command, not an npm script) directly in every
12
16
  * matching package's own directory - unlike `run`, there's no `package.json` script to resolve
@@ -16,10 +20,11 @@ export var ExecService;
16
20
  * (falling back to a classic one-line-per-package log when it's off), and the same
17
21
  * `--scope`/`--ignore`/`--deps`/`--dependents`/`--changed` package filtering.
18
22
  */
19
- async function exec(repository, command, options = {}) {
23
+ async exec(command, options = {}) {
24
+ const repository = this.repository;
20
25
  const logLevelDefault = resolveRootLogLevel(repository);
21
26
  const logger = new Logger(options.logLevel ?? logLevelDefault);
22
- const cwdScope = options.root ? undefined : repository.currentPackage;
27
+ const cwdScope = options.fromRoot ? undefined : repository.currentPackage;
23
28
  const topo = options.topo ?? true;
24
29
  let packages = repository.getPackages({ toposort: topo, scope: cwdScope?.name });
25
30
  if (!topo)
@@ -48,7 +53,7 @@ export var ExecService;
48
53
  const children = packages.map(pkg => {
49
54
  const ctx = panel.addItem(pkg.name);
50
55
  const dependencies = topo ? pkg.dependencies.filter(d => names.has(d.name)).map(d => d.name) : [];
51
- return new Task(() => execForPackage(ctx, panel.enabled, pkg.dirname, command, logger, bail ? () => rootTask?.abort() : () => { }), { name: pkg.name, dependencies });
56
+ return new Task(() => execForPackage(this.app, ctx, panel.enabled, pkg.dirname, command, logger, bail ? () => rootTask?.abort() : () => { }), { name: pkg.name, dependencies });
52
57
  });
53
58
  panel.start();
54
59
  let failed = false;
@@ -73,15 +78,15 @@ export var ExecService;
73
78
  throw err;
74
79
  }
75
80
  }
76
- ExecService.exec = exec;
77
- })(ExecService || (ExecService = {}));
78
- async function execForPackage(ctx, panelEnabled, cwd, command, logger, abort) {
81
+ }
82
+ async function execForPackage(app, ctx, panelEnabled, cwd, command, logger, abort) {
79
83
  ctx.status = 'running';
80
84
  ctx.startedAt = Date.now();
81
85
  try {
82
86
  if (panelEnabled) {
83
87
  await execCommand(command, {
84
88
  cwd,
89
+ app,
85
90
  stdio: 'pipe',
86
91
  onLine: line => {
87
92
  ctx.log.push(line);
@@ -93,7 +98,7 @@ async function execForPackage(ctx, panelEnabled, cwd, command, logger, abort) {
93
98
  logger.info(colors.cyan('exec'), colors.cyan(ctx.name), command);
94
99
  const t = Date.now();
95
100
  try {
96
- await execCommand(command, { cwd, stdio: 'inherit' });
101
+ await execCommand(command, { cwd, app, stdio: 'inherit' });
97
102
  logger.info(colors.green('success'), colors.cyan(ctx.name), colors.yellow(`(${Date.now() - t} ms)`));
98
103
  }
99
104
  catch (e) {