Skip to content

Commit 3c047db

Browse files
committed
merge main into selem/docker-for-all
2 parents 7bd27b2 + 50bb739 commit 3c047db

File tree

135 files changed

+4401
-4554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+4401
-4554
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@ updates:
1717
update-types: ["version-update:semver-major", "version-update:semver-minor"]
1818
- dependency-name: "tailwindcss"
1919
update-types: ["version-update:semver-major", "version-update:semver-minor"]
20-
# versions: [">3.4.17"]

actions/base/action.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ runs:
1212
run: |
1313
pnpm remove -r playwright-chromium
1414
pnpm install
15+
pnpm dedupe --check
1516
1617
- name: format-check
1718
shell: bash

cli/build-cmd.test.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ it("getVersion emptyString", async () => {
3535

3636
it("should set package version but leave workspace dependencies as-is", async () => {
3737
const version = new Version("0.0.0-smoke", "^");
38-
const { patchedPackageJson } = await patchPackageJson("package.json", version, undefined, mock);
38+
const { patchedPackageJson } = await patchPackageJson("package.json", version, { mock });
3939
expect(patchedPackageJson.version).toBe("0.0.0-smoke");
4040
// Workspace dependencies are no longer replaced by patchPackageJson - use VersionPinner for that
4141
expect(patchedPackageJson.dependencies).toHaveProperty("@fireproof/vendor", "workspace:0.0.0");
4242
});
4343

4444
it("should only use prefix version in dependencies", async () => {
4545
const version = new Version("0.0.0-smoke", "^");
46-
const { patchedPackageJson } = await patchPackageJson("package.json", version, undefined, mock);
46+
const { patchedPackageJson } = await patchPackageJson("package.json", version, { mock });
4747
const originalPackageJson = (await mock.readJSON()) as unknown as PackageJson;
4848
const jsrConf = await buildJsrConf({ originalPackageJson, patchedPackageJson }, version.prefixedVersion);
4949
expect(jsrConf.version).toBe("0.0.0-smoke");
@@ -217,6 +217,33 @@ it("sanitizeNpmrc with // lhs", async () => {
217217
);
218218
});
219219

220+
it("unmodified dependencies", async () => {
221+
const pjson = {
222+
version: "0.0.0",
223+
scripts: {
224+
pack: "core-cli build --doPack x",
225+
publish: "core-cli build x",
226+
},
227+
dependencies: {
228+
"@fireproof/vendor": "workspace:*",
229+
"xcmd-ts": "^0.13.0",
230+
"ycmd-ts": "~0.13.0",
231+
"zcmd-ts": "0.13.0",
232+
},
233+
};
234+
const patchedPjson = await patchPackageJson("package.json", new Version("1.2.3", ""), { mock: { readJSON: async () => pjson } });
235+
expect(patchedPjson.patchedPackageJson).toEqual({
236+
scripts: {},
237+
version: "1.2.3",
238+
dependencies: {
239+
"@fireproof/vendor": "workspace:*",
240+
"xcmd-ts": "^0.13.0",
241+
"ycmd-ts": "~0.13.0",
242+
"zcmd-ts": "0.13.0",
243+
},
244+
});
245+
});
246+
220247
it("sanitizeNpmrc with http lhs", async () => {
221248
const npmrc = [
222249
"; .npmrc",

cli/build-cmd.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-console */
2-
import { array, command, flag, multioption, option, string } from "cmd-ts";
2+
import { array, command, flag, multioption, option, string, Type } from "cmd-ts";
33
import fs from "fs-extra";
44
import path from "node:path";
55
import { findUp } from "find-up";
@@ -9,6 +9,18 @@ import { SemVer } from "semver";
99
import { exception2Result } from "@adviser/cement";
1010
import { VersionPinner } from "./version-pinner.js";
1111

12+
export type VersionModifier = "~" | "^" | "";
13+
const allowedVersionModifiers: VersionModifier[] = ["~", "^", ""];
14+
15+
const versionModifier: Type<string, VersionModifier> = {
16+
async from(str) {
17+
if (allowedVersionModifiers.includes(str as VersionModifier)) {
18+
return str as VersionModifier;
19+
}
20+
throw new Error(`Invalid version modifier: "${str}". Must be one of: "~", "^", or ""`);
21+
},
22+
};
23+
1224
const reVersionAlphaStart = /^[a-z](\d+\.\d+\.\d+.*)$/;
1325
// const reVersionOptionalAlphaStart = /^[a-z]?(\d+\.\d+\.\d+.*)$/;
1426
const reScopedVersion = /^[^@]+@(.*)$/;
@@ -112,29 +124,34 @@ export interface PackageJson {
112124
devDependencies: Record<string, string>;
113125
}
114126

127+
export interface PatchPackageJsonOptions {
128+
changeScope?: string;
129+
readonly mock?: {
130+
readonly readJSON: typeof fs.readJson;
131+
};
132+
}
133+
115134
export async function patchPackageJson(
116135
packageJsonPath: string,
117136
version: Version,
118-
changeScope?: string,
119-
mock: {
120-
readJSON: typeof fs.readJson;
121-
} = { readJSON: fs.readJson },
137+
opts: PatchPackageJsonOptions = {},
122138
): Promise<{
123139
patchedPackageJson: PackageJson;
124140
originalPackageJson: PackageJson;
125141
}> {
126-
const originalPackageJson = await mock.readJSON(packageJsonPath);
127-
const patchedPackageJson = await mock.readJSON(packageJsonPath);
142+
const mock = opts.mock ?? { readJSON: fs.readJson };
143+
144+
const originalPackageJson = (await mock.readJSON(packageJsonPath)) as PackageJson;
145+
const patchedPackageJson = (await mock.readJSON(packageJsonPath)) as PackageJson;
128146
// ugly double read but this is easier than deep copying
129-
if (changeScope) {
130-
changeScope = changeScope.replace(/^@/, "");
147+
if (opts.changeScope) {
148+
opts.changeScope = opts.changeScope.replace(/^@/, "");
131149
if (originalPackageJson.name.startsWith(`@`)) {
132-
patchedPackageJson.name = patchedPackageJson.name.replace(/^@[^/]+\//, `@${changeScope}/`);
150+
patchedPackageJson.name = patchedPackageJson.name.replace(/^@[^/]+\//, `@${opts.changeScope}/`);
133151
} else {
134-
patchedPackageJson.name = `@${changeScope}/${patchedPackageJson.name}`;
152+
patchedPackageJson.name = `@${opts.changeScope}/${patchedPackageJson.name}`;
135153
}
136154
}
137-
// patchedPackageJson.name = changeScope ? : patchedPackageJson.name;
138155
patchedPackageJson.version = version.version;
139156
delete patchedPackageJson.scripts["pack"];
140157
delete patchedPackageJson.scripts["publish"];
@@ -359,6 +376,16 @@ export function buildCmd(sthis: SuperThis) {
359376
short: "t",
360377
description: "Do not update tsconfig.json in the destination directory.",
361378
}),
379+
patchVersionModify: option({
380+
long: "patchVersionModify",
381+
description: "Modify patch versions in package.json dependencies. Must be one of: ~, ^, or empty string.",
382+
type: versionModifier,
383+
defaultValue: () => "~" as VersionModifier,
384+
}),
385+
noPatchedVersionModify: flag({
386+
long: "noPatchedVersionModify",
387+
description: "Do not modify patch versions in package.json dependencies.",
388+
}),
362389
noPinned: flag({
363390
long: "no-pinned",
364391
description: "Do not pin dependencies in package.json (pinning is enabled by default).",
@@ -519,7 +546,7 @@ export function buildCmd(sthis: SuperThis) {
519546
$.verbose = true;
520547
cd(jsrDstDir);
521548

522-
let packageJson = await patchPackageJson("package.json", version, args.changeScope);
549+
let packageJson = await patchPackageJson("package.json", version, { changeScope: args.changeScope });
523550
if (!args.noPinned) {
524551
console.log(
525552
"Prepared package.json with pinning for",
@@ -533,6 +560,7 @@ export function buildCmd(sthis: SuperThis) {
533560
...packageJson,
534561
patchedPackageJson: pinner.pinVersions(packageJson.patchedPackageJson, {
535562
workspaceVersion: version.prefixedVersion,
563+
_3rdPartyVersionModifier: args.noPatchedVersionModify ? undefined : args.patchVersionModify,
536564
}),
537565
};
538566
}
@@ -605,6 +633,8 @@ export function buildCmd(sthis: SuperThis) {
605633
const semVer = new SemVer(args.version);
606634
if (semVer.prerelease.find((i) => typeof i === "string" && i.includes("dev"))) {
607635
tags.push("dev");
636+
} else {
637+
tags.push("latest"); // override to latest in prod
608638
}
609639
} catch (e) {
610640
console.warn(`Warn parsing version ${args.version}:`, e);

cli/device-id-cmd.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { DeviceIdKey, DeviceIdCSR, DeviceIdCA } from "@fireproof/core-device-id"
55
import { getKeyBag } from "@fireproof/core-keybag";
66
import { decodeJwt } from "jose";
77
import fs from "fs-extra";
8-
import { base58btc } from "multiformats/bases/base58";
98
import { Hono } from "hono";
109
import { serve } from "@hono/node-server";
1110
import open from "open";
1211
import { Future, timeouted, isSuccess, isTimeout, BuildURI } from "@adviser/cement";
12+
import { sts } from "@fireproof/core-runtime";
1313

1414
function getStdin(): Promise<string> {
1515
return new Promise<string>((resolve) => {
@@ -420,11 +420,7 @@ export function deviceIdCmd(sthis: SuperThis) {
420420

421421
// Handle environment variable output format
422422
if (args.envVars) {
423-
// Base58btc encode the private key JSON
424-
const privateKeyJson = JSON.stringify(jwkPrivate);
425-
const privateKeyBase58 = base58btc.encode(sthis.txt.encode(privateKeyJson));
426-
427-
console.log(`DEVICE_ID_CA_PRIV_KEY=${privateKeyBase58}`);
423+
console.log(`DEVICE_ID_CA_PRIV_KEY=${await sts.jwk2env(jwkPrivate)}`);
428424
console.log(`DEVICE_ID_CA_CERT=${certificateJWT}`);
429425
} else if (args.json) {
430426
// Handle JSON output format

cli/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,25 @@
3939
"url": "https://github.com/fireproof-storage/fireproof/issues"
4040
},
4141
"dependencies": {
42-
"@adviser/cement": "^0.5.8",
42+
"@adviser/cement": "^0.5.22",
4343
"@fireproof/core-device-id": "workspace:0.0.0",
4444
"@fireproof/core-keybag": "workspace:0.0.0",
4545
"@fireproof/core-runtime": "workspace:0.0.0",
4646
"@fireproof/core-types-base": "workspace:0.0.0",
4747
"@fireproof/vendor": "workspace:0.0.0",
48-
"@hono/node-server": "^1.19.7",
48+
"@hono/node-server": "^1.19.9",
4949
"@pnpm/lockfile-file": "^9.1.3",
50-
"@typescript/native-preview": "7.0.0-dev.20260104.1",
50+
"@typescript/native-preview": "7.0.0-dev.20260124.1",
5151
"aws4fetch": "^1.0.20",
52-
"cmd-ts": "^0.14.3",
52+
"cmd-ts": "^0.15.0",
5353
"find-up": "^8.0.0",
5454
"fs-extra": "^11.3.3",
55-
"hono": "^4.11.3",
55+
"hono": "^4.11.9",
5656
"jose": "^6.1.3",
5757
"multiformats": "^13.4.2",
5858
"open": "^11.0.0",
59-
"semver": "^7.7.3",
60-
"zod": "^4.3.5",
59+
"semver": "^7.7.4",
60+
"zod": "^4.3.6",
6161
"zx": "^8.8.5"
6262
},
6363
"devDependencies": {

cli/version-pinner.test.ts

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,38 @@ describe("VersionPinner", () => {
9090
});
9191

9292
describe("pinVersions", () => {
93-
it("should pin unpinned dependencies with caret (^)", () => {
94-
const pkg: PackageJson = {
95-
...pkgTemplate,
96-
dependencies: {
97-
"cmd-ts": `^${cmdTsVersion}`,
98-
},
99-
};
100-
101-
const result = pinner.pinVersions(pkg);
102-
103-
// Should pin the version
104-
expect(result.dependencies["cmd-ts"]).toBe(cmdTsVersion);
93+
describe.each(["0.15.0"])("should pin unpinned dependencies (%s)", (version) => {
94+
describe.each(["", "^", "~"])("and in-modifier '%s'", (inModifier) => {
95+
it.each(["", "^", "~"])("and modifier '%s'", (modifier) => {
96+
const pkg: PackageJson = {
97+
...pkgTemplate,
98+
dependencies: {
99+
"cmd-ts": `${inModifier}${version}`,
100+
my: "workspace:*",
101+
},
102+
};
103+
const result = pinner.pinVersions(pkg, {
104+
workspaceVersion: "1.0.0",
105+
_3rdPartyVersionModifier: modifier as "~" | "^" | "",
106+
});
107+
// Should pin the version
108+
expect(result.dependencies["cmd-ts"]).toBe(`${modifier}0.15.0`);
109+
expect(result.dependencies["my"]).toBe(`1.0.0`);
110+
});
111+
112+
it("and modifier undefined", () => {
113+
const pkg: PackageJson = {
114+
...pkgTemplate,
115+
dependencies: {
116+
"cmd-ts": `${inModifier}${version}`,
117+
my: "workspace:*",
118+
},
119+
};
120+
const result = pinner.pinVersions(pkg, { workspaceVersion: "1.0.0" });
121+
expect(result.dependencies["cmd-ts"]).toBe(`${inModifier}${version}`);
122+
expect(result.dependencies["my"]).toBe(`1.0.0`);
123+
});
124+
});
105125
});
106126

107127
it("should pin unpinned dependencies with tilde (~)", () => {
@@ -115,7 +135,7 @@ describe("VersionPinner", () => {
115135
const result = pinner.pinVersions(pkg);
116136

117137
// Should pin the version
118-
expect(result.dependencies["semver"]).toBe(semverVersion);
138+
expect(result.dependencies["semver"]).toBe(`~${semverVersion}`);
119139
});
120140

121141
it("should pin unpinned dependencies with asterisk (*)", () => {
@@ -192,7 +212,7 @@ describe("VersionPinner", () => {
192212

193213
// Only cmd-ts should be in the result, not its transitive dependencies
194214
expect(Object.keys(result.dependencies)).toEqual(["cmd-ts"]);
195-
expect(result.dependencies["cmd-ts"]).toBe(cmdTsVersion);
215+
expect(result.dependencies["cmd-ts"]).toBe(`^${cmdTsVersion}`);
196216
});
197217

198218
it("should sort dependencies alphabetically", async () => {
@@ -230,9 +250,9 @@ describe("VersionPinner", () => {
230250
const result = pinner.pinVersions(pkg);
231251

232252
expect(result.dependencies["cmd-ts"]).toBe(cmdTsVersion); // kept as-is
233-
expect(result.dependencies["semver"]).toBe(semverVersion); // pinned
253+
expect(result.dependencies["semver"]).toBe(`^${semverVersion}`); // pinned
234254
expect(result.dependencies["@fireproof/vendor"]).toBe("2.0.0"); // workspace replaced
235-
expect(result.dependencies["multiformats"]).toBe(multiformatsVersion); // pinned
255+
expect(result.dependencies["multiformats"]).toBe(`~${multiformatsVersion}`); // pinned
236256
});
237257

238258
it("should remove devDependencies by default", () => {
@@ -268,7 +288,7 @@ describe("VersionPinner", () => {
268288

269289
expect(result.devDependencies).toBeDefined();
270290
// devDependencies should also be pinned
271-
expect(result.devDependencies?.vitest).toBe(vitestVersion);
291+
expect(result.devDependencies?.vitest).toBe(`^${vitestVersion}`);
272292
});
273293
});
274294

cli/version-pinner.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PackageJson } from "./build-cmd.js";
33
import { findUp } from "find-up";
44

55
interface PinVersionOptions {
6+
readonly _3rdPartyVersionModifier?: "~" | "^" | "";
67
readonly includeDevDeps?: boolean;
78
readonly workspaceVersion?: string;
89
}
@@ -29,7 +30,11 @@ export class VersionPinner {
2930
/**
3031
* Helper function to pin dependencies
3132
*/
32-
private pinDependencies(deps: Record<string, string> | undefined, workspaceVersion: string): Record<string, string> {
33+
private pinDependencies(
34+
deps: Record<string, string> | undefined,
35+
workspaceVersion: string,
36+
_3rdPartyVersionModifier: "~" | "^" | "" | undefined,
37+
): Record<string, string> {
3338
const pinnedDeps: Record<string, string> = {};
3439

3540
if (!deps) {
@@ -39,20 +44,22 @@ export class VersionPinner {
3944
for (const [name, version] of Object.entries(deps)) {
4045
// Check if version is not pinned (starts with ^ or ~ or *)
4146
// Note: Also catch malformed versions like "1-beta" that should be resolved from lockfile
42-
if (version.match(/^[\^~*]/) || version.match(/^[0-9]+-/)) {
47+
if (version.startsWith("workspace:")) {
48+
// Replace workspace dependencies with the workspace version
49+
pinnedDeps[name] = workspaceVersion;
50+
} else {
4351
// Look up the exact version in lockfile
4452
if (this.allDeps[name]) {
4553
pinnedDeps[name] = this.allDeps[name];
4654
} else {
4755
// Keep original version if not found in lockfile
4856
pinnedDeps[name] = version;
4957
}
50-
} else if (version.startsWith("workspace:")) {
51-
// Replace workspace dependencies with the workspace version
52-
pinnedDeps[name] = workspaceVersion;
53-
} else {
54-
// Already pinned, keep as-is
55-
pinnedDeps[name] = version;
58+
if (typeof _3rdPartyVersionModifier === "string") {
59+
pinnedDeps[name] = `${_3rdPartyVersionModifier}${pinnedDeps[name].replace(/^[\^~]/, "")}`;
60+
} else {
61+
pinnedDeps[name] = version === "*" ? pinnedDeps[name] : version;
62+
}
5663
}
5764
}
5865

@@ -112,7 +119,7 @@ export class VersionPinner {
112119
const workspaceVersion = options.workspaceVersion ?? pkg.version;
113120

114121
// Pin dependencies
115-
const pinnedDeps = this.pinDependencies(pkg.dependencies, workspaceVersion);
122+
const pinnedDeps = this.pinDependencies(pkg.dependencies, workspaceVersion, options._3rdPartyVersionModifier);
116123

117124
// Sort dependencies alphabetically
118125
const sortedDeps: Record<string, string> = {};
@@ -123,7 +130,7 @@ export class VersionPinner {
123130
// Pin devDependencies if includeDevDeps is true
124131
let sortedDevDeps: Record<string, string> | undefined;
125132
if (options.includeDevDeps && pkg.devDependencies) {
126-
const pinnedDevDeps = this.pinDependencies(pkg.devDependencies, workspaceVersion);
133+
const pinnedDevDeps = this.pinDependencies(pkg.devDependencies, workspaceVersion, options._3rdPartyVersionModifier);
127134
sortedDevDeps = {};
128135
for (const name of Object.keys(pinnedDevDeps).sort()) {
129136
sortedDevDeps[name] = pinnedDevDeps[name];

0 commit comments

Comments
 (0)