@smoothbricks/cli 0.11.14 → 0.11.16
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/dist/lib/json.d.ts +9 -0
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +65 -53
- package/dist/monorepo/managed-files.d.ts +9 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +28 -2
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +52 -2
- package/dist/release/github-release.d.ts +16 -0
- package/dist/release/github-release.d.ts.map +1 -1
- package/dist/release/github-release.js +11 -2
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +29 -7
- package/dist/release/private-npm.d.ts +5 -0
- package/dist/release/private-npm.d.ts.map +1 -1
- package/dist/release/private-npm.js +26 -5
- package/managed/raw/tooling/direnv/devenv.smoo.nix +4 -2
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +38 -0
- package/managed/raw/tooling/direnv/secret-references.ts +66 -0
- package/managed/raw/tooling/direnv/setup-environment.ts +81 -26
- package/managed/raw/tooling/direnv/toolchain-stamp.ts +88 -0
- package/managed/templates/github/actions/setup-devenv/action.yml +4 -2
- package/package.json +2 -2
- package/src/lib/json.ts +9 -0
- package/src/monorepo/__tests__/publish-workflow.test.ts +54 -0
- package/src/monorepo/managed-files.test.ts +26 -0
- package/src/monorepo/managed-files.ts +36 -2
- package/src/monorepo/publish-workflow.ts +70 -2
- package/src/monorepo/secret-references.test.ts +68 -0
- package/src/release/__tests__/private-npm-status.test.ts +37 -0
- package/src/release/__tests__/private-npm-workflow.test.ts +20 -0
- package/src/release/github-release.ts +31 -2
- package/src/release/index.ts +30 -4
- package/src/release/private-npm.ts +30 -5
|
@@ -45,6 +45,22 @@ export declare function nxProjectChangelogArgs(pkg: ReleasePackageInfo, previous
|
|
|
45
45
|
export declare function projectChangelogContents(result: ProjectChangelogLookupResult, projectName: string): string;
|
|
46
46
|
export declare function githubReleaseLookupExists(tag: string, exitCode: number, stdout: string, stderr: string): boolean;
|
|
47
47
|
export declare function githubReleaseCommandArgs(tag: string, notesFile: string, releaseExists: boolean, version: string): string[];
|
|
48
|
+
/**
|
|
49
|
+
* A module of the WORKSPACE's Nx, not of smoo's own dependency. smoo installs
|
|
50
|
+
* with its own `nx` in the global virtual store, where the workspace's
|
|
51
|
+
* node_modules is invisible: that Nx cannot resolve the workspace's
|
|
52
|
+
* `versionActions` plugin (`Unable to resolve the "versionActions"
|
|
53
|
+
* implementation ... "@smoothbricks/nx-plugin/version-actions"`), and it is
|
|
54
|
+
* a second Nx version driving one release. Every in-process Nx call for a
|
|
55
|
+
* workspace resolves from that workspace's root, exactly as `nx` on its PATH
|
|
56
|
+
* would.
|
|
57
|
+
*/
|
|
58
|
+
interface WorkspaceNxModules {
|
|
59
|
+
'src/utils/workspace-root.js': typeof import('nx/src/utils/workspace-root.js');
|
|
60
|
+
'src/command-line/release/version.js': typeof import('nx/src/command-line/release/version.js');
|
|
61
|
+
'src/command-line/release/changelog.js': typeof import('nx/src/command-line/release/changelog.js');
|
|
62
|
+
}
|
|
63
|
+
export declare function importWorkspaceNx<Subpath extends keyof WorkspaceNxModules>(root: string, subpath: Subpath): Promise<WorkspaceNxModules[Subpath]>;
|
|
48
64
|
export declare function withNxWorkspaceRoot<T>(root: string, run: () => Promise<T>): Promise<T>;
|
|
49
65
|
export {};
|
|
50
66
|
//# sourceMappingURL=github-release.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-release.d.ts","sourceRoot":"","sources":["../../src/release/github-release.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"github-release.d.ts","sourceRoot":"","sources":["../../src/release/github-release.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,kBAAkB,EAAc,MAAM,WAAW,CAAC;AAYhE,UAAU,4BAA4B;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,6BAA6B,CAAC,OAAO,SAAS,kBAAkB,GAAG,kBAAkB;IACpG,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAsB,gCAAgC,CAAC,KAAK,EAAE,6BAA6B,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5G;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAc1G;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAM1G;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAShH;AAED,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,OAAO,EACtB,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CAgBV;AAMD;;;;;;;;;GASG;AACH,UAAU,kBAAkB;IAC1B,6BAA6B,EAAE,cAAc,gCAAgC,CAAC,CAAC;IAC/E,qCAAqC,EAAE,cAAc,wCAAwC,CAAC,CAAC;IAC/F,uCAAuC,EAAE,cAAc,0CAA0C,CAAC,CAAC;CACpG;AAED,wBAAsB,iBAAiB,CAAC,OAAO,SAAS,MAAM,kBAAkB,EAC9E,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAMtC;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAmB5F"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import { tmpdir } from 'node:os';
|
|
3
4
|
import { join } from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
4
6
|
import { releaseTag } from './core.js';
|
|
5
7
|
const nxRenderOnlyReleaseConfig = {
|
|
6
8
|
changelog: {
|
|
@@ -13,7 +15,7 @@ const nxRenderOnlyReleaseConfig = {
|
|
|
13
15
|
};
|
|
14
16
|
export async function renderNxProjectChangelogContents(input) {
|
|
15
17
|
return withNxWorkspaceRoot(input.root, async () => {
|
|
16
|
-
const { createAPI } = await
|
|
18
|
+
const { createAPI } = await importWorkspaceNx(input.root, 'src/command-line/release/changelog.js');
|
|
17
19
|
const result = await createAPI(nxRenderOnlyReleaseConfig, false)(nxProjectChangelogArgs(input.pkg, input.previousTag, input.dryRun));
|
|
18
20
|
return projectChangelogContents(result, input.pkg.projectName);
|
|
19
21
|
});
|
|
@@ -84,8 +86,15 @@ export function githubReleaseCommandArgs(tag, notesFile, releaseExists, version)
|
|
|
84
86
|
function isPrereleaseVersion(version) {
|
|
85
87
|
return version.includes('-');
|
|
86
88
|
}
|
|
89
|
+
export async function importWorkspaceNx(root, subpath) {
|
|
90
|
+
const resolved = createRequire(join(root, 'package.json')).resolve(`nx/${subpath}`);
|
|
91
|
+
// A dynamic specifier types as `any`; the map above is the declared shape of
|
|
92
|
+
// the module the workspace's Nx serves at that subpath.
|
|
93
|
+
const module = await import(pathToFileURL(resolved).href);
|
|
94
|
+
return module;
|
|
95
|
+
}
|
|
87
96
|
export async function withNxWorkspaceRoot(root, run) {
|
|
88
|
-
const workspaceRootModule = await
|
|
97
|
+
const workspaceRootModule = await importWorkspaceNx(root, 'src/utils/workspace-root.js');
|
|
89
98
|
const previousWorkspaceRoot = workspaceRootModule.workspaceRoot;
|
|
90
99
|
const previousEnvWorkspaceRoot = process.env.NX_WORKSPACE_ROOT_PATH;
|
|
91
100
|
const previousCwd = process.cwd();
|
package/dist/release/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { assertPackedArtifact, type ReleasePackManifest, type ReleasePackOptions, type ReleasePackPackageEntry, releasePack, resolvePackClosure, } from './pack.js';
|
|
2
|
-
export { assertPrivatePackage, checkPrivatePackage, isPrivatePackage, type NpmStatusOptions, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, type PrivateNpmRegistry, type PrivateNpmRegistryConfigError, type PrivateNpmRegistryConfigErrorKind, type PublishDestination, privateNpmPublishArgs, privateNpmUserconfigContent, publishPrivateWithDiagnostics, type Result as PrivateNpmResult, requirePrivateNpmRegistry, resolvePrivateNpmRegistry, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
2
|
+
export { assertPrivatePackage, checkPrivatePackage, isPrivatePackage, type NpmStatusOptions, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, type PrivateNpmRegistry, type PrivateNpmRegistryConfigError, type PrivateNpmRegistryConfigErrorKind, type PublishDestination, privateNpmPublishArgs, privateNpmTokenEnvForMode, privateNpmUserconfigContent, publishPrivateWithDiagnostics, type Result as PrivateNpmResult, requirePrivateNpmRegistry, resolvePrivateNpmRegistry, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
3
3
|
import { type BootstrapNpmPackagesOptions } from './bootstrap-npm-packages.js';
|
|
4
4
|
import { type ReleasePackageInfo } from './core.js';
|
|
5
5
|
export interface ReleaseVersionOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,WAAW,EACX,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,gBAAgB,IAAI,0BAA0B,EAC9C,yBAAyB,IAAI,mCAAmC,EAChE,KAAK,kBAAkB,EACvB,KAAK,6BAA6B,EAClC,KAAK,iCAAiC,EACtC,KAAK,kBAAkB,EACvB,qBAAqB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,KAAK,MAAM,IAAI,gBAAgB,EAC/B,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"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"}
|
package/dist/release/index.js
CHANGED
|
@@ -15,18 +15,19 @@ import { parseNxJsonText, parseNxProjectJsonText, parsePackageJsonText, } from '
|
|
|
15
15
|
import { decode, run, runInteractiveStatus, runResult, runText } from '../lib/run.js';
|
|
16
16
|
import { getWorkspacePackages, listPublicReleasePackages, listReleasePackages, readPackageJson, repositoryInfo, } from '../lib/workspace.js';
|
|
17
17
|
export { assertPackedArtifact, releasePack, resolvePackClosure, } from './pack.js';
|
|
18
|
-
export { assertPrivatePackage, checkPrivatePackage, isPrivatePackage, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, privateNpmPublishArgs, privateNpmUserconfigContent, publishPrivateWithDiagnostics, requirePrivateNpmRegistry, resolvePrivateNpmRegistry, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
18
|
+
export { assertPrivatePackage, checkPrivatePackage, isPrivatePackage, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, privateNpmPublishArgs, privateNpmTokenEnvForMode, privateNpmUserconfigContent, publishPrivateWithDiagnostics, requirePrivateNpmRegistry, resolvePrivateNpmRegistry, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
19
|
+
import { PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-policy';
|
|
19
20
|
import { ciApiContext } from '../github-ci/api.js';
|
|
20
21
|
import { readProjectTargets } from '../nx/index.js';
|
|
21
22
|
import { bootstrapNpmPackages, NPM_BOOTSTRAP_DIST_TAG, NPM_BOOTSTRAP_VERSION, } from './bootstrap-npm-packages.js';
|
|
22
23
|
import { resolveBuildInputPatterns } from './build-inputs.js';
|
|
23
24
|
import { parseReleaseProjectSelection, releaseCandidatePackages, } from './candidates.js';
|
|
24
25
|
import { classifyReleaseBranchPush, collectOwnedReleaseTagRecords, divergedReleaseBranchMessage, pendingReleaseTargets, releaseTag, releaseTagAliases, } from './core.js';
|
|
25
|
-
import { createOrUpdateGithubRelease, githubReleaseLookupExists, renderNxProjectChangelogContents, withNxWorkspaceRoot, } from './github-release.js';
|
|
26
|
+
import { createOrUpdateGithubRelease, githubReleaseLookupExists, importWorkspaceNx, renderNxProjectChangelogContents, withNxWorkspaceRoot, } from './github-release.js';
|
|
26
27
|
import { publishWithAuthDiagnostics } from './npm-auth.js';
|
|
27
28
|
import { bumpStableReleaseToNext, collectRepairPlatformOutputs, completeReleaseAtHead as completeReleaseAtHeadWithShell, prepareReleaseCandidate, repairPendingTargets, runReleaseTag, runReleaseVersion, } from './orchestration.js';
|
|
28
29
|
import { packReleaseTarball } from './pack.js';
|
|
29
|
-
import { assertPrivatePackage, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, privateNpmPublishArgs, publishPrivateWithDiagnostics, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
30
|
+
import { assertPrivatePackage, npmPackageExists as npmPackageExistsOnRegistry, npmPublishedVersionExists as npmPublishedVersionExistsOnRegistry, privateNpmPublishArgs, privateNpmTokenEnvForMode, publishPrivateWithDiagnostics, selectPublishDestination, selectRegistryForPackage, withPrivateNpmUserconfig, } from './private-npm.js';
|
|
30
31
|
import { retagUnpublished } from './retag-unpublished.js';
|
|
31
32
|
import { forgejoAuthHeaders, forgejoReleaseLookupExists, resolveSourceReleaseEndpoint, resolveSourceRepository, sourceReleaseTokenEnvNames, sourceReleaseUrl, } from './source-release.js';
|
|
32
33
|
export async function releaseVersion(root, options) {
|
|
@@ -426,7 +427,12 @@ async function publishPrivatePackedPackage(root, pkg, tag, dryRun, registry) {
|
|
|
426
427
|
}
|
|
427
428
|
await withPrivateNpmUserconfig(registry, 'publish', async (userconfig) => {
|
|
428
429
|
await publishPrivateWithDiagnostics(pkg, {
|
|
429
|
-
publish: () => runNpm(root, args, {
|
|
430
|
+
publish: () => runNpm(root, args, {
|
|
431
|
+
NPM_CONFIG_USERCONFIG: userconfig,
|
|
432
|
+
// Env config outranks a committed project `.npmrc` auth line; see
|
|
433
|
+
// npmStatusEnv in private-npm.ts for why that matters here.
|
|
434
|
+
[`npm_config_${registry.authKey}`]: process.env[privateNpmTokenEnvForMode(registry, 'publish')] ?? '',
|
|
435
|
+
}),
|
|
430
436
|
// The open publish userconfig already authenticates this registry,
|
|
431
437
|
// so the failure probe reuses it. Opening a second read-mode
|
|
432
438
|
// userconfig here demanded a read credential the publishing job need
|
|
@@ -435,6 +441,7 @@ async function publishPrivatePackedPackage(root, pkg, tag, dryRun, registry) {
|
|
|
435
441
|
versionExists: () => npmPublishedVersionExistsOnRegistry(root, pkg.name, pkg.version, {
|
|
436
442
|
registry: registry.registry,
|
|
437
443
|
userconfig,
|
|
444
|
+
credential: { authKey: registry.authKey, tokenEnv: privateNpmTokenEnvForMode(registry, 'publish') },
|
|
438
445
|
}),
|
|
439
446
|
log: (message) => console.log(message),
|
|
440
447
|
error: (message) => console.error(message),
|
|
@@ -520,8 +527,8 @@ async function runNxReleaseVersion(root, projects, bump, dryRun) {
|
|
|
520
527
|
}
|
|
521
528
|
async function runNxReleaseVersionPreview(root, projects, bump) {
|
|
522
529
|
return withNxWorkspaceRoot(root, async () => {
|
|
523
|
-
const { createAPI } = await
|
|
524
|
-
const { createAPI: createChangelogAPI } = await
|
|
530
|
+
const { createAPI } = await importWorkspaceNx(root, 'src/command-line/release/version.js');
|
|
531
|
+
const { createAPI: createChangelogAPI } = await importWorkspaceNx(root, 'src/command-line/release/changelog.js');
|
|
525
532
|
const projectNames = projects.split(',').filter(Boolean);
|
|
526
533
|
const result = await createAPI({}, false)({
|
|
527
534
|
specifier: bump === 'auto' ? undefined : bump,
|
|
@@ -782,6 +789,17 @@ function releaseRepairShell(root, platformOutputs) {
|
|
|
782
789
|
const sourceSha = await gitHead(root);
|
|
783
790
|
const outputs = platformOutputs.map((base) => join(base, sourceSha));
|
|
784
791
|
if (outputs.length === 0) {
|
|
792
|
+
// No producer job handed outputs over, so this runner is the producer:
|
|
793
|
+
// build the platform legs at the checked-out ref. `build` alone gives a
|
|
794
|
+
// package only the legs its own graph depends on — the host one and
|
|
795
|
+
// whatever it declares — and the release gate demands the whole declared
|
|
796
|
+
// native closure, so a foreign leg it never asked for was simply absent.
|
|
797
|
+
console.log('Repair pending releases: building cross-platform outputs on this runner.');
|
|
798
|
+
await githubCiNxRunMany(root, {
|
|
799
|
+
targets: PLATFORM_TARGET_GLOBS.join(','),
|
|
800
|
+
projects: releasePackageProjects(packages),
|
|
801
|
+
allowEmptyProjects: true,
|
|
802
|
+
});
|
|
785
803
|
return;
|
|
786
804
|
}
|
|
787
805
|
console.log(`Repair pending releases: applying cross-platform outputs from ${outputs.join(', ')}.`);
|
|
@@ -1366,7 +1384,11 @@ async function npmVersionExists(root, name, version) {
|
|
|
1366
1384
|
assertPublicStatusAllowed(root, name);
|
|
1367
1385
|
return npmPublishedVersionExistsOnRegistry(root, name, version);
|
|
1368
1386
|
}
|
|
1369
|
-
return withPrivateNpmUserconfig(registry, 'read', (userconfig) => npmPublishedVersionExistsOnRegistry(root, name, version, {
|
|
1387
|
+
return withPrivateNpmUserconfig(registry, 'read', (userconfig) => npmPublishedVersionExistsOnRegistry(root, name, version, {
|
|
1388
|
+
registry: registry.registry,
|
|
1389
|
+
userconfig,
|
|
1390
|
+
credential: { authKey: registry.authKey, tokenEnv: privateNpmTokenEnvForMode(registry, 'read') },
|
|
1391
|
+
}));
|
|
1370
1392
|
}
|
|
1371
1393
|
async function npmPackageExists(root, name) {
|
|
1372
1394
|
const registry = selectRegistryForPackage(root, name);
|
|
@@ -103,6 +103,11 @@ export declare function withPrivateNpmUserconfig<T>(resolved: PrivateNpmRegistry
|
|
|
103
103
|
export interface NpmStatusOptions {
|
|
104
104
|
registry?: string;
|
|
105
105
|
userconfig?: string;
|
|
106
|
+
/** Path-scoped auth key and token env, so the credential can ride env config. */
|
|
107
|
+
credential?: {
|
|
108
|
+
authKey: string;
|
|
109
|
+
tokenEnv: string;
|
|
110
|
+
};
|
|
106
111
|
}
|
|
107
112
|
/**
|
|
108
113
|
* Failure-aware registry status: only a genuine not-found result means absent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-npm.d.ts","sourceRoot":"","sources":["../../src/release/private-npm.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAe,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAGL,KAAK,WAAW,EAIjB,MAAM,qBAAqB,CAAC;AAE7B,uEAAuE;AACvE,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,iCAAiC,GACzC,sBAAsB,GACtB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,CAAC;AAEpB,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,iCAAiC,CAAC;IACxC,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,kBAAkB,EAAE,6BAA6B,CAAC,CAAC;AAMhF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAwFtE;AAoFD;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"private-npm.d.ts","sourceRoot":"","sources":["../../src/release/private-npm.ts"],"names":[],"mappings":"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"}
|
|
@@ -179,7 +179,12 @@ export function resolvePrivateNpmWorkflowConfig(root) {
|
|
|
179
179
|
return undefined;
|
|
180
180
|
}
|
|
181
181
|
const npmrcEnv = npmrcAuthTokenEnv(root, declared.scope);
|
|
182
|
-
|
|
182
|
+
// A publishing workspace reads the registry too: every release after the
|
|
183
|
+
// first checks the previous tag's durable state, and that check runs in the
|
|
184
|
+
// producer jobs that never see the publish credential. A declared read token
|
|
185
|
+
// therefore always renders; only the .npmrc fallback stays consumer-only, so
|
|
186
|
+
// a publish token is never promoted into a read role by inference.
|
|
187
|
+
const readTokenEnv = declared.readTokenEnv ?? (consumes ? npmrcEnv : undefined);
|
|
183
188
|
const publishTokenEnv = publishes ? (declared.publishTokenEnv ?? npmrcEnv) : undefined;
|
|
184
189
|
if (!readTokenEnv && !publishTokenEnv) {
|
|
185
190
|
return undefined;
|
|
@@ -312,8 +317,24 @@ export async function withPrivateNpmUserconfig(resolved, mode, fn) {
|
|
|
312
317
|
await rm(dir, { recursive: true, force: true });
|
|
313
318
|
}
|
|
314
319
|
}
|
|
315
|
-
|
|
316
|
-
|
|
320
|
+
/**
|
|
321
|
+
* npm config precedence is cli > env > project `.npmrc` > userconfig. A
|
|
322
|
+
* repository that commits its own `//host/path:_authToken=${SOME_ENV}` line
|
|
323
|
+
* therefore OVERRIDES the userconfig this module writes, and when that env is
|
|
324
|
+
* unset npm sends the unexpanded value: the registry answers 401 on reads with
|
|
325
|
+
* a perfectly good read credential, naming nothing. So the credential also
|
|
326
|
+
* rides as env config, which the project file cannot outrank, and the token
|
|
327
|
+
* value stays out of every file on disk.
|
|
328
|
+
*/
|
|
329
|
+
function npmStatusEnv(userconfig, credential) {
|
|
330
|
+
if (!userconfig)
|
|
331
|
+
return undefined;
|
|
332
|
+
const env = { NPM_CONFIG_USERCONFIG: userconfig };
|
|
333
|
+
const token = credential ? process.env[credential.tokenEnv] : undefined;
|
|
334
|
+
if (credential && token) {
|
|
335
|
+
env[`npm_config_${credential.authKey}`] = token;
|
|
336
|
+
}
|
|
337
|
+
return env;
|
|
317
338
|
}
|
|
318
339
|
function npmCommandFailedMessage(npmArgs, exitCode, stdout = '', stderr = '') {
|
|
319
340
|
const detail = `${stdout}\n${stderr}`.trim();
|
|
@@ -330,7 +351,7 @@ export async function npmPublishedVersionExists(root, name, version, options = {
|
|
|
330
351
|
if (options.registry) {
|
|
331
352
|
args.push('--registry', options.registry);
|
|
332
353
|
}
|
|
333
|
-
const result = await runResult('npm', args, root, npmStatusEnv(options.userconfig));
|
|
354
|
+
const result = await runResult('npm', args, root, npmStatusEnv(options.userconfig, options.credential));
|
|
334
355
|
if (result.exitCode === 0) {
|
|
335
356
|
return true;
|
|
336
357
|
}
|
|
@@ -345,7 +366,7 @@ export async function npmPackageExists(root, name, options = {}) {
|
|
|
345
366
|
if (options.registry) {
|
|
346
367
|
args.push('--registry', options.registry);
|
|
347
368
|
}
|
|
348
|
-
const result = await runResult('npm', args, root, npmStatusEnv(options.userconfig));
|
|
369
|
+
const result = await runResult('npm', args, root, npmStatusEnv(options.userconfig, options.credential));
|
|
349
370
|
if (result.exitCode === 0) {
|
|
350
371
|
return true;
|
|
351
372
|
}
|
|
@@ -372,9 +372,11 @@
|
|
|
372
372
|
esac
|
|
373
373
|
''}
|
|
374
374
|
'')
|
|
375
|
-
# Epilogue
|
|
376
|
-
#
|
|
375
|
+
# Epilogue, after every project step (a project's own enterShell may resolve
|
|
376
|
+
# SDKROOT/compilers; the toolchain identity must see the final values). The
|
|
377
|
+
# stamp logic lives in toolchain-stamp.ts; see its header for why.
|
|
377
378
|
(lib.mkAfter ''
|
|
379
|
+
bun "$DEVENV_ROOT/toolchain-stamp.ts" || exit $?
|
|
378
380
|
if [ -n "$DEVENV_SHELL_PWD" ]; then
|
|
379
381
|
cd "$DEVENV_SHELL_PWD"
|
|
380
382
|
fi
|
|
@@ -140,14 +140,52 @@ devenv_path_and_caches() {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
build_devenv_shell() {
|
|
143
|
+
local since
|
|
144
|
+
since="$(date +%s)"
|
|
143
145
|
# One evaluation: the shell that captures the environment is the shell
|
|
144
146
|
# build. A separate `devenv shell -- date` evaluated everything twice.
|
|
145
147
|
persist_devenv_environment
|
|
148
|
+
# Only the ephemeral-runner path of setup-devenv opts in: the scan walks the
|
|
149
|
+
# whole store, which on a developer host or a host runner is large and not
|
|
150
|
+
# ours to root.
|
|
151
|
+
if [ "${SMOO_ROOT_BUILD_INPUTS:-false}" = true ]; then
|
|
152
|
+
root_build_inputs "$since"
|
|
153
|
+
fi
|
|
146
154
|
# Add repo-local tools only after the shell exists; cleanup steps use an
|
|
147
155
|
# explicit PATH because failures before this point must still refresh caches.
|
|
148
156
|
add_repo_paths
|
|
149
157
|
}
|
|
150
158
|
|
|
159
|
+
# The store cache's post phase collects garbage down to the live roots before
|
|
160
|
+
# it saves, and the shell's own root (.devenv/gc/shell) covers only the build
|
|
161
|
+
# closure. What evaluation fetched and built - flake input sources, the
|
|
162
|
+
# import-from-derivation outputs devenv-nixpkgs-patched and cabal2nix-* - has
|
|
163
|
+
# no root, so it was collected on the miss run and every hit run rebuilt it
|
|
164
|
+
# (measured at ~80s of a 150s setup on hosted macOS, run 34427309827). devenv
|
|
165
|
+
# records no store paths in its eval cache, so the exact set is the paths this
|
|
166
|
+
# build registered: root them under .devenv/gc, which the devenv cache carries
|
|
167
|
+
# and /nix/var/nix/gcroots/auto (inside the store cache) points at.
|
|
168
|
+
root_build_inputs() {
|
|
169
|
+
local since="$1" gcdir="$PWD/.devenv/gc/build-inputs" paths
|
|
170
|
+
rm -rf "$gcdir"
|
|
171
|
+
mkdir -p "$gcdir"
|
|
172
|
+
# `nix path-info --all --json` is an object keyed by path on Nix >= 2.19 and
|
|
173
|
+
# an array of {path} objects before that.
|
|
174
|
+
paths="$(nix path-info --all --json 2>/dev/null | jq -r --argjson since "$since" '
|
|
175
|
+
(if type == "array" then map({key: .path, value: .}) | from_entries else . end)
|
|
176
|
+
| to_entries[] | select(.value.registrationTime >= $since) | select(.key | endswith(".drv") | not) | .key')"
|
|
177
|
+
if [ -z "$paths" ]; then
|
|
178
|
+
echo "no store paths registered since $since; nothing to root"
|
|
179
|
+
return 0
|
|
180
|
+
fi
|
|
181
|
+
# Store paths are installables; --out-link names the first link and numbers
|
|
182
|
+
# the rest, each an indirect gc root. Derivations are excluded: keep-derivations
|
|
183
|
+
# retains the ones whose outputs are rooted, and realising a .drv would build it.
|
|
184
|
+
# shellcheck disable=SC2086
|
|
185
|
+
nix build --out-link "$gcdir/path" $paths
|
|
186
|
+
echo "rooted $(printf '%s\n' "$paths" | wc -l | tr -d ' ') store paths registered during the shell build under $gcdir"
|
|
187
|
+
}
|
|
188
|
+
|
|
151
189
|
# Workflow steps run their commands directly — never through `devenv shell` —
|
|
152
190
|
# so nothing the shell hooks export survives this composite action on its own.
|
|
153
191
|
# Persisting a hand-picked list here proved to be whack-a-mole: the TTSC
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
* Failures aggregate so a single direnv reload surfaces every problem.
|
|
27
27
|
* Error text names variables and exit codes only: secret values, provider
|
|
28
28
|
* stdout/stderr, and command arguments are never echoed.
|
|
29
|
+
* A resolved value still reaches the environment of the install its caller
|
|
30
|
+
* runs next, so `maskSecretValues` redacts those values out of any captured
|
|
31
|
+
* child output that caller replays.
|
|
29
32
|
*
|
|
30
33
|
* The variable `smoo.remoteCache.tokenSecret` names is never resolved here,
|
|
31
34
|
* nor anywhere at shell entry. A remote cache is an optimization, and shell
|
|
@@ -311,6 +314,69 @@ const runSecretCommand: SecretCommandRunner = async (argv) => {
|
|
|
311
314
|
return stdout;
|
|
312
315
|
};
|
|
313
316
|
|
|
317
|
+
/** '*', the byte a redacted secret leaves behind. */
|
|
318
|
+
const MASK_BYTE = 0x2a;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* A copy of captured child output with every resolved secret value replaced
|
|
322
|
+
* by the same number of `*` bytes.
|
|
323
|
+
*
|
|
324
|
+
* setup-environment.ts puts the values this file resolves into the
|
|
325
|
+
* environment of the install it then runs, and replays that child's captured
|
|
326
|
+
* stdout/stderr verbatim when it fails. Whatever the child echoes — a prepare
|
|
327
|
+
* script printing its environment, a registry URL carrying an inline
|
|
328
|
+
* credential — would otherwise walk straight past the suppression the rest of
|
|
329
|
+
* this file maintains. Redaction is byte-exact and same-length, so the
|
|
330
|
+
* failure being reported stays legible and every offset in it still lines up.
|
|
331
|
+
* Output with nothing to redact is returned as it came in, so the path that
|
|
332
|
+
* has no secrets to hide copies nothing.
|
|
333
|
+
*/
|
|
334
|
+
export function maskSecretValues(output: Uint8Array, values: Iterable<string>): Uint8Array {
|
|
335
|
+
const encoder = new TextEncoder();
|
|
336
|
+
let masked: Uint8Array | undefined;
|
|
337
|
+
for (const value of values) {
|
|
338
|
+
const needle = encoder.encode(value);
|
|
339
|
+
if (needle.length === 0 || needle.length > output.length) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
for (let from = 0; from + needle.length <= output.length; ) {
|
|
343
|
+
const at = indexOfBytes(masked ?? output, needle, from);
|
|
344
|
+
if (at < 0) {
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
// `new Uint8Array`, not `.slice()`: a captured child's output arrives as
|
|
348
|
+
// a node Buffer, whose `slice` is `subarray` — a view. Masking through
|
|
349
|
+
// one would rewrite the caller's own captured bytes.
|
|
350
|
+
masked ??= new Uint8Array(output);
|
|
351
|
+
masked.fill(MASK_BYTE, at, at + needle.length);
|
|
352
|
+
from = at + needle.length;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return masked ?? output;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** First occurrence of `needle` in `haystack` at or after `from`, or -1. */
|
|
359
|
+
function indexOfBytes(haystack: Uint8Array, needle: Uint8Array, from: number): number {
|
|
360
|
+
const first = needle[0];
|
|
361
|
+
if (first === undefined) {
|
|
362
|
+
return -1;
|
|
363
|
+
}
|
|
364
|
+
const last = haystack.length - needle.length;
|
|
365
|
+
for (let at = haystack.indexOf(first, from); at >= 0 && at <= last; at = haystack.indexOf(first, at + 1)) {
|
|
366
|
+
let matches = true;
|
|
367
|
+
for (let offset = 1; offset < needle.length; offset += 1) {
|
|
368
|
+
if (haystack[at + offset] !== needle[offset]) {
|
|
369
|
+
matches = false;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (matches) {
|
|
374
|
+
return at;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return -1;
|
|
378
|
+
}
|
|
379
|
+
|
|
314
380
|
/**
|
|
315
381
|
* The declared secrets an install needs, resolved before it runs. The cache
|
|
316
382
|
* token is deliberately not among them: a remote cache is an optimization, so
|
|
@@ -4,7 +4,7 @@ import { mkdir, rmdir, stat } from 'node:fs/promises';
|
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { $ } from 'bun';
|
|
7
|
-
import { resolveSecretEnvironment } from './secret-references.ts';
|
|
7
|
+
import { maskSecretValues, resolveSecretEnvironment } from './secret-references.ts';
|
|
8
8
|
|
|
9
9
|
// DEVENV_ROOT is set by the devenv shell, which is how this script normally
|
|
10
10
|
// runs. CI jobs that install dependencies without building that shell (the
|
|
@@ -24,6 +24,14 @@ class CapturedCommandError extends Error {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Every value resolved out of smoo.secrets below. The install inherits them,
|
|
28
|
+
// so whatever a failing child echoed is redacted before its captured output
|
|
29
|
+
// is replayed. Declared above the first replay site (resolveProjectRoot's own
|
|
30
|
+
// failure) because module consts are not hoisted: a reference from there to a
|
|
31
|
+
// const declared further down would report a temporal-dead-zone error instead
|
|
32
|
+
// of the failure it was called to report.
|
|
33
|
+
const resolvedSecretValues: string[] = [];
|
|
34
|
+
|
|
27
35
|
async function resolveProjectRoot(): Promise<string> {
|
|
28
36
|
if (devenvRoot) {
|
|
29
37
|
return path.resolve(`${devenvRoot}/../..`);
|
|
@@ -66,19 +74,11 @@ const TYPESCRIPT_API_VERSION = '6.0.3';
|
|
|
66
74
|
process.chdir(projectRoot);
|
|
67
75
|
|
|
68
76
|
try {
|
|
69
|
-
// Provider-declared secrets (smoo.secrets) resolve before any install. The
|
|
70
|
-
// values land in THIS process environment only — bun install, the prepare
|
|
71
|
-
// scripts it runs, and every later child of this script inherit them (for
|
|
72
|
-
// example .npmrc `${VAR}` auth); the direnv shell itself does not, which is
|
|
73
|
-
// the point: this script must never act as a global shell export.
|
|
74
|
-
for (const [name, value] of Object.entries(await resolveSecretEnvironment({ root: projectRoot }))) {
|
|
75
|
-
process.env[name] = value;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
77
|
// Bootstrap only: install deps + wire local git hooks/config.
|
|
79
78
|
// Do not import workspace packages here — this script is what installs them,
|
|
80
79
|
// and package resolution/Typia transforms are not available yet.
|
|
81
80
|
if (process.env.CI) {
|
|
81
|
+
await resolveSecrets();
|
|
82
82
|
// Concurrent devenv activations share one node_modules, so the CI
|
|
83
83
|
// installs race exactly like local ones (EEXIST link failures under
|
|
84
84
|
// parallel shells). Serialize them under the same setup lock. Nothing
|
|
@@ -118,24 +118,60 @@ try {
|
|
|
118
118
|
process.exit(1);
|
|
119
119
|
}
|
|
120
120
|
} else {
|
|
121
|
-
|
|
121
|
+
// A local secret-resolution or install failure (a provider that is not
|
|
122
|
+
// signed in, an unpublished private package, a missing registry
|
|
123
|
+
// credential, a lockfile that needs `devenv update`) must not take the
|
|
124
|
+
// shell down with it: direnv drops the whole environment on a non-zero
|
|
125
|
+
// exit, and then bun, nx, op and every other tool needed to repair the
|
|
126
|
+
// install are gone too. Report it, finish what does not depend on the
|
|
127
|
+
// install, and load the shell. CI above stays strict.
|
|
128
|
+
const installError = await installLocalDependencies();
|
|
129
|
+
if (installError === undefined) {
|
|
130
|
+
// Pin unscoped typescript → API 6 for root and Bun's shared .bun hoist (Nx).
|
|
131
|
+
ensureTypeScriptApiPackage(projectRoot);
|
|
132
|
+
}
|
|
133
|
+
await applyWorkspaceGitConfig(projectRoot);
|
|
134
|
+
if (installError !== undefined) {
|
|
135
|
+
reportDegradedSetup(installError);
|
|
136
|
+
}
|
|
122
137
|
}
|
|
123
|
-
|
|
124
|
-
// Pin unscoped typescript → API 6 for root and Bun's shared .bun hoist (Nx).
|
|
125
|
-
ensureTypeScriptApiPackage(projectRoot);
|
|
126
|
-
|
|
127
|
-
await applyWorkspaceGitConfig(projectRoot);
|
|
128
138
|
} catch (error) {
|
|
129
139
|
reportSetupFailure(error);
|
|
130
140
|
}
|
|
131
141
|
|
|
132
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Provider-declared secrets (smoo.secrets) resolve before any install. The
|
|
144
|
+
* values land in THIS process environment only — bun install, the prepare
|
|
145
|
+
* scripts it runs, and every later child of this script inherit them (for
|
|
146
|
+
* example .npmrc `${VAR}` auth); the direnv shell itself does not, which is
|
|
147
|
+
* the point: this script must never act as a global shell export.
|
|
148
|
+
*/
|
|
149
|
+
async function resolveSecrets(): Promise<void> {
|
|
150
|
+
for (const [name, value] of Object.entries(await resolveSecretEnvironment({ root: projectRoot }))) {
|
|
151
|
+
process.env[name] = value;
|
|
152
|
+
resolvedSecretValues.push(value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function installLocalDependencies(): Promise<unknown> {
|
|
157
|
+
try {
|
|
158
|
+
await resolveSecrets();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return error;
|
|
161
|
+
}
|
|
133
162
|
// bun install runs the root prepare script. Multiple concurrent direnv
|
|
134
163
|
// activations can otherwise race while mutating the same files under
|
|
135
|
-
// node_modules.
|
|
164
|
+
// node_modules. Nothing that exits the process may run inside the callback
|
|
165
|
+
// (see the CI branch), so the failure is returned, not thrown.
|
|
166
|
+
let installError: unknown;
|
|
136
167
|
await withSetupLock(async () => {
|
|
137
|
-
|
|
168
|
+
try {
|
|
169
|
+
await runSetupCommand('bun install --no-summary', $`bun install --no-summary`);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
installError = error;
|
|
172
|
+
}
|
|
138
173
|
});
|
|
174
|
+
return installError;
|
|
139
175
|
}
|
|
140
176
|
|
|
141
177
|
function ensureTypeScriptApiPackage(root: string): void {
|
|
@@ -374,25 +410,44 @@ function isFileExistsError(error: unknown): boolean {
|
|
|
374
410
|
}
|
|
375
411
|
|
|
376
412
|
function reportSetupFailure(error: unknown): never {
|
|
413
|
+
describeFailure('ERROR', error);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function reportDegradedSetup(error: unknown): void {
|
|
418
|
+
describeFailure('WARNING', error);
|
|
419
|
+
console.error(
|
|
420
|
+
'The shell is loaded WITHOUT installed dependencies so the tools to repair this stay available.\n' +
|
|
421
|
+
'Fix the cause above (missing registry credential, unpublished package, stale lockfile → `devenv update`),\n' +
|
|
422
|
+
'then run `bun install` or `direnv reload`.',
|
|
423
|
+
);
|
|
424
|
+
console.error('---');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function describeFailure(level: 'ERROR' | 'WARNING', error: unknown): void {
|
|
377
428
|
if (error instanceof CapturedCommandError) {
|
|
378
|
-
console.error(`---
|
|
429
|
+
console.error(`--- ${level}: setup-environment.ts failed while running: ${error.command}`);
|
|
379
430
|
console.error(`exit code: ${error.exitCode}`);
|
|
380
431
|
} else {
|
|
381
|
-
console.error(`---
|
|
432
|
+
console.error(`--- ${level}: setup-environment.ts failed: ${error}`);
|
|
382
433
|
}
|
|
383
434
|
replayCapturedOutput(error);
|
|
384
435
|
console.error('\n---');
|
|
385
|
-
process.exit(1);
|
|
386
436
|
}
|
|
387
437
|
|
|
388
438
|
function replayCapturedOutput(error: unknown): void {
|
|
389
439
|
if (!(error instanceof CapturedCommandError)) {
|
|
390
440
|
return;
|
|
391
441
|
}
|
|
392
|
-
|
|
393
|
-
|
|
442
|
+
// The install ran with every resolved secret in its environment, so its
|
|
443
|
+
// output is redacted before replay. Masking is same-length, which keeps the
|
|
444
|
+
// failure this replay exists to show intact.
|
|
445
|
+
const stdout = maskSecretValues(error.stdout, resolvedSecretValues);
|
|
446
|
+
const stderr = maskSecretValues(error.stderr, resolvedSecretValues);
|
|
447
|
+
if (stdout.length > 0) {
|
|
448
|
+
process.stdout.write(stdout);
|
|
394
449
|
}
|
|
395
|
-
if (
|
|
396
|
-
process.stderr.write(
|
|
450
|
+
if (stderr.length > 0) {
|
|
451
|
+
process.stderr.write(stderr);
|
|
397
452
|
}
|
|
398
453
|
}
|