@saltcorn/cli 1.6.0-alpha.1 → 1.6.0-alpha.2

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.
@@ -264,6 +264,12 @@
264
264
  "allowNo": false,
265
265
  "type": "boolean"
266
266
  },
267
+ "syncOnAppResume": {
268
+ "description": "When offline mode is enabled, synchronize the synchedTables tables when the app is resumed.",
269
+ "name": "syncOnAppResume",
270
+ "allowNo": false,
271
+ "type": "boolean"
272
+ },
267
273
  "pushSync": {
268
274
  "description": "When offline mode is enabled, synchronize the synchedTables tables when a push notification is received.",
269
275
  "name": "pushSync",
@@ -277,6 +283,12 @@
277
283
  "multiple": false,
278
284
  "type": "option"
279
285
  },
286
+ "noProvisioningProfile": {
287
+ "description": "Do not use a provisioning profile, only for simulator builds (iOS only)",
288
+ "name": "noProvisioningProfile",
289
+ "allowNo": false,
290
+ "type": "boolean"
291
+ },
280
292
  "provisioningProfile": {
281
293
  "description": "This profile will be used to sign your app",
282
294
  "name": "provisioningProfile",
@@ -298,6 +310,12 @@
298
310
  "multiple": false,
299
311
  "type": "option"
300
312
  },
313
+ "allowClearTextTraffic": {
314
+ "description": "Enable this to allow unsecure HTTP connections. Useful for local testing.",
315
+ "name": "allowClearTextTraffic",
316
+ "allowNo": false,
317
+ "type": "boolean"
318
+ },
301
319
  "androidKeystore": {
302
320
  "description": "A self-signed certificate that includes the private key used to sign your app.",
303
321
  "name": "androidKeystore",
@@ -2127,6 +2145,14 @@
2127
2145
  "hasDynamicHelp": false,
2128
2146
  "multiple": false,
2129
2147
  "type": "option"
2148
+ },
2149
+ "overwriteDependency": {
2150
+ "char": "o",
2151
+ "description": "Dependency to overwrite with a local plugin (can be used multiple times). Please specify the path to the local plugin, the module name will be taken from there.",
2152
+ "name": "overwriteDependency",
2153
+ "hasDynamicHelp": false,
2154
+ "multiple": true,
2155
+ "type": "option"
2130
2156
  }
2131
2157
  },
2132
2158
  "hasDynamicHelp": false,
@@ -2315,5 +2341,5 @@
2315
2341
  ]
2316
2342
  }
2317
2343
  },
2318
- "version": "1.6.0-alpha.1"
2344
+ "version": "1.6.0-alpha.2"
2319
2345
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@saltcorn/cli",
3
3
  "description": "Command-line interface for Saltcorn, open-source no-code platform",
4
4
  "homepage": "https://saltcorn.com",
5
- "version": "1.6.0-alpha.1",
5
+ "version": "1.6.0-alpha.2",
6
6
  "author": "Tom Nielsen @glutamate",
7
7
  "bin": {
8
8
  "saltcorn": "./bin/saltcorn"
@@ -11,13 +11,13 @@
11
11
  "dependencies": {
12
12
  "@oclif/core": "4.4.0",
13
13
  "@oclif/plugin-plugins": "^5.4.26",
14
- "@saltcorn/admin-models": "1.6.0-alpha.1",
15
- "@saltcorn/common-code": "1.6.0-alpha.1",
16
- "@saltcorn/data": "1.6.0-alpha.1",
17
- "@saltcorn/mobile-app": "1.6.0-alpha.1",
18
- "@saltcorn/mobile-builder": "1.6.0-alpha.1",
19
- "@saltcorn/plugins-loader": "1.6.0-alpha.1",
20
- "@saltcorn/server": "1.6.0-alpha.1",
14
+ "@saltcorn/admin-models": "1.6.0-alpha.2",
15
+ "@saltcorn/common-code": "1.6.0-alpha.2",
16
+ "@saltcorn/data": "1.6.0-alpha.2",
17
+ "@saltcorn/mobile-app": "1.6.0-alpha.2",
18
+ "@saltcorn/mobile-builder": "1.6.0-alpha.2",
19
+ "@saltcorn/plugins-loader": "1.6.0-alpha.2",
20
+ "@saltcorn/server": "1.6.0-alpha.2",
21
21
  "contractis": "^0.1.0",
22
22
  "dateformat": "^4.6.3",
23
23
  "inquirer": "^12.3.3",
@@ -2,9 +2,7 @@ const { Command, Flags } = require("@oclif/core");
2
2
  const path = require("path");
3
3
  const Plugin = require("@saltcorn/data/models/plugin");
4
4
  const { MobileBuilder } = require("@saltcorn/mobile-builder/mobile-builder");
5
- const {
6
- decodeProvisioningProfile,
7
- } = require("@saltcorn/mobile-builder/utils/common-build-utils");
5
+ const { decodeProvisioningProfile } = require("@saltcorn/data/utils");
8
6
  const { init_multi_tenant, getState } = require("@saltcorn/data/db/state");
9
7
  const { loadAllPlugins } = require("@saltcorn/server/load_plugins");
10
8
  const User = require("@saltcorn/data/models/user");
@@ -44,7 +42,7 @@ class BuildAppCommand extends Command {
44
42
  );
45
43
  }
46
44
 
47
- if (flags.platforms.includes("ios")) {
45
+ if (flags.platforms.includes("ios") && !flags.noProvisioningProfile) {
48
46
  if (!flags.provisioningProfile)
49
47
  throw new Error("Please specify a provisioning profile");
50
48
  if (flags.allowShareTo && !flags.shareExtensionProvisioningProfile)
@@ -73,26 +71,30 @@ class BuildAppCommand extends Command {
73
71
  async buildIosParams(flags) {
74
72
  let result = undefined;
75
73
  if (flags.platforms.includes("ios")) {
76
- const mainProfileVals = await decodeProvisioningProfile(
77
- flags.buildDirectory,
78
- flags.provisioningProfile
79
- );
80
- result = {
81
- appleTeamId: mainProfileVals.teamId,
82
- mainProvisioningProfile: {
83
- guuid: mainProfileVals.guuid,
84
- },
85
- };
86
- if (flags.allowShareTo) {
87
- const shareExtProfileVals = await decodeProvisioningProfile(
88
- flags.buildDirectory,
89
- flags.shareExtensionProvisioningProfile
74
+ if (flags.noProvisioningProfile)
75
+ result = {
76
+ noProvisioningProfile: true,
77
+ };
78
+ else {
79
+ const mainProfileVals = await decodeProvisioningProfile(
80
+ flags.provisioningProfile
90
81
  );
91
- result.shareExtensionProvisioningProfile = {
92
- guuid: shareExtProfileVals.guuid,
93
- specifier: shareExtProfileVals.specifier,
94
- identifier: shareExtProfileVals.identifier,
82
+ result = {
83
+ appleTeamId: mainProfileVals.teamId,
84
+ mainProvisioningProfile: {
85
+ guuid: mainProfileVals.guuid,
86
+ },
95
87
  };
88
+ if (flags.allowShareTo) {
89
+ const shareExtProfileVals = await decodeProvisioningProfile(
90
+ flags.shareExtensionProvisioningProfile
91
+ );
92
+ result.shareExtensionProvisioningProfile = {
93
+ guuid: shareExtProfileVals.guuid,
94
+ specifier: shareExtProfileVals.specifier,
95
+ identifier: shareExtProfileVals.identifier,
96
+ };
97
+ }
96
98
  }
97
99
  }
98
100
  return result;
@@ -138,6 +140,7 @@ class BuildAppCommand extends Command {
138
140
  showContinueAsPublicUser: flags.showContinueAsPublicUser,
139
141
  allowOfflineMode: flags.allowOfflineMode,
140
142
  syncOnReconnect: flags.syncOnReconnect,
143
+ syncOnAppResume: flags.syncOnAppResume,
141
144
  pushSync: flags.pushSync,
142
145
  syncInterval: flags.syncInterval,
143
146
  allowShareTo: flags.allowShareTo,
@@ -147,6 +150,7 @@ class BuildAppCommand extends Command {
147
150
  iosParams: iosParams,
148
151
  tenantAppName: flags.tenantAppName,
149
152
  buildType: flags.buildType,
153
+ allowClearTextTraffic: flags.allowClearTextTraffic,
150
154
  keyStorePath: flags.androidKeystore,
151
155
  keyStoreAlias: flags.androidKeyStoreAlias,
152
156
  keyStorePassword: flags.androidKeystorePassword,
@@ -324,6 +328,12 @@ BuildAppCommand.flags = {
324
328
  "Run Synchronizations and return into online mode when the network connection is restored. " +
325
329
  "When disabled, you still can do this manually.",
326
330
  }),
331
+ syncOnAppResume: Flags.boolean({
332
+ name: "Sync on app resume",
333
+ string: "syncOnAppResume",
334
+ description:
335
+ "When offline mode is enabled, synchronize the synchedTables tables when the app is resumed.",
336
+ }),
327
337
  pushSync: Flags.boolean({
328
338
  name: "Push sync",
329
339
  string: "pushSync",
@@ -337,6 +347,13 @@ BuildAppCommand.flags = {
337
347
  "Perdiodic interval (in minutes) to run synchronizations in the background. " +
338
348
  "This is just a min interval, depending on system conditions, the actual time may be longer.",
339
349
  }),
350
+ noProvisioningProfile: Flags.boolean({
351
+ name: "no provisioning profile",
352
+ string: "noProvisioningProfile",
353
+ description:
354
+ "Do not use a provisioning profile, only for simulator builds (iOS only)",
355
+ default: false,
356
+ }),
340
357
  provisioningProfile: Flags.string({
341
358
  name: "provisioning profile",
342
359
  string: "provisioningProfile",
@@ -353,6 +370,13 @@ BuildAppCommand.flags = {
353
370
  string: "buildType",
354
371
  description: "debug or release build",
355
372
  }),
373
+ allowClearTextTraffic: Flags.boolean({
374
+ name: "allow clear text traffic",
375
+ string: "allowClearTextTraffic",
376
+ description:
377
+ "Enable this to allow unsecure HTTP connections. Useful for local testing.",
378
+ default: false,
379
+ }),
356
380
  androidKeystore: Flags.string({
357
381
  name: "android key store",
358
382
  string: "androidKeyStore",
@@ -15,7 +15,9 @@ const removePluginsDir = () => {
15
15
  fs.rmSync(pluginsPath, { force: true, recursive: true });
16
16
  };
17
17
 
18
- const writeJestConfigIntoPluginDir = (location) => {
18
+ const writeJestConfigIntoPluginDir = (location, plugin) => {
19
+ const state = getState();
20
+ const module = state.plugins[plugin.name];
19
21
  fs.writeFileSync(
20
22
  path.join(location, "jest.config.js"),
21
23
  `const sqliteDir = process.env.JEST_SC_SQLITE_DIR;
@@ -35,6 +37,17 @@ const config = {
35
37
  "@saltcorn/markup$": markupDir,
36
38
  "@saltcorn/markup/(.*)": markupDir + "/$1",
37
39
  "@saltcorn/admin-models/(.*)": adminModelsDir + "/$1",
40
+ ${(module?.dependencies || [])
41
+ .map((dep) => {
42
+ let pluginLocation = state.plugin_locations[dep];
43
+ if (!pluginLocation) {
44
+ // try without org
45
+ const orgRemoved = dep.replace(/^@[^/]+\//, "");
46
+ pluginLocation = state.plugin_locations[orgRemoved];
47
+ }
48
+ return pluginLocation ? `"${dep}": "${pluginLocation}",` : "";
49
+ })
50
+ .join("\n ")}
38
51
  },
39
52
  modulePaths: [modulePath],
40
53
  };
@@ -77,13 +90,20 @@ const spawnTest = async (installDir, env) => {
77
90
  return ret.status;
78
91
  };
79
92
 
80
- const installPlugin = async (plugin) => {
93
+ const preparePlugin = async (plugin, overwrites) => {
81
94
  await removeOldPlugin(plugin);
82
95
  removePluginsDir();
83
- await loadAndSaveNewPlugin(plugin, false, false);
96
+ await loadAndSaveNewPlugin(
97
+ plugin,
98
+ false,
99
+ false,
100
+ (str) => str,
101
+ false,
102
+ overwrites
103
+ );
84
104
  const location = getPluginLocation(plugin.name);
85
105
  writeMockFilesIntoPluginDir(location);
86
- writeJestConfigIntoPluginDir(location);
106
+ writeJestConfigIntoPluginDir(location, plugin);
87
107
  return location;
88
108
  };
89
109
 
@@ -122,7 +142,7 @@ const getPluginLocation = (pluginName) => {
122
142
  );
123
143
  };
124
144
 
125
- const testLocalPlugin = async (dir, env, backupFile) => {
145
+ const testLocalPlugin = async (dir, env, backupFile, overwrites) => {
126
146
  if (backupFile) {
127
147
  await prep_test_db(path.join(dir, "tests", backupFile));
128
148
  } else await require("@saltcorn/data/db/reset_schema")();
@@ -134,27 +154,49 @@ const testLocalPlugin = async (dir, env, backupFile) => {
134
154
  source: "local",
135
155
  location: path.resolve(dir),
136
156
  });
137
- const installDir = await installPlugin(plugin);
157
+ const installDir = await preparePlugin(plugin, overwrites);
138
158
  return await spawnTest(installDir, env);
139
159
  };
140
160
 
141
- const testReleasedPlugin = async (pluginName, env, backupFile) => {
161
+ const testReleasedPlugin = async (pluginName, env, backupFile, overwrites) => {
142
162
  await require("@saltcorn/data/db/reset_schema")();
143
163
  const plugin = await Plugin.store_by_name(pluginName);
144
164
  delete plugin.id;
145
- const installDir = await installPlugin(plugin);
165
+ const installDir = await preparePlugin(plugin, overwrites);
146
166
  if (backupFile) {
147
167
  await prep_test_db(path.join(installDir, "tests", backupFile));
148
168
  }
149
169
  return await spawnTest(installDir, env);
150
170
  };
151
171
 
172
+ /**
173
+ *
174
+ * @param {string[]} overwrites paths to local plugins
175
+ * @returns an object mapping module names to paths
176
+ */
177
+ const prepOverwritesCfg = (overwrites) => {
178
+ const result = {};
179
+ if (overwrites) {
180
+ for (const flag of overwrites) {
181
+ const pkgpath = path.join(flag, "package.json");
182
+ if (!fs.existsSync(pkgpath))
183
+ throw new Error(
184
+ `Overwrite dependency package.json not found in ${flag}`
185
+ );
186
+ const pkg = require(pkgpath);
187
+ result[pkg.name] = flag;
188
+ }
189
+ }
190
+ return result;
191
+ };
192
+
152
193
  /**
153
194
  * Install a plugin, spawn 'npm run test' in the install dir and check the return code
154
195
  */
155
196
  class PluginTestCommand extends Command {
156
197
  async run() {
157
198
  const { flags } = await this.parse(PluginTestCommand);
199
+ const overwrites = prepOverwritesCfg(flags.overwriteDependency);
158
200
  const dbname = flags.database ? flags.database : "saltcorn_test";
159
201
  let env = null;
160
202
  const db = require("@saltcorn/data/db");
@@ -173,14 +215,16 @@ class PluginTestCommand extends Command {
173
215
  jestStatus = await testLocalPlugin(
174
216
  flags.directory,
175
217
  env,
176
- flags.backupFile
218
+ flags.backupFile,
219
+ overwrites
177
220
  );
178
221
  } else if (flags.name) {
179
222
  console.log(`Testing released plugin '${flags.name}'`);
180
223
  jestStatus = await testReleasedPlugin(
181
224
  flags.name,
182
225
  env,
183
- flags.backupFile
226
+ flags.backupFile,
227
+ overwrites
184
228
  );
185
229
  }
186
230
  } catch (error) {
@@ -217,6 +261,13 @@ PluginTestCommand.flags = {
217
261
  string: "database",
218
262
  description: "Run on specified database. Default is 'saltcorn_test''",
219
263
  }),
264
+ overwriteDependency: Flags.string({
265
+ char: "o",
266
+ description:
267
+ "Dependency to overwrite with a local plugin (can be used multiple times). " +
268
+ "Please specify the path to the local plugin, the module name will be taken from there.",
269
+ multiple: true,
270
+ }),
220
271
  };
221
272
 
222
273
  PluginTestCommand.description =