ubriot 1.2.0 → 1.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.
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectUnrealProject = detectUnrealProject;
4
+ exports.unrealAppFields = unrealAppFields;
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const ENGINE_GUID = /^\{?[0-9A-Fa-f-]{32,36}\}?$/;
8
+ function readText(path) {
9
+ try {
10
+ return (0, fs_1.readFileSync)(path, "utf8");
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ function detectUnrealProject(dir) {
17
+ let names;
18
+ try {
19
+ names = (0, fs_1.readdirSync)(dir).filter((name) => name.endsWith(".uproject")).sort();
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ if (!names.length)
25
+ return null;
26
+ if (names.length > 1)
27
+ throw new Error(`More than one .uproject in ${dir}: ${names.join(", ")}`);
28
+ let association;
29
+ try {
30
+ association = JSON.parse(readText((0, path_1.join)(dir, names[0])) ?? "").EngineAssociation;
31
+ }
32
+ catch {
33
+ throw new Error(`${names[0]} is not valid JSON`);
34
+ }
35
+ if (typeof association !== "string" || !association.trim())
36
+ throw new Error(`${names[0]} has no EngineAssociation; open it once in the Unreal Editor`);
37
+ if (ENGINE_GUID.test(association.trim()))
38
+ throw new Error(`${names[0]} uses a source-built engine; Ubriot builds with Epic's released engines such as 5.8`);
39
+ let cpp = false;
40
+ try {
41
+ cpp = (0, fs_1.lstatSync)((0, path_1.join)(dir, "Source")).isDirectory();
42
+ }
43
+ catch { }
44
+ const attributes = readText((0, path_1.join)(dir, ".gitattributes")) ?? "";
45
+ return {
46
+ uproject: names[0],
47
+ engineVersion: association.trim(),
48
+ kind: cpp ? "cpp" : "blueprint",
49
+ usesLfs: attributes.split("\n").some((line) => !line.trim().startsWith("#") && /\bfilter=lfs\b/.test(line)),
50
+ };
51
+ }
52
+ function unrealAppFields(opts, projectDir) {
53
+ if (!opts.unreal)
54
+ return null;
55
+ const detected = detectUnrealProject(projectDir);
56
+ const engineVersion = opts.engineVersion ?? detected?.engineVersion;
57
+ if (!engineVersion)
58
+ throw new Error(`No .uproject found in ${projectDir}. Run this from the Unreal project folder, or pass --engine-version.`);
59
+ const unrealConfig = { engineVersion };
60
+ if (opts.unrealConfiguration)
61
+ unrealConfig.buildConfiguration = opts.unrealConfiguration;
62
+ if (opts.packageFormat)
63
+ unrealConfig.packageFormat = opts.packageFormat;
64
+ return {
65
+ projectType: "unreal",
66
+ unrealConfig,
67
+ ...(opts.acceptUnrealEula ? { unrealLicenceAccepted: true } : {}),
68
+ };
69
+ }
package/dist/x509.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OID_CODE_SIGNING = void 0;
4
+ exports.buildCertificatePem = buildCertificatePem;
5
+ const crypto_1 = require("crypto");
6
+ const OID_SHA256_WITH_RSA = "1.2.840.113549.1.1.11";
7
+ const OID_COMMON_NAME = "2.5.4.3";
8
+ const OID_BASIC_CONSTRAINTS = "2.5.29.19";
9
+ const OID_KEY_USAGE = "2.5.29.15";
10
+ const OID_EXT_KEY_USAGE = "2.5.29.37";
11
+ const OID_SUBJECT_KEY_ID = "2.5.29.14";
12
+ const OID_AUTHORITY_KEY_ID = "2.5.29.35";
13
+ exports.OID_CODE_SIGNING = "1.3.6.1.5.5.7.3.3";
14
+ function encodeLength(length) {
15
+ if (length < 0x80)
16
+ return Buffer.from([length]);
17
+ const bytes = [];
18
+ let remaining = length;
19
+ while (remaining > 0) {
20
+ bytes.unshift(remaining & 0xff);
21
+ remaining >>= 8;
22
+ }
23
+ return Buffer.from([0x80 | bytes.length, ...bytes]);
24
+ }
25
+ function tlv(tag, content) {
26
+ return Buffer.concat([Buffer.from([tag]), encodeLength(content.length), content]);
27
+ }
28
+ function sequence(...items) {
29
+ return tlv(0x30, Buffer.concat(items));
30
+ }
31
+ function set(...items) {
32
+ return tlv(0x31, Buffer.concat(items));
33
+ }
34
+ function integer(value) {
35
+ let bytes = typeof value === "number" ? Buffer.from([value]) : Buffer.from(value);
36
+ while (bytes.length > 1 && bytes[0] === 0 && (bytes[1] & 0x80) === 0)
37
+ bytes = bytes.subarray(1);
38
+ if (bytes[0] & 0x80)
39
+ bytes = Buffer.concat([Buffer.from([0]), bytes]);
40
+ return tlv(0x02, bytes);
41
+ }
42
+ function boolean(value) {
43
+ return tlv(0x01, Buffer.from([value ? 0xff : 0x00]));
44
+ }
45
+ function oid(value) {
46
+ const parts = value.split(".").map((part) => Number(part));
47
+ const bytes = [parts[0] * 40 + parts[1]];
48
+ for (const part of parts.slice(2)) {
49
+ const chunk = [part & 0x7f];
50
+ let remaining = Math.floor(part / 128);
51
+ while (remaining > 0) {
52
+ chunk.unshift((remaining & 0x7f) | 0x80);
53
+ remaining = Math.floor(remaining / 128);
54
+ }
55
+ bytes.push(...chunk);
56
+ }
57
+ return tlv(0x06, Buffer.from(bytes));
58
+ }
59
+ function nullValue() {
60
+ return Buffer.from([0x05, 0x00]);
61
+ }
62
+ function utf8String(value) {
63
+ return tlv(0x0c, Buffer.from(value, "utf8"));
64
+ }
65
+ function bitString(content, unusedBits = 0) {
66
+ return tlv(0x03, Buffer.concat([Buffer.from([unusedBits]), content]));
67
+ }
68
+ function octetString(content) {
69
+ return tlv(0x04, content);
70
+ }
71
+ function time(date) {
72
+ const pad = (n) => String(n).padStart(2, "0");
73
+ const year = date.getUTCFullYear();
74
+ const rest = `${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}Z`;
75
+ if (year >= 1950 && year < 2050)
76
+ return tlv(0x17, Buffer.from(`${pad(year % 100)}${rest}`, "ascii"));
77
+ return tlv(0x18, Buffer.from(`${year}${rest}`, "ascii"));
78
+ }
79
+ function name(commonName) {
80
+ return sequence(set(sequence(oid(OID_COMMON_NAME), utf8String(commonName))));
81
+ }
82
+ function extension(id, critical, value) {
83
+ return critical ? sequence(oid(id), boolean(true), octetString(value)) : sequence(oid(id), octetString(value));
84
+ }
85
+ function subjectKeyId(spki) {
86
+ return (0, crypto_1.createHash)("sha1").update(spki).digest();
87
+ }
88
+ function keyUsageBits(ca) {
89
+ return ca ? bitString(Buffer.from([0x06]), 1) : bitString(Buffer.from([0x80]), 7);
90
+ }
91
+ function buildCertificatePem(input) {
92
+ if (!(input.notAfter.getTime() > input.notBefore.getTime()))
93
+ throw new Error("notAfter must be later than notBefore");
94
+ const spki = (0, crypto_1.createPublicKey)(input.publicKeyPem).export({ type: "spki", format: "der" });
95
+ const issuerSpki = (0, crypto_1.createPublicKey)(input.issuerPublicKeyPem).export({ type: "spki", format: "der" });
96
+ const serial = (0, crypto_1.randomBytes)(16);
97
+ serial[0] &= 0x7f;
98
+ if (serial[0] === 0)
99
+ serial[0] = 0x01;
100
+ const algorithm = sequence(oid(OID_SHA256_WITH_RSA), nullValue());
101
+ const extensions = [];
102
+ if (input.ca) {
103
+ const constraints = input.pathLength == null ? sequence(boolean(true)) : sequence(boolean(true), integer(input.pathLength));
104
+ extensions.push(extension(OID_BASIC_CONSTRAINTS, true, constraints));
105
+ }
106
+ extensions.push(extension(OID_KEY_USAGE, true, keyUsageBits(input.ca)));
107
+ if (input.codeSigning) {
108
+ extensions.push(extension(OID_EXT_KEY_USAGE, true, sequence(oid(exports.OID_CODE_SIGNING))));
109
+ }
110
+ extensions.push(extension(OID_SUBJECT_KEY_ID, false, octetString(subjectKeyId(spki))));
111
+ extensions.push(extension(OID_AUTHORITY_KEY_ID, false, sequence(tlv(0x80, subjectKeyId(issuerSpki)))));
112
+ const tbs = sequence(tlv(0xa0, integer(2)), integer(serial), algorithm, name(input.issuerCommonName), sequence(time(input.notBefore), time(input.notAfter)), name(input.subjectCommonName), spki, tlv(0xa3, sequence(...extensions)));
113
+ const signature = (0, crypto_1.sign)("sha256", tbs, (0, crypto_1.createPrivateKey)(input.signingPrivateKeyPem));
114
+ const der = sequence(tbs, algorithm, bitString(signature));
115
+ const body = der.toString("base64").match(/.{1,64}/g).join("\n");
116
+ return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----\n`;
117
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ubriot",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Mobile release infrastructure CLI. Build, ship to TestFlight and Google Play, and push OTA updates for iOS and Android from your terminal. React Native and Expo supported.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://ubriot.dev",
@@ -24,10 +24,13 @@
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsc",
27
+ "pretest": "npm run build",
28
+ "test": "node --test test/*.test.mjs",
27
29
  "dev": "ts-node src/index.ts",
28
30
  "prepublishOnly": "npm run build"
29
31
  },
30
32
  "dependencies": {
33
+ "@expo/code-signing-certificates": "^0.0.6",
31
34
  "axios": "^1.7.2",
32
35
  "commander": "^12.1.0",
33
36
  "dotenv": "^16.4.7"