@upfluxhq/cli 1.1.0 ā 1.3.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/README.md +117 -248
- package/dist/client/apiClient.js +0 -1
- package/dist/client/directUpload.js +25 -3
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +196 -0
- package/dist/commands/login.js +1 -1
- package/dist/commands/release.js +334 -34
- package/dist/index.js +2 -0
- package/dist/utils/native-detector.js +8 -6
- package/package.json +6 -2
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const UPFLUX_JSON_FILE = path_1.default.join(process.cwd(), "upflux.json");
|
|
12
|
+
const APP_JSON_FILE = path_1.default.join(process.cwd(), "app.json");
|
|
13
|
+
const DEFAULT_SERVER_URL = "https://api.upflux.in";
|
|
14
|
+
/**
|
|
15
|
+
* Build the upflux.json content from collected answers
|
|
16
|
+
*/
|
|
17
|
+
function buildUpfluxJson(appId, serverUrl, runtimeVersion, profiles) {
|
|
18
|
+
const deployments = {};
|
|
19
|
+
for (const profile of profiles) {
|
|
20
|
+
const entry = {
|
|
21
|
+
clientKey: profile.clientKey,
|
|
22
|
+
};
|
|
23
|
+
if (profile.extendsProfile) {
|
|
24
|
+
entry.extends = profile.extendsProfile;
|
|
25
|
+
}
|
|
26
|
+
// Set sensible defaults per profile name
|
|
27
|
+
if (profile.name === "development") {
|
|
28
|
+
entry.checkOnLaunch = "always";
|
|
29
|
+
entry.installMode = "on_next_restart";
|
|
30
|
+
}
|
|
31
|
+
else if (profile.name === "production") {
|
|
32
|
+
entry.checkOnLaunch = "always";
|
|
33
|
+
entry.installMode = "immediate";
|
|
34
|
+
}
|
|
35
|
+
deployments[profile.name] = entry;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
version: 1,
|
|
39
|
+
app: Object.assign({ id: appId, buildNumber: 0, runtimeVersion }, (serverUrl !== DEFAULT_SERVER_URL ? { serverUrl } : {})),
|
|
40
|
+
deployments,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const init = new commander_1.Command("init")
|
|
44
|
+
.description("Scaffold upflux.json in your project root")
|
|
45
|
+
.option("--force", "Overwrite existing upflux.json")
|
|
46
|
+
.action(async (opts) => {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
console.log(chalk_1.default.bold("\nš Upflux Project Setup\n"));
|
|
49
|
+
// Detect Expo project
|
|
50
|
+
let isExpo = false;
|
|
51
|
+
let expoAppJson = null;
|
|
52
|
+
if (fs_1.default.existsSync(APP_JSON_FILE)) {
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(APP_JSON_FILE, "utf-8"));
|
|
55
|
+
if (parsed && parsed.expo) {
|
|
56
|
+
isExpo = true;
|
|
57
|
+
expoAppJson = parsed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (_c) {
|
|
61
|
+
// Ignore parse errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Check if upflux.json already exists
|
|
65
|
+
if (fs_1.default.existsSync(UPFLUX_JSON_FILE) && !opts.force) {
|
|
66
|
+
console.log(chalk_1.default.yellow("ā ļø upflux.json already exists."));
|
|
67
|
+
const { overwrite } = await inquirer_1.default.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: "confirm",
|
|
70
|
+
name: "overwrite",
|
|
71
|
+
message: "Overwrite it?",
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
if (!overwrite) {
|
|
76
|
+
console.log(chalk_1.default.gray("Aborted."));
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Step 1: App ID
|
|
81
|
+
const { appId } = await inquirer_1.default.prompt([
|
|
82
|
+
{
|
|
83
|
+
type: "input",
|
|
84
|
+
name: "appId",
|
|
85
|
+
message: "App ID:",
|
|
86
|
+
validate: (val) => val.trim() ? true : "App ID is required",
|
|
87
|
+
},
|
|
88
|
+
]);
|
|
89
|
+
// Step 2: Server URL
|
|
90
|
+
const { serverUrl } = await inquirer_1.default.prompt([
|
|
91
|
+
{
|
|
92
|
+
type: "input",
|
|
93
|
+
name: "serverUrl",
|
|
94
|
+
message: `Server URL (press Enter for ${DEFAULT_SERVER_URL}):`,
|
|
95
|
+
default: DEFAULT_SERVER_URL,
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
// Step 3: Runtime version (semver, used to form auto release labels)
|
|
99
|
+
const defaultRuntimeVersion = (isExpo && ((_a = expoAppJson === null || expoAppJson === void 0 ? void 0 : expoAppJson.expo) === null || _a === void 0 ? void 0 : _a.version))
|
|
100
|
+
? expoAppJson.expo.version
|
|
101
|
+
: "1.0.0";
|
|
102
|
+
const { runtimeVersion } = await inquirer_1.default.prompt([
|
|
103
|
+
{
|
|
104
|
+
type: "input",
|
|
105
|
+
name: "runtimeVersion",
|
|
106
|
+
message: "Runtime version (e.g. 1.0.0 ā update when shipping a new binary):",
|
|
107
|
+
default: defaultRuntimeVersion,
|
|
108
|
+
validate: (val) => {
|
|
109
|
+
if (!val.trim())
|
|
110
|
+
return "Runtime version is required";
|
|
111
|
+
if (!/^\d+\.\d+\.\d+/.test(val.trim()))
|
|
112
|
+
return "Use semver format, e.g. 1.0.0";
|
|
113
|
+
return true;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
const { selectedProfiles } = await inquirer_1.default.prompt([
|
|
118
|
+
{
|
|
119
|
+
type: "checkbox",
|
|
120
|
+
name: "selectedProfiles",
|
|
121
|
+
message: "Deployment profiles to create:",
|
|
122
|
+
choices: [
|
|
123
|
+
{ name: "production", checked: true },
|
|
124
|
+
{ name: "staging", checked: true },
|
|
125
|
+
{ name: "development", checked: true },
|
|
126
|
+
],
|
|
127
|
+
validate: (selected) => selected.length > 0 ? true : "Select at least one profile",
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
// Step 4: Client keys per profile
|
|
131
|
+
const profiles = [];
|
|
132
|
+
for (const profileName of selectedProfiles) {
|
|
133
|
+
const { clientKey } = await inquirer_1.default.prompt([
|
|
134
|
+
{
|
|
135
|
+
type: "input",
|
|
136
|
+
name: "clientKey",
|
|
137
|
+
message: `Client key for "${chalk_1.default.cyan(profileName)}" ${chalk_1.default.gray("(upx_cli_xxxxx)")}:`,
|
|
138
|
+
validate: (val) => {
|
|
139
|
+
if (!val.trim())
|
|
140
|
+
return "Client key is required";
|
|
141
|
+
if (val.trim().length < 10)
|
|
142
|
+
return "Client key looks too short";
|
|
143
|
+
return true;
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
profiles.push({ name: profileName, clientKey: clientKey.trim() });
|
|
148
|
+
}
|
|
149
|
+
// Build and write upflux.json
|
|
150
|
+
const content = buildUpfluxJson(appId.trim(), serverUrl.trim(), runtimeVersion.trim(), profiles);
|
|
151
|
+
fs_1.default.writeFileSync(UPFLUX_JSON_FILE, JSON.stringify(content, null, 2));
|
|
152
|
+
console.log(chalk_1.default.green("\nā Created upflux.json\n"));
|
|
153
|
+
// Show what was created
|
|
154
|
+
console.log(chalk_1.default.gray("Preview:"));
|
|
155
|
+
console.log(chalk_1.default.gray(JSON.stringify(content, null, 2)));
|
|
156
|
+
if (isExpo && expoAppJson) {
|
|
157
|
+
const primaryProfile = profiles.find(p => p.name === "production") || profiles[0];
|
|
158
|
+
if (primaryProfile) {
|
|
159
|
+
const { updateAppJson } = await inquirer_1.default.prompt([
|
|
160
|
+
{
|
|
161
|
+
type: "confirm",
|
|
162
|
+
name: "updateAppJson",
|
|
163
|
+
message: `Configure app.json with Expo Updates URL and client key for "${primaryProfile.name}"?`,
|
|
164
|
+
default: true,
|
|
165
|
+
},
|
|
166
|
+
]);
|
|
167
|
+
if (updateAppJson) {
|
|
168
|
+
expoAppJson.expo = expoAppJson.expo || {};
|
|
169
|
+
expoAppJson.expo.runtimeVersion = runtimeVersion.trim();
|
|
170
|
+
expoAppJson.expo.updates = Object.assign(Object.assign({}, (expoAppJson.expo.updates || {})), { url: `${serverUrl.trim()}/api/updates/check`, requestHeaders: Object.assign(Object.assign({}, (((_b = expoAppJson.expo.updates) === null || _b === void 0 ? void 0 : _b.requestHeaders) || {})), { "x-client-key": primaryProfile.clientKey.trim() }) });
|
|
171
|
+
fs_1.default.writeFileSync(APP_JSON_FILE, JSON.stringify(expoAppJson, null, 2));
|
|
172
|
+
console.log(chalk_1.default.green("ā Updated app.json with Expo Updates config\n"));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
console.log(chalk_1.default.bold("\nš± Expo Usage:\n"));
|
|
176
|
+
console.log(chalk_1.default.white(` 1. Install expo-updates:\n` +
|
|
177
|
+
` npx expo install expo-updates\n\n` +
|
|
178
|
+
` 2. Publish a release:\n` +
|
|
179
|
+
` upflux release\n`));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Usage hint
|
|
183
|
+
console.log(chalk_1.default.bold("\nš¦ SDK Usage:\n"));
|
|
184
|
+
console.log(chalk_1.default.white(` import { Upflux } from '@upfluxhq/react-native';\n\n` +
|
|
185
|
+
` // Auto-selects 'development' in dev, 'production' in release\n` +
|
|
186
|
+
` // Auto-increments buildNumber on every release\n` +
|
|
187
|
+
` await Upflux.init();\n\n` +
|
|
188
|
+
` // Or specify a profile:\n` +
|
|
189
|
+
` await Upflux.init('production');`));
|
|
190
|
+
}
|
|
191
|
+
// Security note about committing the file
|
|
192
|
+
console.log(chalk_1.default.cyan("\nš” Tip: upflux.json is safe to commit to git.\n" +
|
|
193
|
+
" upx_cli_* keys are read-only ā they cannot publish releases.\n" +
|
|
194
|
+
" Only upx_pub_* publish keys should be kept secret.\n"));
|
|
195
|
+
});
|
|
196
|
+
exports.default = init;
|
package/dist/commands/login.js
CHANGED
|
@@ -21,7 +21,7 @@ function validateKeyFormat(key) {
|
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
// Check for common publish key prefixes
|
|
24
|
-
const validPrefixes = ["upflux_pub_", "upflux_cli_", "pk_"];
|
|
24
|
+
const validPrefixes = ["UPX-PUB-", "UPX-CLT-", "upx_pub_", "upx_cli_", "upflux_pub_", "upflux_cli_", "pk_"];
|
|
25
25
|
const hasValidPrefix = validPrefixes.some(prefix => key.startsWith(prefix));
|
|
26
26
|
// Also allow generic keys that are alphanumeric with underscores
|
|
27
27
|
const isAlphanumeric = /^[a-zA-Z0-9_-]+$/.test(key);
|