Skip to content

Commit b9e6e5c

Browse files
fix(cli): os doctor reads an unset NODE_ENV as development in the source checkout os dev serves (#22231)
Fixes #22163 Clause-②: no ## What changed `os doctor`'s `NODE_ENV` row now depends on the directory it runs in. With `NODE_ENV` unset, it picks one of two rows. Doctor's exit code does not change in either case. | Where doctor runs | Row | |---|---| | The source checkout that `os dev` serves: an `objectstack.config.ts` that boots as itself, either alone or beside its own `dist/objectstack.json` | `✓ NODE_ENV Not set — os dev runs this project as development; os start forces production`, with no fix text | | Anywhere else: a config beside `OS_ARTIFACT_URL`, a config beside an `OS_ARTIFACT_PATH` naming another artifact, an artifact with no config, or an empty directory | The #5673 `⚠ … treated as production` warning and its fix. Both are byte-identical to what they were before this change | A `NODE_ENV` that is set still prints no row in either case. The maintainer's direction, quoted in the card: 「`os doctor` 在开发项目里警告 "NODE_ENV not set — treated as production"。 刚脚手架完就被告知在跑生产,令人困惑;旁边有 `objectstack.config.ts` 且无产物时应按开发语义判断。」 - `packages/cli/src/commands/doctor.ts`: - `nodeEnvCheck(env, opts)` gains `opts.sourcePosture`, which defaults to `false`. A caller that does not pass it gets the #5673 row, so every existing caller and pin compiles and behaves as before. - The new `nodeEnvSourcePosture(cwd, env)` decides the posture by calling `utils/artifact-precedence.ts`'s own `resolveArtifactBootSource` and then `cwdConfigJoinsBoot`. It does not copy the precedence order, which that module's header forbids. - The docblock now sets the #22163 direction beside #5673's argument. I also rewrote the `run()` call-site comment, which the change made false. - Unchanged: - `doctorNodeEnv()` and the `.env*` cascade doctor resolves. The `Environment files` row still says `node_env=production`. - The `/discovery` default. - How `os serve`, `os start` and `os dev` resolve the mode. - No file under `serve.ts`, `start.ts`, `dev.ts`, `packages/spec`, `content/docs` or `docs/qa` is touched. ## The PM readings, measured - **H1: no artifact does not survive the first `os dev`. Confirmed.** - Setup: I scaffolded with the published `create-objectstack@17.7.0 h1-app --template blank` (packages installed), then ran the published `os doctor`. - Then I ran the compile `os dev` runs (`os compile --output $PWD/dist/objectstack.json`, which wrote `dist/objectstack.json`, 2.4 KB), and ran doctor again. - Both runs printed the same two rows: ```text ✓ Environment files No .env* files here (node_env=production) — environment read from this process only — no environment input set ⚠ NODE_ENV Not set — this environment is being treated as production ``` - So the card's literal test ("no `dist/objectstack.json`") would turn this very project back to the production row after its first `os dev`. The posture therefore accepts the config's own compiled output at the conventional path. - With this branch's CLI, the same project prints the following, both before and after `dist/` exists: ```text ✓ Environment files No .env* files here (node_env=production) — environment read from this process only — no environment input set ✓ NODE_ENV Not set — os dev runs this project as development; os start forces production ``` - Under `--verbose`, no fix line follows the `NODE_ENV` row. Exit code 0. - **H2: the posture comes from `artifact-precedence.ts`. Held, with one deliberate difference from `os dev`.** - `os dev` reads `OS_ARTIFACT_URL` / `OS_ARTIFACT_PATH` after a `.env*` load. `dev.ts:330`, `dotenvFlow.config({ node_env: 'development', silent: true })`, runs before `resolveArtifactBootSource` at `:348`. - The posture mirrors that order: this process's environment over the node_env=development cascade, with the shell winning. This is a second, private read of the files. The cascade the `Environment files` row reports does not change. - Not mirrored: the `configCompiledTo` that `dev.ts:366` passes. Under a local `OS_ARTIFACT_PATH`, `os dev` compiles the config into that path and counts the file as the config's own. With that input, `cwdConfigJoinsBoot` answers `true` for "a config plus an `OS_ARTIFACT_PATH` naming another artifact", which contradicts both the triage direction ("everywhere else #5673's production default stands") and pin (c). - So the posture passes no `configCompiledTo` and no `homeDir`. Its table is the PM's six rows exactly. - **H3:** the row is `ok`, has the card's message, and carries no `fix`. `HealthCheckResult.status` is not widened. - **H4:** the two rows a freshly scaffolded project prints are quoted under H1. My reading is in the Acceptance notes. - **H5:** `nodeEnvCheck` and `nodeEnvSourcePosture` cannot be reached from any `@objectstack/cli` entry export. The built `dist/index.d.ts` re-exports only `default as DoctorCommand` from `commands/doctor.js`. `console.d.ts` and `hook-body.d.ts` do not mention doctor. So `Clause-②: no` stands. - **H6:** done, as described above. ## Pins - **Unit pins, in `doctor-node-env-default.test.ts`:** - **Source posture:** `ok`, the message names `os dev` and `os start`, the production sentence is absent, and `fix` is undefined. - **Explicit `sourcePosture: false`:** `toEqual` the default call. The default call is the row that the unchanged #5673 cases in the same file pin. - **A set `NODE_ENV`:** no row in either posture. - **The posture table** (`nodeEnvSourcePosture`): - Source posture: a config alone; a config beside its own `dist/`. - Not source: - a config beside an `OS_ARTIFACT_PATH` naming another artifact, or beside `OS_ARTIFACT_URL`; - an artifact with no config; - an empty directory. - The cascade it reads: - `.env.development` naming an artifact moves the posture to not-source. - `.env.production` does not, because `os dev` never loads it. - **Run level** (`Doctor.run` in a temp cwd, in the same harness as the #5673 cases). `NODE_ENV` is unset in (a) to (d), and doctor exits 0 in all five: - (a) A config alone gives the `✓` row naming `os dev` and `os start`, with no `treated as production`. - (b) A config plus `dist/objectstack.json` gives the same row. - (c) A config plus `OS_ARTIFACT_PATH` naming another artifact gives the `⚠ … treated as production` row. - (d) An empty directory: the existing #5673 case `prints the row when NODE_ENV is unset`, kept as it was. - (e) `NODE_ENV=development` and `NODE_ENV=production` in the source posture: no `NODE_ENV` row. - **The config's own load does not decide the row.** The run-level config is the scaffold's first line (`import { defineStack } from '@objectstack/spec'`), and nothing in the temp cwd can resolve it. Case (a) asserts that `Could not load config for analysis` is printed in the same report as the development row. - **The drift guard in `doctor-env-provenance.test.ts`.** The docblock names `OS_ARTIFACT_URL` and `OS_ARTIFACT_PATH`, and the guard requires every `OS_*` name in `doctor.ts` to be declared in `DOCTOR_ENV_INPUTS`. I did not declare them there: - The `Environment files` row attributes those inputs against the node_env=production cascade, but these two are read through the development one. A line for them in that row would name the wrong file, and H4 rules out rewording that row. - Setting either variable can only send the row back to its #5673 text. - They sit in a named `POSTURE_ONLY` allowance with that reason. A second assertion requires both names to still appear in `doctor.ts`, so the allowance cannot outlive its reader. ## Ablation (one-time proof, not kept) - Committed `12e2b1ed` first. - `node scripts/ablation-replace.mjs` replaced the call-site anchor `nodeEnvCheck(process.env, { sourcePosture: nodeEnvSourcePosture(cwd) })` with `sourcePosture: false`: - anchor count 1 → 0, replacement count 0 → 1; - blob `e38feeb4a35e` → `f875089c53b6`. - No build or `dist/` was involved: the test imports `./doctor.js`, which resolves to `src`. - Predicted: (a) and (b) turn red and everything else stays green. Observed: exactly that. - `× (a) … expected ' ⚠ NODE_ENV Not set — th…' to contain '✓'` - `× (b) … expected ' ⚠ NODE_ENV …' to contain 'os dev'` - `Tests 2 failed | 21 passed (23)` - Restore: blob after restore `e38feeb4a35e` equals the blob at HEAD, and `git diff HEAD` is empty. ## Byte identity outside the source posture - In an artifact-only directory, `doctor --verbose` printed the `NODE_ENV` row and its 11 fix lines byte-identical (`cmp`) from this branch's CLI and from the published 17.7.0 CLI. - The `doctor.ts` diff removes no line of the warning's literal. ## Verification All of the following ran on HEAD `12e2b1ed`. - **Build.** `pnpm turbo run build --filter=@objectstack/cli... --concurrency=2`: 59 tasks, 57 cached, exit 0. - **The two pin files.** `vitest run --project unit` on the two pin files: `Test Files 2 passed (2)`, `Tests 47 passed (47)`. - **`@objectstack/cli` unit tier.** `Test Files 1 failed | 262 passed (263)`, `Tests 1 failed | 3886 passed (3887)`. - The one failure is `test/hook-timeout-override-refusal.test.ts › ⭐ CONTROL — a run naming no override passes and says nothing`, with `Test timed out in 5000ms`. That file does not touch doctor. - The run held the shared box for 10m47s while the gate battery ran beside it. - Rerun alone: `Test Files 1 passed (1)`, `Tests 4 passed (4)`. - **The integration tier** is declared to CI: the diff touches no integration-tier file and no spawn entry. - **`@objectstack/cli` typecheck:** exit 0. - `tsc --noEmit --listFiles` counts both pin files in the program. - `check:test-typecheck: OK`. - **Gates.** The PM's 64 commands match the list re-derived by `dispatch-gates.mjs` on `12e2b1ed` line for line. All 64 exited 0. - `check:dual-build-cjs-loads` and `check:i18n-coverage` first exited 3 (PREREQUISITE NOT MET: packages unbuilt in this worktree). After a full `turbo run build` (72 tasks, 71 cached) both exited 0: - `✓ check:dual-build-cjs-loads — 106 published require entry point(s) across 66 package(s) load` - `check-i18n-coverage: OK (13 config(s), 621 baselined untranslated string(s), none new)` - `dispatch-gates --ran`: `64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED`. - **Full `pnpm lint`** (`eslint . --no-inline-config`): exit 0, with no findings printed. ## Acceptance notes - **H4, the two rows side by side.** They do not contradict on the facts. `Environment files` reports the cascade doctor resolved, which is the one `os serve` / `os start` load with `NODE_ENV` unset. The `NODE_ENV` row now says what `os dev` does. A newcomer still reads `node_env=production` one line above "runs this project as development", so a milder form of this card's confusion remains on that row. - **The cascade gap behind it.** In a source checkout, doctor attributes `OS_*` inputs against the production cascade, while `os dev` loads the development one. That is the boundary the PM ruling on #5387 drew when it declined a `--dev` mode for doctor. It is not changed here. Following up is the `domain:cli` seat's call. Carrier: `domain:cli` seat. - **`OS_ARTIFACT_PATH` set to the conventional path.** `OS_ARTIFACT_PATH=./dist/objectstack.json` beside a config counts as the source posture. That is the shared predicate's answer (the artifact is the config's own compiled output), not a separate rule here. --- _Generated by [Claude Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0b7ba4d commit b9e6e5c

4 files changed

Lines changed: 329 additions & 12 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
fix(cli): `os doctor` no longer tells a freshly scaffolded project that it is running in production (#22163)
6+
7+
Clause-②: no
8+
9+
- **What changed.** With `NODE_ENV` unset, in a directory whose `objectstack.config.ts` boots as itself, the `NODE_ENV` row now reads `✓ Not set — os dev runs this project as development; os start forces production`, with no fix text. That covers a config alone, and a config beside its own compiled `dist/objectstack.json`, which the first `os dev` writes. Before, the row read `⚠ Not set — this environment is being treated as production` and, under `--verbose`, told the operator to set `NODE_ENV=development`, which `os dev` already sets.
10+
- **What did not change.** In every other directory, the `⚠ … treated as production` warning and its fix text are the same as before. That includes a config beside `OS_ARTIFACT_URL` or beside an `OS_ARTIFACT_PATH` naming another artifact, an artifact with no config, and a directory with neither. `doctor` reads the two variables the way `os dev` does: from the process environment over the `.env*` files loaded for development.
11+
- **Nothing else moves.** The `Environment files` row still reports the `.env*` cascade for `node_env=production`. The `/discovery` `environment` default is unchanged, and so is the way `os serve`, `os start` and `os dev` resolve the mode. A `NODE_ENV` that is set still prints no row, and `doctor`'s exit code is unchanged.

‎packages/cli/src/commands/doctor-env-provenance.test.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,23 @@ describe('DOCTOR_ENV_INPUTS — the declaration a new env-derived check must joi
327327
// nothing, add it to a prose allowance here and say why; if doctor reads
328328
// it, declare it in DOCTOR_ENV_INPUTS so its provenance is reported.
329329
const PROSE_ONLY = new Set<string>();
330+
// [#22163] Read by `nodeEnvSourcePosture` only to choose WHICH of the two
331+
// NODE_ENV rows prints; no row reports either value. Not declared above on
332+
// purpose: DOCTOR_ENV_INPUTS is attributed against the cascade doctor
333+
// resolves for doctorNodeEnv() (node_env=production), while these two are
334+
// read through the one `os dev` loads (node_env=development), so a line in
335+
// the `Environment files` row would name the wrong file. And setting either
336+
// can only move that choice back to the #5673 row, whose text does not
337+
// depend on them. Every OTHER name still has to be declared.
338+
const POSTURE_ONLY = new Set<string>(['OS_ARTIFACT_URL', 'OS_ARTIFACT_PATH']);
330339

331340
for (const name of named) {
332-
if (PROSE_ONLY.has(name)) continue;
341+
if (PROSE_ONLY.has(name) || POSTURE_ONLY.has(name)) continue;
333342
expect(DOCTOR_ENV_INPUTS as readonly string[]).toContain(name);
334343
}
344+
// The allowance cannot outlive its reader: a name doctor.ts stopped using
345+
// leaves this set, rather than waving through a future read of it.
346+
for (const name of POSTURE_ONLY) expect(named).toContain(name);
335347
// Guard the guard: a regex that matched nothing would pass vacuously.
336348
expect(named.size).toBeGreaterThan(0);
337349
});

‎packages/cli/src/commands/doctor-node-env-default.test.ts‎

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@
3636
*
3737
* The `/discovery` half of the same ruling lives in
3838
* `packages/runtime/src/discovery-schema-conformance.test.ts`.
39+
*
40+
* ── #22163: one posture where the row reads as development ──────────────
41+
*
42+
* The maintainer's direction on #22163: beside an `objectstack.config.ts` with
43+
* no artifact from elsewhere — the source checkout `os dev` serves — an unset
44+
* `NODE_ENV` reads as development; everywhere else #5673's row stands. So the
45+
* cases below that pin the `warning` are the cases OUTSIDE that posture (an
46+
* empty temp directory, or an explicit `sourcePosture: false`), and the
47+
* `#22163` blocks pin the posture itself and the row it selects. The new row's
48+
* wording is pinned only by the subjects it names (`os dev`, `os start`) and by
49+
* the production sentence being absent.
3950
*/
4051

4152
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
@@ -44,7 +55,7 @@ import os from 'node:os';
4455
import path from 'node:path';
4556
import { fileURLToPath } from 'node:url';
4657

47-
import Doctor, { nodeEnvCheck, doctorNodeEnv } from './doctor.js';
58+
import Doctor, { nodeEnvCheck, nodeEnvSourcePosture, doctorNodeEnv } from './doctor.js';
4859

4960
/** `packages/cli` — the oclif root the real command is loaded against below. */
5061
const CLI_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
@@ -127,6 +138,98 @@ describe('[#5673] nodeEnvCheck — the row, and when it exists', () => {
127138
});
128139
});
129140

141+
describe('[#22163] nodeEnvCheck — which row the posture selects', () => {
142+
it('in the source posture: `ok`, naming `os dev` and `os start`, with no fix', () => {
143+
const finding = nodeEnvCheck({} as NodeJS.ProcessEnv, { sourcePosture: true })!;
144+
145+
expect(finding.name).toBe('NODE_ENV');
146+
// `ok` renders `✓` and can never reach `hasWarnings` / `hasErrors`.
147+
expect(finding.status).toBe('ok');
148+
expect(finding.message).toContain('os dev');
149+
expect(finding.message).toContain('os start');
150+
expect(finding.message).not.toContain('treated as production');
151+
// Nothing to fix, so nothing for `--verbose` to expand.
152+
expect(finding.fix).toBeUndefined();
153+
});
154+
155+
it("outside it: the #5673 warning and its fix, exactly what every existing caller gets", () => {
156+
// The default IS "outside": a caller that never asked about the posture —
157+
// every caller before #22163 — keeps the conservative row. The describe
158+
// block above pins that row's halves; this pins that the explicit `false`
159+
// is the same object, field for field.
160+
const explicit = nodeEnvCheck({} as NodeJS.ProcessEnv, { sourcePosture: false });
161+
expect(explicit).toEqual(nodeEnvCheck({} as NodeJS.ProcessEnv));
162+
expect(explicit!.status).toBe('warning');
163+
expect(explicit!.fix).toBeDefined();
164+
});
165+
166+
it('a SET NODE_ENV prints no row in either posture', () => {
167+
for (const sourcePosture of [true, false]) {
168+
for (const value of ['production', 'development']) {
169+
expect(nodeEnvCheck({ NODE_ENV: value } as NodeJS.ProcessEnv, { sourcePosture }), `${value}/${sourcePosture}`)
170+
.toBeUndefined();
171+
}
172+
}
173+
});
174+
});
175+
176+
describe('[#22163] nodeEnvSourcePosture — the source checkout `os dev` serves', () => {
177+
let dir: string;
178+
beforeEach(() => {
179+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'os-doctor-22163-posture-'));
180+
});
181+
afterEach(() => {
182+
fs.rmSync(dir, { recursive: true, force: true });
183+
});
184+
185+
const write = (rel: string, body: string) => {
186+
fs.mkdirSync(path.dirname(path.join(dir, rel)), { recursive: true });
187+
fs.writeFileSync(path.join(dir, rel), body);
188+
};
189+
const NO_ENV = {} as NodeJS.ProcessEnv;
190+
const CONFIG = 'export default {};\n';
191+
192+
it('a config alone is the source posture', () => {
193+
write('objectstack.config.ts', CONFIG);
194+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(true);
195+
});
196+
197+
it('…and stays so beside its OWN compiled `dist/objectstack.json` — the file the first `os dev` writes', () => {
198+
write('objectstack.config.ts', CONFIG);
199+
write('dist/objectstack.json', '{}\n');
200+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(true);
201+
});
202+
203+
it('a config beside a NAMED artifact is not: OS_ARTIFACT_PATH elsewhere, or OS_ARTIFACT_URL', () => {
204+
write('objectstack.config.ts', CONFIG);
205+
const elsewhere = path.join(dir, 'release', 'objectstack.json');
206+
expect(nodeEnvSourcePosture(dir, { OS_ARTIFACT_PATH: elsewhere } as NodeJS.ProcessEnv)).toBe(false);
207+
expect(nodeEnvSourcePosture(dir, { OS_ARTIFACT_URL: 'https://example.invalid/a.json' } as NodeJS.ProcessEnv))
208+
.toBe(false);
209+
});
210+
211+
it('an artifact with no config, and a directory with neither, are not', () => {
212+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(false);
213+
write('dist/objectstack.json', '{}\n');
214+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(false);
215+
});
216+
217+
it('reads the artifact variables where `os dev` does: over the node_env=development `.env*` cascade', () => {
218+
// `os dev` loads `.env*` for development BEFORE it resolves the rungs, so a
219+
// reference committed to `.env.development` drives its boot — and is a named
220+
// artifact here too.
221+
write('objectstack.config.ts', CONFIG);
222+
write('.env.development', 'OS_ARTIFACT_URL=https://example.invalid/a.json\n');
223+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(false);
224+
});
225+
226+
it('…and not over the production one, which `os dev` never loads', () => {
227+
write('objectstack.config.ts', CONFIG);
228+
write('.env.production', 'OS_ARTIFACT_URL=https://example.invalid/a.json\n');
229+
expect(nodeEnvSourcePosture(dir, NO_ENV)).toBe(true);
230+
});
231+
});
232+
130233
describe('[#5673] os doctor, end to end — the row reaches the report', () => {
131234
/**
132235
* `node_modules/` exists in the temp cwd on purpose — without it doctor's
@@ -224,4 +327,91 @@ describe('[#5673] os doctor, end to end — the row reaches the report', () => {
224327
expect(verbose.out).toContain('NODE_ENV=development');
225328
expect(verbose.out).toContain('NODE_ENV=production');
226329
}, E2E_TIMEOUT);
330+
331+
// ── #22163 — the posture decides which row, in the real report ──────────
332+
//
333+
// The case above IS the "empty directory" leg: its temp cwd holds only
334+
// `node_modules/`, so it is outside the source posture and keeps the
335+
// `warning`. The legs below add a config to the same cwd.
336+
describe('[#22163] the source checkout `os dev` serves reads as development', () => {
337+
const ARTIFACT_VARS = ['OS_ARTIFACT_URL', 'OS_ARTIFACT_PATH'] as const;
338+
let savedArtifactVars: Record<string, string | undefined> = {};
339+
340+
beforeEach(() => {
341+
savedArtifactVars = Object.fromEntries(ARTIFACT_VARS.map((k) => [k, process.env[k]]));
342+
for (const k of ARTIFACT_VARS) delete process.env[k];
343+
// The scaffold's own first line. Nothing under this temp cwd can resolve
344+
// it (its `node_modules/` is empty), so the config LOAD fails — on
345+
// purpose: the posture is read from the file's presence, never from a
346+
// load, and these legs prove it by passing beside a failed one.
347+
fs.writeFileSync(
348+
path.join(tmp, 'objectstack.config.ts'),
349+
"import { defineStack } from '@objectstack/spec';\nexport default defineStack({});\n",
350+
);
351+
});
352+
353+
afterEach(() => {
354+
for (const k of ARTIFACT_VARS) {
355+
if (savedArtifactVars[k] === undefined) delete process.env[k];
356+
else process.env[k] = savedArtifactVars[k];
357+
}
358+
});
359+
360+
/** The report rows whose name column is `NODE_ENV`. */
361+
const nodeEnvRows = (out: string) => out.split('\n').filter((line) => /^\s*\S+\s+NODE_ENV {2,}/.test(line));
362+
363+
it('(a) a config alone: the development row, beside a config that did not load', async () => {
364+
delete process.env.NODE_ENV;
365+
366+
const run = await runDoctor();
367+
368+
const rows = nodeEnvRows(run.out);
369+
expect(rows).toHaveLength(1);
370+
expect(rows[0]).toContain('✓');
371+
expect(rows[0]).toContain('os dev');
372+
expect(rows[0]).toContain('os start');
373+
expect(run.out).not.toContain('treated as production');
374+
// The load really failed, so the row above was not decided by it.
375+
expect(run.out).toContain('Could not load config for analysis');
376+
expect(run.exitCode).toBeUndefined();
377+
}, E2E_TIMEOUT);
378+
379+
it('(b) …and still after the first `os dev` compiled it to `dist/objectstack.json`', async () => {
380+
delete process.env.NODE_ENV;
381+
fs.mkdirSync(path.join(tmp, 'dist'));
382+
fs.writeFileSync(path.join(tmp, 'dist', 'objectstack.json'), '{}\n');
383+
384+
const run = await runDoctor();
385+
386+
const rows = nodeEnvRows(run.out);
387+
expect(rows).toHaveLength(1);
388+
expect(rows[0]).toContain('os dev');
389+
expect(run.out).not.toContain('treated as production');
390+
expect(run.exitCode).toBeUndefined();
391+
}, E2E_TIMEOUT);
392+
393+
it('(c) a config beside an OS_ARTIFACT_PATH naming another artifact: the #5673 warning', async () => {
394+
delete process.env.NODE_ENV;
395+
process.env.OS_ARTIFACT_PATH = path.join(tmp, 'release', 'objectstack.json');
396+
397+
const run = await runDoctor();
398+
399+
const rows = nodeEnvRows(run.out);
400+
expect(rows).toHaveLength(1);
401+
expect(rows[0]).toContain('⚠');
402+
expect(rows[0]).toContain('treated as production');
403+
expect(run.exitCode).toBeUndefined();
404+
}, E2E_TIMEOUT);
405+
406+
it('(e) NODE_ENV set in the source posture: no row at all', async () => {
407+
for (const value of ['development', 'production']) {
408+
process.env.NODE_ENV = value;
409+
410+
const run = await runDoctor();
411+
412+
expect(nodeEnvRows(run.out), value).toHaveLength(0);
413+
expect(run.exitCode).toBeUndefined();
414+
}
415+
}, E2E_TIMEOUT * 2);
416+
});
227417
});

0 commit comments

Comments
 (0)