@downdraft/cli 0.3.1 → 0.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downdraft/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/knackstedt/downdraft.git",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@aws-sdk/client-s3": "^3.700.0",
15
- "@downdraft/engine": "^0.3.1",
15
+ "@downdraft/engine": "^0.4.0",
16
16
  "eta": "^3.5.0",
17
17
  "jimp": "^1.6.1",
18
18
  "resedit": "3.1.0"
@@ -6,8 +6,9 @@
6
6
  //
7
7
  // --runtime=bun|node|deno JS host embedded in the package (default:
8
8
  // package.json build.runtime, else bun)
9
- // --format=<csv> dir | deb | appimage | flatpak (default:
10
- // package.json build.linux.target, else dir)
9
+ // --format=<csv> dir | deb | appimage | flatpak | rpm
10
+ // (default: package.json build.linux.target,
11
+ // else dir)
11
12
  // --mode=dev|debug|prod bundle mode (default: prod)
12
13
  // --product-name=<name> display name (default: pkg.productName)
13
14
  // --product-version=<v> version (default: pkg.version)
@@ -49,7 +50,8 @@
49
50
  // linux: { target: ["deb","AppImage","flatpak"], category, maintainer,
50
51
  // synopsis, description, icon },
51
52
  // deb: { section, priority, depends: [] },
52
- // flatpak:{ runtime, runtimeVersion, sdk, finishArgs: [] }
53
+ // flatpak:{ runtime, runtimeVersion, sdk, finishArgs: [] },
54
+ // rpm: { license, release, depends: [] }
53
55
  // }
54
56
  //
55
57
  // Formats:
@@ -58,6 +60,8 @@
58
60
  // assembled with dpkg-deb
59
61
  // appimage — AppDir + AppRun + appimagetool (downloaded+cached on demand)
60
62
  // flatpak — generated manifest + flatpak-builder + build-bundle
63
+ // rpm — same payload layout as deb, assembled with rpmbuild from a
64
+ // generated .spec + tarball Source0
61
65
  // ============================================================================
62
66
 
63
67
  import { spawnSync } from "node:child_process";
@@ -82,7 +86,7 @@ const [entry, outdirArg] = positional;
82
86
 
83
87
  if (!entry || !outdirArg) {
84
88
  console.error(
85
- "usage: bun package-desktop.mjs [--runtime=bun|node|deno] [--format=dir|deb|appimage|flatpak,...] " +
89
+ "usage: bun package-desktop.mjs [--runtime=bun|node|deno] [--format=dir|deb|appimage|flatpak|rpm,...] " +
86
90
  "[--mode=...] [--product-name=] [--product-version=] [--app-id=] [--executable-name=] [--icon=] " +
87
91
  "[--node-bin=] [--deno-bin=] [--mcp] <entry.ts> <outdir>");
88
92
  process.exit(1);
@@ -101,10 +105,10 @@ if (!["bun", "node", "deno"].includes(runtime)) {
101
105
  }
102
106
  const formats = (flagValue("format") ?? (Array.isArray(linuxCfg.target) ? linuxCfg.target.join(",") : linuxCfg.target) ?? "dir")
103
107
  .split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
104
- const KNOWN_FORMATS = new Set(["dir", "deb", "appimage", "flatpak"]);
108
+ const KNOWN_FORMATS = new Set(["dir", "deb", "appimage", "flatpak", "rpm"]);
105
109
  for (const f of formats.values()) {
106
110
  if (!KNOWN_FORMATS.has(f)) {
107
- console.error(`unknown --format "${f}" — expected dir, deb, appimage, or flatpak`);
111
+ console.error(`unknown --format "${f}" — expected dir, deb, appimage, flatpak, or rpm`);
108
112
  process.exit(1);
109
113
  }
110
114
  }
@@ -924,6 +928,75 @@ if (formats.includes("flatpak")) {
924
928
  artifacts.push(`flatpak ${out}`);
925
929
  }
926
930
 
931
+ // ── Format: rpm ──
932
+
933
+ if (formats.includes("rpm")) {
934
+ const rpmCfg = build.rpm ?? {};
935
+ if (!runOut("sh", ["-c", "command -v rpmbuild"])) {
936
+ throw new Error("rpmbuild not installed — required for the rpm format (apt install rpm)");
937
+ }
938
+ const stage = mkdtempSync(join(tmpdir(), "dd-rpm-"));
939
+ const topdir = join(stage, "rpmbuild");
940
+ for (const d of ["BUILD", "RPMS", "SOURCES", "SPECS"]) {
941
+ mkdirSync(join(topdir, d), { recursive: true });
942
+ }
943
+
944
+ // rpm versions can't contain "-" — "~" is rpm's "sorts before" marker,
945
+ // so 1.2.3-beta.1 → 1.2.3~beta.1 still orders below the final release.
946
+ const rpmVersion = version.replace(/-/g, "~");
947
+
948
+ // Source0 tarball — %setup unpacks it into BUILD/payload and %install
949
+ // copies the staged appdir + packaging metadata into %{buildroot}.
950
+ const payload = join(stage, "payload");
951
+ mkdirSync(payload, { recursive: true });
952
+ cpSync(appdir, join(payload, "appdir"), { recursive: true, verbatimSymlinks: true });
953
+ writeFileSync(join(payload, `${appId}.desktop`), desktopFile());
954
+ writeFileSync(join(payload, `${appId}.metainfo.xml`), metainfoXml());
955
+ if (iconPath) await iconSet(join(payload, "hicolor"));
956
+ run("tar", ["-czf", join(topdir, "SOURCES", `${exeName}-${rpmVersion}.tar.gz`), "-C", stage, "payload"]);
957
+
958
+ const depends = (rpmCfg.depends ?? ["glibc", "libstdc++", "vulkan-loader"]).join(", ");
959
+ writeFileSync(join(topdir, "SPECS", `${exeName}.spec`),
960
+ `# ${productName} — generated by package-desktop.mjs\n` +
961
+ // A `bun build --compile` binary appends its embedded bundle past the
962
+ // ELF sections — brp-strip/__os_install_post would rewrite the file
963
+ // and silently drop it. AutoReqProv is off: the binary is self-
964
+ // contained, so only explicit Requires apply.
965
+ `%define debug_package %{nil}\n` +
966
+ `%global __os_install_post %{nil}\n` +
967
+ `AutoReqProv: no\n` +
968
+ `Name: ${exeName}\n` +
969
+ `Version: ${rpmVersion}\n` +
970
+ `Release: ${rpmCfg.release ?? "1%{?dist}"}\n` +
971
+ `Summary: ${synopsis}\n` +
972
+ `License: ${rpmCfg.license ?? "Proprietary"}\n` +
973
+ `Requires: ${depends}\n` +
974
+ `Source0: %{name}-%{version}.tar.gz\n` +
975
+ `\n%description\n${description}\n` +
976
+ `\n%prep\n%setup -q -n payload\n` +
977
+ `\n%build\n# prebuilt binary — nothing to compile\n` +
978
+ `\n%install\n` +
979
+ `mkdir -p %{buildroot}/usr/lib/%{name} %{buildroot}/usr/bin %{buildroot}%{_datadir}/applications %{buildroot}%{_datadir}/metainfo` +
980
+ (iconPath ? ` %{buildroot}%{_datadir}/icons\n` : "\n") +
981
+ `cp -a appdir/. %{buildroot}/usr/lib/%{name}/\n` +
982
+ `ln -s ../lib/%{name}/%{name} %{buildroot}/usr/bin/%{name}\n` +
983
+ `install -Dm644 ${appId}.desktop %{buildroot}%{_datadir}/applications/${appId}.desktop\n` +
984
+ `install -Dm644 ${appId}.metainfo.xml %{buildroot}%{_datadir}/metainfo/${appId}.metainfo.xml\n` +
985
+ (iconPath ? `cp -a hicolor %{buildroot}%{_datadir}/icons/\n` : "") +
986
+ `\n%files\n` +
987
+ `/usr/lib/%{name}\n` +
988
+ `/usr/bin/%{name}\n` +
989
+ `%{_datadir}/applications/${appId}.desktop\n` +
990
+ `%{_datadir}/metainfo/${appId}.metainfo.xml\n` +
991
+ (iconPath ? `%{_datadir}/icons/hicolor/*/apps/%{name}.png\n` : ""));
992
+
993
+ run("rpmbuild", ["-bb", "--define", `_topdir ${topdir}`, join(topdir, "SPECS", `${exeName}.spec`)]);
994
+ const built = readdirSync(join(topdir, "RPMS", "x86_64")).find((f) => f.endsWith(".rpm"));
995
+ const out = join(outdir, `${exeName}-${version}-linux-${runtime}.x86_64.rpm`);
996
+ copyFileSync(join(topdir, "RPMS", "x86_64", built), out);
997
+ artifacts.push(`rpm ${out}`);
998
+ }
999
+
927
1000
  // ── koffi prebuild fetch (same mechanism as package-mobile) ──
928
1001
 
929
1002
  async function fetchKoffiPrebuild(pkgName, ver) {
package/src/usage.ts CHANGED
@@ -46,7 +46,7 @@ export const COMMANDS: CommandEntry[] = [
46
46
  { name: "game", alias: "g", type: "string", description: "Game to release (games/<game>). For multiple games, use --games. If omitted, infers from the current directory, or defaults to the only game in games/." },
47
47
  { name: "games", type: "string", description: "Comma-separated game names (e.g. sandjongg,to-the-ocean)" },
48
48
  { name: "target", alias: "t", type: "string", default: "all", enum: [...RELEASE_TARGETS], description: "Target platform(s): win, linux, mac, android, or all (android can't mix with desktop targets)" },
49
- { name: "format", type: "string", description: "Package format(s), comma-separated: dir, deb, appimage, flatpak (linux only; default: build.linux.target in package.json, else dir)" },
49
+ { name: "format", type: "string", description: "Package format(s), comma-separated: dir, deb, appimage, flatpak, rpm (linux only; default: build.linux.target in package.json, else dir)" },
50
50
  { name: "runtime", type: "string", enum: ["bun", "node", "deno", "all"], description: "JS runtime embedded in the package: bun (compiled binary), node, deno, or all (default: build.runtime in package.json, \"runtime\" in downdraft.config.json, else bun). node/deno are linux-only today" },
51
51
  { name: "stage", type: "string", default: "release", enum: [...RELEASE_STAGES], description: "Stage: build, package, or release (all compile the native binary)" },
52
52
  { name: "mode", type: "string", default: "prod", enum: [...BUILD_MODES], description: "Build mode" },