@superblocksteam/sdk 2.0.123 → 2.0.124-next.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/.turbo/turbo-build.log +1 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts +37 -1
- package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.js +162 -10
- package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.test.js +377 -8
- package/dist/cli-replacement/automatic-upgrades.test.js.map +1 -1
- package/dist/cli-replacement/dependency-install-classifier.d.mts +21 -0
- package/dist/cli-replacement/dependency-install-classifier.d.mts.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.mjs +83 -0
- package/dist/cli-replacement/dependency-install-classifier.mjs.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.test.d.mts +2 -0
- package/dist/cli-replacement/dependency-install-classifier.test.d.mts.map +1 -0
- package/dist/cli-replacement/dependency-install-classifier.test.mjs +51 -0
- package/dist/cli-replacement/dependency-install-classifier.test.mjs.map +1 -0
- package/dist/cli-replacement/dev-s3-restore.test.mjs +170 -14
- package/dist/cli-replacement/dev-s3-restore.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs +33 -2
- package/dist/cli-replacement/dev-startup-git-before-dbfs-order.test.mjs.map +1 -1
- package/dist/cli-replacement/dev-token-priming.test.d.mts +31 -0
- package/dist/cli-replacement/dev-token-priming.test.d.mts.map +1 -0
- package/dist/cli-replacement/dev-token-priming.test.mjs +87 -0
- package/dist/cli-replacement/dev-token-priming.test.mjs.map +1 -0
- package/dist/cli-replacement/dev.d.mts +36 -0
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.interception.test.d.mts +2 -0
- package/dist/cli-replacement/dev.interception.test.d.mts.map +1 -0
- package/dist/cli-replacement/dev.interception.test.mjs +68 -0
- package/dist/cli-replacement/dev.interception.test.mjs.map +1 -0
- package/dist/cli-replacement/dev.mjs +396 -62
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/cli-replacement/home-npmrc.d.mts +180 -0
- package/dist/cli-replacement/home-npmrc.d.mts.map +1 -0
- package/dist/cli-replacement/home-npmrc.mjs +283 -0
- package/dist/cli-replacement/home-npmrc.mjs.map +1 -0
- package/dist/cli-replacement/home-npmrc.test.d.mts +10 -0
- package/dist/cli-replacement/home-npmrc.test.d.mts.map +1 -0
- package/dist/cli-replacement/home-npmrc.test.mjs +582 -0
- package/dist/cli-replacement/home-npmrc.test.mjs.map +1 -0
- package/dist/cli-replacement/install-packages.classify.test.d.mts +2 -0
- package/dist/cli-replacement/install-packages.classify.test.d.mts.map +1 -0
- package/dist/cli-replacement/install-packages.classify.test.mjs +125 -0
- package/dist/cli-replacement/install-packages.classify.test.mjs.map +1 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.d.mts +2 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.d.mts.map +1 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.mjs +260 -0
- package/dist/cli-replacement/install-packages.npm-registry.test.mjs.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.d.mts +58 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.d.mts.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.mjs +224 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.mjs.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.d.mts +11 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.d.mts.map +1 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.mjs +317 -0
- package/dist/cli-replacement/post-upgrade-lockfile-strip.test.mjs.map +1 -0
- package/dist/cli-replacement/userconfig-env.integration.test.d.mts +26 -0
- package/dist/cli-replacement/userconfig-env.integration.test.d.mts.map +1 -0
- package/dist/cli-replacement/userconfig-env.integration.test.mjs +148 -0
- package/dist/cli-replacement/userconfig-env.integration.test.mjs.map +1 -0
- package/dist/dev-utils/dev-server-metrics.d.mts +25 -0
- package/dist/dev-utils/dev-server-metrics.d.mts.map +1 -1
- package/dist/dev-utils/dev-server-metrics.mjs +84 -0
- package/dist/dev-utils/dev-server-metrics.mjs.map +1 -1
- package/dist/dev-utils/dev-server-metrics.test.d.mts +2 -0
- package/dist/dev-utils/dev-server-metrics.test.d.mts.map +1 -0
- package/dist/dev-utils/dev-server-metrics.test.mjs +26 -0
- package/dist/dev-utils/dev-server-metrics.test.mjs.map +1 -0
- package/dist/dev-utils/dev-server.d.mts +23 -1
- package/dist/dev-utils/dev-server.d.mts.map +1 -1
- package/dist/dev-utils/dev-server.mjs +21 -9
- package/dist/dev-utils/dev-server.mjs.map +1 -1
- package/dist/dev-utils/dev-server.status.test.d.mts +2 -0
- package/dist/dev-utils/dev-server.status.test.d.mts.map +1 -0
- package/dist/dev-utils/dev-server.status.test.mjs +41 -0
- package/dist/dev-utils/dev-server.status.test.mjs.map +1 -0
- package/dist/dev-utils/token-manager.d.ts +31 -0
- package/dist/dev-utils/token-manager.d.ts.map +1 -1
- package/dist/dev-utils/token-manager.js +34 -0
- package/dist/dev-utils/token-manager.js.map +1 -1
- package/dist/telemetry/local-obs.js +1 -1
- package/dist/telemetry/local-obs.js.map +1 -1
- package/dist/telemetry/util.js +1 -1
- package/dist/types/scoped-jwt-token-payload.d.ts +1 -0
- package/dist/types/scoped-jwt-token-payload.d.ts.map +1 -1
- package/dist/version-control.d.mts.map +1 -1
- package/dist/version-control.mjs +6 -7
- package/dist/version-control.mjs.map +1 -1
- package/package.json +12 -12
- package/src/cli-replacement/automatic-upgrades.test.ts +530 -8
- package/src/cli-replacement/automatic-upgrades.ts +179 -7
- package/src/cli-replacement/dependency-install-classifier.mts +118 -0
- package/src/cli-replacement/dependency-install-classifier.test.mts +72 -0
- package/src/cli-replacement/dev-s3-restore.test.mts +210 -14
- package/src/cli-replacement/dev-startup-git-before-dbfs-order.test.mts +35 -2
- package/src/cli-replacement/dev-token-priming.test.mts +103 -0
- package/src/cli-replacement/dev.interception.test.mts +80 -0
- package/src/cli-replacement/dev.mts +495 -92
- package/src/cli-replacement/home-npmrc.mts +409 -0
- package/src/cli-replacement/home-npmrc.test.mts +757 -0
- package/src/cli-replacement/install-packages.classify.test.mts +168 -0
- package/src/cli-replacement/install-packages.npm-registry.test.mts +345 -0
- package/src/cli-replacement/post-upgrade-lockfile-strip.mts +296 -0
- package/src/cli-replacement/post-upgrade-lockfile-strip.test.mts +482 -0
- package/src/cli-replacement/userconfig-env.integration.test.mts +189 -0
- package/src/dev-utils/dev-server-metrics.mts +96 -0
- package/src/dev-utils/dev-server-metrics.test.mts +38 -0
- package/src/dev-utils/dev-server.mts +48 -8
- package/src/dev-utils/dev-server.status.test.mts +58 -0
- package/src/dev-utils/token-manager.ts +36 -0
- package/src/telemetry/local-obs.ts +1 -1
- package/src/telemetry/util.ts +1 -1
- package/src/types/scoped-jwt-token-payload.ts +1 -0
- package/src/version-control.mts +8 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-publish-package.log +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { PRESERVE_NPMRC_SCOPES, restoreInitialNpmrc, snapshotInitialNpmrc, writeNpmrc, } from "@superblocksteam/vite-plugin-file-sync/npm-registry";
|
|
5
|
+
import { getErrorMeta } from "../telemetry/logging.js";
|
|
6
|
+
/**
|
|
7
|
+
* File mode applied to the userconfig after every write. `0o400` (owner
|
|
8
|
+
* read, no write) strips the owner write bit so any `npm config set` or
|
|
9
|
+
* stray write that opens the file `O_WRONLY` fails loudly instead of
|
|
10
|
+
* silently retargeting the configured registry. The atomic rename inside
|
|
11
|
+
* `writeNpmrc` is governed by parent-dir perms — not the target's mode —
|
|
12
|
+
* so this writer can still re-sync the file on subsequent config changes.
|
|
13
|
+
* The stricter mode is a speed bump, not a security boundary: the CLI
|
|
14
|
+
* user can always `chmod +w` first.
|
|
15
|
+
*/
|
|
16
|
+
export const HOME_NPMRC_MODE = 0o400;
|
|
17
|
+
const LOG_PREFIX = "[home-npmrc]";
|
|
18
|
+
/**
|
|
19
|
+
* Most recent `snapshotInitialNpmrc` outcome, keyed by userconfig target
|
|
20
|
+
* path. `syncHomeNpmrc` runs once per `NpmRegistryClient` cache refresh,
|
|
21
|
+
* so the snapshot (taken on a `configured`/`stale` resolve) and the
|
|
22
|
+
* destructive restore (on a later `not-configured` resolve) happen on
|
|
23
|
+
* SEPARATE invocations within one pod's lifetime. We remember the outcome
|
|
24
|
+
* across invocations so the not-configured branch can refuse to unlink the
|
|
25
|
+
* userconfig when the snapshot is known to have FAILED — a silent EXDEV
|
|
26
|
+
* (target and backup on different filesystems) leaves a real baked-in file
|
|
27
|
+
* with no backup, and unlinking it would fail the customer's install open
|
|
28
|
+
* to public npm (APPS-4428).
|
|
29
|
+
*
|
|
30
|
+
* Keyed by path so the test suite's per-test tmpdir `homeDir`s stay
|
|
31
|
+
* isolated. Absent (never snapshotted this process — e.g. not-configured
|
|
32
|
+
* from boot, or the policy-only path that deliberately skips the snapshot)
|
|
33
|
+
* preserves the original APPS-4328 behaviour: a missing backup is "nothing
|
|
34
|
+
* to preserve", so the unlink is allowed.
|
|
35
|
+
*/
|
|
36
|
+
const lastSnapshotOutcomeByPath = new Map();
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the Superblocks-owned npm userconfig path. Centralised so the
|
|
39
|
+
* image-build and CLI auto-upgrade `--userconfig=` plumbing and the
|
|
40
|
+
* runtime writer all agree on the same location. The file lives inside a
|
|
41
|
+
* hidden directory, so it is named `npmrc` (without a leading dot) by
|
|
42
|
+
* convention; the directory itself carries the visibility bit. `homeDir`
|
|
43
|
+
* defaults to `os.homedir()`; tests override it.
|
|
44
|
+
*/
|
|
45
|
+
export function superblocksNpmrcPath(homeDir) {
|
|
46
|
+
return path.join(homeDir ?? os.homedir(), ".superblocks", "npmrc");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the directory npm should write its per-run debug logs to during
|
|
50
|
+
* Superblocks-spawned installs in the live-edit pod. Unlike
|
|
51
|
+
* `superblocksNpmrcPath` (home-based), this is rooted at the application
|
|
52
|
+
* working directory — `<app>/.superblocks/logs` — so each app's install
|
|
53
|
+
* logs are co-located with the app and collectable from a predictable
|
|
54
|
+
* place. Wired into the subprocess env as `NPM_CONFIG_LOGS_DIR` via
|
|
55
|
+
* `buildInstallEnv`; npm creates the directory if it does not exist.
|
|
56
|
+
*/
|
|
57
|
+
export function superblocksLogsPath(appDir) {
|
|
58
|
+
return path.join(appDir, ".superblocks", "logs");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the on-disk backup path for the Superblocks-owned npm
|
|
62
|
+
* userconfig. Symmetric with `superblocksNpmrcPath`: lives next to the
|
|
63
|
+
* userconfig under `~/.superblocks/`. `syncHomeNpmrc` hardlinks the
|
|
64
|
+
* image-baked userconfig to this path on the first `configured` resolve
|
|
65
|
+
* so a later transition to `not-configured` has a baseline to restore
|
|
66
|
+
* from (APPS-4328) — mirroring the project-`.npmrc` capture/restore added
|
|
67
|
+
* in APPS-4320.
|
|
68
|
+
*
|
|
69
|
+
* The leading-dot filename mirrors the project-side `NPMRC_DEFAULT_FILENAME`
|
|
70
|
+
* convention even though the live userconfig is `npmrc` (no dot). The dot
|
|
71
|
+
* here is a convention-only marker that this file is the backup, not the
|
|
72
|
+
* active userconfig.
|
|
73
|
+
*/
|
|
74
|
+
export function superblocksNpmrcBackupPath(homeDir) {
|
|
75
|
+
return path.join(homeDir ?? os.homedir(), ".superblocks", "npmrc.default");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Materialises `~/.superblocks/npmrc` from the server-fetched per-org
|
|
79
|
+
* npm registry config. Designed to run at dev-server CLI startup, after
|
|
80
|
+
* the `NpmRegistryClient` is wired but BEFORE the global Superblocks CLI
|
|
81
|
+
* auto-upgrade: with the file in place and the auto-upgrade invoking
|
|
82
|
+
* npm with `NPM_CONFIG_USERCONFIG=~/.superblocks/npmrc`, the
|
|
83
|
+
* `npm install -g @superblocksteam/cli@…` invocation resolves through
|
|
84
|
+
* the customer's private registry rather than `registry.npmjs.org`.
|
|
85
|
+
*
|
|
86
|
+
* Behaviour:
|
|
87
|
+
*
|
|
88
|
+
* - `client.getConfig()` resolves `configured` (or `stale`): snapshot
|
|
89
|
+
* the image-default userconfig to `~/.superblocks/npmrc.default`
|
|
90
|
+
* (once per pod boot via `link(2)`), then write
|
|
91
|
+
* `~/.superblocks/npmrc` atomically with mode `0o400`. The snapshot
|
|
92
|
+
* captures whatever the image baked (EE GHPR scope + token, or a
|
|
93
|
+
* no-file baseline on a fresh developer laptop) so the
|
|
94
|
+
* `not-configured` branch below has a baseline to restore from. The
|
|
95
|
+
* parent dir is `mkdir -p`'d first so a fresh `$HOME` on a
|
|
96
|
+
* developer's laptop is handled the same as a pod where the image
|
|
97
|
+
* already pre-creates `/home/node/.superblocks/`. If the snapshot
|
|
98
|
+
* attempt FAILS against a real baked file (e.g. EXDEV), we fail
|
|
99
|
+
* closed and SKIP the rewrite: see `skipped-snapshot-failed` in
|
|
100
|
+
* `SyncHomeNpmrcOutcome` for why (APPS-4428).
|
|
101
|
+
*
|
|
102
|
+
* - `client.getConfig()` resolves `not-configured`: restore the
|
|
103
|
+
* image-default userconfig from the `~/.superblocks/npmrc.default`
|
|
104
|
+
* backup captured on a prior `configured` boot. When no backup
|
|
105
|
+
* exists (cold boot where the org was deconfigured before the first
|
|
106
|
+
* `configured` resolve), the userconfig is unlinked entirely so a
|
|
107
|
+
* long-lived Superblocks-spawned npm/pnpm process cannot silently
|
|
108
|
+
* inherit a previously-configured org's registry/token. Closes the
|
|
109
|
+
* stale-userconfig hole flagged on PR #19621 (APPS-4328). One
|
|
110
|
+
* exception: if this process already SAW a snapshot attempt FAIL for
|
|
111
|
+
* this path, the configured branch above will have left the baked
|
|
112
|
+
* userconfig in place (no managed rewrite happened), so unlinking
|
|
113
|
+
* here would delete the baked baseline. Withhold the unlink (APPS-4428).
|
|
114
|
+
*
|
|
115
|
+
* - `client.getConfig()` resolves `unreachable`: cold cache + the
|
|
116
|
+
* registry server is down (or JWT refresh failed). Leave the file
|
|
117
|
+
* alone — the fail-closed gate in the CLI auto-upgrade is the
|
|
118
|
+
* backstop if the upgrade reaches a public registry.
|
|
119
|
+
*
|
|
120
|
+
* - Any throw (fs error, etc.): log + return `outcome: "error"`. We
|
|
121
|
+
* must not crash dev-server startup on a best-effort step.
|
|
122
|
+
*
|
|
123
|
+
* Idempotent for a given resolved config — safe to invoke on every
|
|
124
|
+
* `NpmRegistryClient` cache refresh as well as on startup.
|
|
125
|
+
*/
|
|
126
|
+
export async function syncHomeNpmrc(deps) {
|
|
127
|
+
const targetPath = superblocksNpmrcPath(deps.homeDir);
|
|
128
|
+
const backupPath = superblocksNpmrcBackupPath(deps.homeDir);
|
|
129
|
+
let result;
|
|
130
|
+
try {
|
|
131
|
+
result = await deps.npmRegistryClient.getConfig();
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// `NpmRegistryClient` only throws for the deliberate-denial branches
|
|
135
|
+
// (RBAC 403, malformed request 400, double-401). All three signal a
|
|
136
|
+
// structurally broken session — surface the warn but keep the
|
|
137
|
+
// userconfig as-is rather than forging a "default" file that masks
|
|
138
|
+
// the real failure.
|
|
139
|
+
deps.logger.warn(`${LOG_PREFIX} client.getConfig() failed; ~/.superblocks/npmrc left unchanged`, { path: targetPath, ...getErrorMeta(error) });
|
|
140
|
+
return { outcome: "error", path: targetPath };
|
|
141
|
+
}
|
|
142
|
+
if (result.source === "unreachable") {
|
|
143
|
+
deps.logger.warn(`${LOG_PREFIX} registry server unreachable with no cached config; ~/.superblocks/npmrc left unchanged`, { path: targetPath });
|
|
144
|
+
return { outcome: "skipped-unreachable", path: targetPath };
|
|
145
|
+
}
|
|
146
|
+
if (!result.config.configured) {
|
|
147
|
+
if (result.config.allowInstallScripts === false) {
|
|
148
|
+
// Org policy disallows install scripts even without registry rows.
|
|
149
|
+
// Write a policy-only userconfig with `ignore-scripts=true`.
|
|
150
|
+
//
|
|
151
|
+
// IMPORTANT: we deliberately skip `snapshotInitialNpmrc` here.
|
|
152
|
+
// On a repeated sync the target already contains our own policy
|
|
153
|
+
// file; snapshotting it would poison the backup with policy content
|
|
154
|
+
// instead of the image-baked baseline, so a later flip to
|
|
155
|
+
// allowInstallScripts=true could never restore the original.
|
|
156
|
+
try {
|
|
157
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
158
|
+
await writeNpmrc(targetPath, result.config, {
|
|
159
|
+
preserveScopeLines: PRESERVE_NPMRC_SCOPES,
|
|
160
|
+
mode: HOME_NPMRC_MODE,
|
|
161
|
+
});
|
|
162
|
+
deps.logger.info(`${LOG_PREFIX} wrote policy-only userconfig (ignore-scripts)`, { path: targetPath });
|
|
163
|
+
return { outcome: "written", path: targetPath };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
deps.logger.warn(`${LOG_PREFIX} policy-only write failed`, {
|
|
167
|
+
path: targetPath,
|
|
168
|
+
...getErrorMeta(error),
|
|
169
|
+
});
|
|
170
|
+
return { outcome: "error", path: targetPath };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Restore the image-default userconfig from the backup captured on a
|
|
175
|
+
// prior configured boot. When the backup is absent (the org was never
|
|
176
|
+
// configured on this pod, OR the pod was rewritten before this
|
|
177
|
+
// commit landed) the userconfig is unlinked instead — leaving a
|
|
178
|
+
// stale Superblocks-owned userconfig in place would silently pin
|
|
179
|
+
// npm/pnpm at a previous org's registry/token, the exact hole
|
|
180
|
+
// gpoulios-sb flagged on PR #19621.
|
|
181
|
+
//
|
|
182
|
+
// EXCEPT when this process's most recent snapshot attempt FAILED:
|
|
183
|
+
// the snapshot ran against a real baked-in userconfig but `link(2)`
|
|
184
|
+
// failed silently (e.g. EXDEV across filesystems), so no backup
|
|
185
|
+
// exists for an unrelated reason. Unlinking here would delete a file
|
|
186
|
+
// that SHOULD have been preserved and restored. We can't distinguish
|
|
187
|
+
// that from "no backup" inside `restoreInitialNpmrc`, so we withhold
|
|
188
|
+
// the unlink option and leave the userconfig in place (APPS-4428).
|
|
189
|
+
// Absent state (never snapshotted: not-configured from boot, or the
|
|
190
|
+
// policy-only path) keeps the original APPS-4328 unlink behaviour.
|
|
191
|
+
const snapshotFailed = lastSnapshotOutcomeByPath.get(targetPath) === "failed";
|
|
192
|
+
if (snapshotFailed) {
|
|
193
|
+
deps.logger.warn(`${LOG_PREFIX} registry not configured but the userconfig snapshot failed earlier; leaving the userconfig in place rather than risk deleting an un-backed-up baseline`, { path: targetPath });
|
|
194
|
+
}
|
|
195
|
+
// Best-effort: `restoreInitialNpmrc` swallows fs errors as
|
|
196
|
+
// warn-and-no-op so dev-server startup never crashes on cleanup.
|
|
197
|
+
try {
|
|
198
|
+
await restoreInitialNpmrc(targetPath, backupPath, {
|
|
199
|
+
unlinkTargetWhenBackupMissing: !snapshotFailed,
|
|
200
|
+
});
|
|
201
|
+
// On the `snapshotFailed` branch we passed
|
|
202
|
+
// `unlinkTargetWhenBackupMissing: false`, so `restoreInitialNpmrc`
|
|
203
|
+
// intentionally no-ops (leaves the userconfig in place). Return
|
|
204
|
+
// the dedicated `skipped-snapshot-failed` outcome rather than
|
|
205
|
+
// `restored-not-configured` — nothing was restored or removed,
|
|
206
|
+
// and conflating the two would mislead telemetry/dashboards into
|
|
207
|
+
// thinking the baseline rollback ran when it didn't. The warn
|
|
208
|
+
// above already describes the decision; an info log here would
|
|
209
|
+
// contradict it during incident investigation.
|
|
210
|
+
if (snapshotFailed) {
|
|
211
|
+
return { outcome: "skipped-snapshot-failed", path: targetPath };
|
|
212
|
+
}
|
|
213
|
+
deps.logger.info(`${LOG_PREFIX} registry not configured; restored image-default userconfig (or removed Superblocks-owned file when no backup exists)`, { path: targetPath });
|
|
214
|
+
return { outcome: "restored-not-configured", path: targetPath };
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
deps.logger.warn(`${LOG_PREFIX} restore failed`, {
|
|
218
|
+
path: targetPath,
|
|
219
|
+
...getErrorMeta(error),
|
|
220
|
+
});
|
|
221
|
+
return { outcome: "error", path: targetPath };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
// `mkdir -p` so a fresh `$HOME` (developer laptop, unprovisioned
|
|
227
|
+
// sandbox) doesn't ENOENT inside `writeNpmrc`'s atomic-rename. Pod
|
|
228
|
+
// images already create `/home/node/.superblocks/` at build time, so
|
|
229
|
+
// this is a no-op there.
|
|
230
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
231
|
+
// Snapshot the image-default userconfig BEFORE the first rewrite so
|
|
232
|
+
// the not-configured branch above has a restore baseline. Hardlink
|
|
233
|
+
// is atomic + idempotent: only one of N concurrent callers wins
|
|
234
|
+
// (kernel-serialised), and a subsequent `writeNpmrc` tmp+rename
|
|
235
|
+
// gives the userconfig a fresh inode while the backup inode (the
|
|
236
|
+
// baked content) stays alive. EEXIST after the first capture is
|
|
237
|
+
// expected and not a warning.
|
|
238
|
+
//
|
|
239
|
+
// Remember the outcome so a later `not-configured` resolve (a
|
|
240
|
+
// separate `syncHomeNpmrc` invocation) can tell whether a usable
|
|
241
|
+
// backup actually exists before it asks `restoreInitialNpmrc` to
|
|
242
|
+
// unlink the userconfig. A silent `failed` here (e.g. EXDEV) must
|
|
243
|
+
// NOT lead the not-configured branch to delete a real baked file
|
|
244
|
+
// that has no backup (APPS-4428).
|
|
245
|
+
const snapshotOutcome = await snapshotInitialNpmrc(targetPath, backupPath);
|
|
246
|
+
lastSnapshotOutcomeByPath.set(targetPath, snapshotOutcome);
|
|
247
|
+
// FAIL CLOSED on snapshot failure (APPS-4428, gpoulios-sb review of
|
|
248
|
+
// PR #19690). When a real baked file existed but the hardlink to
|
|
249
|
+
// `backupPath` failed (only `"failed"` reports this — `"no-source"`
|
|
250
|
+
// means there was nothing to snapshot in the first place), we have
|
|
251
|
+
// no way to undo a managed rewrite later: a `not-configured`
|
|
252
|
+
// transition could not restore the baseline (no backup) and could
|
|
253
|
+
// not safely unlink the file (it would delete the baseline). The
|
|
254
|
+
// earlier guard only addressed the unlink side and let the rewrite
|
|
255
|
+
// happen anyway, which still left stale private-registry config and
|
|
256
|
+
// auth on disk after deconfigure. The only safe choice is to leave
|
|
257
|
+
// the baked userconfig alone here — the install path falls back to
|
|
258
|
+
// whatever the image baked (typically EE GHPR), which is the
|
|
259
|
+
// pre-private-registry default behaviour.
|
|
260
|
+
if (snapshotOutcome === "failed") {
|
|
261
|
+
deps.logger.warn(`${LOG_PREFIX} userconfig snapshot failed; refusing to overwrite the baked userconfig with managed private-registry content (no restore baseline exists)`, { path: targetPath, source: result.source });
|
|
262
|
+
return { outcome: "skipped-snapshot-failed", path: targetPath };
|
|
263
|
+
}
|
|
264
|
+
await writeNpmrc(targetPath, result.config, {
|
|
265
|
+
preserveScopeLines: PRESERVE_NPMRC_SCOPES,
|
|
266
|
+
mode: HOME_NPMRC_MODE,
|
|
267
|
+
});
|
|
268
|
+
deps.logger.info(`${LOG_PREFIX} wrote ~/.superblocks/npmrc`, {
|
|
269
|
+
path: targetPath,
|
|
270
|
+
source: result.source,
|
|
271
|
+
mode: HOME_NPMRC_MODE.toString(8).padStart(4, "0"),
|
|
272
|
+
});
|
|
273
|
+
return { outcome: "written", path: targetPath, source: result.source };
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
deps.logger.warn(`${LOG_PREFIX} write failed`, {
|
|
277
|
+
path: targetPath,
|
|
278
|
+
...getErrorMeta(error),
|
|
279
|
+
});
|
|
280
|
+
return { outcome: "error", path: targetPath };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=home-npmrc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home-npmrc.mjs","sourceRoot":"","sources":["../../src/cli-replacement/home-npmrc.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the CLI-startup `~/.superblocks/npmrc` writer.
|
|
3
|
+
*
|
|
4
|
+
* Strategy: stand up a tmpdir as a fake `$HOME` and a fake
|
|
5
|
+
* `NpmRegistryClient` whose `getConfig()` returns the canned response
|
|
6
|
+
* under test. Asserts cover the four state transitions plus the 0o400
|
|
7
|
+
* mode bit.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=home-npmrc.test.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home-npmrc.test.d.mts","sourceRoot":"","sources":["../../src/cli-replacement/home-npmrc.test.mts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|