1 import { existsSync, readFileSync, readdirSync } from "node:fs";
2 import { resolve } from "node:path";
3
4 const repoRoot = resolve(import.meta.dirname, "..");
5 const expectedVersion = readFileSync(resolve(repoRoot, "crates/cli/Cargo.toml"), "utf8")
6 .match(/^version\s*=\s*"([^"]+)"/m)?.[1];
7
8 if (!expectedVersion) {
9 throw new Error("Unable to determine the release version from crates/cli/Cargo.toml");
10 }
11
12 const mismatches = [];
13
14 function read(path) {
15 return readFileSync(resolve(repoRoot, path), "utf8");
16 }
17
18 function check(path, actualVersion) {
19 if (actualVersion !== expectedVersion) {
20 mismatches.push(`${path}: ${actualVersion ?? "missing"} (expected ${expectedVersion})`);
21 }
22 }
23
24 function cargoPackageVersion(path) {
25 return read(path).match(/^\[package\][\s\S]*?^version\s*=\s*"([^"]+)"/m)?.[1];
26 }
27
28 for (const directory of ["crates", "sdk/gems"]) {
29 const directoryPath = resolve(repoRoot, directory);
30 for (const entry of readdirSync(directoryPath, { withFileTypes: true })) {
31 if (!entry.isDirectory()) {
32 continue;
33 }
34
35 const candidates = [
36 `${directory}/${entry.name}/Cargo.toml`,
37 ...readdirSync(resolve(directoryPath, entry.name), { withFileTypes: true })
38 .filter((nestedEntry) => nestedEntry.isDirectory())
39 .map((nestedEntry) => `${directory}/${entry.name}/${nestedEntry.name}/Cargo.toml`),
40 ];
41
42 for (const candidate of candidates) {
43 if (existsSync(resolve(repoRoot, candidate))) {
44 const version = cargoPackageVersion(candidate);
45 if (version) {
46 check(candidate, version);
47 }
48 }
49 }
50 }
51 }
52
53 for (const match of read("Cargo.toml").matchAll(
54 /^([\w-]+)\s*=\s*\{\s*version\s*=\s*"([^"]+)"\s*,\s*path\s*=\s*"crates\//gm,
55 )) {
56 check(`Cargo.toml workspace dependency ${match[1]}`, match[2]);
57 }
58
59 for (const path of [
60 "sdk/gems/auth/lib/auth/version.rb",
61 "sdk/gems/email/lib/email/version.rb",
62 "sdk/gems/model/lib/model/version.rb",
63 ]) {
64 check(path, read(path).match(/VERSION\s*=\s*['"]([^'"]+)['"]/)?.[1]);
65 }
66
67 const npmPackage = JSON.parse(read("npm/smbcloud-cli/package.json"));
68 check("npm/smbcloud-cli/package.json", npmPackage.version);
69
70 const sdkNpmPackage = JSON.parse(read("sdk/npm/smbcloud-auth/package.json"));
71 check("sdk/npm/smbcloud-auth/package.json", sdkNpmPackage.version);
72
73 const npmLock = JSON.parse(read("npm/smbcloud-cli/package-lock.json"));
74 check("npm/smbcloud-cli/package-lock.json", npmLock.version);
75 check("npm/smbcloud-cli/package-lock.json packages root", npmLock.packages?.[""]?.version);
76
77 const npmXcrsPackage = JSON.parse(read("npm/xcrs/package.json"));
78 check("npm/xcrs/package.json", npmXcrsPackage.version);
79
80 const npmXcrsLock = JSON.parse(read("npm/xcrs/package-lock.json"));
81 check("npm/xcrs/package-lock.json", npmXcrsLock.version);
82 check("npm/xcrs/package-lock.json packages root", npmXcrsLock.packages?.[""]?.version);
83
84 for (const path of ["server.json", "server-xcrs.json"]) {
85 const server = JSON.parse(read(path));
86 check(path, server.version);
87 for (const [index, packageEntry] of (server.packages ?? []).entries()) {
88 check(`${path} package ${index + 1}`, packageEntry.version);
89 }
90 }
91
92 check(
93 "nuget/smbcloud-cli/SmbCloud.Cli.csproj",
94 read("nuget/smbcloud-cli/SmbCloud.Cli.csproj").match(/<PackageVersion>([^<]+)<\/PackageVersion>/)?.[1],
95 );
96 check(
97 "nuget/xcrs/Xcrs.csproj",
98 read("nuget/xcrs/Xcrs.csproj").match(/<PackageVersion>([^<]+)<\/PackageVersion>/)?.[1],
99 );
100 check(
101 "sdk/gems/email/Gemfile.lock",
102 read("sdk/gems/email/Gemfile.lock").match(/smbcloud-email \(([^)]+)\)/)?.[1],
103 );
104
105 if (mismatches.length > 0) {
106 console.error(`Release version mismatch detected for ${expectedVersion}:`);
107 for (const mismatch of mismatches) {
108 console.error(`- ${mismatch}`);
109 }
110 process.exit(1);
111 }
112
113 console.log(`All release artifacts use version ${expectedVersion}.`);