@rightkit/release 0.6.5 → 0.6.6
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/README.md +79 -7
- package/android-play-upload.mjs +4 -4
- package/app-check.mjs +474 -17
- package/apple-profile-create.mjs +4 -3
- package/apple-profile-renew.mjs +5 -4
- package/bin/darwin/rightkit-swap +0 -0
- package/bin/rightkit-gate.mjs +431 -44
- package/binary-license-notices.mjs +2 -1
- package/build-release.mjs +7 -6
- package/cache-command.mjs +4 -3
- package/cargo-license-notices.mjs +2 -1
- package/cargo-manifest-text.mjs +25 -0
- package/cargo-profile-contract.mjs +26 -13
- package/cli/right-release.mjs +8 -1
- package/command-spec.mjs +4 -5
- package/deps.mjs +5 -4
- package/deterministic-scans.mjs +280 -32
- package/development.mjs +5 -5
- package/github-release.mjs +5 -3
- package/github-upload-gate.mjs +6 -6
- package/init-command.mjs +134 -8
- package/ios-upload.mjs +4 -4
- package/mirror-root-artifact.mjs +3 -4
- package/npm-license-notices.mjs +2 -1
- package/outdated-command.mjs +91 -0
- package/package.json +37 -1
- package/pipeline-admission.mjs +12 -0
- package/publish-cargo.mjs +215 -3
- package/publish-update.mjs +5 -3
- package/release-config.mjs +36 -5
- package/release-dependencies-command.mjs +4 -3
- package/release-qa-audit.mjs +12 -10
- package/release-qa-command.mjs +42 -7
- package/release-qa-hardening.mjs +10 -3
- package/release-qa-policy.mjs +214 -47
- package/release.mjs +4 -4
- package/rightkit-registry-resolver.mjs +5 -2
- package/rightkit-versions.json +11 -8
- package/stage-sidecar-command.mjs +5 -5
- package/test-command.mjs +3 -2
- package/upload-release.mjs +3 -2
- package/web-build.mjs +2 -1
|
@@ -18,6 +18,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import { pathToFileURL } from "node:url";
|
|
20
20
|
import { CommandSpecError, loadAppConfig, relativeInside } from "./command-spec.mjs";
|
|
21
|
+
import { defaultReleaseConfigFile } from "./release-config.mjs";
|
|
21
22
|
import { escapeHtml } from "./npm-license-notices.mjs";
|
|
22
23
|
|
|
23
24
|
const fail = (message) => { throw new CommandSpecError(message); };
|
|
@@ -123,7 +124,7 @@ export function generateBinaryNotices({ appRoot, config, flags = {}, check = fal
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
export function parseBinaryNoticeArgs(argv) {
|
|
126
|
-
const opts = { config:
|
|
127
|
+
const opts = { config: defaultReleaseConfigFile(), output: undefined, productName: undefined, receipt: undefined, check: false };
|
|
127
128
|
for (let i = 0; i < argv.length; i += 1) {
|
|
128
129
|
const arg = argv[i];
|
|
129
130
|
if (arg === "--config" || arg === "--output" || arg === "--product-name" || arg === "--receipt") {
|
package/build-release.mjs
CHANGED
|
@@ -23,7 +23,8 @@ import { spawn, spawnSync } from "./background-process.mjs";
|
|
|
23
23
|
import { RightKitRegistryError, SNAPSHOT_ENV, SNAPSHOT_FILE, assertManifestRegistryEvidence, loadAdmittedSnapshot, prepareRightKitResolution, registryManifestFields, rightKitLockInventory, writeAdmissionRecord, writeSnapshot } from "./rightkit-registry-resolver.mjs";
|
|
24
24
|
import { formatAdmission, formatNotRun, runAdmission } from "./admission.mjs";
|
|
25
25
|
import { sccacheRefusal } from "./admission-host.mjs";
|
|
26
|
-
import {
|
|
26
|
+
import { defaultReleaseConfigFile, importReleaseConfig } from "./release-config.mjs";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
27
28
|
import { assertPrimaryReleaseCheckout, dirtyBuildInputs, resolveReleaseBuildInputs } from "./release-invocation.mjs";
|
|
28
29
|
import { resolveNativeCargoLayout } from "./native-cargo-layout.mjs";
|
|
29
30
|
import { admitSourceQa } from "./release-qa-audit.mjs";
|
|
@@ -111,7 +112,7 @@ const TOOL_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
|
111
112
|
const WORKER = path.join(TOOL_ROOT, "release.mjs");
|
|
112
113
|
const RELEASE_TOOL_VERSION = JSON.parse(readFileSync(path.join(TOOL_ROOT, "package.json"), "utf8")).version;
|
|
113
114
|
const args = process.argv.slice(2);
|
|
114
|
-
let configName =
|
|
115
|
+
let configName = defaultReleaseConfigFile();
|
|
115
116
|
let platform = process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : process.platform;
|
|
116
117
|
let dryRun = false;
|
|
117
118
|
let skipChecks = false;
|
|
@@ -195,7 +196,7 @@ try {
|
|
|
195
196
|
const shortCommit = commit.slice(0, 8);
|
|
196
197
|
const dirtyConfig = dirtyBuildInputs(repoRoot, { include: [relativeConfig], required: [relativeConfig] }, commit);
|
|
197
198
|
if (dirtyConfig.length > 0) fail(`dirty release config cannot be executed:\n${dirtyConfig.map((file) => `- ${file}`).join("\n")}`);
|
|
198
|
-
const config =
|
|
199
|
+
const config = await importReleaseConfig(configPath, { query: `commit=${commit}` });
|
|
199
200
|
if (!config?.app || !config?.version) fail("release config must expose app and version");
|
|
200
201
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(config.app) || !/^[A-Za-z0-9][A-Za-z0-9._+-]*$/.test(config.version)) {
|
|
201
202
|
fail("app and version must be safe release-id components");
|
|
@@ -566,7 +567,7 @@ async function admit() {
|
|
|
566
567
|
commit = git(invocationRoot, ["rev-parse", "HEAD"]);
|
|
567
568
|
const dirtyConfig = dirtyBuildInputs(repoRoot, { include: [relativeConfig], required: [relativeConfig] }, commit);
|
|
568
569
|
if (dirtyConfig.length > 0) fail(`dirty release config cannot be executed:\n${dirtyConfig.map((file) => `- ${file}`).join("\n")}`);
|
|
569
|
-
try { config =
|
|
570
|
+
try { config = await importReleaseConfig(configPath, { query: `commit=${commit}` }); } catch (error) { fail(`release config could not be loaded (${configPath}): ${error instanceof Error ? error.message : String(error)}`); }
|
|
570
571
|
} catch (error) {
|
|
571
572
|
if (error?.reported) process.exit(1);
|
|
572
573
|
throw error;
|
|
@@ -630,7 +631,7 @@ function sqlCipherFeatures(text) {
|
|
|
630
631
|
}
|
|
631
632
|
|
|
632
633
|
function sealRelease({ configRoot, managedCargoTarget, androidTarget, iosSpec, nativeLayout, sealedDir, releaseId, config, target, platform, commit, inputHashes, toolVersions, cacheKey, signingIdentity, ownershipEvidence, qaAdmission, managedRoot, stateRoot, rightkitRegistryFields }) {
|
|
633
|
-
// Every app's
|
|
634
|
+
// Every app's release config names installer/updater artifacts
|
|
634
635
|
// relative to its resolved native target. On a broker-managed host that
|
|
635
636
|
// directory is never populated (the broker owns CARGO_TARGET_DIR and
|
|
636
637
|
// targetBridge.ensure() deliberately leaves a real local directory alone,
|
|
@@ -1053,7 +1054,7 @@ function writeJson(file, value) {
|
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1055
1056
|
function usage(code) {
|
|
1056
|
-
console.log("usage: right-release build --platform win|mac|android|ios [--config right-release.config.
|
|
1057
|
+
console.log("usage: right-release build --platform win|mac|android|ios [--config right-release.config.json] [--dry-run]");
|
|
1057
1058
|
process.exit(code);
|
|
1058
1059
|
}
|
|
1059
1060
|
|
package/cache-command.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, realpathSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { fileURLToPath
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
5
|
import {
|
|
6
6
|
applyCachePrune,
|
|
7
7
|
inspectCache,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
resolveSharedCacheIdentity,
|
|
13
13
|
resolveSharedCacheRoot,
|
|
14
14
|
} from "./cache-policy.mjs";
|
|
15
|
+
import { importReleaseConfig } from "./release-config.mjs";
|
|
15
16
|
import { resolveReleaseLayout } from "./release-state.mjs";
|
|
16
17
|
import { resolveNativeCargoLayout } from "./native-cargo-layout.mjs";
|
|
17
18
|
import { brokerManagedHost } from "./cargo-contract.mjs";
|
|
@@ -52,11 +53,11 @@ export async function runCacheCommand(args = process.argv.slice(2), { env = proc
|
|
|
52
53
|
}
|
|
53
54
|
if (command === "migrate") {
|
|
54
55
|
const configPath = option(rest, "--config");
|
|
55
|
-
if (!configPath) throw new Error("cache migrate requires --config <right-release.config.
|
|
56
|
+
if (!configPath) throw new Error("cache migrate requires --config <right-release.config.json>");
|
|
56
57
|
rejectUnknown(rest.filter((value) => value !== "--config" && value !== configPath), new Set(["--json", "--dry-run", "--apply"]));
|
|
57
58
|
if (rest.includes("--dry-run") && rest.includes("--apply")) throw new Error("cache migrate accepts either --dry-run or --apply");
|
|
58
59
|
const configFile = path.resolve(configPath);
|
|
59
|
-
const config =
|
|
60
|
+
const config = await importReleaseConfig(configFile);
|
|
60
61
|
if (!config?.app) throw new Error("cache migrate config must expose app");
|
|
61
62
|
const appRoot = path.dirname(configFile);
|
|
62
63
|
const configuredTarget = config.targets?.[platform] ?? {};
|
|
@@ -15,6 +15,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
17
|
import { CommandSpecError, loadAppConfig, relativeInside } from "./command-spec.mjs";
|
|
18
|
+
import { defaultReleaseConfigFile } from "./release-config.mjs";
|
|
18
19
|
import { runCargoSync } from "./managed-cargo.mjs";
|
|
19
20
|
|
|
20
21
|
const fail = (message) => { throw new CommandSpecError(message); };
|
|
@@ -73,7 +74,7 @@ export function generateCargoNotices({ appRoot, config, flags = {}, check = fals
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export function parseCargoNoticeArgs(argv) {
|
|
76
|
-
const opts = { config:
|
|
77
|
+
const opts = { config: defaultReleaseConfigFile(), output: undefined, check: false };
|
|
77
78
|
for (let i = 0; i < argv.length; i += 1) {
|
|
78
79
|
const arg = argv[i];
|
|
79
80
|
if (arg === "--config" || arg === "--output") {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// A line reading of the [package] table of a Cargo.toml, for the scans that must run without a TOML parser (the license scan and
|
|
2
|
+
// the secret scan of the repository gate). This module has no imports on purpose: publish-cargo.mjs is in the trusted runtime
|
|
3
|
+
// helper closure (scripts/ci/runtime-supplement.mjs), which may only follow static relative imports of plain modules.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `{ hasPackage, unpublished }` for the text of a Cargo.toml: whether it has a `[package]` table, and whether that table says
|
|
7
|
+
* `publish = false` or `publish = []`. A crate that can never be published ships its licence to no one, like a package.json with
|
|
8
|
+
* `"private": true`. An array-of-tables header (`[[bin]]`, `[[package.metadata.x]]`) starts a new table like any other, so a
|
|
9
|
+
* `publish` key under it is not the package's.
|
|
10
|
+
*/
|
|
11
|
+
export function cargoManifestState(text) {
|
|
12
|
+
let table = "";
|
|
13
|
+
let hasPackage = false;
|
|
14
|
+
let unpublished = false;
|
|
15
|
+
for (const line of String(text).split(/\r?\n/)) {
|
|
16
|
+
const header = /^\s*\[\[?([^\]]+)\]\]?\s*(?:#.*)?$/.exec(line);
|
|
17
|
+
if (header) {
|
|
18
|
+
table = header[1].trim();
|
|
19
|
+
if (table === "package") hasPackage = true;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (table === "package" && /^\s*publish\s*=\s*(?:false\b|\[\s*\])/.test(line)) unpublished = true;
|
|
23
|
+
}
|
|
24
|
+
return { hasPackage, unpublished };
|
|
25
|
+
}
|
|
@@ -19,6 +19,7 @@ import path from "node:path";
|
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import { parse } from "smol-toml";
|
|
21
21
|
import { findCargoManifests, isExcludedScanDirectory } from "./cargo-contract.mjs";
|
|
22
|
+
import { defaultReleaseConfigFile, isJsonReleaseConfig } from "./release-config.mjs";
|
|
22
23
|
|
|
23
24
|
export const CARGO_PROFILE_TEMPLATE = fileURLToPath(new URL("./templates/cargo-profiles.toml", import.meta.url));
|
|
24
25
|
export const RETIRED_PROFILE = "release-iterate";
|
|
@@ -74,28 +75,40 @@ export function checkManifestProfiles(manifest, template, file) {
|
|
|
74
75
|
// Non-shipping workspaces: fixed prefixes plus the app's declared `cargoProfileExempt` directories.
|
|
75
76
|
const NON_SHIPPING_PREFIXES = ["qa", "benchmarks"];
|
|
76
77
|
const CARGO_PROFILE_EXEMPT_KEY = "cargoProfileExempt";
|
|
77
|
-
const RELEASE_CONFIG = "right-release.config.mjs";
|
|
78
78
|
const STRING_LITERAL = /"([^"\\\n]*)"|'([^'\\\n]*)'/g;
|
|
79
79
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
80
|
+
// This scanner is synchronous and has only the app root, so it reads `cargoProfileExempt` from the release config file itself and
|
|
81
|
+
// never imports it. A JSON config (the one `defaultReleaseConfigFile` picks when it exists) is parsed as data: the array must hold
|
|
82
|
+
// plain strings. A legacy `.mjs` config, which release.mjs still imports as an ES module, is read as text: the literal array must
|
|
83
|
+
// hold plain string literals. Either way every entry names a directory inside the app, and anything else is a finding, never
|
|
84
|
+
// silently ignored. M24 command admission is not run here: this is a pure file reader.
|
|
83
85
|
export function readCargoProfileExempt(root) {
|
|
84
|
-
const
|
|
86
|
+
const configFile = defaultReleaseConfigFile(root);
|
|
87
|
+
const file = path.join(root, configFile);
|
|
85
88
|
const findings = [];
|
|
86
89
|
if (!existsSync(file)) return { paths: [], findings };
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
let entries;
|
|
91
|
+
if (isJsonReleaseConfig(configFile)) {
|
|
92
|
+
let declared;
|
|
93
|
+
try { declared = JSON.parse(readFileSync(file, "utf8").replace(/^\uFEFF/, ""))?.[CARGO_PROFILE_EXEMPT_KEY]; }
|
|
94
|
+
catch (error) { return { paths: [], findings: [{ rule: "invalid-exempt", file: configFile, message: `is not valid JSON, so ${CARGO_PROFILE_EXEMPT_KEY} cannot be read: ${error.message}` }] }; }
|
|
95
|
+
if (declared === undefined) return { paths: [], findings };
|
|
96
|
+
if (!Array.isArray(declared) || declared.some((entry) => typeof entry !== "string")) findings.push({ rule: "invalid-exempt", file: configFile, message: `${CARGO_PROFILE_EXEMPT_KEY} must be an array of strings` });
|
|
97
|
+
entries = Array.isArray(declared) ? declared.filter((entry) => typeof entry === "string") : [];
|
|
98
|
+
} else {
|
|
99
|
+
const match = new RegExp(`\\b${CARGO_PROFILE_EXEMPT_KEY}\\s*:\\s*\\[([^\\]]*)\\]`).exec(readFileSync(file, "utf8"));
|
|
100
|
+
if (!match) return { paths: [], findings };
|
|
101
|
+
const body = match[1];
|
|
102
|
+
if (body.replace(STRING_LITERAL, "").replace(/[,\s]/g, "") !== "") {
|
|
103
|
+
findings.push({ rule: "invalid-exempt", file: configFile, message: `${CARGO_PROFILE_EXEMPT_KEY} must be an array of string literals` });
|
|
104
|
+
}
|
|
105
|
+
entries = [...body.matchAll(STRING_LITERAL)].map(([, double, single]) => double ?? single);
|
|
92
106
|
}
|
|
93
107
|
const paths = [];
|
|
94
|
-
for (const
|
|
95
|
-
const entry = double ?? single;
|
|
108
|
+
for (const entry of entries) {
|
|
96
109
|
const normalized = path.posix.normalize(entry.replace(/\\/g, "/")).replace(/\/+$/, "");
|
|
97
110
|
if (normalized === "." || normalized === "" || path.posix.isAbsolute(normalized) || normalized.split("/").includes("..")) {
|
|
98
|
-
findings.push({ rule: "invalid-exempt", file:
|
|
111
|
+
findings.push({ rule: "invalid-exempt", file: configFile, message: `${CARGO_PROFILE_EXEMPT_KEY} entry ${JSON.stringify(entry)} must be a relative directory inside the app` });
|
|
99
112
|
} else paths.push(normalized);
|
|
100
113
|
}
|
|
101
114
|
return { paths, findings };
|
package/cli/right-release.mjs
CHANGED
|
@@ -13,6 +13,7 @@ const commands = new Map([
|
|
|
13
13
|
["publish", "upload-release.mjs"],
|
|
14
14
|
["upload", "upload-release.mjs"],
|
|
15
15
|
["deps", "deps.mjs"],
|
|
16
|
+
["update", "outdated-command.mjs"],
|
|
16
17
|
["publish-update", "publish-update.mjs"],
|
|
17
18
|
["hardening", "hardeningscan.mjs"],
|
|
18
19
|
["harden", "hardeningscan.mjs"],
|
|
@@ -295,8 +296,12 @@ Commands:
|
|
|
295
296
|
ios-credentials store --key-id <KEYID> --issuer <uuid> | check
|
|
296
297
|
Put the App Store Connect API key (stdin), key id and issuer into the login Keychain once, or check them; never a file (macOS)
|
|
297
298
|
qa --stage source|installed --platform mac|win [--release <id>] Run rightkit-qa (and the journeys lane when the config sets qa.journeys) and write the sealed QA receipt. Refused inside a QA run. The receipt must match the platform and release id, come from an approved runner digest, and show every release-critical journey passed
|
|
298
|
-
Admission is per app in qa-admission-policy.json: warn (default) records a refusal and continues; refuse blocks
|
|
299
|
+
Admission is per app, and per stage (source = build, installed = upload and publish), in qa-admission-policy.json: warn (default) records a refusal and continues; refuse blocks that stage. Only adrian moves an app or stage from refuse back to warn, or approves a runner
|
|
299
300
|
qa audit [--app <dir>] [--json] [--since <YYYY-MM-DD>] List source/installed QA status per release; exit 1 if an uploaded build lacks passing QA
|
|
301
|
+
qa policy show | set --app <app> --mode warn|refuse --set-by <name> [--stage source|installed]
|
|
302
|
+
Show the QA admission policy, or set an app's mode (both stages, or one with --stage)
|
|
303
|
+
qa policy approve-runner --runner <file> --version <v> --platform mac|win --set-by adrian [--note <text>]
|
|
304
|
+
Approve a rightkit-qa runner build for one platform: the file must report "rightkit-qa <v>" from --version and <v> must be the published version
|
|
300
305
|
publish-update --release <id> --platform mac|win --tier patch|update
|
|
301
306
|
Upload sealed bytes to R2 & register RightApps
|
|
302
307
|
release Compatibility alias for build
|
|
@@ -371,6 +376,8 @@ Commands:
|
|
|
371
376
|
disk run self-check, check, dry-run sweep, move sweep, warn (the scheduled job)
|
|
372
377
|
disk schedule install|uninstall|status Daily launchd job (Mac) / hidden Task Scheduler entry (Windows), bound to the stable per-user copy
|
|
373
378
|
deps --check|--audit|--update Shared dependency lane
|
|
379
|
+
update check|apply|gate|land|discard|run|schedule [--repo <path>]
|
|
380
|
+
Run the pinned @rightkit/outdated updater (right-update) that moves stale pins; reads .rightkit/update.json
|
|
374
381
|
hardening <artifact...> Run the Right Suite hardening scan
|
|
375
382
|
lsclean <AppName.app> Clear macOS LaunchServices duplicates
|
|
376
383
|
generate-dmg-background <options> Generate the branded multi-resolution DMG background
|
package/command-spec.mjs
CHANGED
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
// `cwd` and `node` are app-relative and may not leave the app. `nodeFlags` go before the script, `nodeOptions` are merged
|
|
6
6
|
// (de-duplicated) into NODE_OPTIONS for the child. Unknown keys are refused so a typo cannot silently change a run.
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { defaultReleaseConfigFile, importReleaseConfig } from "./release-config.mjs";
|
|
9
9
|
|
|
10
|
-
/** Load an app's `right-release.config.mjs
|
|
11
|
-
export async function loadAppConfig(configFile =
|
|
10
|
+
/** Load an app's release config (`right-release.config.json`, or the legacy `.mjs` while it is the only one); the app root is the config file's directory. */
|
|
11
|
+
export async function loadAppConfig(configFile = defaultReleaseConfigFile()) {
|
|
12
12
|
const configPath = path.resolve(configFile);
|
|
13
|
-
|
|
14
|
-
return { config: loaded.default ?? loaded.config ?? loaded, appRoot: path.dirname(configPath), configPath };
|
|
13
|
+
return { config: await importReleaseConfig(configPath, { lenient: true }), appRoot: path.dirname(configPath), configPath };
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export class CommandSpecError extends Error {
|
package/deps.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { defaultReleaseConfigFile, importReleaseConfig } from "./release-config.mjs";
|
|
4
4
|
import { spawnSync } from "./background-process.mjs";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { cargoAuditWorkdirs, runCargoAudits } from "./deps-cargo.mjs";
|
|
7
7
|
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
const opts = {
|
|
10
|
-
config:
|
|
10
|
+
config: defaultReleaseConfigFile(),
|
|
11
11
|
mode: "check",
|
|
12
12
|
latest: false,
|
|
13
13
|
};
|
|
@@ -25,7 +25,8 @@ for (let i = 0; i < args.length; i++) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const configPath = path.resolve(opts.config);
|
|
28
|
-
|
|
28
|
+
let config;
|
|
29
|
+
try { config = await importReleaseConfig(configPath); } catch (error) { fail(`release config could not be loaded (${configPath}): ${error instanceof Error ? error.message : String(error)}`); }
|
|
29
30
|
if (config?.schema !== 1) fail(`unsupported config schema: ${config?.schema ?? "<missing>"} (expected 1)`);
|
|
30
31
|
if (config.packageManager !== "pnpm") fail(`deps tool expects pnpm, got ${config.packageManager}`);
|
|
31
32
|
|
|
@@ -69,7 +70,7 @@ if (failures.length > 0) {
|
|
|
69
70
|
|
|
70
71
|
function usage(code, message) {
|
|
71
72
|
if (message) console.error(`right-deps: ${message}\n`);
|
|
72
|
-
console.error("usage: node deps.mjs [--config right-release.config.
|
|
73
|
+
console.error("usage: node deps.mjs [--config right-release.config.json] [--check|--audit|--update] [--latest]");
|
|
73
74
|
process.exit(code);
|
|
74
75
|
}
|
|
75
76
|
|