@rightkit/release 0.2.110 → 0.2.112
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/cargo-contract.mjs +230 -230
- package/cargo-target.mjs +53 -53
- package/cli/right-release.mjs +5 -1
- package/development.mjs +34 -4
- package/hardeningscan.mjs +144 -144
- package/macos-signing-identity.mjs +13 -13
- package/package.json +1 -1
- package/publication-gate.mjs +92 -0
- package/publish-cargo.mjs +10 -3
- package/publish-swift.mjs +102 -54
- package/registry-parity.mjs +22 -4
- package/rightkit-versions.json +61 -4
package/publish-swift.mjs
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// secret scan -> swift test -> release build ->
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// Publish RightKitSwift by the ONE Swift distribution contract: a git repository pinned by URL +
|
|
2
|
+
// exact version tag (docs/RIGHTKIT-SHARED-PACKAGES.md, Swift section). Gates, in order:
|
|
3
|
+
// secret scan -> swift test -> release build -> mirror tree into a git checkout + tag ->
|
|
4
|
+
// FRESH CONSUMER RESOLUTION (a throwaway app pins the tag with `exact:` and runs
|
|
5
|
+
// `swift package resolve`) -> push. A dry-run performs everything except the push, so a green
|
|
6
|
+
// dry-run proves the mirror layout, the tag, and clean consumer resolution -- not just that tests passed.
|
|
7
7
|
// swift-tools is macOS/Linux only, so the plan builder, arg parser, and version check are
|
|
8
8
|
// pure and unit-tested on any OS (publish-swift.test.mjs); the swift steps run where swift exists.
|
|
9
9
|
import fs from "node:fs";
|
|
10
|
+
import os from "node:os";
|
|
10
11
|
import path from "node:path";
|
|
11
|
-
import { createHash } from "node:crypto";
|
|
12
12
|
import { spawnSync } from "./background-process.mjs";
|
|
13
13
|
import { scanCrateForSecrets } from "./publish-cargo.mjs";
|
|
14
|
+
import { enforcePublicationGate, defaultEvidencePath } from "./publication-gate.mjs";
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
export const DEFAULT_SWIFT_GIT_URL = "https://github.com/bogusyogi/rightkit-swift.git";
|
|
17
|
+
const MIRROR_EXCLUDES = new Set([".build", ".swiftpm", ".git", "Package.resolved", ".DS_Store"]);
|
|
16
18
|
|
|
17
19
|
export function parseSwiftPublishArgs(argv, env = process.env) {
|
|
18
|
-
const allowed = new Set(["--
|
|
19
|
-
let
|
|
20
|
-
let url = env.RIGHTKIT_SWIFT_REGISTRY_URL ?? "";
|
|
20
|
+
const allowed = new Set(["--repo", "--dry-run", "--allow-dirty", "--evidence", "--workspace"]);
|
|
21
|
+
let repo = env.RIGHTKIT_SWIFT_GIT_URL ?? DEFAULT_SWIFT_GIT_URL;
|
|
21
22
|
let dryRun = false;
|
|
22
23
|
let allowDirty = false;
|
|
24
|
+
let evidence;
|
|
25
|
+
let workspace;
|
|
23
26
|
for (let index = 0; index < argv.length; index += 1) {
|
|
24
27
|
const arg = argv[index];
|
|
25
28
|
if (!allowed.has(arg)) throw new Error(`unexpected argument: ${arg}`);
|
|
@@ -28,14 +31,14 @@ export function parseSwiftPublishArgs(argv, env = process.env) {
|
|
|
28
31
|
else {
|
|
29
32
|
const value = argv[(index += 1)];
|
|
30
33
|
if (value === undefined) throw new Error(`${arg} needs a value`);
|
|
31
|
-
if (arg === "--
|
|
32
|
-
if (arg === "--
|
|
34
|
+
if (arg === "--evidence") evidence = value;
|
|
35
|
+
else if (arg === "--workspace") workspace = value;
|
|
36
|
+
else repo = value;
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
|
-
if (
|
|
39
|
+
if (!/^(https:\/\/|git@|ssh:\/\/|file:\/\/)/.test(repo)) throw new Error(`invalid swift git url: ${repo}`);
|
|
36
40
|
if (allowDirty && !dryRun) throw new Error("--allow-dirty is only valid with --dry-run");
|
|
37
|
-
|
|
38
|
-
return { scope, url, dryRun, allowDirty };
|
|
41
|
+
return { repo, dryRun, allowDirty, evidence, workspace };
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
// The VERSION file is the single source of truth; Package.swift must name the package and the
|
|
@@ -54,58 +57,103 @@ export function readPackageIdentity(packageDir) {
|
|
|
54
57
|
return { name: nameMatch[1], version };
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
export function buildSwiftPublishPlan({ packageDir,
|
|
58
|
-
const packageId = `${scope}.${name}`;
|
|
59
|
-
const archive = path.join(packageDir, ".build", `${name}-${version}.zip`);
|
|
60
|
+
export function buildSwiftPublishPlan({ packageDir, name, version, repo, dryRun }) {
|
|
60
61
|
const plan = [
|
|
61
62
|
{ label: "secret scan", internal: "secret-scan" },
|
|
62
63
|
{ label: "tests", command: "swift", args: ["test", "--package-path", packageDir] },
|
|
63
64
|
{ label: "release build", command: "swift", args: ["build", "-c", "release", "--package-path", packageDir] },
|
|
64
|
-
//
|
|
65
|
-
{ label: "
|
|
66
|
-
{ label: "
|
|
65
|
+
// Dry-run mirrors into a fresh local repo; a real publish clones the distribution repo first.
|
|
66
|
+
{ label: "mirror and tag", internal: "mirror", tag: version, repo: dryRun ? null : repo },
|
|
67
|
+
{ label: "fresh consumer resolution (exact pin)", internal: "consume", tag: version, name },
|
|
67
68
|
];
|
|
68
|
-
if (dryRun) {
|
|
69
|
-
plan.push({ label: "registry dry-run (validate only)", internal: "dry-run-note", packageId, version });
|
|
70
|
-
return plan;
|
|
71
|
-
}
|
|
72
|
-
plan.push({
|
|
73
|
-
label: "registry publish",
|
|
74
|
-
command: "swift",
|
|
75
|
-
args: ["package-registry", "publish", packageId, version, "--url", url, "--package-path", packageDir],
|
|
76
|
-
});
|
|
69
|
+
if (!dryRun) plan.push({ label: "git push (branch and tag)", internal: "push", tag: version, repo });
|
|
77
70
|
return plan;
|
|
78
71
|
}
|
|
79
72
|
|
|
73
|
+
function git(args, cwd) {
|
|
74
|
+
const result = spawnSync("git", args, { cwd, encoding: "utf8", windowsHide: true });
|
|
75
|
+
if (result.error) throw result.error;
|
|
76
|
+
if (result.status !== 0) throw new Error(`git ${args.join(" ")} failed: ${(result.stderr ?? "").trim()}`);
|
|
77
|
+
return (result.stdout ?? "").trim();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function copyTree(from, to) {
|
|
81
|
+
fs.mkdirSync(to, { recursive: true });
|
|
82
|
+
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
83
|
+
if (MIRROR_EXCLUDES.has(entry.name)) continue;
|
|
84
|
+
const source = path.join(from, entry.name);
|
|
85
|
+
const target = path.join(to, entry.name);
|
|
86
|
+
if (entry.isDirectory()) copyTree(source, target);
|
|
87
|
+
else if (entry.isFile()) fs.copyFileSync(source, target);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function mirrorAndTag(step, packageDir, work) {
|
|
92
|
+
const mirror = path.join(work, "rightkit-swift"); // SwiftPM names the package by repo directory
|
|
93
|
+
if (step.repo) git(["clone", step.repo, mirror], work);
|
|
94
|
+
else { fs.mkdirSync(mirror, { recursive: true }); git(["init", "-q", "-b", "main"], mirror); }
|
|
95
|
+
for (const entry of fs.readdirSync(mirror)) if (entry !== ".git") fs.rmSync(path.join(mirror, entry), { recursive: true, force: true });
|
|
96
|
+
copyTree(packageDir, mirror);
|
|
97
|
+
git(["add", "-A"], mirror);
|
|
98
|
+
git(["-c", "user.email=release@rightsuite.invalid", "-c", "user.name=right-release", "commit", "-q", "--allow-empty", "-m", `RightKitSwift ${step.tag}`], mirror);
|
|
99
|
+
if (git(["tag", "-l", step.tag], mirror) !== "") throw new Error(`tag ${step.tag} already exists: bump VERSION`);
|
|
100
|
+
git(["tag", step.tag], mirror);
|
|
101
|
+
return mirror;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function consumeFresh(step, mirror, work, execute) {
|
|
105
|
+
const app = path.join(work, "consumer");
|
|
106
|
+
fs.mkdirSync(path.join(app, "Sources", "App"), { recursive: true });
|
|
107
|
+
fs.writeFileSync(path.join(app, "Package.swift"), [
|
|
108
|
+
"// swift-tools-version: 6.0", "import PackageDescription", "let package = Package(",
|
|
109
|
+
' name: "App", platforms: [.iOS(.v17), .macOS(.v14)],',
|
|
110
|
+
` dependencies: [.package(url: "file://${mirror}", exact: "${step.tag}")],`,
|
|
111
|
+
' targets: [.target(name: "App", dependencies: [.product(name: "RightKitStore", package: "rightkit-swift")])]', ")", "",
|
|
112
|
+
].join("\n"));
|
|
113
|
+
fs.writeFileSync(path.join(app, "Sources", "App", "App.swift"), "import RightKitStore\npublic let ok = true\n");
|
|
114
|
+
const result = execute("swift", ["package", "resolve"], { cwd: app, encoding: "utf8", windowsHide: true, stdio: "inherit" });
|
|
115
|
+
if (result.error || result.status !== 0) throw new Error("fresh consumer resolution failed");
|
|
116
|
+
const pins = fs.readFileSync(path.join(app, "Package.resolved"), "utf8");
|
|
117
|
+
if (!pins.includes(`"version" : "${step.tag}"`)) throw new Error(`consumer did not resolve exact tag ${step.tag}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
80
120
|
export function runSwiftPublish(options, execute = spawnSync) {
|
|
81
121
|
const packageDir = options.packageDir;
|
|
82
122
|
const { name, version } = readPackageIdentity(packageDir);
|
|
123
|
+
const workspaceRoot = path.resolve(options.workspace ?? path.join(packageDir, "..", ".."));
|
|
124
|
+
enforcePublicationGate({
|
|
125
|
+
kind: "swift", name, version, packagePath: packageDir,
|
|
126
|
+
evidencePath: path.resolve(options.evidence ?? defaultEvidencePath(workspaceRoot, "swift", name)),
|
|
127
|
+
}, { dryRun: options.dryRun });
|
|
83
128
|
const plan = buildSwiftPublishPlan({ ...options, packageDir, name, version });
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
continue;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
const work = fs.mkdtempSync(path.join(os.tmpdir(), "rightkit-swift-publish-"));
|
|
130
|
+
let mirror = null;
|
|
131
|
+
try {
|
|
132
|
+
for (const step of plan) {
|
|
133
|
+
process.stdout.write(`[right-release] swift ${step.label}\n`);
|
|
134
|
+
if (step.internal === "secret-scan") {
|
|
135
|
+
const violations = scanCrateForSecrets(packageDir);
|
|
136
|
+
if (violations.length) throw new Error(`secret scan rejected: ${violations.join(", ")}`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (step.internal === "mirror") { mirror = mirrorAndTag(step, packageDir, work); continue; }
|
|
140
|
+
if (step.internal === "consume") { consumeFresh(step, mirror, work, execute); continue; }
|
|
141
|
+
if (step.internal === "push") {
|
|
142
|
+
git(["push", "origin", "HEAD"], mirror);
|
|
143
|
+
git(["push", "origin", step.tag], mirror);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const result = execute(step.command, step.args, { cwd: packageDir, encoding: "utf8", windowsHide: true, stdio: "inherit" });
|
|
147
|
+
if (result.error) {
|
|
148
|
+
if (result.error.code === "ENOENT") {
|
|
149
|
+
throw new Error(`${step.command} not found -- run publish swift on macOS/Linux with the Swift toolchain installed`);
|
|
150
|
+
}
|
|
151
|
+
throw result.error;
|
|
105
152
|
}
|
|
106
|
-
throw result.
|
|
153
|
+
if (result.status !== 0) throw new Error(`${step.label} failed with exit code ${result.status}`);
|
|
107
154
|
}
|
|
108
|
-
|
|
155
|
+
} finally {
|
|
156
|
+
fs.rmSync(work, { recursive: true, force: true });
|
|
109
157
|
}
|
|
110
158
|
return { name, version, published: !options.dryRun };
|
|
111
159
|
}
|
package/registry-parity.mjs
CHANGED
|
@@ -65,8 +65,24 @@ async function extract(archive, destination) {
|
|
|
65
65
|
return join(destination, 'package');
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// Every npm package RightKit publishes: the names in rightkit-versions.json (adopted, staged, legacy).
|
|
69
|
+
export async function publishedPackageNames() {
|
|
70
|
+
const versions = JSON.parse(await readFile(join(packageRoot, 'rightkit-versions.json'), 'utf8'));
|
|
71
|
+
return [...new Set([...Object.keys(versions.npm ?? {}), ...Object.keys(versions.stagedNpm ?? {}), ...Object.keys(versions.legacyNpm ?? {})])].sort();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function verifyAllRegistryParity({ allowUnpublished = false, fetchImpl = fetch, names } = {}) {
|
|
75
|
+
const results = [];
|
|
76
|
+
for (const name of names ?? await publishedPackageNames()) {
|
|
77
|
+
results.push(await verifyRegistryParity({ allowUnpublished, fetchImpl, packageName: name }));
|
|
78
|
+
}
|
|
79
|
+
return Object.freeze(results);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function verifyRegistryParity({ allowUnpublished = false, fetchImpl = fetch, packageName = '@rightkit/release' } = {}) {
|
|
83
|
+
const sourceRoot = packageName === '@rightkit/release' ? packageRoot : join(packageRoot, '..', packageName.replace('@rightkit/', ''));
|
|
84
|
+
const manifest = JSON.parse(await readFile(join(sourceRoot, 'package.json'), 'utf8'));
|
|
85
|
+
if (manifest.name !== packageName) throw new Error(`source ${sourceRoot} is ${manifest.name}, expected ${packageName}`);
|
|
70
86
|
const encodedName = encodeURIComponent(manifest.name);
|
|
71
87
|
const metadata = await fetchImpl(`https://registry.npmjs.org/${encodedName}/${manifest.version}`);
|
|
72
88
|
if (metadata.status === 404 && allowUnpublished) return Object.freeze({ status: 'UNPUBLISHED', name: manifest.name, version: manifest.version });
|
|
@@ -77,7 +93,7 @@ export async function verifyRegistryParity({ allowUnpublished = false, fetchImpl
|
|
|
77
93
|
try {
|
|
78
94
|
const localDir = join(root, 'local'); const publishedArchive = join(root, 'published.tgz');
|
|
79
95
|
await mkdir(localDir, { recursive: true });
|
|
80
|
-
const packed = runPnpm(['pack', '--pack-destination', localDir, '--json'], { cwd:
|
|
96
|
+
const packed = runPnpm(['pack', '--pack-destination', localDir, '--json'], { cwd: sourceRoot, encoding: 'utf8' });
|
|
81
97
|
const localArchive = join(localDir, basename(packedArchivePath(packed)));
|
|
82
98
|
const response = await fetchImpl(record.dist.tarball);
|
|
83
99
|
if (!response.ok) throw new Error(`registry tarball failed: HTTP ${response.status}`);
|
|
@@ -93,7 +109,9 @@ export async function verifyRegistryParity({ allowUnpublished = false, fetchImpl
|
|
|
93
109
|
}
|
|
94
110
|
|
|
95
111
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
96
|
-
|
|
112
|
+
const flag = process.argv.indexOf('--package');
|
|
113
|
+
const allowUnpublished = process.argv.includes('--allow-unpublished');
|
|
114
|
+
(flag > 0 ? verifyRegistryParity({ allowUnpublished, packageName: process.argv[flag + 1] }) : verifyAllRegistryParity({ allowUnpublished }))
|
|
97
115
|
.then((result) => process.stdout.write(`${JSON.stringify(result)}\n`))
|
|
98
116
|
.catch((error) => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; });
|
|
99
117
|
}
|
package/rightkit-versions.json
CHANGED
|
@@ -13,17 +13,26 @@
|
|
|
13
13
|
"@rightkit/updates": "0.2.3"
|
|
14
14
|
},
|
|
15
15
|
"stagedNpm": {
|
|
16
|
+
"@rightkit/app-shell": "0.2.0",
|
|
16
17
|
"@rightkit/ax": "0.2.4",
|
|
18
|
+
"@rightkit/doc-render": "0.1.0",
|
|
17
19
|
"@rightkit/git": "0.2.25",
|
|
18
20
|
"@rightkit/hooks": "0.1.1",
|
|
21
|
+
"@rightkit/i18n": "0.1.0",
|
|
19
22
|
"@rightkit/legal": "0.3.1",
|
|
20
|
-
"@rightkit/legal-ui": "0.1.
|
|
23
|
+
"@rightkit/legal-ui": "0.1.2",
|
|
21
24
|
"@rightkit/license": "0.1.8",
|
|
22
25
|
"@rightkit/logs": "0.1.4",
|
|
26
|
+
"@rightkit/media-ui": "0.2.0",
|
|
27
|
+
"@rightkit/onboarding": "0.2.0",
|
|
28
|
+
"@rightkit/pdf-ui": "0.1.0",
|
|
23
29
|
"@rightkit/platform-ui": "0.1.1",
|
|
24
30
|
"@rightkit/qa": "0.2.1",
|
|
25
|
-
"@rightkit/release": "0.2.
|
|
31
|
+
"@rightkit/release": "0.2.112",
|
|
32
|
+
"@rightkit/settings-ui": "0.2.0",
|
|
33
|
+
"@rightkit/shell": "0.1.0",
|
|
26
34
|
"@rightkit/tauri": "0.1.1",
|
|
35
|
+
"@rightkit/theme": "0.2.0",
|
|
27
36
|
"@rightkit/updates": "0.2.4"
|
|
28
37
|
},
|
|
29
38
|
"legacyNpm": {
|
|
@@ -59,7 +68,8 @@
|
|
|
59
68
|
"0.2.24"
|
|
60
69
|
],
|
|
61
70
|
"@rightkit/legal-ui": [
|
|
62
|
-
"0.1.0"
|
|
71
|
+
"0.1.0",
|
|
72
|
+
"0.1.1"
|
|
63
73
|
],
|
|
64
74
|
"@rightkit/hooks": [
|
|
65
75
|
"0.1.0"
|
|
@@ -120,11 +130,28 @@
|
|
|
120
130
|
"0.2.106",
|
|
121
131
|
"0.2.107",
|
|
122
132
|
"0.2.108",
|
|
123
|
-
"0.2.109"
|
|
133
|
+
"0.2.109",
|
|
134
|
+
"0.2.110",
|
|
135
|
+
"0.2.111"
|
|
124
136
|
],
|
|
125
137
|
"@rightkit/qa": [
|
|
126
138
|
"0.1.0",
|
|
127
139
|
"0.2.0"
|
|
140
|
+
],
|
|
141
|
+
"@rightkit/app-shell": [
|
|
142
|
+
"0.1.0"
|
|
143
|
+
],
|
|
144
|
+
"@rightkit/media-ui": [
|
|
145
|
+
"0.1.0"
|
|
146
|
+
],
|
|
147
|
+
"@rightkit/theme": [
|
|
148
|
+
"0.1.0"
|
|
149
|
+
],
|
|
150
|
+
"@rightkit/settings-ui": [
|
|
151
|
+
"0.1.0"
|
|
152
|
+
],
|
|
153
|
+
"@rightkit/onboarding": [
|
|
154
|
+
"0.1.0"
|
|
128
155
|
]
|
|
129
156
|
},
|
|
130
157
|
"cargo": {
|
|
@@ -134,6 +161,30 @@
|
|
|
134
161
|
"rightkit-tauri": "0.1.0"
|
|
135
162
|
},
|
|
136
163
|
"stagedCargo": {
|
|
164
|
+
"rightkit-automation": "0.1.0",
|
|
165
|
+
"rightkit-audio": "0.1.0",
|
|
166
|
+
"rightkit-browser": "0.1.0",
|
|
167
|
+
"rightkit-cache": "0.1.0",
|
|
168
|
+
"rightkit-control": "0.1.0",
|
|
169
|
+
"rightkit-crash": "0.1.0",
|
|
170
|
+
"rightkit-db": "0.1.0",
|
|
171
|
+
"rightkit-framed-sidecar": "0.1.0",
|
|
172
|
+
"rightkit-fs": "0.1.0",
|
|
173
|
+
"rightkit-jobs": "0.1.0",
|
|
174
|
+
"rightkit-logs": "0.2.0",
|
|
175
|
+
"rightkit-media": "0.1.0",
|
|
176
|
+
"rightkit-models": "0.1.0",
|
|
177
|
+
"rightkit-pdf": "0.1.0",
|
|
178
|
+
"rightkit-playback": "0.1.0",
|
|
179
|
+
"rightkit-process": "0.2.0",
|
|
180
|
+
"rightkit-qa": "0.1.0",
|
|
181
|
+
"rightkit-sandbox": "0.1.0",
|
|
182
|
+
"rightkit-search": "0.1.0",
|
|
183
|
+
"rightkit-service": "0.1.0",
|
|
184
|
+
"rightkit-settings": "0.1.0",
|
|
185
|
+
"rightkit-shell": "0.1.0",
|
|
186
|
+
"rightkit-speech-client": "0.1.0",
|
|
187
|
+
"rightkit-sync": "0.1.0",
|
|
137
188
|
"rightkit-license": "0.1.3",
|
|
138
189
|
"rightkit-tauri": "0.1.1"
|
|
139
190
|
},
|
|
@@ -142,5 +193,11 @@
|
|
|
142
193
|
"url": "https://github.com/bogusyogi/rightkit-swift.git",
|
|
143
194
|
"version": "0.1.1"
|
|
144
195
|
}
|
|
196
|
+
},
|
|
197
|
+
"stagedSwift": {
|
|
198
|
+
"rightkit-swift": {
|
|
199
|
+
"url": "https://github.com/bogusyogi/rightkit-swift.git",
|
|
200
|
+
"version": "0.3.1"
|
|
201
|
+
}
|
|
145
202
|
}
|
|
146
203
|
}
|