rman 2.1.0 → 2.2.0

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.
@@ -148,7 +148,9 @@ export interface VersionExtraKeys {
148
148
  /** Files whose hard-coded version is rewritten to the version being written, in the same commit
149
149
  * as the bump - paths relative to the package's own directory (e.g. `["src/constants.ts"]`).
150
150
  * Per-package cascaded; a listed file a package doesn't have is a silent no-op, so one `"[*]"`
151
- * declaration covers a repo where only some packages carry one.
151
+ * declaration covers a repo where only some packages carry one. A file that *exists* and holds
152
+ * nothing rewritable is an error instead - unless the entry is `{ file, optional: true }`, which
153
+ * is how a shared preset says "stamp it where there is one".
152
154
  *
153
155
  * Stamping the source, not the build output: a build-time rewrite leaves the checked-in file
154
156
  * claiming a placeholder, so anything running from source reports that placeholder, git never
@@ -168,11 +170,22 @@ export interface VersionExtraKeys {
168
170
  /** Same, after it (`postversion` in a Node repository). */
169
171
  after?: RunStepValue | RunStepValue[];
170
172
  }
171
- /** One `version.stamp` entry: a path, or a path plus the identifier to rewrite when it is not
172
- * spelled `version`. */
173
+ /**
174
+ * One `version.stamp` entry: a path, or a path plus the identifier to rewrite when it is not spelled
175
+ * `version` and whether the entry is `optional`.
176
+ *
177
+ * **`optional` marks an entry whose author cannot know whether the file holds a version.** A file
178
+ * that exists and has nothing to rewrite is otherwise an error - that is what catches a typo'd path
179
+ * or a renamed identifier before it silently ships a stale constant on every release. The case it
180
+ * is wrong for is a *shared preset* naming one path for every package of a technology, which is
181
+ * saying "stamp it where there is one" and cannot know which repositories keep a constant there.
182
+ * A bare string still throws; the asker chooses, the way `file.exists()` and `file.resolve()`
183
+ * already split the same question.
184
+ */
173
185
  export type VersionStampEntry = string | {
174
186
  file: string;
175
187
  constant?: string;
188
+ optional?: boolean;
176
189
  };
177
190
  type Args = RmanConfig.ArgsOf<typeof config, typeof COMMAND>;
178
191
  declare const versionCommand: (app: import("../index.js").RmanApplication) => {
package/constants.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.1.0';
1
+ export const version = '2.2.0';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rman",
3
3
  "description": "Repository manager",
4
- "version": "2.1.0",
4
+ "version": "2.2.0",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
@@ -291,6 +291,10 @@ export class VersionService extends Service {
291
291
  const before = fs.readFileSync(file, 'utf-8');
292
292
  const next = Manifest.stampVersion(pkg, file, before, version, { constant: entry.constant });
293
293
  if (next === undefined) {
294
+ /** Unreachable in a normal run - `assertStampable` already refused, before any write. Kept
295
+ * because this function is exported and a caller may reach it directly. */
296
+ if (entry.optional)
297
+ continue;
294
298
  const relative = path.relative(pkg.dirname, file);
295
299
  throw new Error(`"version.stamp" lists "${relative}" for "${pkg.name}", but nothing in it could be ` +
296
300
  `rewritten to ${version}.\n` +
@@ -320,12 +324,26 @@ export class VersionService extends Service {
320
324
  const VERSION_LIFECYCLE = 'version';
321
325
  /**
322
326
  * `.rmanrc "version.stamp"`, normalized: a bare string is the file, an object may also name the
323
- * identifier that holds the version (`{ file: 'src/version.go', constant: 'Version' }`).
327
+ * identifier that holds the version (`{ file: 'src/version.go', constant: 'Version' }`) and may
328
+ * mark the entry `optional`.
324
329
  *
325
330
  * The object form exists because the identifier was unnameable: `stampVersionConstant` took a
326
331
  * `name` and nothing ever passed it, so only the exact lowercase word `version` was ever matched -
327
332
  * `VERSION`, `Version` and `appVersion` were all silently skipped.
328
333
  */
334
+ /* **`optional` exists because the entry and the expectation can have different authors.** A file
335
+ * that exists and holds nothing rewritable is an error precisely because it means somebody asked
336
+ * for something and did not get it - but "somebody" is not always the repository. A *shared preset*
337
+ * naming `src/constants.ts` for every package of a technology is saying "stamp it where there is
338
+ * one", and it cannot know which of forty repositories actually keeps a version constant there.
339
+ * Measured on `panates/postgrejs`, which extends `@panates/rman-preset`: the file exists, has never
340
+ * held a version constant, and `rman version` therefore refused the release for a line no one in
341
+ * that repository wrote.
342
+ *
343
+ * So the *asker* says which it meant, and the two spellings keep their own failure modes: a bare
344
+ * string still throws, which is what catches a typo'd path or a renamed identifier before it
345
+ * silently ships a stale constant on every release from then on. It is the `file.exists()` /
346
+ * `file.resolve()` split again - the optional form and the throwing form, chosen by the caller. */
329
347
  function readStampEntries(pkg) {
330
348
  const configured = pkg.config?.version?.stamp;
331
349
  const list = Array.isArray(configured) ? configured : configured ? [configured] : [];
@@ -334,7 +352,11 @@ function readStampEntries(pkg) {
334
352
  if (typeof item === 'string')
335
353
  entries.push({ file: item });
336
354
  else if (item && typeof item === 'object' && typeof item.file === 'string') {
337
- entries.push({ file: item.file, constant: item.constant });
355
+ entries.push({
356
+ file: item.file,
357
+ constant: item.constant,
358
+ optional: item.optional === true,
359
+ });
338
360
  }
339
361
  }
340
362
  return entries;
@@ -355,6 +377,10 @@ function assertStampable(pkg, version) {
355
377
  });
356
378
  if (next !== undefined)
357
379
  continue;
380
+ /** The entry said "stamp it if there is one", so there being none is the answer rather than a
381
+ * mistake - see `readStampEntries`. */
382
+ if (entry.optional)
383
+ continue;
358
384
  const relative = path.relative(pkg.dirname, file);
359
385
  throw new Error(`"version.stamp" lists "${relative}" for "${pkg.name}", but nothing in it could be ` +
360
386
  `rewritten to ${version}.\n` +