package-registry-manager 0.23.0 → 0.24.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.
package/CHANGELOG.md CHANGED
@@ -24,9 +24,16 @@ currently shares its release version with the Rust implementation.
24
24
  variables and secret, `packages: write`, and package linking (#5).
25
25
  - `setup --browser default|automated` chooses where sign-in and approval URLs
26
26
  open; `default`, the default, uses the user's default browser (#9).
27
+ - An npm first publish stops before the sign-in when the packed tarball
28
+ contains repository development directories such as `.github/` or `tests/`,
29
+ which a package.json without a `files` allowlist lets in.
27
30
 
28
31
  ### Changed
29
32
 
33
+ - `setup` stays signed in by default: the npm session token and the crates.io
34
+ sign-in in the automated profile are kept, so the next run (npm: within two
35
+ hours) skips the sign-in. `--sign-out` signs out at the end instead;
36
+ `--keep-session` is still accepted and is now the default.
30
37
  - npm web-auth URLs (`npm login`, `npm publish` 2FA, `npm trust`) open in the
31
38
  user's default browser (`open`, `xdg-open`, or the Windows URL handler),
32
39
  where they are usually already signed in. The automated browser profile is
@@ -35,7 +42,10 @@ currently shares its release version with the Rust implementation.
35
42
  outside any repository (`~/Library/Application Support` on macOS,
36
43
  `$XDG_STATE_HOME` or `~/.local/state` on Linux, `%LOCALAPPDATA%` on Windows,
37
44
  each followed by `package-registry-manager/browser-profile`) (#8).
38
- - `browser-commander` is required at `^0.22.0`, its latest release (#28).
45
+ - `browser-commander` is required at `^0.34.0`, its latest release (#28). A
46
+ launch still names its channel (`chrome` by default), so the upstream switch
47
+ of channel-less launches to Chrome for Testing does not change which browser
48
+ opens.
39
49
 
40
50
  ### Fixed
41
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "package-registry-manager",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Inspect repositories and guide secure package-registry setup",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@link-foundation/gh-manager": "https://codeload.github.com/link-foundation/gh-manager/tar.gz/29808e088ac8fd8374361920bbebab07a4d63ac6",
44
- "browser-commander": "^0.31.2",
44
+ "browser-commander": "^0.34.0",
45
45
  "command-stream": "^2.4.0",
46
46
  "playwright": "^1.63.0",
47
47
  "selenium-webdriver": "^4.50.0"
package/src/cli-main.mjs CHANGED
@@ -68,10 +68,12 @@ Setup options:
68
68
  --manual crates.io: create, use, and revoke the
69
69
  first-publish token by hand instead of
70
70
  through the crates.io API
71
- --keep-session Stay signed in after setup: npm keeps its
72
- session token in npm's user configuration
73
- until npm logout; the shared browser profile
74
- keeps registry sessions across packages
71
+ --sign-out Sign out at the end of setup: npm logout
72
+ for the npm session this run signed in, and
73
+ removal of the crates.io sign-in it brought
74
+ into the automated profile. Without it the
75
+ next run reuses both sessions; npm ends its
76
+ session token after two hours
75
77
  --browser <default|automated> Open sign-in and approval pages in your
76
78
  default browser, or in the automated profile;
77
79
  npm reuses the automated profile
@@ -132,6 +134,7 @@ export async function main(args = process.argv.slice(2)) {
132
134
  yes: { type: "boolean", default: false },
133
135
  "no-browser": { type: "boolean", default: false },
134
136
  "open-with": { type: "string" },
137
+ "sign-out": { type: "boolean", default: false },
135
138
  "keep-session": { type: "boolean", default: false },
136
139
  manual: { type: "boolean", default: false },
137
140
  browser: { type: "string", default: "default" },
@@ -249,6 +252,9 @@ export async function main(args = process.argv.slice(2)) {
249
252
  if (values["open-with"] !== undefined && values["no-browser"]) {
250
253
  throw new Error("--open-with and --no-browser are mutually exclusive");
251
254
  }
255
+ if (values["sign-out"] && values["keep-session"]) {
256
+ throw new Error("--sign-out and --keep-session are mutually exclusive");
257
+ }
252
258
  if (!browserOptions.attach) {
253
259
  browserOptions.import ??= { browser: "auto", profile: null };
254
260
  }
@@ -276,7 +282,7 @@ export async function main(args = process.argv.slice(2)) {
276
282
  defaultBrowserProfile({ channel: browserOptions.channel }),
277
283
  ),
278
284
  openWith: nonEmpty(values["open-with"], "--open-with"),
279
- keepSession: values["keep-session"],
285
+ keepSession: !values["sign-out"],
280
286
  planOptions: {
281
287
  workflow: values.workflow,
282
288
  publisherEnvironment: values.environment,
@@ -364,6 +370,9 @@ export async function main(args = process.argv.slice(2)) {
364
370
  if (values["open-with"] !== undefined && values["no-browser"]) {
365
371
  throw new Error("--open-with and --no-browser are mutually exclusive");
366
372
  }
373
+ if (values["sign-out"] && values["keep-session"]) {
374
+ throw new Error("--sign-out and --keep-session are mutually exclusive");
375
+ }
367
376
 
368
377
  const plans = buildPlans(inspection, registries, planOptions);
369
378
  const selected = values.all
@@ -381,7 +390,7 @@ export async function main(args = process.argv.slice(2)) {
381
390
  yes: values.yes,
382
391
  noBrowser: values["no-browser"],
383
392
  openWith: nonEmpty(values["open-with"], "--open-with"),
384
- keepSession: values["keep-session"],
393
+ keepSession: !values["sign-out"],
385
394
  secretName: values["secret-name"],
386
395
  rotateToken,
387
396
  migrate: values.migrate,
@@ -407,7 +407,7 @@ export async function runCratesApiStep(session, step) {
407
407
  case "crates-sign-out":
408
408
  if (options.keepSession) {
409
409
  console.log(
410
- `Keeping the ${domains.join(" / ")} sign-in in the automated profile (--keep-session); the next run reuses it. No token is kept.`,
410
+ `Keeping the ${domains.join(" / ")} sign-in in the automated profile; the next run reuses it. No token is kept.`,
411
411
  );
412
412
  } else if (page.clearCookies) {
413
413
  await page.clearCookies(domains);
package/src/flows.mjs CHANGED
@@ -477,7 +477,7 @@ export function npmFlow(packageInfo, context) {
477
477
  "sign-out",
478
478
  "Sign out of npm",
479
479
  "command",
480
- "Revoke the session token created by the web login.",
480
+ "With --sign-out, revoke the session token created by the web login now; otherwise the next run reuses it and npm ends it after two hours.",
481
481
  { command: command("npm", ["logout"]), when: "tool-signed-in" },
482
482
  ),
483
483
  removeWorktreeStep(),
@@ -555,7 +555,7 @@ function apiCratesFlow(packageInfo, context) {
555
555
  "crates-sign-out",
556
556
  "Sign the automated browser out of crates.io",
557
557
  "api",
558
- "Remove the sign-in this run brought into the automated profile; --keep-session keeps it. No token outlives the run either way.",
558
+ "With --sign-out, remove the sign-in this run brought into the automated profile; otherwise the next run reuses it. No token outlives the run either way.",
559
559
  { when: "crates-signed-in" },
560
560
  ),
561
561
  removeWorktreeStep(),
package/src/setup.mjs CHANGED
@@ -107,14 +107,14 @@ export class SetupSession {
107
107
  ) {
108
108
  if (step.id === "sign-out" && this.keepsSession(step)) {
109
109
  console.log(
110
- "Keeping the npm session for reuse; run npm logout to revoke it.",
110
+ "Keeping the npm session for reuse; npm ends it after two hours, npm logout sooner.",
111
111
  );
112
112
  } else {
113
113
  deferred.push(step);
114
114
  }
115
115
  } else if (step.id === "sign-out" && this.keepsSession(step)) {
116
116
  console.log(
117
- "Keeping the npm session for reuse: its token stays in npm's user configuration until you run npm logout, and the next run reuses it while npm whoami succeeds.",
117
+ "Keeping the npm session for reuse: the next run within two hours skips the sign-in while npm whoami succeeds. npm ends the session token after two hours; npm logout ends it now.",
118
118
  );
119
119
  } else if (this.conditions.has(step.when) && step.kind === "api") {
120
120
  this.log(`==> ${step.title}`);
@@ -608,6 +608,12 @@ export class SetupSession {
608
608
  console.log(
609
609
  ` ${packed.filename}: ${packed.size} bytes packed, ${packed.unpackedSize} bytes unpacked, ${packed.entryCount} files`,
610
610
  );
611
+ const development = developmentDirectories(packed);
612
+ if (development.length > 0) {
613
+ throw new Error(
614
+ `the package would publish repository development files (${development.join(", ")}); add a "files" allowlist to package.json, commit it to the published ref, and re-run. Nothing was published.`,
615
+ );
616
+ }
611
617
  this.values.tarball = path.join(
612
618
  this.values.pack_destination,
613
619
  packed.filename,
@@ -882,6 +888,39 @@ export function packedEntry(output) {
882
888
  return packed;
883
889
  }
884
890
 
891
+ /**
892
+ * Top-level repository directories of development files: CI, tests, and
893
+ * editor and build state, which belong in no published package.
894
+ */
895
+ const DEVELOPMENT_DIRECTORIES = new Set([
896
+ ".changeset",
897
+ ".github",
898
+ ".husky",
899
+ ".idea",
900
+ ".vscode",
901
+ "__tests__",
902
+ "coverage",
903
+ "experiments",
904
+ "target",
905
+ "test",
906
+ "tests",
907
+ ]);
908
+
909
+ /**
910
+ * The development directories a packed tarball contains, as `name/`, which
911
+ * a package.json without a `files` allowlist lets in.
912
+ */
913
+ export function developmentDirectories(packed) {
914
+ const found = new Set();
915
+ for (const file of packed.files ?? []) {
916
+ const [top, ...rest] = String(file.path).split("/");
917
+ if (rest.length > 0 && DEVELOPMENT_DIRECTORIES.has(top)) {
918
+ found.add(`${top}/`);
919
+ }
920
+ }
921
+ return [...found].sort();
922
+ }
923
+
885
924
  function reportPackWarnings(stderr) {
886
925
  const warnings = packWarnings(stderr);
887
926
  if (warnings.length === 0) {